diff --git a/imcsdk/apis/v2/__init__.py b/imcsdk/apis/v2/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/imcsdk/apis/v2/admin/__init__.py b/imcsdk/apis/v2/admin/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/imcsdk/apis/v2/admin/certificate.py b/imcsdk/apis/v2/admin/certificate.py new file mode 100644 index 00000000..53243ea8 --- /dev/null +++ b/imcsdk/apis/v2/admin/certificate.py @@ -0,0 +1,432 @@ +# Copyright 2016 Cisco Systems, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +""" +This module provides apis for certificate related functionality +""" + +country_codes = { + "Albania": "AL", + "Algeria": "DZ", + "American Samoa": "AS", + "Andorra": "AD", + "Angola": "AO", + "Anguilla": "AI", + "Antarctica": "AQ", + "Antigua and Barbuda": "AG", + "Argentina": "AR", + "Armenia": "AM", + "Aruba": "AW", + "Australia": "AU", + "Austria": "AT", + "Azerbaijan": "AZ", + "Bahamas": "BS", + "Bahrain": "BH", + "Bangladesh": "BD", + "Barbados": "BB", + "Belarus": "BY", + "Belgium": "BE", + "Belize": "BZ", + "Benin": "BJ", + "Bermuda": "BM", + "Bhutan": "BT", + "Bolivia": "BO", + "Bosnia and Herzegovina": "BA", + "Botswana": "BW", + "Bouvet Island": "BV", + "Brazil": "BR", + "British Indian Ocean Territory": "IO", + "Brunei Darussalam": "BN", + "Bulgaria": "BG", + "Burkina Faso": "BF", + "Burundi": "BI", + "Cambodia": "KH", + "Cameroon": "CM", + "Canada": "CA", + "Cape Verde": "CV", + "Cayman Islands": "KY", + "Central African Republic": "CF", + "Chad": "TD", + "Chile": "CL", + "China": "CN", + "Christmas Island": "CX", + "Cocos (Keeling) Islands": "CC", + "Colombia": "CO", + "Comoros": "KM", + "Congo": "CD", + "Cook Islands": "CK", + "Costa Rica": "CR", + "Cote D'Ivoire (Ivory Coast)": "CI", + "Croatia (Hrvatska)": "HR", + "Cuba": "CU", + "Cyprus": "CY", + "Czech Republic": "CZ", + "Czechoslovakia": "CZ", + "Denmark": "DK", + "Djibouti": "DJ", + "Dominica": "DM", + "Dominican Republic": "DO", + "East Timor": "TL", + "Ecuador": "EC", + "Egypt": "EG", + "El Salvador": "SV", + "Equatorial Guinea": "GQ", + "Eritrea": "ER", + "Estonia": "EE", + "Ethiopia": "ET", + "Falkland Islands (Malvinas)": "FK", + "Faroe Islands": "FO", + "Fiji": "FJ", + "Finland": "FI", + "France": "FR", + "France, Metropolitan": "FX", + "French Guiana": "GF", + "French Polynesia": "PF", + "French Southern Territories": "TF", + "Gabon": "GA", + "Gambia": "GM", + "Georgia": "GE", + "Germany": "DE", + "Ghana": "GH", + "Gibraltar": "GI", + "Great Britain (UK)": "UK", + "Greece": "GR", + "Greenland": "GL", + "Grenada": "GD", + "Guadeloupe": "GP", + "Guam": "GU", + "Guatemala": "GT", + "Guinea": "GN", + "Guinea-Bissau": "GW", + "Guyana": "GY", + "Haiti": "HT", + "Heard and McDonald Islands": "HM", + "Honduras": "HN", + "Hong Kong": "HK", + "Hungary": "HU", + "Iceland": "IS", + "India": "IN", + "Indonesia": "ID", + "Iran": "IR", + "Iraq": "IQ", + "Ireland": "IE", + "Israel": "IL", + "Italy": "IT", + "Jamaica": "JM", + "Japan": "JP", + "Jordan": "JO", + "Kazakhstan": "KZ", + "Kenya": "KE", + "Kiribati": "KI", + "Korea (North)": "KP", + "Korea (South)": "KR", + "Kuwait": "KW", + "Kyrgyzstan": "KG", + "Laos": "LA", + "Latvia": "LV", + "Lebanon": "LB", + "Lesotho": "LS", + "Liberia": "LR", + "Libya": "LY", + "Liechtenstein": "LI", + "Lithuania": "LT", + "Luxembourg": "LU", + "Macau": "MO", + "Macedonia": "MK", + "Madagascar": "MG", + "Malawi": "MW", + "Malaysia": "MY", + "Maldives": "MV", + "Mali": "ML", + "Malta": "MT", + "Marshall Islands": "MH", + "Martinique": "MQ", + "Mauritania": "MR", + "Mauritius": "MU", + "Mayotte": "YT", + "Mexico": "MX", + "Micronesia": "FM", + "Moldova": "MD", + "Monaco": "MC", + "Mongolia": "MN", + "Montserrat": "MS", + "Morocco": "MA", + "Mozambique": "MZ", + "Myanmar": "MM", + "Namibia": "NA", + "Nauru": "NR", + "Nepal": "NP", + "Netherlands": "NL", + "Netherlands Antilles": "AN", + "Neutral Zone": "NT", + "New Caledonia": "NC", + "New Zealand (Aotearoa)": "NZ", + "Nicaragua": "NI", + "Niger": "NE", + "Nigeria": "NG", + "Niue": "NU", + "Norfolk Island": "NF", + "Northern Mariana Islands": "MP", + "Norway": "NO", + "Oman": "OM", + "Pakistan": "PK", + "Palau": "PW", + "Panama": "PA", + "Papua New Guinea": "PG", + "Paraguay": "PY", + "Peru": "PE", + "Philippines": "PH", + "Pitcairn": "PN", + "Poland": "PL", + "Portugal": "PT", + "Puerto Rico": "PR", + "Qatar": "QA", + "Reunion": "RE", + "Romania": "RO", + "Russian Federation": "RU", + "Rwanda": "RW", + "S. Georgia and S. Sandwich Isls.": "GS", + "Saint Kitts and Nevis": "KN", + "Saint Lucia": "LC", + "Saint Vincent and the Grenadines": "VC", + "Samoa": "WS", + "San Marino": "SM", + "Sao Tome and Principe": "ST", + "Saudi Arabia": "SA", + "Senegal": "SN", + "Seychelles": "SC", + "Sierra Leone": "SL", + "Singapore": "SG", + "Slovak Republic": "SK", + "Slovenia": "SI", + "Solomon Islands": "SB", + "Somalia": "SO", + "South Africa": "ZA", + "Spain": "ES", + "Sri Lanka": "LK", + "St. Helena": "SH", + "St. Pierre and Miquelon": "PM", + "Sudan": "SD", + "Suriname": "SR", + "Svalbard and Jan Mayen Islands": "SJ", + "Swaziland": "SZ", + "Sweden": "SE", + "Switzerland": "CH", + "Syria": "SY", + "Taiwan": "TW", + "Tajikistan": "TJ", + "Tanzania": "TZ", + "Thailand": "TH", + "Togo": "TG", + "Tokelau": "TK", + "Tonga": "TO", + "Trinidad and Tobago": "TT", + "Tunisia": "TN", + "Turkey": "TR", + "Turkmenistan": "TM", + "Turks and Caicos Islands": "TC", + "Tuvalu": "TV", + "US Minor Outlying Islands": "UM", + "USSR (former)": "SU", + "Uganda": "UG", + "Ukraine": "UA", + "United Arab Emirates": "AE", + "United Kingdom": "GB", + "United States": "US", + "Uruguay": "UY", + "Uzbekistan": "UZ", + "Vanuatu": "VU", + "Vatican City State (Holy See)": "VA", + "Venezuela": "VE", + "Viet Nam": "VN", + "Virgin Islands (British)": "VG", + "Virgin Islands (U.S.)": "VI", + "Wallis and Futuna Islands": "WF", + "Western Sahara": "EH", + "Yemen": "YE", + "Yugoslavia": "YU", + "Zaire": "ZR", + "Zambia": "ZM", + "Zimbabwe": "ZW", +} + + +def current_certificate_get(handle): + """ + This api gets the current certificate installed on the system + + Args: + handle (ImcHandle) + + Returns: + CurrentCertificate object + """ + + cert_mo = handle.query_classid("CurrentCertificate") + return cert_mo[0] + + +def certificate_signing_request_generate(handle, name, org, locality, state, + country, org_unit=None, email=None, + server=None, + username=None, password=None, + file_name=None, protocol=None, + self_signed=False): + """ + This api generates a new certificate signing request + + Args: + handle (ImcHandle) + name (string): Name for the Certificate + org (string): Organization + locality (string): Locality + state (string): State + country (string): COUNTRY_CODE_* from \ + GenerateCertificateSigningRequestConsts + org_unit (string): Organizational Unit + email (string): Email + server (string): ip address of the remote server + username (string): username + password (string): password + file_name (string): filename for the certificate file + protocol (string): "ftp", "http", "none", "scp", "sftp", "tftp" + self_signed (bool): if self-signed, + (user, password, server, file, protocol) not required + + Returns: + None + + Examples: + certificate_signing_request_generate( + handle, name="test-cert", org="test-org", org_unit="test-unit", + country=GenerateCertificateSigningRequestConsts.COUNTRY_CODE_UNITED_STATES, + state="California", locality="San Francisco", self_signed=True) + """ + + from imcsdk.mometa.generate.GenerateCertificateSigningRequest import \ + GenerateCertificateSigningRequest + + mo = GenerateCertificateSigningRequest(parent_mo_or_dn="sys/cert-mgmt") + + params = { + "common_name": name, + "organization": org, + "locality": locality, + "state": state, + "country_code": country, + "organizational_unit": org_unit, + "email": email + } + + if self_signed: + params["self_signed"] = "yes" + else: + params["self_signed"] = "no" + params["remote_server"] = server + params["user"] = username + params["pwd"] = password + params["remote_file"] = file_name + params["protocol"] = protocol + + mo.set_prop_multiple(**params) + handle.add_mo(mo, modify_present=True) + + +def certificate_exists(handle, **kwargs): + """ + checks if certificate exists. + + Args: + handle (ImcHandle) + kwargs: Key-Value paired arguments relevant to CurrentCertificate mo + + Returns: + True, CurrentCertificate MO if found, else False, None + + Example: + certificate_exists( + handle, name="test-cert", org="test-org", org_unit="test-unit", + country=GenerateCertificateSigningRequestConsts.COUNTRY_CODE_UNITED_STATES, + state="California", locality="San Francisco") + """ + return False, None + # TBD: How to handle self-signed vs ca-signed certificate? + # current_certificate = current_certificate_get(handle) + # if current_certificate is None: + # return False, None + + # params = { + # "common_name": kwargs['name'], + # "organization": kwargs['org'], + # "locality": kwargs['locality'], + # "state": kwargs['state'], + # "country_code": country_codes[kwargs['country']] + # } + + # if 'org_unit' in kwargs and kwargs['org_unit']: + # params['organizational_unit'] = kwargs['org_unit'] + + # if current_certificate.check_prop_match(**params): + # return True, current_certificate + # return False, None + + +def certificate_signing_status_get(handle): + """ + This api checks the status of the certificate generation request + submitted previously + + Args: + handle (Imchandle) + + Returns: + Status of the certificate generation activity (string) + """ + + mo = handle.query_classid("GenerateCertificateSigningRequest") + return mo[0].csr_status if mo else "" + + +def certificate_upload(handle, username, password, server, file_name, protocol): + """ + This api uploads the certificate generated using \ + certificate_signing_request_generate + + Args: + handle (ImcHandle) + username (string): username + password (string): password + server (string): ip address of the remote server + file_name (string): full path to the certificate file + protocol (string): "ftp", "http", "none", "scp", "sftp", "tftp" + + Returns: + None + """ + + from imcsdk.mometa.upload.UploadCertificate import UploadCertificate, \ + UploadCertificateConsts + + mo = UploadCertificate(parent_mo_or_dn="sys/cert-mgmt") + params = { + "admin_action": UploadCertificateConsts.ADMIN_ACTION_REMOTE_CERT_UPLOAD, + "user": username, + "pwd": password, + "remote_server": server, + "remote_file": file_name, + "protocol": protocol, + } + + mo.set_prop_multiple(**params) + handle.add_mo(mo, modify_present=True) diff --git a/imcsdk/apis/v2/admin/ip.py b/imcsdk/apis/v2/admin/ip.py new file mode 100644 index 00000000..c3dcd968 --- /dev/null +++ b/imcsdk/apis/v2/admin/ip.py @@ -0,0 +1,330 @@ +# Copyright 2016 Cisco Systems, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +""" +This module implements the APIs for IP Blocking and IP Filtering +""" +import logging +from imcsdk.mometa.ip.IpBlocking import IpBlocking +from imcsdk.mometa.ip.IpFiltering import IpFiltering, IpFilteringConsts +from imcsdk.apis.utils import _get_mo, _is_valid_arg, _is_invalid_value +from imcsdk.imccoreutils import get_server_dn, IMC_PLATFORM +from imcsdk.imcexception import ImcOperationError + +log = logging.getLogger('imc') + + +def _get_mgmt_if_dn(handle, id=1): + from imcsdk.mometa.mgmt.MgmtIf import MgmtIf + + if handle.platform == IMC_PLATFORM.TYPE_CLASSIC: + parent_dn = get_server_dn(handle) + '/mgmt' + elif handle.platform == IMC_PLATFORM.TYPE_MODULAR: + parent_dn = 'sys/chassis-1' + + mo = MgmtIf(parent_mo_or_dn=parent_dn) + return mo.dn + + +def ip_blocking_enable(handle, fail_count='5', fail_window='60', + penalty_time='300', **kwargs): + """ + Enables IP Blocking + + Args: + handle (ImcHandle) + fail_count (str): Number of times a user can attempt to log in + unsuccessfully, before the system locks that user out + Range [3-10] attempts + fail_window (str): Length of time, in seconds, in which the + unsuccessful login attempts must occur in order + for the user to be locked out. + Range [60-120] seconds + penalty_time (str): The number of seconds the user remains locked out. + Range [300-900] seconds + kwargs: Key-Value paired arguments for future use + + Returns: + IpBlocking object + + Examples: + ip_blocking_enable(handle, fail_count='6', + fail_window='120', penalty_time='800') + """ + + mo = IpBlocking(parent_mo_or_dn=_get_mgmt_if_dn(handle)) + + params = { + 'enable': 'yes', + 'fail_count': str(fail_count), + 'fail_window': str(fail_window), + 'penalty_time': str(penalty_time) + } + mo.set_prop_multiple(**params) + mo.set_prop_multiple(**kwargs) + handle.set_mo(mo) + return handle.query_dn(mo.dn) + + +def ip_blocking_disable(handle): + """ + Disables IP Blocking + + Args: + handle (ImcHandle) + + Returns: + None + + Examples: + ip_blocking_disable(handle) + """ + mo = IpBlocking(parent_mo_or_dn=_get_mgmt_if_dn(handle)) + mo.enable = 'no' + handle.set_mo(mo) + + +def is_ip_blocking_enabled(handle): + """ + Checks if IP Blocking is enabled + + Args: + handle (ImcHandle) + + Returns: + bool + + Examples: + is_ip_blocking_enabled(handle) + """ + mo = IpBlocking(parent_mo_or_dn=_get_mgmt_if_dn(handle)) + mo = handle.query_dn(mo.dn) + return mo.enable.lower() in ['yes', 'true'] + + +def ip_blocking_exists(handle, **kwargs): + """ + Checks if IP blocking settings match according to the parameters specified. + + Args: + handle (ImcHandle) + kwargs: Key-Value paired arguments relevant to IpBlocking object + + Returns: + (True, IpBlocking object) if exists, else (False, None) + + Examples: + ip_blocking_exists(handle, fail_count='6', + fail_window='120', penalty_time='800') + """ + mo = IpBlocking(parent_mo_or_dn=_get_mgmt_if_dn(handle)) + mo = _get_mo(handle, dn=mo.dn) + + if mo.check_prop_match(**kwargs): + return (True, mo) + + return (False, None) + + +_IP_FILTER_LIST = ['filter1', 'filter2', 'filter3', 'filter4'] + + +def _set_ip_filters(mo, filters): + if len(filters) > len(_IP_FILTER_LIST): + raise ImcOperationError("Set Ip Filters", + "Cannot specify more than %d filters" % + len(_IP_FILTER_LIST)) + + params = {'filter' + str(x['id']): str(x['filter']) for x in filters} + mo.set_prop_multiple(**params) + + +def ip_filtering_enable(handle, filters=[]): + """ + Enables IP filtering with the filters specified + Connection may drop during this activity. + + Args: + handle (ImcHandle) + filters (list): List of dictionaries of the format + {'id': 1, 'filter': "10.10.10.10", + 'id': 2, 'filter': "2.2.2.2-3.3.3.3"} + + Returns: + IpFiltering object + + Examples: + ip_filtering_enable(handle, filters=[{"id": 1, "filter": "1.1.1.0-255.255.255.255"}, + {"id": 2, "filter": "2.2.2.2"}]) + """ + log.warning('Changes to IP Filtering will be applied immediately. ' + 'Connectivity to Cisco IMC will be lost and a re-login is required.') + mo = IpFiltering(parent_mo_or_dn=_get_mgmt_if_dn(handle)) + mo.enable = 'yes' + _set_ip_filters(mo, filters) + + handle.set_mo(mo) + # Not returning the server copy as the connection drops after the above action + return mo + + +def ip_filtering_disable(handle): + """ + Disables IP filtering + + Args: + handle (ImcHandle) + + Returns: + None + + Examples: + ip_filtering_disable(handle) + """ + mo = IpFiltering(parent_mo_or_dn=_get_mgmt_if_dn(handle)) + mo.enable = 'no' + handle.set_mo(mo) + + +def is_ip_filtering_enabled(handle): + """ + Checks if IP Filtering is enabled + + Args: + handle (ImcHandle) + + Returns: + bool + + Examples: + is_ip_filtering_enabled(handle) + """ + mo = IpFiltering(parent_mo_or_dn=_get_mgmt_if_dn(handle)) + mo = handle.query_dn(mo.dn) + return mo.enable.lower() in ['yes', 'true'] + + +def ip_filtering_modify(handle, filters=[]): + """ + Modifies IP filtering with the filters specified. + Connection may drop during this activity. + + Args: + handle (ImcHandle) + filters (list): List of dictionaries of the format + [{'id': 1, 'filter': '10.10.10.10'}, + {'id': 2, 'filter': '2.2.2.2-3.3.3.3'}] + + Returns: + IpFiltering object + + Examples: + ip_filtering_modify(handle, filters=[{'id': 1, 'filter': '10.10.10.10'}]) + """ + log.warning('Changes to IP Filtering will be applied immediately. ' + 'Connectivity to Cisco IMC will be lost and a re-login is required.') + mo = IpFiltering(parent_mo_or_dn=_get_mgmt_if_dn(handle)) + _set_ip_filters(mo, filters) + + handle.set_mo(mo) + # Not returning the server copy as the connection drops after the above action + return mo + + +def ip_filtering_clear(handle, filter_id=''): + """ + Clears the IP filters specified by the input. + Connection may drop during this activity. + + Args: + handle (ImcHandle) + filter_id (str): String representing the filter id + + Returns: + IpFiltering object + + Examples: + ip_filtering_clear(handle, filter_id='3') + ip_filtering_clear(handle, filter_id='all') + """ + log.warning('Changes to IP Filtering will be applied immediately. ' + 'Connectivity to Cisco IMC maybe lost based on the filter being cleared') + mo = IpFiltering(parent_mo_or_dn=_get_mgmt_if_dn(handle)) + + if filter_id == 'all': + mo.admin_action = IpFilteringConsts.ADMIN_ACTION_CLEAR_ALL + handle.set_mo(mo) + # Not returning the server copy as the connection drops after the above action + return mo + + if not filter_id.isdigit(): + raise ImcOperationError("Clear Ip Filter", + "Filter-id must be a digit(1-%d) or 'all'" % + len(_IP_FILTER_LIST)) + + if int(filter_id) > len(_IP_FILTER_LIST): + raise ImcOperationError("Clear Ip Filter", + "Filter-id cannot be more than %d" % + len(_IP_FILTER_LIST)) + + mo.admin_action = 'clearFilter' + filter_id + handle.set_mo(mo) + # Not returning the server copy as the connection drops after the above action + return mo + + +def _check_ip_filter_match(ip_mo, mo): + for prop in _IP_FILTER_LIST: + configured_value = getattr(ip_mo, prop) + in_value = getattr(mo, prop) + if _is_invalid_value(configured_value) and \ + _is_invalid_value(in_value): + continue + if configured_value != in_value: + return False + + return True + + +def ip_filtering_exists(handle, **kwargs): + """ + Checks if IP filtering settings match according to the parameters specified. + + Args: + Args: + handle (ImcHandle) + kwargs: Key-Value paired arguments relevant to IpFiltering object + + Returns: + (True, IpFiltering object) if exists, else (False, None) + + Examples: + ip_filtering_exists(handle, enable='yes', + filters=[{"id": 1, "filter": "1.1.1.0-255.255.255.255"}, + {"id": 2, "filter": "2.2.2.2"}]) + """ + ip_mo = IpFiltering(parent_mo_or_dn=_get_mgmt_if_dn(handle)) + ip_mo = handle.query_dn(ip_mo.dn) + + if _is_valid_arg('enable', kwargs): + if ip_mo.enable.lower() != kwargs.get('enable').lower(): + return False, None + + if _is_valid_arg('filters', kwargs): + mo = IpFiltering(parent_mo_or_dn=_get_mgmt_if_dn(handle)) + _set_ip_filters(mo, kwargs.get('filters')) + if not _check_ip_filter_match(ip_mo, mo): + return False, None + + return True, ip_mo diff --git a/imcsdk/apis/v2/admin/ipmi.py b/imcsdk/apis/v2/admin/ipmi.py new file mode 100644 index 00000000..84981975 --- /dev/null +++ b/imcsdk/apis/v2/admin/ipmi.py @@ -0,0 +1,116 @@ +# Copyright 2016 Cisco Systems, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +""" +This module implements all the communication services +""" +from imcsdk.mometa.comm.CommIpmiLan import CommIpmiLan +from imcsdk.imccoreutils import get_server_dn, IMC_PLATFORM + + +def _get_comm_mo_dn(handle, server_id=1): + """ + Internal method to get the IPMI mo's parent_dn based \ + on the type of platform + """ + from imcsdk.imcexception import ImcValidationException + + if handle.platform == IMC_PLATFORM.TYPE_CLASSIC: + return("sys/svc-ext") + elif handle.platform == IMC_PLATFORM.TYPE_MODULAR: + return(get_server_dn(handle, server_id) + "/svc-ext") + else: + raise ImcValidationException("Invalid platform detected:%s" % + handle.platform) + + +def ipmi_enable(handle, priv=None, key=None, server_id=1): + """ + Enable IPMI over LAN. + + Args: + handle (ImcHandle) + priv (string): Optional privilege level: 'admin', 'user', 'read-only' + key (string): Optional encryption key as hexadecimal string + server_id (int): Server Id to be specified for C3260 platforms + + Returns: + CommIpmiLan object + + Raises: + ValueError if privilege or key are invalid + + Example: + if ipmi_enable(handle): + print "IPMI Enabled" + """ + + # Verify key is a hex number + try: + if key: + hex(int(key, 16))[2:] + except ValueError: + raise ValueError('{0}: ERROR: Encryption key is not hex number: ' + + '"{1}"'.format(handle.ip, key)) + + # Create enabled IPMI object + mo = CommIpmiLan(parent_mo_or_dn=_get_comm_mo_dn(handle, server_id)) + mo.admin_state = "enabled" + mo.priv = priv + mo.key = key + + # Configure IPMI object on CIMC + handle.set_mo(mo) + return mo + + +def ipmi_disable(handle, server_id=1): + """ + Disable IPMI over LAN. + Args: + handle (ImcHandle) + server_id (int): Server Id to be specified for C3260 platforms + + Returns: + CommIpmiLan object + """ + + # Create disabled IPMI object + mo = CommIpmiLan(parent_mo_or_dn=_get_comm_mo_dn(handle, server_id)) + mo.admin_state = "disabled" + + # Configure IPMI object on CIMC + handle.set_mo(mo) + return mo + + +def ipmi_exists(handle, server_id=1, **kwargs): + """ + Check if IPMI over LAN is enabled + Args: + handle (ImcHandle) + server_id (int): Server Id to be specified for C3260 platforms + + Returns: + True/False, MO/None + """ + + mo = CommIpmiLan(parent_mo_or_dn=_get_comm_mo_dn(handle, server_id)) + mo = handle.query_dn(mo.dn) + if mo is None: + return False, None + + kwargs['admin_state'] = "enabled" + mo_exists = mo.check_prop_match(**kwargs) + return (mo_exists, mo) diff --git a/imcsdk/apis/v2/admin/ldap.py b/imcsdk/apis/v2/admin/ldap.py new file mode 100644 index 00000000..4ace1644 --- /dev/null +++ b/imcsdk/apis/v2/admin/ldap.py @@ -0,0 +1,759 @@ +# Copyright 2016 Cisco Systems, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +""" +This module implements apis to configure ldap +""" + +import logging +from imcsdk.imcexception import ImcOperationError +from imcsdk.imcexception import ImcOperationErrorDetail +from imcsdk.imccoreutils import process_conf_mos_response +from imcsdk.imccoreutils import ConfigConfMosConstants as ccmconst +from imcsdk.imccoreutils import sanitize_message +from imcsdk.apis.utils import _get_mo, _is_valid_arg, _is_invalid_value,\ + _validate_api_prop +from imcsdk.mometa.aaa.AaaLdapRoleGroup import AaaLdapRoleGroup + +log = logging.getLogger('imc') + +_LDAP_SERVERS = ['ldap_server1', 'ldap_server2', 'ldap_server3', + 'ldap_server4', 'ldap_server5', 'ldap_server6'] +_LDAP_SERVER_PORTS = ['ldap_server_port1', 'ldap_server_port2', + 'ldap_server_port3', 'ldap_server_port4', + 'ldap_server_port5', 'ldap_server_port6'] +LDAP_DN = "sys/ldap-ext" + + +def _prepare_ldap_servers(ldap_servers): + if len(ldap_servers) > len(_LDAP_SERVERS): + raise ImcOperationError("Configure LDAP", "Cannot configure more than" + "%d servers" % len(_LDAP_SERVERS)) + servers = {} + ports = {} + for server in ldap_servers: + if 'id' not in server: + raise ImcOperationError("Enable LDAP", + "Provide 'id'.") + id = server['id'] + if id == 0 or id > 6: + raise ImcOperationError("Enable LDAP", + "Provide valid 'id' (1-6).") + id = str(id) + if 'ip' in server: + servers['ldap_server' + id] = server['ip'] + if 'port' in server: + ports['ldap_server_port' + id] = str(server['port']) + + return servers, ports + + +def _set_ldap_servers(mo, ldap_servers): + servers, ports = _prepare_ldap_servers(ldap_servers) + mo.set_prop_multiple(**servers) + mo.set_prop_multiple(**ports) + + +def ldap_enable(handle, + basedn=None, + domain=None, + encryption=None, + timeout=None, + user_search_precedence=None, + bind_method=None, + bind_dn=None, + change_password=False, + password=None, + filter=None, + group_attribute=None, + attribute=None, + group_nested_search=None, + group_auth=None, + ldap_servers=[], + locate_directory_using_dns=None, + dns_domain_source=None, + dns_search_domain=None, + dns_search_forest=None, + **kwargs): + """ + Configures LDAP + + Args: + handle (ImcHandle) + basedn (str): Represents the Base Distinguished Name. Describes where + to load users and groups from. It must be in the 'dc=domain,dc=com' + format for Active Directory servers. + domain (str): The domain that all users must be in. + encryption (str): "Disabled", "Enabled", "disabled", "enabled" + timeout (int): [0-180] The number of seconds the Cisco IMC waits until + the LDAP search operation times out. + user_search_precedence (str): ['local-user-db', 'ldap-user-db']. + Preference to search in local user db versus ldap user db + bind_method (str): ['login-credentials', 'configured-credentials', + 'anonymous'] + bind_dn (str): Represents the distinguished name (DN) of the user. This + field is applicable only for bind_method='configured-credentials' + change_password (bool): True if you want to change the user password. + password (str): The password of the user. This field is applicable only + for bind_method='configured-credentials' + filter (str): Represents the filter attribute in the schema on the LDAP + server. + attribute (str): Represents the role and locale information. Should + match the attribute specified on the LDAP server. + group_attribute (str): Represents the group attribute in the schema on + the LDAP server. + group_nested_search (int): Represents the depth of a nested group + search + group_auth (str): "disabled" or "enabled". Enables authentication at + the group level for LDAP users that are not found in the local user + database. + ldap_servers (list): Represents the list of preconfigured LDAP server. + List of dictionaries in the format:- + [{"id": 1, "ip": "192.168.1.1", "port": 300}, + {"id": 2, "ip": "192.168.1.2", "port": 400}] + locate_directory_using_dns (str): "yes" or "no" + dns_domain_source (str): Represents the method to obtain domain name. + ['extracted-domain', 'configured-domain', + 'extracted-configured-domain'] + dns_search_domain: Domain name to be used for DNS query. Disabled when + domain_name_source='extracted-domain' + dns_search_forest: Forest name to used for DNS query. Disabled when + domain_name_source='extracted-domain' + kwargs: Key-Value paired arguments. Reserved for future use + + Returns: + AaaLdap object + + Raises: + ImcOperationError when AaaLdap object doesn't exist + + Examples: + ldap_configure(handle, + basedn='DC=LAB,DC=cisco,DC=com', + domain='LAB.cisco.com', + timeout=20, group_auth='enabled', + bind_dn='CN=administrator,CN=Users,DC=LAB,DC=cisco, + DC=com', password='abcdefg', ldap_servers=ldap_servers) + """ + + mo = _get_mo(handle, dn=LDAP_DN) + + params = { + 'admin_state': 'enabled', + 'basedn': basedn, + 'domain': domain, + 'encryption': encryption, + 'timeout': str(timeout) if timeout is not None else None, + 'user_search_precedence': user_search_precedence, + 'bind_method': bind_method, + 'bind_dn': bind_dn, + 'password': password if change_password else None, + 'filter': filter, + 'attribute': attribute, + 'group_attribute': group_attribute, + 'group_nested_search': + str(group_nested_search) if group_nested_search else None, + 'group_auth': group_auth, + 'locate_directory_using_dns': locate_directory_using_dns, + 'dns_domain_source': dns_domain_source, + 'dns_search_domain': None if dns_search_domain == "" else dns_search_domain, #IMC XML API issue : CSCvg92190 + 'dns_search_forest': None if dns_search_forest == "" else dns_search_forest + } + + mo.set_prop_multiple(**params) + + if ldap_servers: + _set_ldap_servers(mo, ldap_servers) + + mo.set_prop_multiple(**kwargs) + handle.set_mo(mo) + return mo + + +def _check_match(prop1, prop2): + if _is_invalid_value(prop1) and _is_invalid_value(prop2): + return None + return prop1 == prop2 + + +def _check_ldap_server_match(ldap_mo, ldap_servers): + servers, ports = _prepare_ldap_servers(ldap_servers) + + for server in servers: + if servers[server] != getattr(ldap_mo, server): + return False + + for port in ports: + if ports[port] != getattr(ldap_mo, port): + return False + + return True + + +def ldap_exists(handle, change_password=False, **kwargs): + """ + Checks if the specified LDAP settings are already applied + + Args: + handle (ImcHandle) + kwargs: Key-Value paired arguments + + Returns: + (True, AaaLdap) if settings match, else (False, None) + + Examples: + match, mo = ldap_exists( + handle, enabled=True, + basedn='DC=LAB,DC=cisco,DC=com', + domain='LAB.cisco.com', + timeout=20, group_auth=True, + bind_dn='CN=administrator,CN=Users,DC=LAB,DC=cisco,DC=com', + password='abcdefg', ldap_servers=ldap_servers) + """ + + mo = _get_mo(handle, dn=LDAP_DN) + if mo is None: + return False, None + + if _is_valid_arg('ldap_servers', kwargs): + if not _check_ldap_server_match(mo, kwargs.pop('ldap_servers')): + return False, mo + + if 'password' in kwargs and not change_password: + kwargs.pop('password', None) + + if 'dns_search_domain' in kwargs and kwargs['dns_search_domain'] == "": + kwargs.pop('dns_search_domain', None) + + if 'dns_search_forest' in kwargs and kwargs['dns_search_forest'] == "": + kwargs.pop('dns_search_forest', None) + + kwargs['admin_state'] = 'enabled' + if not mo.check_prop_match(**kwargs): + return False, mo + + return True, mo + + +def ldap_disable(handle): + """ + Disables the ldap settings + + Args: + handle (ImcHandle) + + Returns: + AaaLdap Managed Object + + Examples: + ldap_disable(handle) + """ + + mo = _get_mo(handle, dn=LDAP_DN) + mo.admin_state = "disabled" + handle.set_mo(mo) + return mo + + +def is_ldap_enabled(handle): + """ + Checks if LDAP is enabled + + Args: + handle (ImcHandle) + + Returns: + bool + + Examples: + is_ldap_enabled(handle) + """ + + mo = _get_mo(handle, dn=LDAP_DN) + return mo.admin_state.lower() == "enabled" + + +def _get_free_ldap_role_group_id(handle): + mos = handle.query_classid('AaaLdapRoleGroup') + for mo in mos: + if not mo.name and not mo.domain: + return mo.id + + raise ImcOperationError("LDAP role group create", + "No free role group available") + + +def ldap_role_group_create(handle, domain, name, role='read-only', **kwargs): + """ + Creates an LDAP role group + + Args: + handle (ImcHandle) + domain (str): The LDAP server domain the group resides in. + name (str): The name of the group in the LDAP server database. + role (str): The role assigned to all users in this LDAP server group. + ['read-only', 'user', 'admin'] + kwargs: Key-Value paired arguments for future use + + + Returns: + AaaLdapRoleGroup object + + Examples: + ldap_role_group_create(handle, domain='abcd.pqrs.com', name='abcd', + role='user') + """ + + match, mo = ldap_role_group_exists(handle, domain=domain, name=name, + role=role) + if match: + log.info("LDAP Role Group with domain:%s name:%s exists" % (domain, + name)) + return mo + + if mo is None: + free_id = _get_free_ldap_role_group_id(handle) + mo = AaaLdapRoleGroup(parent_mo_or_dn=LDAP_DN, id=free_id) + + mo.domain = domain + mo.name = name + mo.role = role + mo.set_prop_multiple(**kwargs) + handle.set_mo(mo) + return mo + + +def ldap_role_group_get(handle, domain, name): + """ + Gets the ldap role group based on domain and name + + Args: + handle (ImcHandle) + domain (str): The LDAP server domain the group resides in. + name (str): The name of the group in the LDAP server database. + kwargs: Key-Value paired arguments for future use + + Returns: + AaaLdapRoleGroup object + + Examples: + ldap_role_group_get(handle, domain='abcd.pqrs.com', name='abcd') + """ + + mos = handle.query_classid('AaaLdapRoleGroup') + for mo in mos: + if mo.domain == domain and mo.name == name: + return mo + + return None + + +def ldap_role_group_exists(handle, domain, name, **kwargs): + """ + Checks if the ldap group exists with the specified settings + + Args: + handle (ImcHandle) + domain (str): The LDAP server domain the group resides in. + name (str): The name of the group in the LDAP server database. + kwargs: Key-Value paired arguments for future use + + Returns: + (True, AaaLdapRoleGroup) if match found, else (False, None) + + Examples: + ldap_role_group_exists(handle, domain='abcd.pqrs.com', name='abcd', + role='user') + """ + mo = ldap_role_group_get(handle, domain=domain, name=name) + if not mo: + return False, None + + if not mo.check_prop_match(**kwargs): + return False, mo + + return True, mo + + +def ldap_role_group_delete(handle, domain, name): + """ + Deletes the ldap role group based on domain and name + + Args: + handle (ImcHandle) + domain (str): The LDAP server domain the group resides in. + name (str): The name of the group in the LDAP server database. + kwargs: Key-Value paired arguments for future use + + Returns: + None + + Examples: + ldap_role_group_delete(handle, domain='abcd.pqrs.com', name='abcd') + """ + + mo = ldap_role_group_get(handle, domain=domain, name=name) + if mo is None: + raise ImcOperationError("Delete LDAP Role Group", + "Role Group Not found") + + mo.admin_action = 'clear' + handle.set_mo(mo) + + +def ldap_role_group_create_all(handle, groups=None): + """ + Creates an LDAP role groups. + Note: This will overwrite the exisiting role groups. + + Args: + handle (ImcHandle) + groups (list of LDAP group dict) + keys: + domain (str): The LDAP server domain the group resides in. + name (str): The name of the group in the LDAP server database. + role (str): The role assigned to all users in this LDAP server + group. + ['read-only', 'user', 'admin'] + example: + [{'domain': 'abcd.pqrs.com', + 'name': 'abcd', + 'role': 'user'} + ] + + Returns: + List of AaaLdapRoleGroup object + + Examples: + ldap_role_group_create_all(handle, + groups= [ + {'domain': 'abcd.pqrs.com', + 'name': 'abcd', + 'role': 'user'}]) + """ + api = 'ldap_role_group_create_all' + + dn_to_group_dict = {} + domain_name_str_list = [] + mos = [] + id = 0 + + if len(groups) > 28: + raise ImcOperationError(api, "Maximum allowed LDAP groups are 28.") + + for group in groups: + domain = group.get('domain', None) + name = group.get('name', None) + role = group.get('role', 'read-only') + + domain = domain.strip() if domain else None + name = name.strip() if name else None + role = role.strip() if role else None + + _validate_api_prop('domain', domain, api) + _validate_api_prop('name', name, api) + _validate_api_prop('role', role, api, + True, ['read-only', 'user', 'admin']) + + domain_name_str = domain + "_" + name + if domain_name_str in domain_name_str_list: + raise ImcOperationError( + api, + "LDAP Role Group with domain:%s name:%s already exists." % ( + domain, name)) + domain_name_str_list.append(domain_name_str) + + params = { + 'domain': domain, + 'name': name, + 'role': role + } + + id += 1 + mo = AaaLdapRoleGroup(parent_mo_or_dn=LDAP_DN, id=str(id)) + mo.set_prop_multiple(**params) + mos.append(mo) + + dn_to_group_dict[mo.dn] = mo.name + + ret = [] + response = handle.set_mos(mos) + if response: + ret = process_conf_mos_response(response, api, False, + "Create LDAP groups failed", + ldap_role_group_callback, + dn_to_group_dict) + + if len(ret) != 0: + error_msg = 'Create LDAP groups failed:\n' + for item in ret: + object = item["Object"] + error = item["Error"] + error = sanitize_message(error) + error_msg += object + ": " + error + "\n" + + raise ImcOperationErrorDetail(api, error_msg, ret) + + results = {} + results["changed"] = True + results["msg"] = "" + results["msg_params"] = ret + + return results + + +def ldap_role_group_callback(dn, dn_to_group_dict): + return dn_to_group_dict.get(dn, "Unknown LDAP Role Group:" + dn) + + +def ldap_role_group_delete_all(handle): + """ + Delete all the ldap role groups + + Args: + handle (ImcHandle) + + Returns: + None + + Examples: + ldap_role_group_delete_all(handle) + """ + + api = 'ldap_role_group_delete_all' + dn_to_group_dict = {} + mos = [] + + groups = handle.query_classid('AaaLdapRoleGroup') + for group in groups: + if not group.name and not group.domain: + continue + group.admin_action = 'clear' + mos.append(group) + dn_to_group_dict[group.dn] = group.name + + ret = [] + response = handle.set_mos(mos) + if response: + ret = process_conf_mos_response(response, api, False, + "Delete LDAP groups failed", + ldap_role_group_callback, + dn_to_group_dict) + + if len(ret) != 0: + error_msg = 'Delete LDAP groups failed:\n' + for item in ret: + object = item["Object"] + error = item["Error"] + error = sanitize_message(error) + error_msg += object + ": " + error + "\n" + + raise ImcOperationErrorDetail(api, error_msg, ret) + + results = {} + results["changed"] = True + results["msg"] = "" + results["msg_params"] = ret + + return results + + +def ldap_role_group_exists_any(handle): + """ + Checks if any ldap role group exists + + Args: + handle (ImcHandle) + + Returns: + None + + Examples: + ldap_role_group_delete_all(handle) + """ + + mos = handle.query_classid('AaaLdapRoleGroup') + for mo in mos: + if mo.name and mo.domain: + return True + + return False + + +def ldap_certificate_management_enable(handle): + """ + Enables ldap certificate management + + Args: + handle (ImcHandle) + + Returns: + LdapCACertificateManagement object + """ + mo = _get_mo(handle, dn="sys/ldap-ext/ldap-ca-cert-mgmt") + mo.binding_certificate = "enabled" + handle.set_mo(mo) + return mo + + +def ldap_certificate_management_disable(handle): + """ + Disables ldap certificate management + + Args: + handle (ImcHandle) + + Returns: + LdapCACertificateManagement object + """ + mo = _get_mo(handle, dn="sys/ldap-ext/ldap-ca-cert-mgmt") + mo.binding_certificate = "disabled" + handle.set_mo(mo) + return mo + + +def ldap_certificate_management_exists(handle): + """ + Checks if LDAP certificate management is enabled + + Args: + handle (ImcHandle) + + Returns: + bool + """ + mo = _get_mo(handle, dn="sys/ldap-ext/ldap-ca-cert-mgmt") + return mo.binding_certificate.lower() == "enabled" + + +def ldap_certificate_download(handle, remote_server, remote_file, + user=None, pwd=None, protocol='tftp', **kwargs): + """ + Download LDAP CA certificate from remote server to Cisco IMC + + Args: + handle (ImcHandle) + remote_server (str): Remote Server IP or Hostname + remote_file (str): Remote file path + user (str): Username for the remote server + pwd (str): Password for the remote server + protocol (str): Protocol for downloading the certificate + ['tftp', 'ftp', 'http', 'scp', 'sftp'] + kwargs: Key-Value paired arguments for future use + + Returns: + DownloadLdapCACertificate object + + Examples: + ldap_certificate_download(handle, user='abcd', pwd='pqrs', + remote_server='1.1.1.1', remote_file='/tmp/cert', protocol='scp') + """ + mo = _get_mo(handle, + dn='sys/ldap-ext/ldap-ca-cert-mgmt/ldap-ca-cert-download') + params = { + 'user': user, + 'pwd': pwd, + 'remote_server': remote_server, + 'remote_file': remote_file, + 'protocol': protocol + } + mo.set_prop_multiple(**params) + mo.set_prop_multiple(**kwargs) + handle.set_mo(mo) + return handle.query_dn(mo.dn) + + +def ldap_certificate_export(handle, remote_server, remote_file, + user=None, pwd=None, protocol='tftp', **kwargs): + """ + Export the LDAP CA certificate from the Cisco IMC to a remote location + + Args: + handle (ImcHandle) + remote_server (str): Remote Server IP or Hostname + remote_file (str): Remote file path + user (str): Username for the remote server + pwd (str): Password for the remote server + protocol (str): Protocol for downloading the certificate + ['tftp', 'ftp', 'http', 'scp', 'sftp'] + kwargs: Key-Value paired arguments for future use + + Returns: + ExportLdapCACertificate object + + Examples: + ldap_certificate_export(handle, user='abcd', pwd='pqrs', + remote_server='1.1.1.1', remote_file='/tmp/cert', protocol='scp') + """ + mo = _get_mo(handle, + dn='sys/ldap-ext/ldap-ca-cert-mgmt/ldap-ca-cert-export') + params = { + 'user': user, + 'pwd': pwd, + 'remote_server': remote_server, + 'remote_file': remote_file, + 'protocol': protocol + } + mo.set_prop_multiple(**params) + mo.set_prop_multiple(**kwargs) + handle.set_mo(mo) + return handle.query_dn(mo.dn) + + +def ldap_certificate_binding_check(handle, user=None, pwd=None, **kwargs): + """ + Tests the LDAP CA certificate binding + + Args: + handle (ImcHandle) + user (str): Username for the remote server + pwd (str): Password for the remote server + + Returns: + LdapCACertificate object + + Examples: + ldap_certificate_binding_check(handle, user='abcd', pwd='pqrs') + """ + mo = _get_mo(handle, dn='sys/ldap-ext/ldap-ca-cert-mgmt/ldap-ca-cert') + params = { + 'user': user, + 'pwd': pwd, + 'admin_action': 'test-ldap-binding' + } + mo.set_prop_multiple(**params) + mo.set_prop_multiple(**kwargs) + handle.set_mo(mo) + return handle.query_dn(mo.dn) + + +def ldap_certificate_delete(handle): + """ + Deletes the LDAP CA certificate + + Args: + handle (ImcHandle) + user (str): Username for the remote server + pwd (str): Password for the remote server + + Returns: + LdapCACertificate object + + Examples: + ldap_certificate_delete(handle) + """ + mo = _get_mo(handle, dn='sys/ldap-ext/ldap-ca-cert-mgmt/ldap-ca-cert') + mo.admin_action = 'delete-ca-certificate' + handle.set_mo(mo) + return handle.query_dn(mo.dn) diff --git a/imcsdk/apis/v2/admin/network.py b/imcsdk/apis/v2/admin/network.py new file mode 100644 index 00000000..d7aa5477 --- /dev/null +++ b/imcsdk/apis/v2/admin/network.py @@ -0,0 +1,638 @@ +# Copyright 2017 Cisco Systems, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +""" +This module implements all the services +""" +from imcsdk.imcexception import ImcOperationError, ImcValidationException +from imcsdk.imccoreutils import IMC_PLATFORM +from imcsdk.apis.utils import _is_valid_arg + + +def _get_mgmtif_mo_dn(handle): + """ + Internal method to get the mgmt_if dn based on the type of platform + """ + if handle.platform == IMC_PLATFORM.TYPE_CLASSIC: + return("sys/rack-unit-1/mgmt/if-1") + elif handle.platform == IMC_PLATFORM.TYPE_MODULAR: + return("sys/chassis-1/if-1") + else: + raise ImcValidationException("Invalid platform detected:%s" % + handle.platform) + + +def _get_mgmtif_mo(handle): + """ + Internal method to get the mgmt_if mo + """ + dn = _get_mgmtif_mo_dn(handle) + mo = handle.query_dn(dn) + if mo is None: + raise ImcOperationError("common_prop_configure", + "%s does not exist." % dn) + return mo + + +def common_prop_configure(handle, hostname=None, ddns_enable=None, + ddns_domain=None): + """ + Configures networking common properties. + + Args: + handle (ImcHandle) + ddns_enable (string): Dynamic DNS. "yes" or "no" + ddns_domain (string): Dynamic DNS update domain. + + Returns: + MgmtIf object + + Raises: + ImcOperationError + + Example: + common_prop_configure(handle, ddns_enable="yes") + """ + return mgmt_if_configure( + handle, + hostname=hostname, + ddns_enable=ddns_enable, + ddns_domain=ddns_domain + ) + + +def ipv4_configure(handle, dhcp_enable=None, ext_ip=None, ext_mask=None, + ext_gw=None, dns_using_dhcp=None, dns_alternate=None, + dns_preferred=None, + ): + """ + Configures networking ipv4 properties. + + Args: + handle (ImcHandle) + dhcp_enable (string): Use DHCP. "yes" or "no" + ext_ip (string): + ext_mask (string): + dns_using_dhcp (string): Use DHCP for DNS servers. "yes" or "no" + dns_alternate (string): + dns_preferred (string): + + Returns: + MgmtIf object + + Raises: + ImcOperationError + + Example: + ipv4_configure(handle, dns_using_dhcp="yes") + """ + return mgmt_if_configure( + handle, + dhcp_enable=dhcp_enable, + ext_ip=ext_ip, + ext_mask=ext_mask, + ext_gw=ext_gw, + dns_using_dhcp=dns_using_dhcp, + dns_alternate=dns_alternate, + dns_preferred=dns_preferred + ) + + +def ipv6_configure(handle, v6ext_enabled=None, v6dhcp_enable=None, + v6ext_ip=None, v6ext_gw=None, v6prefix=None, + v6dns_using_dhcp=None, v6dns_preferred=None, + v6dns_alternate=None, + ): + """ + Configures networking ipv4 properties. + + Args: + handle (ImcHandle) + v6ext_enabled (string): Use DHCP. "yes" or "no" + v6dhcp_enable (string): Use DHCP. "yes" or "no" + v6ext_ip (string): + v6ext_gw (string): + v6prefix (int): + v6dns_using_dhcp (string): Use DHCP for DNS servers. "yes" or "no" + v6dns_preferred (string): + v6dns_alternate (string): + + Returns: + MgmtIf object + + Raises: + ImcOperationError + + Example: + ipv6_configure(handle, v6ext_enabled="yes") + """ + return mgmt_if_configure( + handle, + v6ext_enabled=v6ext_enabled, + v6dhcp_enable=v6dhcp_enable, + v6ext_ip=v6ext_ip, + v6ext_gw=v6ext_gw, + v6prefix=str(v6prefix) if v6prefix is not None else None, + v6dns_using_dhcp=v6dns_using_dhcp, + v6dns_preferred=v6dns_preferred, + v6dns_alternate=v6dns_alternate + ) + + +def vlan_enable(handle, vlan_id=None, vlan_priority=None): + """ + Enables management vlan + + Args: + handle (ImcHandle) + vlan_id (int): VLAN Id. 1-4094 + vlan_priority (int): VLAN Priority. 0-7 + + Returns: + MgmtIf object + + Raises: + ImcOperationError + + Example: + vlan_enable(handle) + """ + mo = mgmt_if_configure( + handle, + vlan_enable="yes", + vlan_id=str(vlan_id) if vlan_id is not None else None, + vlan_priority=str(vlan_priority) + if vlan_priority is not None else None, + ) + return mo + + +def vlan_exists(handle, **kwargs): + """ + Checks if management vlan exists. + + Args: + handle (ImcHandle) + kwargs: key-value paired arguments + + Returns: + True/False, MO/None + + Raises: + ImcOperationError + + Example: + vlan_exists(handle) + """ + exists, mo = mgmt_if_exists(handle, **kwargs) + if exists and mo: + if mo.vlan_enable.lower() in ["yes", "true"]: + return True, mo + return False, mo + + +def vlan_disable(handle): + """ + Disables management vlan + + Args: + handle (ImcHandle) + + Returns: + MgmtIf object + + Raises: + ImcOperationError + + Example: + vlan_disable(handle) + """ + return mgmt_if_configure(handle, vlan_enable="no") + + +def mgmt_if_configure(handle, + admin_duplex=None, + admin_net_speed=None, + auto_neg=None, + ddns_domain=None, + ddns_enable=None, + dhcp_enable=None, + dns_alternate=None, + dns_preferred=None, + dns_using_dhcp=None, + ext_gw=None, + ext_ip=None, + ext_mask=None, + hostname=None, + nic_mode=None, + nic_redundancy=None, + port_profile=None, + v4_ip_addr=None, + v4_ip_addr_bmc1=None, + v4_ip_addr_bmc2=None, + v4_ip_addr_cmc1=None, + v4_ip_addr_cmc2=None, + v6_ip_addr=None, + v6_ip_addr_bmc1=None, + v6_ip_addr_bmc2=None, + v6_ip_addr_cmc1=None, + v6_ip_addr_cmc2=None, + v6dhcp_enable=None, + v6dns_alternate=None, + v6dns_preferred=None, + v6dns_using_dhcp=None, + v6ext_enabled=None, + v6ext_gw=None, + v6ext_ip=None, + v6prefix=None, + v_hostname=None, + vic_slot=None, + vlan_enable=None, + vlan_id=None, + vlan_priority=None, + **kwargs + ): + """ + This method configures the network settings of CIMC. + + Args: + handle(ImcHandle) + admin_duplex(str): + admin_net_speed(str): + auto_neg(str): + ddns_domain(str): + ddns_enable(str): + dhcp_enable(str): + dns_alternate(str): + dns_preferred(str): + dns_using_dhcp(str): + ext_gw(str): + ext_ip(str): + ext_mask(str): + hostname(str): + nic_mode(str): + nic_redundancy(str): + port_profile(str): + v4_ip_addr(str): + v4_ip_addr_bmc1(str): + v4_ip_addr_bmc2(str): + v4_ip_addr_cmc1(str): + v4_ip_addr_cmc2(str): + v6_ip_addr(str): + v6_ip_addr_bmc1(str): + v6_ip_addr_bmc2(str): + v6_ip_addr_cmc1(str): + v6_ip_addr_cmc2(str): + v6dhcp_enable(str): + v6dns_alternate(str): + v6dns_preferred(str): + v6dns_using_dhcp(str): + v6ext_enabled(str): + v6ext_gw(str): + v6ext_ip(str): + v6prefix(str): + v_hostname(str): + vic_slot(str): + vlan_enable(str): + vlan_id(str): + vlan_priority(str): + + Returns: + MgmtIf object + + Raises: + ImcOperationError + + Example: + mgmt_if_configure(handle) + """ + mo = _get_mgmtif_mo(handle) + + args = { + 'admin_duplex': admin_duplex, + 'admin_net_speed': admin_net_speed, + 'auto_neg': auto_neg, + 'ddns_domain': ddns_domain, + 'ddns_enable': ddns_enable, + 'dhcp_enable': dhcp_enable, + 'dns_alternate': dns_alternate, + 'dns_preferred': dns_preferred, + 'dns_using_dhcp': dns_using_dhcp, + 'ext_gw': ext_gw, + 'ext_ip': ext_ip, + 'ext_mask': ext_mask, + 'hostname': hostname, + 'nic_mode': nic_mode, + 'nic_redundancy': nic_redundancy, + 'port_profile': port_profile, + 'v4_ip_addr': v4_ip_addr, + 'v4_ip_addr_bmc1': v4_ip_addr_bmc1, + 'v4_ip_addr_bmc2': v4_ip_addr_bmc2, + 'v4_ip_addr_cmc1': v4_ip_addr_cmc1, + 'v4_ip_addr_cmc2': v4_ip_addr_cmc2, + 'v6_ip_addr': v6_ip_addr, + 'v6_ip_addr_bmc1': v6_ip_addr_bmc1, + 'v6_ip_addr_bmc2': v6_ip_addr_bmc2, + 'v6_ip_addr_cmc1': v6_ip_addr_cmc1, + 'v6_ip_addr_cmc2': v6_ip_addr_cmc2, + 'v6dhcp_enable': v6dhcp_enable, + 'v6dns_alternate': v6dns_alternate, + 'v6dns_preferred': v6dns_preferred, + 'v6dns_using_dhcp': v6dns_using_dhcp, + 'v6ext_enabled': v6ext_enabled, + 'v6ext_gw': v6ext_gw, + 'v6ext_ip': v6ext_ip, + 'v6prefix': str(v6prefix) if v6prefix is not None else None, + 'v_hostname': v_hostname, + 'vic_slot': vic_slot, + 'vlan_enable': vlan_enable, + 'vlan_id': str(vlan_id) if vlan_id is not None else None, + 'vlan_priority': str(vlan_priority) if vlan_priority is not None else None, + } + + mo.set_prop_multiple(**args) + mo.set_prop_multiple(**kwargs) + handle.set_mo(mo) + return mo + + +def _match_yes_no_value(prop_name, prop_value, mo): + _ENABLE = ['true', 'yes'] + + prop_value = prop_value.lower() + mo_prop_value = getattr(mo, prop_name).lower() + + if prop_value in _ENABLE and mo_prop_value not in _ENABLE: + return False + elif prop_value not in _ENABLE and mo_prop_value in _ENABLE: + return False + return True + + +def mgmt_if_exists(handle, **kwargs): + + try: + mo = _get_mgmtif_mo(handle) + except: + return False, None + + ddns_enable = kwargs.pop('ddns_enable', None) + if ddns_enable and not _match_yes_no_value('ddns_enable', + ddns_enable, + mo): + return False, mo + + dhcp_enable = kwargs.pop('dhcp_enable', None) + if dhcp_enable and not _match_yes_no_value('dhcp_enable', + dhcp_enable, + mo): + return False, mo + + dns_using_dhcp = kwargs.pop('dns_using_dhcp', None) + if dns_using_dhcp and not _match_yes_no_value('dns_using_dhcp', + dns_using_dhcp, + mo): + return False, mo + + v6dhcp_enable = kwargs.pop('v6dhcp_enable', None) + if v6dhcp_enable and not _match_yes_no_value('v6dhcp_enable', + v6dhcp_enable, + mo): + return False, mo + + v6dns_using_dhcp = kwargs.pop('v6dns_using_dhcp', None) + if v6dns_using_dhcp and not _match_yes_no_value('v6dns_using_dhcp', + v6dns_using_dhcp, + mo): + return False, mo + + v6ext_enabled = kwargs.pop('v6ext_enabled', None) + if v6ext_enabled and not _match_yes_no_value('v6ext_enabled', + v6ext_enabled, + mo): + return False, mo + + vlan_enable = kwargs.pop('vlan_enable', None) + if vlan_enable and not _match_yes_no_value('vlan_enable', + vlan_enable, + mo): + return False, mo + + if 'v6prefix' in kwargs: + kwargs['v6prefix'] = str(kwargs['v6prefix']) if kwargs['v6prefix'] is not None else None + + if 'vlan_id' in kwargs: + kwargs['vlan_id'] = str(kwargs['vlan_id']) if kwargs['vlan_id'] is not None else None + + if 'vlan_priority' in kwargs: + kwargs['vlan_priority'] = str(kwargs['vlan_priority']) if kwargs['vlan_priority'] is not None else None + + + + return mo.check_prop_match(**kwargs), mo + + +def ip_blocking_enable(handle, fail_count=None, fail_window=None, + penalty_time=None, **kwargs): + """ + Enables IP Blocking and Configures. + + Args: + handle (ImcHandle) + fail_count (int): 3-10 + fail_window (int): 60-120 + penalty_time (int): 300-900 + + Returns: + IPBlocing object + + Raises: + ImcOperationError + + Example: + ip_blocking_enable(handle, fail_count=3) + """ + dn = _get_mgmtif_mo_dn(handle) + "/ip-block" + mo = handle.query_dn(dn) + if mo is None: + raise ImcOperationError("ip_blocking_enable", + "%s does not exist." % dn) + + args = { + 'enable': "yes", + 'fail_count': str(fail_count) if fail_count is not None else None, + 'fail_window': str(fail_window) if fail_count is not None else None, + 'penalty_time': + str(penalty_time) if penalty_time is not None else None + } + + mo.set_prop_multiple(**args) + mo.set_prop_multiple(**kwargs) + handle.set_mo(mo) + return mo + + +def ip_blocking_exists(handle, **kwargs): + """ + Checks if IP blocking is enabled. + + Args: + handle (ImcHandle) + kwargs: key-value paired arguments + + Returns: + True/False, MO/None + + Raises: + None + + Example: + ip_blocking_exists(handle, fail_count=3) + """ + dn = _get_mgmtif_mo_dn(handle) + "/ip-block" + mo = handle.query_dn(dn) + if mo is None: + return False, None + + if mo.enable.lower() not in ["yes", "true"]: + return False, mo + + return mo.check_prop_match(**kwargs), mo + + +def ip_blocking_disable(handle): + """ + Disables IP Blocking. + + Args: + handle (ImcHandle) + + Returns: + IPBlocing object + + Raises: + ImcOperationError + + Example: + ip_blocking_disable(handle) + """ + dn = _get_mgmtif_mo_dn(handle) + "/ip-block" + mo = handle.query_dn(dn) + if mo is None: + raise ImcOperationError("ip_blocking_enable", + "%s does not exist." % dn) + + mo.enable = "no" + handle.set_mo(mo) + return mo + + +_IP_FILTERS_LIST = ["filter1", "filter2", "filter3", "filter4"] + + +def _get_ip_filters(ip_filters): + return {"filter" + str(x["id"]): x["ip_filter"] for x in ip_filters} + + +def _set_ip_filters(mo, ip_filters): + if len(ip_filters) > len(_IP_FILTERS_LIST): + raise ImcOperationError("Set IP Filters", + "Cannot specify more than %d filters" + % len(_IP_FILTERS_LIST)) + args = _get_ip_filters(ip_filters) + mo.set_prop_multiple(**args) + + +def ip_filtering_enable(handle, ip_filters=None): + """ + Enables NTP and configures the NTP servers provided + + Args: + handle (ImcHandle) + ip_filters (list): List of dictionaries in the format + [{"id": 1, "ip_filter": "192.168.1.1"}, + {"id": 2, "ip": "192.168.1.2-192.168.1.4"}] + Upto 4 ip filters can be specified. + + Returns: + IPFiltering object + + Example: + ip_filtering_enable(handle, + ip_filters = [ + {"id": 1, "ip_filter": "192.168.1.1"}, + {"id": 2, "ip_filter": "192.168.1.2-192.168.1.4"}] + """ + dn = _get_mgmtif_mo_dn(handle) + "/ip-filter" + mo = handle.query_dn(dn) + if mo is None: + raise ImcOperationError("ip_filtering_enable", + "%s does not exist." % dn) + mo.enable = 'yes' + + _set_ip_filters(mo, ip_filters) + handle.set_mo(mo) + return mo + + +def ip_filtering_disable(handle): + """ + Disables IP Filtering. + + Args: + handle (ImcHandle) + + Returns: + IPFiltering object + + Raises: + ImcOperationError + + Example: + ip_filtering_disable(handle) + """ + dn = _get_mgmtif_mo_dn(handle) + "/ip-filter" + mo = handle.query_dn(dn) + if mo is None: + raise ImcOperationError("ip_filtering_enable", + "%s does not exist." % dn) + + mo.enable = "no" + handle.set_mo(mo) + return mo + + +def ip_filtering_exists(handle, **kwargs): + """ + Checks if the ip filtering already exists. + Args: + handle (ImcHandle) + kwargs: key-value paired arguments + + Returns: + True/False, MO/None + """ + dn = _get_mgmtif_mo_dn(handle) + "/ip-filter" + mo = handle.query_dn(dn) + if mo is None: + return False, None + + kwargs['enable'] = 'yes' + + if _is_valid_arg("ip_filters", kwargs): + args = _get_ip_filters(kwargs['ip_filters']) + del kwargs['ip_filters'] + kwargs.update(args) + + return mo.check_prop_match(**kwargs), mo diff --git a/imcsdk/apis/v2/admin/ntp.py b/imcsdk/apis/v2/admin/ntp.py new file mode 100644 index 00000000..465659f1 --- /dev/null +++ b/imcsdk/apis/v2/admin/ntp.py @@ -0,0 +1,211 @@ +# Copyright 2016 Cisco Systems, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +""" +This module implements all the ntp related functionality +""" + +from imcsdk.imcexception import ImcOperationError +from imcsdk.apis.utils import _get_mo, _is_invalid_value, _is_valid_arg + +import logging + +log = logging.getLogger('imc') + +COMM_EXT_DN = "sys/svc-ext" +NTP_DN = "sys/svc-ext/ntp-svc" +_NTP_SERVER_LIST = ["ntp_server1", "ntp_server2", "ntp_server3", "ntp_server4"] + + +def _get_ntp_servers(ntp_servers): + return {"ntp_server" + str(x["id"]): x["ip"] for x in ntp_servers} + + +def _set_ntp_servers(mo, ntp_servers): + if len(ntp_servers) > len(_NTP_SERVER_LIST): + raise ImcOperationError("Set NTP Servers", + "Cannot specify more than %d servers" + % len(_NTP_SERVER_LIST)) + args = _get_ntp_servers(ntp_servers) + mo.set_prop_multiple(**args) + + +def ntp_enable(handle, ntp_servers=[]): + """ + Enables NTP and configures the NTP servers provided + + Args: + handle (ImcHandle) + ntp_servers (list): List of dictionaries in the format + [{"id": 1, "ip": "192.168.1.1"}, + {"id": 2, "ip": "192.168.1.2"}] + Upto 4 ntp servers can be specified. + + Returns: + CommNtpProvider object + + Example: + ntp_enable(handle, + ntp_servers = [{"id": 1, "ip": "192.168.1.1"}, + {"id": 2, "ip": "192.168.1.2"}] + """ + + log.warning('IPMI Set SEL Time command will disable if NTP is enabled.') + mo = _get_mo(handle, dn=NTP_DN) + mo.ntp_enable = "yes" + + _set_ntp_servers(mo, ntp_servers) + handle.set_mo(mo) + return handle.query_dn(mo.dn) + + +def ntp_disable(handle): + """ + Disables NTP + Args: + handle (ImcHandle) + + Returns: + CommNtpProvider object + """ + + log.warning( + 'Disabling NTP may cause Cisco IMC to lose timesync with server/s') + mo = _get_mo(handle, dn=NTP_DN) + mo.ntp_enable = "no" + + handle.set_mo(mo) + return mo + + +def ntp_servers_clear(handle, ntp_servers=[]): + """ + Clears the NTP servers provided in the arguments. + Clears all the NTP servers, only if ntp is disabled. + + Args: + handle (ImcHandle) + ntp_servers (list): List of NTP servers in the format + ["192.168.1.1", "192.168.1.2"] + + Returns: + CommNtpProvider object + """ + + mo = _get_mo(handle, dn=NTP_DN) + args = {} + + if ntp_servers: + args = {x: "" for x in _NTP_SERVER_LIST + if getattr(mo, x) in ntp_servers} + else: + args = {x: "" for x in _NTP_SERVER_LIST} + + if mo.ntp_enable.lower() in ["yes", "true"] and \ + len(args) == len(_NTP_SERVER_LIST): + raise ImcOperationError( + "Clear NTP Servers", + "Cannot clear all NTP servers when NTP is enabled") + + mo.set_prop_multiple(**args) + mo.ntp_enable = mo.ntp_enable + handle.set_mo(mo) + return handle.query_dn(mo.dn) + + +def ntp_servers_modify(handle, ntp_servers=[]): + """ + Modifies the configured NTP servers + Args: + handle (ImcHandle) + ntp_servers (list): List of dictionaries in the format + [{"id": 1, "ip": "192.168.1.1"}, + {"id": 2, "ip": "192.168.1.2"}] + Upto 4 ntp servers can be specified. + + Returns: + CommNtpProvider object + + Example: + ntp_servers_modify(handle, + ntp_servers = [{"id": 1, "ip": "192.168.1.1"}, + {"id": 2, "ip": "192.168.1.2"}, + {"id": 3, "ip": ""}] + """ + + # While sending the modified list of servers, it is imperative to send + # ntp_enable property in the request. + # Hence, query the MO and reassign the same value to ntp_enable + mo = _get_mo(handle, dn=NTP_DN) + mo.ntp_enable = mo.ntp_enable + _set_ntp_servers(mo, ntp_servers) + + handle.set_mo(mo) + return handle.query_dn(mo.dn) + + +def is_ntp_enabled(handle): + """ + Check if NTP is enabled + Args: + handle (ImcHandle) + + Returns: + bool + """ + + mo = _get_mo(handle, dn=NTP_DN) + return (mo.ntp_enable.lower() in ["true", "yes"]) + + +def _check_ntp_server_match(ntp_mo, mo): + for prop in _NTP_SERVER_LIST: + configured_value = getattr(ntp_mo, prop) + in_value = getattr(mo, prop) + + if _is_invalid_value(configured_value) and \ + _is_invalid_value(in_value): + continue + if configured_value != in_value: + return False + + return True + + +def ntp_setting_exists(handle, **kwargs): + """ + Check if the specified NTP settings are already applied + Args: + handle (ImcHandle) + kwargs: key-value paired arguments + + Returns: + (True, CommNtpProvider) if settings match, (False, None) otherwise + """ + + mo = _get_mo(handle, dn=NTP_DN) + if mo is None: + return False, None + + kwargs['ntp_enable'] = "yes" + + if _is_valid_arg("ntp_servers", kwargs): + args = _get_ntp_servers(kwargs['ntp_servers']) + del kwargs['ntp_servers'] + kwargs.update(args) + + if not mo.check_prop_match(**kwargs): + return False, mo + + return True, mo diff --git a/imcsdk/apis/v2/admin/redfish.py b/imcsdk/apis/v2/admin/redfish.py new file mode 100644 index 00000000..4983062a --- /dev/null +++ b/imcsdk/apis/v2/admin/redfish.py @@ -0,0 +1,73 @@ +# Copyright 2016 Cisco Systems, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +""" +This module implements apis to setup redfish support +""" + +import logging +from imcsdk.mometa.comm.CommRedfish import CommRedfish, CommRedfishConsts + +log = logging.getLogger('imc') + + +def redfish_enable(handle): + """ + This method will enable redfish support + + Args: + handle (ImcHandle) + enable (bool) + + Returns: + CommRedfish object + """ + + mo = CommRedfish(parent_mo_or_dn="sys/svc-ext") + mo.admin_state = CommRedfishConsts.ADMIN_STATE_ENABLED + handle.set_mo(mo) + return handle.query_dn(mo.dn) + + +def redfish_disable(handle): + """ + This method will disable redfish support + + Args: + handle (ImcHandle) + enable (bool) + + Returns: + CommRedfish object + """ + + mo = CommRedfish(parent_mo_or_dn="sys/svc-ext") + mo.admin_state = CommRedfishConsts.ADMIN_STATE_DISABLED + handle.set_mo(mo) + return handle.query_dn(mo.dn) + + +def is_redfish_enabled(handle): + """ + This method will check if redfish support is enabled + + Args: + handle(ImcHandle) + + Returns: + True if enabled, else False + """ + + mos = handle.query_classid("CommRedfish") + return (mos[0].admin_state == CommRedfishConsts.ADMIN_STATE_ENABLED) diff --git a/imcsdk/apis/v2/admin/smtp.py b/imcsdk/apis/v2/admin/smtp.py new file mode 100644 index 00000000..8e8c074a --- /dev/null +++ b/imcsdk/apis/v2/admin/smtp.py @@ -0,0 +1,277 @@ +# Copyright 2017 Cisco Systems, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +This module implements all the smtp related functionality +""" + +from imcsdk.imcexception import ImcOperationError +from imcsdk.mometa.mail.MailRecipient import MailRecipientConsts +from imcsdk.mometa.comm.CommMailAlert import CommMailAlertConsts +from imcsdk.apis.utils import _get_mo, _is_invalid_value + +import logging + +log = logging.getLogger('imc') + +_SMTP_DN = 'sys/svc-ext/mail-alert-svc' + +_MAIL_ALERT_SEVERITY_LEVELS = [ + CommMailAlertConsts.MIN_SEVERITY_LEVEL_CONDITION, + CommMailAlertConsts.MIN_SEVERITY_LEVEL_CRITICAL, + CommMailAlertConsts.MIN_SEVERITY_LEVEL_MAJOR, + CommMailAlertConsts.MIN_SEVERITY_LEVEL_MINOR, + CommMailAlertConsts.MIN_SEVERITY_LEVEL_WARNING] + + +def _is_valid_severity_level(min_severity_level): + return min_severity_level in _MAIL_ALERT_SEVERITY_LEVELS + + +def smtp_enable(handle, ip_address=None, port=None, min_severity_level=None, + **kwargs): + """ + Enables SMTP Policy and sets the given properties + + Args: + handle (ImcHandle) + ip_address (str): Ip Address of the SMTP server + port (int): Port number of the SMTP server + min_severity_level (str): Minimum fault severity level + Valid values: "condition", "critical", "major", "minor", "warning" + kwargs: key-value paired arguments for future use + + Returns: + CommMailAlert object + + Raises: + ImcOperationError if the severity level is not correct + + Example: + smtp_enable(handle, '10.105.110.219', 25, 'minor') + """ + if min_severity_level and not _is_valid_severity_level(min_severity_level): + raise ImcOperationError( + 'Configure SMTP Policy', + 'Invalid severity level %s ' % min_severity_level) + + mo = _get_mo(handle, dn=_SMTP_DN) + params = { + 'admin_state': 'enabled', + 'ip_address': ip_address, + 'port': str(port) if port is not None else None, + 'min_severity_level': min_severity_level + } + mo.set_prop_multiple(**params) + mo.set_prop_multiple(**kwargs) + handle.set_mo(mo) + return mo + + +def smtp_exists(handle, **kwargs): + """ + Check whether the specified SMTP settings already exist + + Args: + handle (ImcHandle) + kwargs: key-value paired arguments + + Returns: + (True, CommMailAlert) if settings match, (False, None) otherwise + """ + try: + mo = _get_mo(handle, dn=_SMTP_DN) + except: + return False, None + + kwargs['admin_state'] = 'enabled' + + if not mo.check_prop_match(**kwargs): + return False, mo + return True, None + + +def smtp_disable(handle): + """ + Disable SMTP Settings + + Args: + handle (ImcHandle) + + Raises: + ImcOperationError + + Returns: + CommMailAlert object + """ + mo = _get_mo(handle, dn=_SMTP_DN) + mo.admin_state = 'disabled' + handle.set_mo(mo) + return mo + + +def _get_smtp_recipients(handle): + mos = handle.query_children(in_dn=_SMTP_DN, class_id='MailRecipient') + if mos is None: + raise ImcOperationError("Add SMTP recipient", + "No recipient slot present.") + return mos + + +def _get_smtp_recipient(handle, email): + mos = handle.query_children(in_dn=_SMTP_DN, class_id='MailRecipient') + if mos is None: + raise ImcOperationError("Get SMTP recipient", + "No recipient slot present.") + + for mo in mos: + if mo.email == email: + return mo + return None + + +def _get_free_smtp_recipient(handle): + mos = _get_smtp_recipients(handle) + for mo in mos: + if _is_invalid_value(mo.email): + return mo + + raise ImcOperationError("Add SMTP recipient", + "Max number of emails already added.") + + +def smtp_recipient_add(handle, email): + """ + Adds the smtp recipient + + Args: + handle (ImcHandle) + email (str): email address + + Returns + MailRecipient + + Raises: + ImcOperationError if email cannot be added + + Example: + smtp_recipient_add(handle, 'cec@cisco.com') + """ + mo = _get_free_smtp_recipient(handle) + mo.email = email + handle.set_mo(mo) + return mo + + +def smtp_recipient_exists(handle, email): + """ + Checks whether the smtp recipient is already present + + Args: + handle (ImcHandle) + email (str): email address + + Returns: + True/False, MO/None + + Example: + smtp_recipient_exists(handle, 'cec@cisco.com') + """ + try: + mos = _get_smtp_recipients(handle) + except: + return False, None + + for mo in mos: + if mo.email == email: + return True, mo + return False, mo + + +def smtp_recipient_remove(handle, email): + """ + Clears the smtp recipient with given email address + + Args: + handle (ImcHandle) + email (int): SMTP recipient email address + + Returns: + MailRecipient object + + Raises: + ImcOperationError + + Example: + smtp_recipient_remove(handle, 'mail1@cisco.com') + """ + mo = _get_smtp_recipient(handle, email) + if mo is None: + raise ImcOperationError("smtp_recipient_remove", + "Recipient does not exist") + mo.admin_action = MailRecipientConsts.ADMIN_ACTION_CLEAR + handle.set_mo(mo) + return mo + + +def smtp_recipient_remove_all(handle): + """ + Clears the smtp recipient with given id + + Args: + handle (ImcHandle) + + Returns: + None + + Raises: + ImcOperationError + + Example: + smtp_recipient_remove(handle,1) + """ + mos = _get_smtp_recipients(handle) + if mos is None: + raise ImcOperationError("smtp_recipient_remove_all", + "No Recipient exist") + for mo in mos: + if not mo.email: + continue + mo.admin_action = MailRecipientConsts.ADMIN_ACTION_CLEAR + handle.set_mo(mo) + + +def smtp_recipient_exists_any(handle): + """ + Checks if any SMTP recipient exists. + + Args: + handle (ImcHandle) + + Returns: + None + + Raises: + ImcOperationError + + Example: + smtp_recipient_exists_any(handle) + """ + mos = _get_smtp_recipients(handle) + if mos is None: + raise ImcOperationError("smtp_recipient_remove_all", + "No Recipient exist") + for mo in mos: + if mo.email: + return True + return False diff --git a/imcsdk/apis/v2/admin/snmp.py b/imcsdk/apis/v2/admin/snmp.py new file mode 100644 index 00000000..fddccbba --- /dev/null +++ b/imcsdk/apis/v2/admin/snmp.py @@ -0,0 +1,857 @@ +# Copyright 2017 Cisco Systems, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +This module performs the operations related to snmp server, user and traps. +""" + +from imcsdk.imcexception import ImcOperationError, ImcOperationErrorDetail +from imcsdk.apis.utils import _get_mo +from imcsdk.imccoreutils import process_conf_mos_response, sanitize_message + +SNMP_DN = 'sys/svc-ext/snmp-svc' + + +def snmp_enable(handle, port=None, community=None, + com2_sec=None, trap_community=None, + sys_contact=None, sys_location=None, + engine_id_key=None, **kwargs): + """ + Enables SNMP. + + Args: + handle (ImcHandle) + port (int): port on which SNMP agent runs + community (string): community + com2_sec (string): "disabled", "limited", "full" + trap_community(string): community to be used when generating traps + sys_contact (string): sys_contact + sys_location (string): sys_location + engine_id_key (string): engine id key + kwargs: key-value paired arguments for future use + + Returns: + CommSnmp: Managed object + + Raises: + ImcOperationError: If CommSnmp Mo is not present + + Example: + mo = snmp_enable(handle, + community="username", + sys_contact="user contact", + sys_location="user location") + """ + from imcsdk.mometa.comm.CommSnmp import CommSnmpConsts + + mo = _get_mo(handle, dn=SNMP_DN) + + params = { + 'admin_state': CommSnmpConsts.ADMIN_STATE_ENABLED, + 'port': str(port) if port is not None else None, + 'community': community, + 'com2_sec': com2_sec, + 'trap_community': trap_community, + 'sys_contact': sys_contact, + 'sys_location': sys_location, + 'engine_id_key': engine_id_key + } + + mo.set_prop_multiple(**params) + mo.set_prop_multiple(**kwargs) + handle.set_mo(mo) + return mo + + +def snmp_disable(handle): + """ + Disables SNMP. + + Args: + handle (ImcHandle) + + Returns: + CommSnmp: Managed Object + + Raises: + ValueError: If CommSnmp Mo is not present + + Example: + snmp_disable(handle) + """ + from imcsdk.mometa.comm.CommSnmp import CommSnmpConsts + + mo = _get_mo(handle, dn=SNMP_DN) + mo.admin_state = CommSnmpConsts.ADMIN_STATE_DISABLED + handle.set_mo(mo) + return mo + + +def snmp_exists(handle, **kwargs): + """ + Checks if snmp is enabled or not + + Args: + handle (ImcHandle) + kwargs: Key-Value paired arguments relevant to CommSnmp object + + Returns: + True/false, CommSnmp MO/None + + Example: + snmp_exists(handle) + """ + from imcsdk.mometa.comm.CommSnmp import CommSnmpConsts + + mo = _get_mo(handle, dn=SNMP_DN) + kwargs['admin_state'] = CommSnmpConsts.ADMIN_STATE_ENABLED + return mo.check_prop_match(**kwargs), mo + + +def _get_snmp_traps(handle): + return handle.query_children(in_dn=SNMP_DN, class_id="CommSnmpTrap") + + +def _get_free_snmp_trap(handle): + traps = _get_snmp_traps(handle) + for trap in traps: + if trap.hostname == "0.0.0.0": + return trap + return None + + +def snmp_trap_get(handle, hostname): + traps = _get_snmp_traps(handle) + for trap in traps: + if trap.hostname == hostname: + return trap + return None + + +def snmp_trap_add(handle, hostname, version, notification_type, + admin_state="enabled", user=None, port=None, + **kwargs): + """ + Adds snmp trap. + + Args: + handle (ImcHandle) + hostname (string): ip address + admin_state (string): enabled, disabled + version (string): "v2c", "v3" + notification_type (string): "informs", "traps" + Required only for version "v2c" and "v3" + user (string): send traps for a specific user + port (int): port + kwargs: Key-Value paired arguments for future use + + Returns: + CommSnmpTrap: Managed Object + + Raises: + ImcOperationError if all traps are configured + + Example: + snmp_trap_add(handle, hostname="10.10.10.10", + port="162", + version="v2c", + notification_type="informs") + """ + from imcsdk.mometa.comm.CommSnmpTrap import CommSnmpTrapConsts + + if version == CommSnmpTrapConsts.VERSION_V2C and user: + user = None + + mo = snmp_trap_get(handle, hostname) + if mo is None: + mo = _get_free_snmp_trap(handle) + if mo is None: + raise ImcOperationError("Snmp Trap Add", + "No free traps available to configure") + + params = { + 'hostname': hostname, + 'version': version, + 'notification_type': notification_type, + 'admin_state': admin_state, + 'port': str(port) if port is not None else None, + 'user': user + } + + mo.set_prop_multiple(**params) + mo.set_prop_multiple(**kwargs) + handle.set_mo(mo) + return mo + + +def snmp_trap_exists(handle, hostname, **kwargs): + """ + checks if snmp trap exists + + Args: + handle (ImcHandle) + kwargs: Key-Value paired arguments relevant to CommSnmpTrap object + + Returns: + True, CommSnmpTrap MO if found, else False, None + + Example: + snmp_trap_exists(handle, hostname="10.10.10.10", + port="162", + version="v2c", + notification_type="informs", + user="username") + + """ + from imcsdk.mometa.comm.CommSnmpTrap import CommSnmpTrapConsts + + mo = snmp_trap_get(handle, hostname) + if mo is None: + return False, None + + if mo.version == CommSnmpTrapConsts.VERSION_V2C: + kwargs.pop('user', None) + + return mo.check_prop_match(**kwargs), mo + + +def snmp_trap_modify(handle, hostname, **kwargs): + """ + Modifies snmp trap referred to by id + + Args: + handle (ImcHandle) + kwargs : Key-Value paired arguments relevant to CommSnmpTrap object + + Returns: + CommSnmpTrap: Managed Object + + Raises: + ImcOperationError if trap not found + + Example: + snmp_trap_modify(handle, id="5", hostname="10.10.10.10", + port="162", + version="v3", + notification_type="traps", + user="username") + """ + mo = snmp_trap_get(handle, hostname) + if mo is None: + raise ImcOperationError("Modify SNMP trap", "Trap does not exist.") + + mo.set_prop_multiple(**kwargs) + handle.set_mo(mo) + return mo + + +def snmp_trap_delete(handle, hostname): + """ + Deletes snmp trap. + + Args: + handle (ImcHandle) + hostname (string): SNMP hostname + + Returns: + None + + Raises: + ImcOperationError if trap not found + + Example: + snmp_trap_delete(handle, trap_id=6) + """ + from imcsdk.mometa.comm.CommSnmpTrap import CommSnmpTrapConsts + + mo = snmp_trap_get(handle, hostname) + mo.admin_state = CommSnmpTrapConsts.ADMIN_STATE_DISABLED + mo.admin_action = CommSnmpTrapConsts.ADMIN_ACTION_CLEAR + handle.set_mo(mo) + + +def snmp_trap_add_all(handle, traps=None): + """ + Adds snmp trap. + + Args: + handle (ImcHandle) + traps (list): list of trap dict + keys: + hostname (string): ip address + admin_state (string): enabled, disabled + version (string): "v2c", "v3" + notification_type (string): "informs", "traps" + Required only for version "v2c" and "v3" + user (string): send traps for a specific user + port (int): port + + Returns: + list: List of CommSnmpTrap Managed Object + + Example: + snmp_trap_add_all(handle, + traps=[{hostname: "10.10.10.10", + port: "162", + version:"v2c", + notification_type:"informs"}] + ) + """ + from imcsdk.mometa.comm.CommSnmp import CommSnmpConsts + from imcsdk.mometa.comm.CommSnmpTrap import CommSnmpTrap + from imcsdk.mometa.comm.CommSnmpTrap import CommSnmpTrapConsts + + api = 'snmp_trap_add_all' + parent_mo = _get_mo(handle, dn=SNMP_DN) + if parent_mo.admin_state != CommSnmpConsts.ADMIN_STATE_ENABLED: + raise ImcOperationError(api, 'SNMP is not enabled.') + + dn_to_trap_dict = {} + mos = [] + id = 0 + for trap in traps: + hostname = trap.pop('hostname', None) + _validate_api_prop('hostname', hostname, api) + + version = trap.pop('version', None) + _validate_api_prop('version', version, api, True, + [CommSnmpTrapConsts.VERSION_V1, + CommSnmpTrapConsts.VERSION_V2C, + CommSnmpTrapConsts.VERSION_V3]) + + notification_type = trap.pop('notification_type', None) + _validate_api_prop('notification_type', notification_type, api, + True, + [CommSnmpTrapConsts.NOTIFICATION_TYPE_INFORMS, + CommSnmpTrapConsts.NOTIFICATION_TYPE_TRAPS]) + + admin_state = trap.pop('admin_state', 'enabled') + _validate_api_prop('admin_state', admin_state, api, True, + [CommSnmpTrapConsts.ADMIN_STATE_ENABLED, + CommSnmpTrapConsts.ADMIN_STATE_DISABLED]) + + user = trap.pop('user', None) + port = trap.pop('port', None) + + if version == CommSnmpTrapConsts.VERSION_V2C and user: + user = None + if version == CommSnmpTrapConsts.VERSION_V3: + notification_type = CommSnmpTrapConsts.NOTIFICATION_TYPE_TRAPS + + params = { + 'hostname': hostname, + 'version': version, + 'notification_type': notification_type, + 'admin_state': admin_state, + 'port': str(port) if port else None, + 'user': user + } + + id += 1 + mo = CommSnmpTrap(parent_mo_or_dn=parent_mo, id=str(id)) + mo.set_prop_multiple(**params) + mos.append(mo) + dn_to_trap_dict[mo.dn] = mo.hostname + + response = handle.set_mos(mos) + if response: + ret = process_conf_mos_response(response, api, False, + 'Create SNMP traps failed', + snmp_traps_callback, + dn_to_trap_dict) + if len(ret) != 0: + error_msg = 'Create/Update SNMP traps failed:\n' + for item in ret: + obj = item["Object"] + error = item["Error"] + error = sanitize_message(error) + error_msg += "[Trap " + obj + "] " + error + "\n" + + raise ImcOperationErrorDetail(api, error_msg, ret) + + results = {} + results["changed"] = True + results["msg"] = "" + results["msg_params"] = ret + + return results + + +def snmp_traps_callback(dn, dn_to_trap_dict): + return dn_to_trap_dict.get(dn, "Unknown Trap:" + dn) + + +def snmp_trap_delete_all(handle): + """ + delete all snmp traps. + + Args: + handle (ImcHandle) + + Returns: + None + + Raises: + ImcOperationError if trap not found + + Example: + snmp_trap_delete_all(handle) + """ + from imcsdk.mometa.comm.CommSnmpTrap import CommSnmpTrapConsts + from imcsdk.mometa.comm.CommSnmp import CommSnmpConsts + + api = 'snmp_trap_delete_all' + parent_mo = _get_mo(handle, dn=SNMP_DN) + if parent_mo.admin_state != CommSnmpConsts.ADMIN_STATE_ENABLED: + raise ImcOperationError(api, 'SNMP is not enabled.') + + mos = [] + traps = _get_snmp_traps(handle) + for trap in traps: + if trap.hostname == "0.0.0.0": + continue + trap.admin_state = CommSnmpTrapConsts.ADMIN_STATE_DISABLED + trap.admin_action = CommSnmpTrapConsts.ADMIN_ACTION_CLEAR + mos.append(trap) + + response = handle.set_mos(mos) + if response: + process_conf_mos_response(response, api) + + +def snmp_trap_exists_any(handle): + """ + Checks if any snmp trap exists. + + Args: + handle (ImcHandle) + + Returns: + None + + Raises: + ImcOperationError if trap not found + + Example: + snmp_trap_exists_any(handle) + """ + mos = _get_snmp_traps(handle) + for mo in mos: + if mo.hostname != "0.0.0.0": + return True + return False + + +def _get_snmp_users(handle): + return handle.query_children(in_dn=SNMP_DN, class_id="CommSnmpUser") + + +def _get_free_snmp_user(handle): + users = _get_snmp_users(handle) + for user in users: + if user.name == "": + return user + + raise ImcOperationError("Snmp User Add", + "Maximum number of users already configured") + + +def snmp_user_get(handle, name): + """ + Gets the snmp user + + Args: + handle (ImcHandle) + name (str): snmp username + + Returns: + CommSnmpUser object + + Examples: + user = snmp_user_get(handle, name = "snmp-user") + """ + users = _get_snmp_users(handle) + for user in users: + if user.name == name: + return user + return None + + +def snmp_user_add(handle, name, security_level, + auth=None, auth_pwd=None, change_auth_pwd=False, + privacy=None, privacy_pwd=None, change_privacy_pwd=False, + **kwargs): + """ + Adds snmp user. + + Args: + handle (ImcHandle) + name (string): snmp username + security_level (string): "authpriv", "authnopriv", "noauthnopriv" + auth (string): "MD5", "SHA" + auth_pwd (string): password + change_auth_pwd (bool): set to True, if wants to change password for + existing user + privacy (string): "AES", "DES" + privacy_pwd (string): privacy password + change_privacy_pwd (bool): set to True, if wants to change password for + existing user + + Returns: + CommSnmpUser: Managed Object + + Raises: + ImcOperationError is maximum number of users already configured + + Example: + snmp_user_add(handle, name="snmpuser", + security_level="authpriv", auth_pwd="abcd", + auth="MD5", privacy_pwd="xyz", privacy="DES") + """ + + from imcsdk.mometa.comm.CommSnmpUser import CommSnmpUserConsts + + auth_type = ( + CommSnmpUserConsts.SECURITY_LEVEL_AUTHNOPRIV, + CommSnmpUserConsts.SECURITY_LEVEL_AUTHPRIV + ) + + priv_type = ( + CommSnmpUserConsts.SECURITY_LEVEL_AUTHPRIV + ) + + mo = snmp_user_get(handle, name) + if mo: + params = {} + if mo.security_level != security_level: + params['security_level'] = security_level + else: + mo = _get_free_snmp_user(handle) + params = { + 'name': name, + 'security_level': security_level + } + + if mo is None: + raise ImcOperationError("Add SNMP user", + "No free user available.") + + if security_level in auth_type: + if mo.auth and mo.auth == auth: + params['auth_pwd'] = auth_pwd if change_auth_pwd else None + else: + params['auth'] = auth + params['auth_pwd'] = auth_pwd + + if security_level in priv_type: + if mo.privacy and mo.privacy == privacy: + params['privacy_pwd'] = privacy_pwd if change_privacy_pwd else None + else: + params['privacy'] = privacy + params['privacy_pwd'] = privacy_pwd + + if not params: + return mo + + mo.set_prop_multiple(**params) + mo.set_prop_multiple(**kwargs) + handle.set_mo(mo) + return mo + + +def snmp_user_exists(handle, name, security_level, change_auth_pwd=False, + change_privacy_pwd=False, **kwargs): + """ + checks if snmp user exists. + + Args: + handle (ImcHandle) + name (string): snmp username + + Returns: + True, CommSnmpUser MO if found, else False, None + + Example: + snmp_user_exists(handle, name="snmpuser") + """ + from imcsdk.mometa.comm.CommSnmpUser import CommSnmpUserConsts + + mo = snmp_user_get(handle, name) + if mo is None: + return False, None + + if not change_auth_pwd: + kwargs.pop('auth_pwd', None) + + if not change_privacy_pwd: + kwargs.pop('privacy_pwd', None) + + if security_level == CommSnmpUserConsts.SECURITY_LEVEL_NOAUTHNOPRIV: + kwargs.pop('auth', None) + kwargs.pop('auth_pwd', None) + kwargs.pop('privacy', None) + kwargs.pop('privacy_pwd', None) + elif security_level == CommSnmpUserConsts.SECURITY_LEVEL_AUTHNOPRIV: + kwargs.pop('privacy', None) + kwargs.pop('privacy_pwd', None) + + kwargs['security_level'] = security_level + + return mo.check_prop_match(**kwargs), mo + + +def snmp_user_modify(handle, name, **kwargs): + """ + Modifies snmp user. Use this after getting the id from snmp_user_exists + + Args: + handle (ImcHandle) + name (string) : SNMP user name + kwargs: Key-Value paired arguments relevant to CommSnmpUser object + + Returns: + CommSnmpUser: Managed Object + + Raises: + ImcOperationError: If user is not present + + Example: + snmp_user_modify(handle, name="snmpuser", + security_level="authpriv", auth_pwd="password", + auth="MD5", privacy="AES", privacy_pwd="password") + """ + mo = snmp_user_get(handle, name) + if mo is None: + raise ImcOperationError("Modify SNMP User", "User doesn't exist") + + mo.set_prop_multiple(**kwargs) + handle.set_mo(mo) + return mo + + +def snmp_user_delete(handle, name): + """ + deletes snmp user. + + Args: + handle (ImcHandle) + name (string): snmp username + + Returns: + None + + Raises: + ImcOperationError: If user is not present + + Example: + snmp_user_delete(handle, name="snmpuser") + + """ + from imcsdk.mometa.comm.CommSnmpUser import CommSnmpUserConsts + + mo = snmp_user_get(handle, name=name) + if mo is None: + raise ImcOperationError("Snmp User Delete", "User does not exist") + + mo.name = "" + mo.admin_action = CommSnmpUserConsts.ADMIN_ACTION_CLEAR + handle.set_mo(mo) + return mo + + +def _validate_api_prop(prop, value, api, validate_value=False, + valid_values=None): + if value is None: + raise ImcOperationError(api, "Required property '%s' missing." % ( + api, prop)) + if validate_value and value not in valid_values: + raise ImcOperationError( + api, "['%s'] Invalid value '%s'. Valid values are %s" % ( + prop, value, str(valid_values))) + + +def snmp_user_add_all(handle, users=None): + """ + Adds snmp user. + + Args: + handle (ImcHandle) + users (list): list of user dict + keys: + name (string): snmp username + security_level (string): "authpriv", "authnopriv", "noauthnopriv" + auth (string): "MD5", "SHA" + auth_pwd (string): password + for existing user + privacy (string): "AES", "DES" + privacy_pwd (string): privacy password + for existing user + example: + [{'name': 'snmpuser', + 'security_level': 'authpriv', + 'auth': 'MD5', + 'auth_pwd': 'password', + 'privacy': 'AES', + 'privacy_pwd': 'password'} + ] + + Returns: + list: List of CommSnmpUser Managed Object + + Raises: + ImcOperationError is maximum number of users already configured + + Example: + snmp_user_add_all( handle, + users = [{'name': 'snmpuser', + 'security_level': 'authpriv', + 'auth': 'MD5', 'auth_pwd': 'password', + 'privacy': 'AES', 'privacy_pwd': 'password']) + """ + from imcsdk.mometa.comm.CommSnmpUser import CommSnmpUser + from imcsdk.mometa.comm.CommSnmpUser import CommSnmpUserConsts + from imcsdk.mometa.comm.CommSnmp import CommSnmpConsts + + api = 'snmp_user_add_all' + parent_mo = _get_mo(handle, dn=SNMP_DN) + if parent_mo.admin_state != CommSnmpConsts.ADMIN_STATE_ENABLED: + raise ImcOperationError(api, 'SNMP is not enabled.') + + dn_to_user_dict = {} + mos = [] + id = 0 + for user in users: + name = user.pop('name', None) + security_level = user.pop('security_level', None) + _validate_api_prop('name', name, api) + _validate_api_prop('security_level', security_level, api) + + auth = user.pop('auth', None) + auth_pwd = user.pop('auth_pwd', None) + privacy = user.pop('privacy', None) + privacy_pwd = user.pop('privacy_pwd', None) + + params = { + 'name': name, + 'security_level': security_level + } + + if security_level == CommSnmpUserConsts.SECURITY_LEVEL_AUTHNOPRIV: + #_validate_api_prop('auth', auth, api, True, ['MD5', 'SHA']) + _validate_api_prop('auth_pwd', auth_pwd, api) + params['auth'] = auth + params['auth_pwd'] = auth_pwd + elif security_level == CommSnmpUserConsts.SECURITY_LEVEL_AUTHPRIV: + _validate_api_prop('auth', auth, api, True, ['MD5', 'SHA']) + _validate_api_prop('auth_pwd', auth_pwd, api) + _validate_api_prop('privacy', privacy, api, True, ['AES', 'DES']) + _validate_api_prop('privacy_pwd', privacy_pwd, api) + params['auth'] = auth + params['auth_pwd'] = auth_pwd + params['privacy'] = privacy + params['privacy_pwd'] = privacy_pwd + + id += 1 + mo = CommSnmpUser(parent_mo_or_dn=parent_mo, id=str(id)) + mo.set_prop_multiple(**params) + mos.append(mo) + dn_to_user_dict[mo.dn] = mo.name + + response = handle.set_mos(mos) + if response: + ret = process_conf_mos_response(response, api, False, + 'Create SNMP users failed', + snmp_users_callback, + dn_to_user_dict) + if len(ret) != 0: + error_msg = 'Create/Update SNMP users failed:\n' + for item in ret: + obj = item["Object"] + error = item["Error"] + error = sanitize_message(error) + error_msg += "[User " + obj + "] " + error + "\n" + + raise ImcOperationErrorDetail(api, error_msg, ret) + + results = {} + results["changed"] = True + results["msg"] = "" + results["msg_params"] = ret + + return results + + +def snmp_users_callback(dn, dn_to_user_dict): + return dn_to_user_dict.get(dn, "Unknown User:" + dn) + + +def snmp_user_delete_all(handle): + """ + delete all snmp users. + + Args: + handle (ImcHandle) + + Returns: + None + + Raises: + ImcOperationError: If user is not present + + Example: + snmp_user_delete_all(handle) + + """ + from imcsdk.mometa.comm.CommSnmpUser import CommSnmpUserConsts + from imcsdk.mometa.comm.CommSnmp import CommSnmpConsts + + api = 'snmp_user_delete_all' + parent_mo = _get_mo(handle, dn=SNMP_DN) + if parent_mo.admin_state != CommSnmpConsts.ADMIN_STATE_ENABLED: + raise ImcOperationError(api, 'SNMP is not enabled.') + + mos = [] + users = _get_snmp_users(handle) + for user in users: + if user.name == "": + continue + user.admin_action = CommSnmpUserConsts.ADMIN_ACTION_CLEAR + mos.append(user) + + response = handle.set_mos(mos) + if response: + process_conf_mos_response(response, api) + + +def snmp_user_exists_any(handle): + """ + Checks if any snmp user exists. + + Args: + handle (ImcHandle) + + Returns: + None + + Raises: + ImcOperationError: If user is not present + + Example: + snmp_user_exists_any(handle) + + """ + mos = _get_snmp_users(handle) + for mo in mos: + if mo.name != "": + return True + return False diff --git a/imcsdk/apis/v2/admin/ssh.py b/imcsdk/apis/v2/admin/ssh.py new file mode 100644 index 00000000..ea82f182 --- /dev/null +++ b/imcsdk/apis/v2/admin/ssh.py @@ -0,0 +1,107 @@ +# Copyright 2017 Cisco Systems, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +This module implements all the ssh related functionality +""" + +from imcsdk.apis.utils import _get_mo +from imcsdk.imcexception import ImcOperationError + +import logging + +log = logging.getLogger('imc') +_SSH_DN = 'sys/svc-ext/ssh-svc' + + +def ssh_enable(handle, port=None, session_timeout=None, **kwargs): + """ + Enables ssh Policy and sets the given properties + + Args: + handle (ImcHandle) + port (int): Port number used by SSH + session_timeout (int): No of seconds to wait before the system considers a SSH request to have timed out + kwargs: key-value paired arguments for future use + + Returns: + CommSsh object + + Raises: + ImcOperationError if the CommSsh Mo is not present + + Example: + ssh_enable(handle, 22, 120) + """ + from imcsdk.mometa.comm.CommSsh import CommSshConsts + + mo = _get_mo(handle, dn=_SSH_DN) + params = { + 'admin_state': CommSshConsts.ADMIN_STATE_ENABLED, + 'port': str(port) if port else None, + 'session_timeout': str(session_timeout) if session_timeout else None + } + mo.set_prop_multiple(**params) + mo.set_prop_multiple(**kwargs) + handle.set_mo(mo) + return mo + + +def ssh_disable(handle): + """ + Disables ssh. + + Args: + handle (ImcHandle) + + Returns: + CommSsh: Managed Object + + Raises: + ValueError: If CommSsh Mo is not present + + Example: + ssh_disable(handle) + """ + from imcsdk.mometa.comm.CommSsh import CommSshConsts + + mo = _get_mo(handle, dn=_SSH_DN) + mo.admin_state = CommSshConsts.ADMIN_STATE_DISABLED + handle.set_mo(mo) + return mo + + +def ssh_exists(handle, **kwargs): + """ + Checks if ssh is enabled or not + + Args: + handle (ImcHandle) + kwargs: Key-Value paired arguments relevant to CommSsh object + + Returns: + True/false, CommSsh MO/None + + Example: + ssh_exists(handle) + """ + from imcsdk.mometa.comm.CommSsh import CommSshConsts + + try: + mo = _get_mo(handle, dn=_SSH_DN) + except: + return False, None + + kwargs['admin_state'] = CommSshConsts.ADMIN_STATE_ENABLED + return mo.check_prop_match(**kwargs), mo + diff --git a/imcsdk/apis/v2/admin/syslog.py b/imcsdk/apis/v2/admin/syslog.py new file mode 100644 index 00000000..2f311511 --- /dev/null +++ b/imcsdk/apis/v2/admin/syslog.py @@ -0,0 +1,277 @@ +# Copyright 2017 Cisco Systems, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +This module performs the operations related to system logs +""" + +from imcsdk.imcexception import ImcOperationError + +SYSLOG_DN = 'sys/svc-ext/syslog' + +def syslog_get(handle, caller="syslog_get"): + """ + Gets syslog. + + Args: + handle (ImcHandle) + caller (string): name of the calling function + + Returns: + CommSyslog: Managed object + + Raises: + ImcOperationError + + Example: + mo = syslog_get(handle) + """ + mo = handle.query_dn(dn=SYSLOG_DN) + if mo is None: + raise ImcOperationError(caller, "syslog '%s' does not exist" % + SYSLOG_DN) + return mo + + +def syslog_configure(handle, + local_severity=None, + remote_severity=None, + **kwargs): + """ + Configures syslog. + + Args: + handle (ImcHandle) + local_severity (string): local minimmum severity to report + valid values are "alert", "critical", "debug", "emergency", "error", + "informational", "notice", "warning" + remote_severity (string): remote minimmum severity to report + valid values are "alert", "critical", "debug", "emergency", "error", + "informational", "notice", "warning" + kwargs: key-value paired arguments for future use + + Returns: + CommSyslog: Managed object + + Raises: + ImcOperationError + + Example: + mo = syslog_configure(handle, local_severity="notice") + """ + mo = syslog_get(handle) + + params = { + 'local_severity': local_severity, + 'remote_severity': remote_severity + } + + mo.set_prop_multiple(**params) + mo.set_prop_multiple(**kwargs) + handle.set_mo(mo) + return mo + + +def syslog_exists(handle, **kwargs): + """ + checks if syslog exists + + Args: + handle (ImcHandle) + kwargs: Key-Value paired arguments relevant to CommSyslog object + + Returns: + True, CommSyslog MO if found, else False, None + + Example: + syslog_exists(handle, local_severity="debug") + """ + try: + mo = syslog_get(handle) + except ImcOperationError: + return (False, None) + mo_exists = mo.check_prop_match(**kwargs) + return (mo_exists, mo if mo_exists else None) + + +def syslog_remote_get(handle, name, caller="syslog_remote_get"): + """ + Gets syslog for remote client + + Args: + handle (ImcHandle) + caller (string): name of the calling function + + Returns: + CommSyslog: Managed object + + Raises: + ImcOperationError + + Example: + mo = syslog_remote_get(handle, name="primary", caller="myfunc") + """ + dn = SYSLOG_DN + "/client-" + name + mo = handle.query_dn(dn=dn) + if mo is None: + raise ImcOperationError(caller, + "syslog remote client '%s' does not exist" % + dn) + return mo + + +def syslog_remote_enable(handle, hostname, name="primary", port="514", + **kwargs): + """ + Enables Syslog on Remote Client. + + Args: + handle (ImcHandle) + hostname (string): ip address of remote host + name (string): "primary", "secondary", "tertiary" + port(string): port + kwargs: key-value paired arguments for future use + + Returns: + CommSyslogClient: Managed object + + Raises: + ImcOperationError: If CommSyslogClient Mo is not present + + Example: + mo = syslog_remote_enable(handle, hostname="10.10.10.10", + name="primary") + """ + from imcsdk.mometa.comm.CommSyslogClient import CommSyslogClientConsts + + mo = syslog_remote_get(handle, name, caller="syslog_remote_enable") + + params = { + 'admin_state': CommSyslogClientConsts.ADMIN_STATE_ENABLED, + 'hostname': hostname, + 'port': port + } + + mo.set_prop_multiple(**params) + mo.set_prop_multiple(**kwargs) + handle.set_mo(mo) + return mo + + +def syslog_remote_disable(handle, name): + """ + Disables System log on Remote Client. + + Args: + handle (ImcHandle) + + Returns: + CommSyslogClient: Managed Object + + Raises: + ImcOperationError: If CommSyslogClient Mo is not present + + Example: + syslog_remote_disable(handle, name) + """ + from imcsdk.mometa.comm.CommSyslogClient import CommSyslogClientConsts + + mo = syslog_remote_get(handle, name) + mo.admin_state = CommSyslogClientConsts.ADMIN_STATE_DISABLED + handle.set_mo(mo) + return mo + + +def is_syslog_remote_enabled(handle, name, **kwargs): + """ + Checks if system log is enabled or not on remote server + + Args: + handle (ImcHandle) + name (string): "primary", "secondary", "tertiary" + kwargs: Key-Value paired arguments relevant to CommSyslogClient object + + Returns: + True, CommSyslogClient MO if found, else False, None + + Example: + is_syslog_remote_enabled(handle, name) + """ + from imcsdk.mometa.comm.CommSyslogClient import CommSyslogClientConsts + + try: + mo = syslog_remote_get(handle, name) + except ImcOperationError: + return (False, None) + + kwargs['admin_state'] = CommSyslogClientConsts.ADMIN_STATE_ENABLED + + mo_exists = mo.check_prop_match(**kwargs) + return (mo_exists, mo if mo_exists else None) + + +def is_syslog_remote_clear(handle, name): + """ + Checks if configuration of remote system log is at default + + Args: + handle (ImcHandle) + + Returns: + True/False, CommSyslogClient MO, None + + Raises: + ImcOperationError: If CommSyslogClient Mo is not present + + Example: + issyslog_remote_clear(handle, name) + """ + from imcsdk.mometa.comm.CommSyslogClient import CommSyslogClientConsts + + try: + mo = syslog_remote_get(handle, name) + except ImcOperationError: + return (False, None) + + kwargs = { + 'admin_state': CommSyslogClientConsts.ADMIN_STATE_DISABLED, + 'hostname': '0.0.0.0', + 'port': '514' + } + + mo_exists = mo.check_prop_match(**kwargs) + return (mo_exists, mo if mo_exists else None) + + +def syslog_remote_clear(handle, name): + """ + Clears System log on Remote Client. + + Args: + handle (ImcHandle) + + Returns: + CommSyslogClient: Managed Object + + Raises: + ImcOperationError: If CommSyslogClient Mo is not present + + Example: + syslog_remote_clear(handle, name) + """ + from imcsdk.mometa.comm.CommSyslogClient import CommSyslogClientConsts + + mo = syslog_remote_get(handle, name) + mo.admin_action = CommSyslogClientConsts.ADMIN_ACTION_CLEAR + handle.set_mo(mo) + return mo diff --git a/imcsdk/apis/v2/admin/user.py b/imcsdk/apis/v2/admin/user.py new file mode 100644 index 00000000..a68cac52 --- /dev/null +++ b/imcsdk/apis/v2/admin/user.py @@ -0,0 +1,662 @@ +# Copyright 2016 Cisco Systems, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +""" +This module implements apis to create/delete/modify local users +""" + +import logging +from imcsdk.imcexception import ImcOperationError, ImcOperationErrorDetail +from imcsdk.imccoreutils import process_conf_mos_response + +log = logging.getLogger('imc') +MAX_USERS = 15 + + +def password_strong_enable(handle): + """ + This method will enable strong password policy for users + + Args: + handle (ImcHandle) + + Returns: + AaaUserPolicy object + """ + + mos = handle.query_classid("AaaUserPolicy") + mo = mos[0] + + mo.user_password_policy = "enabled" + handle.set_mo(mo) + return mo + + +def password_strong_exists(handle): + """ + This method will check if strong password policy is enabled + + Args: + handle(ImcHandle) + + Returns: + (True, Mo) or (False, None) + """ + + mos = handle.query_classid("AaaUserPolicy") + if len(mos) == 0: + raise ImcOperationError("Check Password Strength", "MO does not exist") + + mo = mos[0] + if mo.user_password_policy != "enabled": + return False, None + return True, mo + + +def password_strong_disable(handle): + """ + This method will disable strong password policy for users + + Args: + handle (ImcHandle) + + Returns: + AaaUserPolicy object + """ + + mos = handle.query_classid("AaaUserPolicy") + mo = mos[0] + + mo.user_password_policy = "disabled" + handle.set_mo(mo) + return mo + + +def password_expire_enable(handle, + password_expiry_duration, + password_history=None, + password_notification_period=None, + password_grace_period=None): + """ + This method sets up the password expiration policy for local users + + Args: + handle(ImcHandle) + password_expiry_duration(int): The time period after which the set + password expires. + Setting this to zero will disable password expiry. + password_history(int): Specifies in number of instances, the + new password entered should not have been used in the past. + password_notification_period(int): Specifies in number of days, the + user will be notified before password expiry. + password_grace_period(int): Specifies in number of days, the + old password will still be valid after the password expiry. + + Returns: + AaaUserPasswordExpiration object + """ + + from imcsdk.mometa.aaa.AaaUserPasswordExpiration import \ + AaaUserPasswordExpiration + + mo = AaaUserPasswordExpiration(parent_mo_or_dn="sys/user-ext") + if not password_expiry_duration > 0: + raise ImcOperationError("Enable password expiration", + "password_expiry_duration should be > 0.") + args = { + "password_expiry_duration": str(password_expiry_duration) + if password_expiry_duration is not None else None, + "password_history": str(password_history) + if password_history is not None else None, + "password_notification_period": str(password_notification_period) + if password_notification_period is not None else None, + "password_grace_period": str(password_grace_period) + if password_grace_period is not None else None, + } + + mo.set_prop_multiple(**args) + handle.set_mo(mo) + + +def password_expire_exists(handle, **kwargs): + """ + This method will check if the password expiration policy exists + + Args: + handle (ImcHandle) + kwargs: key-value paired arguments + + Returns: + (True, AaaUserPasswordExpiration) is policy exists, else (False, None) + + """ + from imcsdk.mometa.aaa.AaaUserPasswordExpiration import \ + AaaUserPasswordExpiration + + mo = AaaUserPasswordExpiration(parent_mo_or_dn="sys/user-ext") + mo = handle.query_dn(mo.dn) + if mo is None: + return False, None + + if not int(mo.password_expiry_duration): + return False, mo + + if not mo.check_prop_match(**kwargs): + return False, mo + + return True, mo + + +def password_expire_disable(handle): + """ + This method disables the password expiration policy for local users + + Args: + handle(ImcHandle) + + Returns: + AaaUserPasswordExpiration object + """ + + from imcsdk.mometa.aaa.AaaUserPasswordExpiration import \ + AaaUserPasswordExpiration + + mo = AaaUserPasswordExpiration(parent_mo_or_dn="sys/user-ext") + + args = { + "password_expiry_duration": "0" + } + + mo.set_prop_multiple(**args) + handle.set_mo(mo) + return mo + +def password_properties_exists(handle, **kwargs): + """ + This method will check if the password expiration policy exists + + Args: + handle (ImcHandle) + kwargs: key-value paired arguments + + Returns: + (True, AaaUserPasswordExpiration) is policy exists, else (False, None) + + """ + from imcsdk.mometa.aaa.AaaUserPasswordExpiration import \ + AaaUserPasswordExpiration + + mo = AaaUserPasswordExpiration(parent_mo_or_dn="sys/user-ext") + mo = handle.query_dn(mo.dn) + if mo is None: + return False, None + + if not mo.check_prop_match(**kwargs): + return False, mo + + return True, mo + + +def password_properties_set(handle, + password_expiry_duration=None, + password_history=None, + password_notification_period=None, + password_grace_period=None): + """ + This method configures password properties for local users + + Args: + handle(ImcHandle) + password_expiry_duration(int): The time period after which the set + password expires. + Setting this to zero will disable password expiry. + password_history(int): Specifies in number of instances, the + new password entered should not have been used in the past. + password_notification_period(int): Specifies in number of days, the + user will be notified before password expiry. + password_grace_period(int): Specifies in number of days, the + old password will still be valid after the password expiry. + + Returns: + AaaUserPasswordExpiration object + """ + + from imcsdk.mometa.aaa.AaaUserPasswordExpiration import \ + AaaUserPasswordExpiration + + mo = AaaUserPasswordExpiration(parent_mo_or_dn="sys/user-ext") + args = { + "password_expiry_duration": str(password_expiry_duration) + if password_expiry_duration is not None else None, + "password_history": str(password_history) + if password_history is not None else None, + "password_notification_period": str(password_notification_period) + if password_notification_period is not None else None, + "password_grace_period": str(password_grace_period) + if password_grace_period is not None else None, + } + + mo.set_prop_multiple(**args) + handle.set_mo(mo) + + +def local_users_get(handle, dump=False): + """ + This method gets the list of local users configured on the server + + Args: + handle (ImcHandle) + dump (bool) + + Returns: + List of AaaUser objects corresponding to the local users + """ + + aaa_users = _get_local_users(handle) + users = [x for x in aaa_users if x.name] + + if dump: + log.info("List of users (id, username, role, status)") + log.info("------------------------------------------") + + for user in users: + log.info(" %s %s %s %s" % + (user.id.rjust(3), user.name.center(15), + user.priv.center(15), user.account_status.center(15))) + return users + + +def _get_local_users(handle): + return handle.query_classid("AaaUser") + + +def _get_local_user(handle, name): + users = _get_local_users(handle) + for user in users: + if user.name == name: + return user + return None + + +def _get_free_user_id(handle): + from imcsdk.mometa.aaa.AaaUser import AaaUserConsts + users = _get_local_users(handle) + for user in users: + if user.account_status == AaaUserConsts.ACCOUNT_STATUS_INACTIVE and \ + not user.name: + return user.id + + raise ImcOperationError("Create Local User", + "Max number of users already configured") + + +def local_user_create(handle, name, pwd, priv="read-only", + account_status="active", change_password=False): + """ + This method will create a new local user and setup it's role. + + Args: + handle (ImcHandle) + name (string): username + pwd (string): pwd + priv (string): "admin", "read-only", "user" + account_status (string): "active", "inactive" + + Returns: + AaaUser object corresponding to the user created + + Raises: + Exception when limit on the number of users has exceeded + """ + + from imcsdk.mometa.aaa.AaaUser import AaaUser + + # (1) local_user_exists(handle, name, pwd, priv) would be used by Ansible. + # (2) local_user_exists(handle, name) would be used by user scripts. + # If the privileges have changed for an existing user, + # (1) will fail, but (2) will pass. + # In that case, Ansible will call local_user_create, which will fail + # because user exists.Hence, special handling is needed in + # local_user_exists to handle modify case. + + user = _get_local_user(handle, name) + if user: + pwd = pwd if change_password else None + return local_user_modify(handle, name=name, pwd=pwd, priv=priv, + account_status=account_status) + + available_user_id = _get_free_user_id(handle) + + new_user = AaaUser(parent_mo_or_dn="sys/user-ext", id=available_user_id) + args = {"name": name, + "pwd": pwd, + "priv": priv, + "account_status": account_status} + new_user.set_prop_multiple(**args) + + handle.set_mo(new_user) + return new_user + +def _delete_users(handle, users=None, endpoint_users=None): + """ + This method deactivates those IMC users that are NOT part of input list of users. + + Args: + handle (ImcHandle) + users (list): list of user dict + + Returns: + list: List of user ids that are NOT deactivated. Input users that are already present on IMC form this list. + boolean: flag that indicates if users were deleted + Raises: + ImcOperationError if the user is not found + """ + + from imcsdk.mometa.aaa.AaaUser import AaaUserConsts + + api = "Update Local Users" + user_mos = [] + skipped_user_ids = [] + aaa_user_prefix = "sys/user-ext/user-" + dn_to_user_dict = {} + delete_users = False + + for endpoint_user in endpoint_users: + delete_user = True + for user in users: + if user['name'] == endpoint_user.name: + delete_user = False + skipped_user_ids.append(int(endpoint_user.id)) + break + if delete_user and endpoint_user.name != 'admin': + endpoint_user.account_status = AaaUserConsts.ACCOUNT_STATUS_INACTIVE + endpoint_user.admin_action = AaaUserConsts.ADMIN_ACTION_CLEAR + dn_to_user_dict[aaa_user_prefix+str(endpoint_user.id)] = endpoint_user.name + user_mos.append(endpoint_user) + delete_users = True + # print("Need to delete:", endpoint_user.name) + + response = handle.set_mos(user_mos) + if response: + process_conf_mos_response(response, api, True, + 'Purging of previous state failed', + user_mos_callback, + dn_to_user_dict) + return skipped_user_ids, delete_users + + +def local_users_update(handle, users=None): + """ + This method will create, modify or delete local users. + It could also be a combination of these operations. + + Args: + handle (ImcHandle) + users (list): list of user dict + keys: + name (string): username + priv (string): "admin", "user", "read-only" + pwd (string): password + account_status(string): "active", "inactive" + change_password(boolean): flag used to change password + example: + [{'name':'dummy', + 'pwd': '*****', + 'priv': 'admin', + 'change_password': true, + 'account_status': 'active'}] + + Returns: + boolean: flag that indicates if users were created, modified or deleted. It could also be a combination of these operations. + + Raises: + IMCOperationError for various failure scenarios. A sample IMC Exception looks something like this: + "Update Local Users failed, error: User:dum1 - [ErrorCode]: 2003[ErrorDescription]: Operation failed. Matching old password(s), please enter a different password.; + """ + + from imcsdk.mometa.aaa.AaaUser import AaaUser + from imcsdk.imccoreutils import sanitize_message + api = "Update Local Users" + if users is None: + raise ImcOperationError(api, "Users are invalid") + if len(users) > MAX_USERS: + raise ImcOperationError(api, "Number of users exceeded max allowed limit on IMC") + update_users = False + create_users = False + endpoint_users = _get_local_users(handle) + used_ids, delete_users = _delete_users(handle, users, endpoint_users) + all_ids= range(2, MAX_USERS + 1) + free_ids = list(set(all_ids) - set(used_ids)) + create_mos = [] + modify_mos = [] + dn_to_user_dict = {} + aaa_user_prefix = "sys/user-ext/user-" + id = 0 + for user in users: + if 'name' not in user: + raise ImcOperationError(api, "User Name is invalid") + if 'pwd' not in user: + raise ImcOperationError(api, "Password is invalid") + if 'priv' not in user: + raise ImcOperationError(api, "Privilege is invalid") + if 'account_status' not in user: + account_status = "active" + else: + account_status = user['account_status'] + if 'change_password' not in user: + change_password = False + else: + change_password = user['change_password'] + name = user['name'] + pwd = user['pwd'] + priv = user['priv'] + args = {"name": name, + "pwd": pwd, + "priv": priv, + "account_status": account_status} + + # Existing users are not touched and hence we can safely check the + # endpoint users list if there is + found_user = None + l = [x for x in endpoint_users if x.name == name] + if len(l) != 0: + found_user = l[0] + if found_user: + if not change_password: + args.pop('pwd', None) + if not found_user.check_prop_match(**args): + update_users = True + dn_to_user_dict[aaa_user_prefix+str(found_user.id)] = name + found_user.set_prop_multiple(**args) + modify_mos.append(found_user) + continue + if len(free_ids) == 0 or id >= len(free_ids): + raise ImcOperationError(api,"Cannot configure more users than allowed limit on IMC") + create_users = True + free_id = free_ids[id] + dn_to_user_dict[aaa_user_prefix+str(free_id)] = name + mo = AaaUser(parent_mo_or_dn="sys/user-ext", id=str(free_id)) + mo.set_prop_multiple(**args) + create_mos.append(mo) + id += 1 + ret = [] + mos = [] + + mos.extend(modify_mos) + mos.extend(create_mos) + + response = handle.set_mos(mos) + if response: + ret = process_conf_mos_response(response, api, False, + 'Create/Update local users failed', + user_mos_callback, + dn_to_user_dict) + if len(ret) != 0: + error_msg = 'Create/Update local users failed:\n' + for item in ret: + user = item["Object"] + error = item["Error"] + error = sanitize_message(error) + error_msg += user + ": " + error + "\n" + + raise ImcOperationErrorDetail(api, error_msg, ret) + + results = {} + # print(create_users, update_users, delete_users) + results["changed"] = create_users or update_users or delete_users + results["msg"] = "" + results["msg_params"] = ret + + return results + + +def user_mos_callback(dn, dn_to_user_dict): + return dn_to_user_dict.get(dn, "Unknown User:" + dn) + + +def local_user_exists(handle, name, change_password=False, **kwargs): + """ + This method checks if a user exists with attributes passed + + Args: + handle (ImcHandle) + kwargs: key-value paired arguments used for user attributes + + Returns: + (True, AaaUser) Or (False, None) + + Examples: + user_exists(handle, user="abcd", priv="admin") + """ + + user = _get_local_user(handle, name=name) + if user is None: + return False, None + + if not change_password: + kwargs.pop('pwd', None) + + return user.check_prop_match(**kwargs), user + + +def local_user_modify(handle, name, **kwargs): + """ + This method will modify the user with the username specified + + Args: + handle (ImcHandle) + name (string): username + kwargs: key-value paired arguments + + Returns: + AaaUser object corresponding to the user created + + Raises: + Exception when user is not found + """ + + found_user = _get_local_user(handle, name=name) + if found_user is None: + raise ImcOperationError("Modify Local User", "User doesn't exist") + + found_user.set_prop_multiple(**kwargs) + handle.set_mo(found_user) + + +def local_user_delete(handle, name): + """ + This method deactivates the user referred to by the username passed + + Args: + handle (ImcHandle) + name (string): username + + Returns: + None + + Raises: + ImcOperationError if the user is not found + """ + + from imcsdk.mometa.aaa.AaaUser import AaaUserConsts + + found_user = _get_local_user(handle, name=name) + if found_user is None: + raise ImcOperationError("Delete Local User", "User doesn't exist") + + found_user.account_status = AaaUserConsts.ACCOUNT_STATUS_INACTIVE + found_user.admin_action = AaaUserConsts.ADMIN_ACTION_CLEAR + + handle.set_mo(found_user) + + +def local_user_delete_all(handle): + """ + This method deactivates all the user except admin user + + Args: + handle (ImcHandle) + + Returns: + None + + Raises: + ImcOperationError if the user is not found + """ + + from imcsdk.mometa.aaa.AaaUser import AaaUserConsts + + users = _get_local_users(handle) + for user in users: + if user.name == 'admin': + continue + user.account_status = AaaUserConsts.ACCOUNT_STATUS_INACTIVE + user.admin_action = AaaUserConsts.ADMIN_ACTION_CLEAR + handle.set_mo(user) + + +def user_sessions_get(handle, dump=False): + """ + This method gets the list of active user sessions + Args: + handle (ImcHandle) + dump (bool) + + Returns: + List of AaaSession objects + """ + + sessions = handle.query_classid("AaaSession") + if dump: + log.info( + "List of Active User Sessions(username, host, type of session):") + log.info( + "--------------------------------------------------------------") + for session in sessions: + log.info( + " %s \t%s \t%s" % (session.user, session.host, session.ui)) + + return sessions + + +def user_validate_inputs(**kwargs): + """ + This method will check if the input parameters are valid + """ + from imcsdk.mometa.aaa.AaaUser import AaaUser + np = {} + for prop in AaaUser.naming_props: + if prop in kwargs: + np[prop] = kwargs[prop] + return AaaUser(parent_mo_or_dn=None, **np).validate_inputs(**kwargs) diff --git a/imcsdk/apis/v2/server/__init__.py b/imcsdk/apis/v2/server/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/imcsdk/apis/v2/server/adaptor.py b/imcsdk/apis/v2/server/adaptor.py new file mode 100644 index 00000000..2cb69016 --- /dev/null +++ b/imcsdk/apis/v2/server/adaptor.py @@ -0,0 +1,338 @@ +# Copyright 2016 Cisco Systems, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +""" +This module provides apis to setup cisco vic adaptor properties \ +and create vnics and vhbas +""" + +from imcsdk.imccoreutils import get_server_dn +from imcsdk.apis.utils import _get_mo + + +def _get_adaptor_dn(handle, adaptor_slot, server_id=1): + server_dn = get_server_dn(handle, server_id) + return(server_dn + "/adaptor-" + str(adaptor_slot)) + + +def adaptor_unit_get(handle, adaptor_slot, server_id=1, **kwargs): + """ + This method fetches the adaptorUnit Managed Object for the specified + adaptor Slot on a server. + + Args: + handle (ImcHandle) + adaptor_slot (string): PCI slot number of the adaptor + server_id (int): Server Id for C3260 platforms + kwargs: key=value paired arguments + + Returns: + AdaptorUnit object + + Examples: + adaptor_unit_get(handle, adaptor_slot=1, server_id=1) + """ + return _get_mo(handle, dn=_get_adaptor_dn(handle, adaptor_slot, server_id)) + + +def adaptor_properties_get(handle, adaptor_slot, server_id=1, **kwargs): + """ + This method is used to get the vic adaptor properties + Args: + handle (ImcHandle) + adaptor_slot (string): PCI slot of the vic adaptor + server_id (int): Server Id to be specified for C3260 platforms + kwargs: key=value paired arguments + + Examples: + For non-3x60 platforms:- + adaptor_properties_get(handle, adaptor_slot="1") + + For 3x60 platforms:- + adaptor_properties_get(handle, adaptor_slot="1", server_id=1) + + Returns: + AdaptorGenProfile object + """ + + dn = _get_adaptor_dn(handle, adaptor_slot, server_id) + "/general" + return _get_mo(handle, dn=dn) + + +def adaptor_properties_set(handle, adaptor_slot, lldp=None, fip_mode=None, + vntag_mode=None, num_vmfex_ifs=None, + server_id=1, **kwargs): + """ + This method setups the vic adaptor properties. + A reboot will be required when these properties are changed + Args: + handle (ImcHandle) + adaptor_slot (string): PCI slot number of the adaptor + fip_mode (bool): Enable fip mode + vntag_mode (bool): Enable vntag mode + num_vmfex_ifs (int): Number of vmfex interfaces to be configured when \ + adaptor is in vntag mode + When the vntag mode is being disabled, + this property will be set to 0 + server_id (int): Server Id to be specified for C3260 platforms + kwargs: key=value paired arguments + + Examples: + For non-C3260 platforms:- + adaptor_properties_set(handle, adaptor_slot="1", + fip_mode=True) + + For C3260 platforms:- + adaptor_properties_set(handle, adaptor_slot="1", + vntag_mode=True, num_of_vm_fex_ifs=5, + server_id=2) + adaptor_properties_set(handle, adaptor_slot="1", + fip_mode=False, server_id=1) + + Returns: + AdaptorGenProfile object + """ + + from imcsdk.mometa.adaptor.AdaptorGenProfile import AdaptorGenProfile + adaptor = adaptor_unit_get(handle, adaptor_slot, server_id, **kwargs) + mo = AdaptorGenProfile(parent_mo_or_dn=adaptor.dn) + # VMFEX feature support is discontinued since 3.0(1c) release + + values = { + True: "enabled", + False: "disabled" + } + + mo.fip_mode = values.get(fip_mode) + mo.vntag_mode = values.get(vntag_mode) + mo.lldp = values.get(lldp) + + handle.set_mo(mo) + return handle.query_dn(mo.dn) + + +def adaptor_reset(handle, adaptor_slot, server_id=1, **kwargs): + """ + + Args: + handle (ImcHandle) + adaptor_slot (string): PCI slot number of the adaptor + server_id (int): Server Id to be specified for C3260 platforms + kwargs: key=value paired arguments + + Returns: + AdaptorUnit object + + """ + from imcsdk.mometa.adaptor.AdaptorUnit import AdaptorUnitConsts + mo = adaptor_unit_get(handle, adaptor_slot, server_id, **kwargs) + mo.admin_state = AdaptorUnitConsts.ADMIN_STATE_ADAPTOR_RESET + handle.set_mo(mo) + return mo + + +def vnic_get(handle, adaptor_slot, name, server_id=1, **kwargs): + """ + This method is used to get a vnic + Args: + handle (ImcHandle) + adaptor_slot (string): PCI slot number of the adaptor + name (string): Name for the vnic to be deleted + server_id (int): Server Id for C3260 platforms + kwargs: key=value paired arguments + + Returns: + AdaptorHostEthIf object + """ + + from imcsdk.mometa.adaptor.AdaptorHostEthIf import AdaptorHostEthIf + + mo = adaptor_unit_get(handle, adaptor_slot, server_id, **kwargs) + vnic_mo = AdaptorHostEthIf(parent_mo_or_dn=mo.dn, name=name) + return handle.query_dn(vnic_mo.dn) + + +def vnic_create(handle, + name, + adaptor_slot=1, + channel_number=None, + mac="AUTO", + mtu=1500, + class_of_service=None, + port_profile=None, + pxe_boot=False, + uplink_port=0, + server_id=1, + **kwargs): + """ + This method is used to create a new vnic + Args: + handle (ImcHandle) + name (string): Name for the vnic + adaptor_slot (string): PCI slot number of the adaptor + channel_number (int): channel number for the vnic + class_of_service (int): class of service. 0-6 + mac (string): mac address for the vnic + mtu (int): mtu size for the vnic + port_profile (string): port-profile name + pxe_boot (bool): enable pxe_boot. True/False + uplink_port (int): uplink port for binding the vnic. 0/1 + server_id (int): Server Id for C3260 platforms + kwargs: key=value paired arguments + + Examples: + For non-C3260 platforms:- + vnic_create(handle, adaptor_slot="1", name="test-vnic", + channel_number=10, mac="00:11:22:33:44:55", + mtu=1500, pxe_boot=True, uplink_port=0) + + For C3260 platforms: + vnic_create(handle, adaptor_slot="1", name="test-vnic", + channel_number=10, mac="00:11:22:33:44:55", + mtu=1500, pxe_boot=True, uplink_port=0, server_id=1) + + Returns: + AdaptorHostEthIf object + """ + + from imcsdk.mometa.adaptor.AdaptorHostEthIf import AdaptorHostEthIf + + mo = adaptor_unit_get(handle, adaptor_slot, server_id, **kwargs) + vnic = AdaptorHostEthIf(parent_mo_or_dn=mo.dn, name=name) + + params = { + "mac": mac, + "mtu": str(mtu), + "pxe_boot": ("disabled", "enabled")[pxe_boot], + "uplink_port": str(uplink_port), + "class_of_service": (None, str(class_of_service))[class_of_service is not None], + "channel_number": (None, str(channel_number))[channel_number is not None], + "port_profile": port_profile, + } + + vnic.set_prop_multiple(**params) + vnic.set_prop_multiple(**kwargs) + handle.add_mo(vnic, modify_present=True) + return handle.query_dn(vnic.dn) + + +def vnic_delete(handle, name, adaptor_slot=1, server_id=1, **kwargs): + """ + This method is used to delete a vnic + Args: + handle (ImcHandle) + name (string): Name for the vnic to be deleted + adaptor_slot (string): PCI slot number of the adaptor + server_id (int): Server Id for C3260 platforms + kwargs: key=value paired arguments + + Returns: + None + """ + + vnic_mo = vnic_get(handle, adaptor_slot, name, server_id, **kwargs) + if vnic_mo: + handle.remove_mo(vnic_mo) + + +def vhba_get(handle, adaptor_slot, name, server_id=1, **kwargs): + """ + This method is used to get a vhba + Args: + handle (ImcHandle) + adaptor_slot (string): PCI slot number of the adaptor + name (string): Name for the vhba to be deleted + server_id (int): Server Id for C3260 platforms + kwargs: key=value paired arguments + + Returns: + AdaptorHostEthIf object + """ + + from imcsdk.mometa.adaptor.AdaptorHostFcIf import AdaptorHostFcIf + + mo = adaptor_unit_get(handle, adaptor_slot, server_id, **kwargs) + vhba_mo = AdaptorHostFcIf(parent_mo_or_dn=mo.dn, name=name) + return handle.query_dn(vhba_mo.dn) + + +def vhba_create(handle, adaptor_slot, name, channel_number, wwnn, wwpn, + port_profile=None, san_boot=False, uplink_port=0, + server_id=1, **kwargs): + """ + This method is used to create a new vhba + Args: + handle (ImcHandle) + adaptor_slot (string): PCI slot number of the adaptor + name (string): Name for the vhba to be deleted + channel_number (int): channel number for the vnic + wwnn (string): wwnn + wwpn (string): wwpn + port_profile (string): port-profile name + san_boot (bool): san-boot + uplink_port (int): uplink port for binding the vhba. "0", "1" + server_id (int): Server Id for C3260 platforms + kwargs: key=value paired arguments + Returns: + AdaptorHostFcIf object + Examples: + For non-3x60 platforms: + vhba_create(handle, adaptor_slot="1", name="test-vhba", + channel_number=101, wwnn="10:00:11:3A:7D:D0:9A:43", + wwpn="20:00:11:3A:7D:D0:9A:43", + san_boot=True, uplink_port=0) + For 3x60 platforms: + vhba_create(handle, adaptor_slot="2", name="test-vhba", + channel_number=100, wwnn="10:00:11:3A:7D:D0:9A:43", + wwpn="20:00:11:3A:7D:D0:9A:43", + san_boot=True, uplink_port=0, server_id=2) + """ + + from imcsdk.mometa.adaptor.AdaptorHostFcIf import AdaptorHostFcIf + + mo = adaptor_unit_get(handle, adaptor_slot, server_id, **kwargs) + vhba_mo = AdaptorHostFcIf(parent_mo_or_dn=mo.dn, name=name) + + params = { + "channel_number": (None, str(channel_number))[channel_number is not None], + "wwnn": wwnn, + "wwpn": wwpn, + "port_profile": port_profile, + "san_boot": ("disabled", "enabled")[san_boot], + "uplink_port": str(uplink_port) + } + + vhba_mo.set_prop_multiple(**params) + vhba_mo.set_prop_multiple(**kwargs) + handle.add_mo(vhba_mo, modify_present=True) + return handle.query_dn(vhba_mo.dn) + + +def vhba_delete(handle, adaptor_slot, name, server_id=1, **kwargs): + """ + This method is used to delete a vnic + Args: + handle (ImcHandle) + adaptor_slot (string): PCI slot number of the adaptor + name (string): Name for the vhba to be deleted + server_id (int): Server Id for C3260 platforms + kwargs: key=value paired arguments + + Returns: + None + """ + + vhba_mo = vhba_get(handle, adaptor_slot, name, server_id, **kwargs) + if vhba_mo: + handle.remove_mo(vhba_mo) diff --git a/imcsdk/apis/v2/server/bios.py b/imcsdk/apis/v2/server/bios.py new file mode 100644 index 00000000..5f382bbb --- /dev/null +++ b/imcsdk/apis/v2/server/bios.py @@ -0,0 +1,511 @@ +# Copyright 2016 Cisco Systems, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +""" +This module provides APIs for bios related configuration like boot order +""" + +import logging +import json +import sys +import imcsdk.imccoreutils as imccoreutils +from imcsdk.imcgenutils import iteritems +from imcsdk.imcexception import ImcOperationError, ImcException +from imcsdk.apis.utils import _is_valid_arg + +log = logging.getLogger('imc') + + +def _get_bios_dn(handle, server_id=1): + server_dn = imccoreutils.get_server_dn(handle, server_id) + return (server_dn + '/bios') + + +def _get_bios_profile_mo(handle, name, server_id=1): + bios_dn = _get_bios_dn(handle, server_id) + parent_dn = bios_dn + '/profile-mgmt' + mos = handle.query_children(in_dn=parent_dn) + for mo in mos: + if mo._class_id == 'BiosProfile' and mo.name == name: + return mo + return None + + +def _get_bios_profile(handle, name, server_id=1): + mo = _get_bios_profile_mo(handle, name=name, server_id=server_id) + if mo is None: + raise ImcOperationError("Get BiosProfile: %s " % name, + "Managed Object not found") + return mo + + +def bios_profile_backup_running(handle, server_id=1, **kwargs): + """ + Backups up the running configuration of various bios tokens to create a + 'cisco_backup_profile'. + Will overwrite the existing backup profile if it exists. + + Args: + handle (ImcHandle) + server_id (int): Id of the server to perform + this operation on C3260 platforms + kwargs : Key-Value paired arguments for future use + + Returns: + BiosProfile object corresponding to the backup profile created + + Raises: + ImcOperationError if the backup profile is not created + + Examples: + bios_profile_backup_running(handle, server_id=1) + """ + + from imcsdk.mometa.bios.BiosProfileManagement import BiosProfileManagement + from imcsdk.mometa.bios.BiosProfileManagement import \ + BiosProfileManagementConsts + + mo = BiosProfileManagement(parent_mo_or_dn=_get_bios_dn(handle, server_id)) + mo.admin_action = BiosProfileManagementConsts.ADMIN_ACTION_BACKUP + mo.set_prop_multiple(**kwargs) + handle.set_mo(mo) + + return _get_bios_profile(handle, name='cisco_backup_profile', + server_id=server_id) + + +def bios_profile_upload(handle, remote_server, remote_file, protocol='tftp', + user=None, pwd=None, server_id=1, **kwargs): + """ + Uploads a user configured bios profile in json format. + Cisco IMC supports uploading a maximum of 3 profiles + + Args: + handle (ImcHandle) + remote_server (str): Remote Server IP or Hostname + remote_file (str): Remote file path + protocol (str): Protocol for downloading the certificate + ['tftp', 'ftp', 'http', 'scp', 'sftp'] + server_id (int): Id of the server to perform + this operation on C3260 platforms + kwargs: Key-Value paired arguments for future use + + Returns: + UploadBiosProfile object + + Examples: + bios_profile_upload(handle, remote_server='1.1.1.1', + remote_file='/tmp/bios_profile', protocol='scp', + user='abcd', pwd='pqrs') + """ + + from imcsdk.mometa.upload.UploadBiosProfile import UploadBiosProfile + bios_dn = _get_bios_dn(handle, server_id=server_id) + mo = UploadBiosProfile( + parent_mo_or_dn=bios_dn + '/profile-mgmt') + params = { + 'remote_server': remote_server, + 'remote_file': remote_file, + 'protocol': protocol, + 'user': user, + 'pwd': pwd + } + mo.set_prop_multiple(**params) + mo.set_prop_multiple(**kwargs) + handle.set_mo(mo) + return handle.query_dn(mo.dn) + + +def bios_profile_get(handle, name, server_id=1): + """ + Gets the bios profile corresponding to the name specified + + Args: + handle (ImcHandle) + name (str): Name of the bios profile. + Corresponds to the name field in the json file. + server_id (int): Id of the server to perform + this operation on C3260 platforms + + Returns: + BiosProfile object corresponding to the name specified + + Raises: + ImcOperationError if the bios profile is not found + + Examples: + bios_profile_get(handle, name='simple') + """ + + return _get_bios_profile_mo(handle, name=name, server_id=server_id) + + +def bios_profile_activate(handle, name, backup_on_activate=True, + reboot_on_activate=False, server_id=1, **kwargs): + """ + Activates the bios profile specified by name on the Cisco IMC Server + + Args: + handle (ImcHandle) + name (str): Name of the bios profile. + Corresponds to the name field in the json file. + backup_on_activate (bool): Backup running bios configuration + before activating this profile. + Will overwrite the previous backup. + reboot_on_activate (bool): Reboot the host/server for the newer bios + configuration to be applied. + server_id (int): Id of the server to perform + this operation on C3260 platforms. + kwargs: Key-Value paired arguments for future use. + + Returns: + BiosProfile object corresponding to the name specified + + Raises: + ImcOperationError if the bios profile is not found + + Examples: + bios_profile_activate(handle, name='simple', + backup_on_activate=True, + reboot_on_activate=False) + """ + + from imcsdk.mometa.bios.BiosProfile import BiosProfileConsts + mo = _get_bios_profile(handle, name=name, server_id=server_id) + params = { + 'backup_on_activate': ('no', 'yes')[backup_on_activate], + 'reboot_on_activate': ('no', 'yes')[reboot_on_activate], + 'enabled': 'yes', + 'admin_action': BiosProfileConsts.ADMIN_ACTION_ACTIVATE + } + mo.set_prop_multiple(**params) + mo.set_prop_multiple(**kwargs) + handle.set_mo(mo) + return handle.query_dn(mo.dn) + + +def bios_profile_delete(handle, name, server_id=1): + """ + Deletes the bios profile specified by the name on the Cisco IMC server + + Args: + handle (ImcHandle) + name (str): Name of the bios profile. + Corresponds to the name field in the json file. + server_id (int): Id of the server to perform + this operation on C3260 platforms. + + Returns: + None + + Raises: + ImcOperationError if the bios profile is not found + + Examples: + bios_profile_delete(handle, name='simple', server_id=2) + """ + from imcsdk.mometa.bios.BiosProfile import BiosProfileConsts + mo = _get_bios_profile(handle, name=name, server_id=server_id) + mo.admin_action = BiosProfileConsts.ADMIN_ACTION_DELETE + handle.set_mo(mo) + + +def is_bios_profile_enabled(handle, name, server_id=1): + """ + Args: + handle (ImcHandle) + name (str): Name of the bios profile. + Corresponds to the name field in the json file. + server_id (int): Id of the server to perform + this operation on C3260 platforms. + + Returns: + bool + + Raises: + ImcOperationError if the bios profile is not found + + Examples: + is_bios_profile_enabled(handle, + name='simple', + server_id=1) + """ + mo = _get_bios_profile(handle, name=name, server_id=server_id) + return mo.enabled.lower() in ['yes', 'true'] + + +def bios_profile_exists(handle, name, server_id=1, **kwargs): + """ + Checks if the bios profile with the specified params exists + + Args: + handle (ImcHandle) + name (str): Name of the bios profile. + Corresponds to the name field in the json file. + server_id (int): Id of the server to perform + this operation on C3260 platforms. + kwargs: Key-Value paired arguments relevant to BiosProfile object + + Returns: + (True, BiosProfile) if the settings match, else (False, None) + + Examples: + match, mo = bios_profile_exists(handle, name='simple', + enabled=True) + """ + + mo = _get_bios_profile_mo(handle, name=name, server_id=server_id) + if mo is None: + return False, None + + params = {} + + if _is_valid_arg('enabled', kwargs): + params['enabled'] = ('No', 'Yes')[kwargs.pop('enabled')] + + if not mo.check_prop_match(**params): + return False, None + + if not mo.check_prop_match(**kwargs): + return False, None + + return True, mo + + +def bios_profile_generate_json(handle, name, server_id=1, file_name=None): + """ + Generates a json output of the bios profile specified by the name on + the Cisco IMC server. + If a file name is specified, it writes the output to the file. + + Args: + handle (ImcHandle) + name (str): Name of the bios profile. + Corresponds to the name field in the json file. + server_id (int): Id of the server to perform + this operation on C3260 platforms. + + Returns: + JSON Output of the Bios Tokens + + Raises: + ImcOperationError if the bios profile is not found + + Examples: + bios_profile_generate_json(handle, name='simple', server_id=2) + """ + + output = {} + output['tokens'] = {} + + mo = _get_bios_profile(handle, name=name, server_id=server_id) + output['name'] = mo.name + output['description'] = mo.description + + tokens = handle.query_children(in_dn=mo.dn) + output['tokens'] = {x.name: x.configured_value for x in tokens} + + if file_name: + f = open(file_name, 'w') + f.write(json.dumps(output)) + f.close() + + return output + + +def bios_tokens_set(handle, tokens={}, server_id=1): + """ + Args: + handle (ImcHandle) + tokens (dictionary) : (key, value) pair of bios tokens with key being the name of the token + server_id (int): Id of the server to perform + this operation on C3260 platforms. + + Returns: + Dictionary with a failure message, if any. + + Examples: + bios_tokens_set(handle, + tokens = { + "baudRate": "19200", + "intelVtdatsSupport": "enabled", + "consoleRedirection": "com-1", + "flowControl": "rts-cts", + "sataModeSelect": "platform-default", + "txtSupport": "platform-default", + "packageCstateLimit": "C0 C1 State"}, + server_id=2) + """ + + from imcsdk.imccoreutils import load_class, sanitize_xml_parsing_error + from imcsdk.mometa.bios.BiosSettings import BiosSettings + + messages = [] + ret = {} + + bios_mo = BiosSettings(parent_mo_or_dn=_get_bios_dn(handle, server_id)) + mo_table = _get_bios_mo_table(handle, tokens, server_id) + server_mos = _get_server_bios_mo_table(handle, dn=bios_mo.dn) + + # Prepare the filtered table i.e. send only those MOs that exist on the server + table = {k: v for k, v in iteritems(mo_table) if k in server_mos} + + # Separate the MOs which have only platform-default + for mo_name, props in table.items(): + non_default_props = {k: v for k, v in iteritems(props) if v != "platform-default"} + # if there are no non-default props, it can be batched + if len(non_default_props) == 0: + # filter properties to only those applicable to the server + server_mo_props = server_mos[mo_name] + filtered_props = {k: v for k, v in iteritems(props) if k in server_mo_props and server_mo_props[k]} + # load an instance of the class + mo_class = load_class(mo_name) + filtered_props["_handle"] = handle + mo_obj = mo_class(parent_mo_or_dn=bios_mo, **filtered_props) + # pop the object from the table dictionary + table.pop(mo_name) + + # Send all the MOs with default properties in one shot + handle.set_mo(bios_mo) + + # Send the rest of the MOs + for mo_name, props in table.items(): + d = {} + server_mo_props = server_mos[mo_name] + filtered_props = {k: v for k, v in iteritems(props) + if k in server_mo_props and server_mo_props[k]} + + if len(filtered_props) != 0: + mo_class = load_class(mo_name) + filtered_props["_handle"] = handle + try: + mo_obj = mo_class(parent_mo_or_dn=bios_mo.dn, **filtered_props) + handle.set_mo(mo_obj) + except ImcException as e: + d["Object"] = mo_name + error = e.error_descr + if e.error_code == "ERR-xml-parse-error": + error = sanitize_xml_parsing_error(e.error_descr) + d["Error"] = error + messages.append(d) + continue + except Exception as e: + d["Object"] = mo_name + d["Error"] = str(e) + messages.append(d) + continue + + message = "" + + if len(messages) != 0: + message = "Following issues were seen during application of BIOS " \ + "tokens: \n" + for m in messages: + message += m["Object"] + ": " + m["Error"] + "\n" + + ret["msg"] = message + ret["msg_params"] = messages + ret["changed"] = True + return ret + + +def bios_tokens_exist(handle, tokens={}, server_id=1): + """ + Args: + handle (ImcHandle) + tokens (dictionary) : (key, value) pair of bios tokens with key being the name of the token + server_id (int): Id of the server to perform + this operation on C3260 platforms. + + Returns: + True/False based on the match with the server side tokens + + Examples: + bios_tokens_exist(handle, + tokens = { + "baudRate": "19200", + "intelVtdatsSupport": "enabled", + "consoleRedirection": "com-1", + "flowControl": "rts-cts", + "sataModeSelect": "platform-default", + "txtSupport": "platform-default", + "packageCstateLimit": "C0 C1 State"}, + server_id=2) +""" + + from imcsdk.imcexception import ImcException + parent_dn = _get_bios_dn(handle, server_id) + "/bios-settings" + mo_table = _get_bios_mo_table(handle, tokens, server_id) + + for mo_name, props in mo_table.items(): + try: + cimc_mos = handle.query_classid(class_id=mo_name) + except ImcException: + log.debug("ManagedObject:%s not found on server! Skipping" % + mo_name) + continue + cimc_mo = None + for mo in cimc_mos: + if mo.dn.startswith(parent_dn): + cimc_mo = mo + break + + if cimc_mo is None: + return False + + # Ideally construct a dictionary with only properties starting with 'vp'. + # But, we have a ready-made dictionary available, hence just re-use it. + cimc_props = cimc_mo.__dict__ + + # Skip comparison when the value to be checked with is "platform-default" and if the token exists on cimc + modified_props = {x: props[x] for x in props if props[x] != "platform-default" and cimc_props[x]} + + if modified_props: + if not cimc_mo.check_prop_match(**modified_props): + return False + + return True + + +def _get_bios_mo_table(handle, tokens={}, server_id=1): + from imcsdk.imcbiostables import bios_tokens_table + + mo_table = {} + + bios_tokens_table_platform = bios_tokens_table.get(handle.platform, + bios_tokens_table[ + 'classic']) + for token, value in tokens.items(): + entry = bios_tokens_table_platform.get(token) + if entry is None: + log.warning("Token not found: %s Platform: %s" % (token, + handle.platform)) + continue + + mo_props = mo_table.get(entry["mo_name"], {}) + mo_props[entry["prop_name"]] = value + mo_table[entry["mo_name"]] = mo_props + + return mo_table + + +def _get_server_bios_mo_table(handle, dn): + mos = handle.query_children(in_dn=dn) + mo_table = {} + + for mo in mos: + props = {k: v for k, v in iteritems(mo.__dict__) if k.startswith('vp')} + mo_table[mo._class_id] = props + + return mo_table diff --git a/imcsdk/apis/v2/server/boot.py b/imcsdk/apis/v2/server/boot.py new file mode 100644 index 00000000..0daa7e55 --- /dev/null +++ b/imcsdk/apis/v2/server/boot.py @@ -0,0 +1,574 @@ +# Copyright 2016 Cisco Systems, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +""" +This module provides APIs for bios related configuration like boot order +""" + +import logging +import imcsdk.imccoreutils as imccoreutils +from imcsdk.mometa.lsboot.LsbootDevPrecision import LsbootDevPrecision + +log = logging.getLogger('imc') + + +def boot_order_precision_get(handle, dump=False, server_id=1): + """ + Gets the precision boot order. + This is supported from EP release onwards only + + Args: + handle (ImcHandle) + dump (bool): True or False + server_id (int): Id of the server in case of C3260 platforms + + Returns: + List of dict in the format + [{"order": '2', "device-type": "pxe", "name": "pxe"}] + + Example: + boot_order_precision(handle, dump=False) + """ + + server_dn = imccoreutils.get_server_dn(handle, server_id) + parent_dn = server_dn + "/bios/bdgep" + + boot_order_list = [] + boot_device_list = handle.query_children( + in_dn=parent_dn, class_id="BiosBootDevPrecision") + + for device in boot_device_list: + device_type = device.type if device.type else str(device.type) + boot_order_list.append({"order": device.order, + "device-type": device_type, + "name": device.name}) + + sorted_boot_order_list = sorted( + boot_order_list, key=lambda item: item["order"]) + + log.debug("sorted_boot_order_list:\n%s" % sorted_boot_order_list) + + if dump: + log.info("Precision Boot Order is [Order, Type, Name]:") + log.info("--------------------------------------------") + for device in sorted_boot_order_list: + log.info(" %s %s %s" % (device["order"].ljust(5), + device["device-type"].ljust(10), + device["name"].ljust(20))) + + return sorted_boot_order_list + + +precision_device_dict = { + "hdd": { + "class_id": "LsbootHdd", + "type": "LOCALHDD" + }, + "iscsi": { + "class_id": "LsbootIscsi", + "type": "ISCSI", + "subtype": "ISCSI" + }, + "pchstorage": { + "class_id": "LsbootPchStorage", + "type": "PCHSTORAGE" + }, + "pxe": { + "class_id": "LsbootPxe", + "type": "PXE", + "subtype": "PXE" + }, + "san": { + "class_id": "LsbootSan", + "type": "SAN", + "subtype": "SAN" + }, + "sdcard": { + "class_id": "LsbootSd", + "type": "SDCARD", + "subtype": "SDCARD" + }, + "uefishell": { + "class_id": "LsbootUefiShell", + "type": "UEFISHELL" + }, + "usb": { + "class_id": "LsbootUsb", + "type": "USB" + }, + "vmedia": { + "class_id": "LsbootVMedia", + "type": "VMEDIA" + }, + "nvme": { + "class_id": "LsbootNVMe", + "type": "NVME" + }, + "cdd": { + "class_id": "LsbootCdd", + "type": "LOCALCDD" + }, +} + + +policy_device_dict = { + "efi": {"class_id": "LsbootEfi", "access": "read-only"}, + "lan": {"class_id": "LsbootLan", "access": "read-only"}, + "storage": {"class_id": "LsbootStorage", "access": "read-write"}, + "cdrom": {"class_id": "LsbootVirtualMedia", "access": "read-only"}, + "fdd": {"class_id": "LsbootVirtualMedia", "access": "read-write"} +} + + +def _is_boot_order_precision(dn): + return dn.find("precision") != -1 + + +def _is_boot_order_policy(dn): + return dn.find("policy") != -1 + + +def _get_device_type(policy_type, in_device): + if policy_type == "boot-order-policy": + for device_type, device_props in policy_device_dict.iteritems(): + if device_props["class_id"] == in_device._class_id and \ + device_props["access"] == in_device.access: + return device_type + return "" + + +def _get_device(parent_mo_or_dn, device_type, device_name): + from imcsdk.imccoreutils import load_class + + # precision boot order supports hierarchy in configConfMO + # legacy boot order does not + # so passing parent Mo to class_struct in case of precision boot order + # in case of legacy boot order, only dn is passed + if type(parent_mo_or_dn) is str: + parent = parent_mo_or_dn + parent_dn = parent_mo_or_dn + else: + parent = parent_mo_or_dn + parent_dn = parent_mo_or_dn.dn + + if _is_boot_order_precision(parent_dn): + if device_type not in precision_device_dict: + return None + class_struct = load_class( + precision_device_dict[device_type]["class_id"]) + class_obj = class_struct(parent_mo_or_dn=parent, name=device_name) + if "type" in precision_device_dict[device_type]: + class_obj.type = precision_device_dict[device_type]["type"] + if "subtype" in precision_device_dict[device_type]: + class_obj.subtype = precision_device_dict[device_type]["subtype"] + elif _is_boot_order_policy(parent_dn): + if device_type not in policy_device_dict: + return None + class_struct = load_class(policy_device_dict[device_type]["class_id"]) + access = policy_device_dict[device_type]["access"] + ''' + cdrom and fdd are of type LsbootVirtualMedia and have "access" as the + naming property. Other objects under LsbootDef do not need this. + Hence cdrom and fdd need special handling below. + ''' + if device_type in ["cdrom", "fdd"]: + class_obj = class_struct(parent_mo_or_dn=parent_dn, access=access) + else: + class_obj = class_struct(parent_mo_or_dn=parent_dn) + class_obj.access = access + else: + return None + + return class_obj + + +def _add_boot_device(handle, parent_mo_or_dn, boot_device): + """ + This method verifies and adds the boot device in the boot order + Used by boot_order_precision_set and boot_order_policy_set + + Args: + handle(ImcHandle) + boot_device(dict): This is a dictionary of the format + {"order":'1', "device-type":"vmedia", "name": "vmedia"} + + Returns: + None + """ + + log.debug("######### %s" % boot_device) + device = _get_device(parent_mo_or_dn, + boot_device["device-type"], + boot_device["name"]) + if device is None: + raise ValueError( + "Unsupported boot-device %s with label %s" % + (boot_device["device-type"], boot_device["name"])) + + device.order = boot_device["order"] + device_props = {key: str(value) for key, value in boot_device.iteritems() + if key not in ["order", "device-type", "name"]} + device.set_prop_multiple(**device_props) + if hasattr(device, "state"): + device.state = boot_device['state'] + + # applies for legacy boot order only + if type(parent_mo_or_dn) is str: + handle.add_mo(device, modify_present=True) + + +def boot_order_precision_set( + handle, + reboot_on_update="no", + reapply="no", + configured_boot_mode="Legacy", + secure_boot="no", + boot_devices=[], + server_id=1): + """ + This method will replace the existing boot order precision with the new one + and also set the boot mode + This functionality is available only in release EP and above + + Args: + handle (ImcHandle) + reboot_on_update (string): "yes", "no" + reapply(string): "yes", "no" + configured_boot_mode(string): "Legacy", "Uefi", "None" + boot_devices (list of dict): format + [{"order":'1', "device-type":"vmedia", "name":"vmedia"}, + {"order":'2', "device-type":"hdd", "name":"hdd"}] + + boot-order(string): Order + boot-device-type(string): "hdd", "iscsi", "pchstorage", "pxe", + "san", "sdcard", "uefishell", "usb", + "vmedia" + boot-device-name(string): Unique label for the boot device + server_id (int): Id of the server to perform + this operation on C3260 platforms + + Returns: + LsBootDevPrecision object + + Examples: + boot_order_precision_set( + handle, + reboot_on_update="no", + reapply="no", + configured_boot_mode="Uefi", + boot_devices = [{"order":'1', "device-type":"vmedia", + "name":"vmedia"}, + {"order":'2', "device-type":"hdd", "name":"hdd"}] + """ + from imcsdk.mometa.lsboot.LsbootDef import LsbootDef + from imcsdk.mometa.lsboot.LsbootBootSecurity import LsbootBootSecurity + + boot_devices = sanitize_input_from_intersight(boot_devices) + + # Insert version check here to gracefully handle older versions of CIMC + + # IMC expects the devices to be configured in sorted order + boot_devices = sorted(boot_devices, key=lambda x: int(x["order"])) + + server_dn = imccoreutils.get_server_dn(handle, server_id) + + # secure boot is a part of LsBootDef + boot_policy = LsbootDef(parent_mo_or_dn=server_dn) + secure_boot_mo = LsbootBootSecurity(parent_mo_or_dn=boot_policy.dn) + if secure_boot == "yes": + secure_boot_mo.secure_boot = "enabled" + else: + secure_boot_mo.secure_boot = "disabled" + handle.set_mo(secure_boot_mo) + + lsbootdev = LsbootDevPrecision(parent_mo_or_dn=server_dn) + + # clean existing configuration + # Need to check if doing this everytime will have any adverse impact + boot_order_child_mos = handle.query_children(in_dn=lsbootdev.dn) + for mo in boot_order_child_mos: + if mo.get_class_id() == "LsbootCdd": + # Deletion of LsbootCdd is not yet supported using XML API + # Remove this check when CSCvh47929 is fixed + # Existing Cdd device will automatically move down the + # order when configuring other devices with CDD device's order + continue + handle.remove_mo(mo) + + # set the boot order precision related properties and devices + lsbootdev.reboot_on_update = reboot_on_update + lsbootdev.reapply = reapply + if secure_boot == "no": + lsbootdev.configured_boot_mode = configured_boot_mode + + for device in boot_devices: + _add_boot_device(handle, lsbootdev, device) + + handle.set_mo(lsbootdev) + return lsbootdev + + +def boot_precision_configured_get(handle, server_id=1): + from imcsdk.imccoreutils import get_server_dn + + configured_boot_order = [] + + class_to_name_dict = { + value["class_id"]: key for key, + value in precision_device_dict.items()} + + server_dn = get_server_dn(handle, server_id) + pmo = LsbootDevPrecision(parent_mo_or_dn=server_dn) + mos = handle.query_children(in_dn=pmo.dn) + for mo in mos: + if mo._class_id not in class_to_name_dict: + print("unknown boot device type " + mo._class_id) + continue + device = {"order": mo.order, + "device-type": class_to_name_dict[mo._class_id], + "name": mo.name} + configured_boot_order.append(device) + return sorted(configured_boot_order, key=lambda x: int(x["order"])) + + +def boot_order_precision_exists(handle, **kwargs): + from imcsdk.imccoreutils import _set_server_dn + from imcsdk.apis.utils import _is_valid_arg + + server_dn = _set_server_dn(handle, kwargs) + mos = handle.query_children(in_dn=server_dn, + class_id="LsbootDevPrecision") + if len(mos) == 0: + return False, "no Mos found" + + mo = mos[0] + + args = { + "configured_boot_mode": kwargs.get("configured_boot_mode") + } + if not mo.check_prop_match(**args): + return False, "parent MO property values do not match" + + if _is_valid_arg("boot_devices", kwargs): + boot_devices = kwargs["boot_devices"] + boot_devices = sanitize_input_from_intersight(boot_devices) + + in_boot_order = sorted( + boot_devices, + key=lambda x: int(x["order"])) + configured_boot_order = boot_precision_configured_get( + handle, kwargs.get("server_id")) + + if len(in_boot_order) != len(configured_boot_order): + return False, "length mismatch" + for i in range(0, len(in_boot_order)): + bt_ord = in_boot_order[i] + cfg_bt_ord = configured_boot_order[i] + if not (bt_ord["order"] == cfg_bt_ord["order"] and + bt_ord["device-type"] == cfg_bt_ord["device-type"] and + bt_ord["name"] == cfg_bt_ord["name"]): + return False, "dictionaries do not match" + return True, None + + +def boot_order_policy_get(handle, dump=False, server_id=1): + """ + Gets the boot order. This is the legacy boot order + + Args: + handle (ImcHandle) + dump (bool): True or False + server_id (int): Id of the server to perform + this operation on C3260 platforms + + Returns: + List of dict in the format + [{"order": '1', "device-type": "pxe", "name": "pxe"}] + + Example: + boot_order_policy_get(handle, dump=False) + """ + + from imcsdk.mometa.lsboot.LsbootBootSecurity import LsbootBootSecurity + + server_dn = imccoreutils.get_server_dn(handle, server_id) + parent_dn = server_dn + "/boot-policy" + + boot_order_list = [] + child_mo_list = handle.query_children( + in_dn=parent_dn) + boot_security_policy = LsbootBootSecurity( + parent_mo_or_dn=parent_dn) + + for device in child_mo_list: + if device.dn == boot_security_policy.dn: + continue + + device_name = "NA" + if hasattr(device, "name"): + device_name = device.name + + device_type = _get_device_type("boot-order-policy", device) + boot_order_list.append({"order": device.order, + "device-type": device_type, + "name": device_name}) + + sorted_boot_order_list = sorted( + boot_order_list, key=lambda x: int(x["order"])) + + if dump: + log.info("Boot Order according to Policy is [Order, Type, Name]:") + log.info("------------------------------------------------------") + + for device_tuple in sorted_boot_order_list: + log.info( + " %s %s %s" % + (device_tuple["order"].ljust(5), + device_tuple["device-type"].center(10), + device_tuple["name"].center(20))) + + return sorted_boot_order_list + + +def boot_order_policy_set(handle, reboot_on_update="no", + secure_boot="no", + boot_devices=[], + server_id=1): + """ + This method will set the boot order policy passed from the user + This is the deprecated way of setting the boot order + and is applicable releases older than EP + + Args: + handle (ImcHandle) + reboot_on_update (string): "yes", "no" + secure_boot (string): "enabled", "disabled" + boot_devices (list of dict): format + [{"order":'1', "device-type":"cdrom", "name":"cdrom0"}, + {"order":'2', "device-type":"lan", "name":"lan"}] + + boot-order(string): Order + boot-device-type(string): "efi", "lan", "storage", "cdrom", "fdd" + boot-device-name(string): Unique label for the boot device + server_id (int): Id of the server to perform + this operation on C3260 platforms + Returns: + LsBootDef object + + Examples: + boot_order_policy_set( + handle, + reboot_on_update="yes", + secure_boot="no", + boot_devices = [{"order":'1', "device-type":"cdrom", + "name":"cdrom0"}, + {"order":'2', "device-type":"lan", "name":"lan"}] + + + """ + + # IMC expects the devices to be configured in sorted order + boot_devices = sorted(boot_devices, key=lambda x: int(x["order"])) + + from imcsdk.mometa.lsboot.LsbootDef import LsbootDef + from imcsdk.mometa.lsboot.LsbootBootSecurity import LsbootBootSecurity + + server_dn = imccoreutils.get_server_dn(handle, server_id) + + boot_policy = LsbootDef(parent_mo_or_dn=server_dn) + boot_policy.reboot_on_update = reboot_on_update + handle.set_mo(boot_policy) + + secure_boot_policy = LsbootBootSecurity(parent_mo_or_dn=boot_policy.dn) + # Secure boot policy is supported only from ImcVersion 2.0(1a) + if handle.version >= secure_boot_policy.get_version(handle.platform): + if secure_boot == "yes": + secure_boot_policy.secure_boot = "enabled" + else: + secure_boot_policy.secure_boot = "disabled" + handle.set_mo(secure_boot_policy) + + boot_policy_child_mos = handle.query_children(in_dn=boot_policy.dn) + for mo in boot_policy_child_mos: + if mo.dn == secure_boot_policy.dn: + continue + handle.remove_mo(mo) + + for device in boot_devices: + _add_boot_device(handle, boot_policy.dn, device) + + boot_policy = handle.query_classid("LsbootDef") + return boot_policy + + +def convert_type(intersight_type): + switcher = { + "boot.LocalDisk": "hdd", + "boot.Pxe": "pxe", + "boot.Iscsi": "iscsi", + "boot.Nvme": "nvme", + "boot.PchStorage": "pchstorage", + "boot.San": "san", + "boot.SdCard": "sdcard", + "boot.UefiShell": "uefishell", + "boot.Usb": "usb", + "boot.VirtualMedia": "vmedia", + "boot.LocalCdd": "cdd" + } + return switcher.get(intersight_type, None) + + +def sanitize_input_from_intersight(boot_devices): + """ + Intersight always sends boot devices in the right order. For this + reason the order propery is not populated by intersight policy + service. We populate the order property here. + + We also convert from intersight device type to sdk device type + """ + import copy + log.debug("##### Input boot devices %s" % boot_devices) + # if order is present, then it is not an input from intersight + if len(boot_devices) > 0 and "order" in boot_devices[0]: + return boot_devices + + bd_copy = copy.deepcopy(boot_devices) + order = 0 + bd = [] + for each in bd_copy: + order += 1 + each["order"] = str(order) + each["device-type"] = convert_type(each["ObjectType"]) + if each["device-type"] is None: + print("Unknown device type " + each["ObjectType"]) + continue + + enabled = each.get("Enabled", True) + if enabled: + each["state"] = "Enabled" + else: + each["state"] = "Disabled" + + # convert MacAddress to mac_address + if each["device-type"] == "pxe": + if each["MacAddress"]: + each["mac_address"] = each["MacAddress"] + each.pop("MacAddress", None) + + each.pop("ObjectType", None) + each.pop("Type", None) + each.pop("Enabled", None) + bd.append({k.lower(): v for k, v in each.items()}) + log.debug("##### Sanitized boot devices %s" % bd) + return bd diff --git a/imcsdk/apis/v2/server/health.py b/imcsdk/apis/v2/server/health.py new file mode 100644 index 00000000..90fa818f --- /dev/null +++ b/imcsdk/apis/v2/server/health.py @@ -0,0 +1,52 @@ +# Copyright 2016 Cisco Systems, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +""" +This module demonstrates the use of health and fault related functionality +""" + + +def faults_get(handle, parent_class_id=None, dump=False): + """ + Fetch fault related information. + By default, fetches all the faults in the system. + + Args: + handle (ImcHandle) + parent_class_id (string): Class Id for which faults are needed. + dump (bool): True or False + + Returns: + FaultInst: List of Managed Objects + + Examples: + faults_get(handle, parent_class_id="computeRackUnit", dump=False) + faults_get(handle, dump=True) + """ + + from imcsdk.imccoreutils import write_object + + if parent_class_id: + fault_list = [] + pmos = handle.query_classid(parent_class_id) + for mo in pmos: + fault_list.append(handle.query_children(in_mo=mo, + class_id="FaultInst")) + else: + fault_list = handle.query_classid(class_id="FaultInst") + + if dump: + write_object(fault_list) + + return fault_list diff --git a/imcsdk/apis/v2/server/inventory.py b/imcsdk/apis/v2/server/inventory.py new file mode 100644 index 00000000..d18081d9 --- /dev/null +++ b/imcsdk/apis/v2/server/inventory.py @@ -0,0 +1,406 @@ +# Copyright 2016 Cisco Systems, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +This module provides apis to query server inventory +""" + +import json +from imcsdk.imcexception import ImcOperationError + +inventory_spec = { + "cpu": { + "class_id": "ProcessorUnit", + "props": [ + {"prop": "dn"}, + {"prop": "id"}, + {"prop": "model"}, + {"prop": "vendor"}, + {"prop": "arch"}] + }, + "memory": { + "class_id": "MemoryUnit", + "ignore": [ + {"prop": "presence", "value": "missing"} + ], + "props": [ + {"prop": "dn"}, + {"prop": "id"}, + {"prop": "model"}, + {"prop": "vendor"}, + {"prop": "serial"}, + {"prop": "capacity"}, + {"prop": "clock"}, + {"prop": "presence"}] + }, + "psu": { + "class_id": "EquipmentPsu", + "ignore": [ + {"prop": "presence", "value": "missing"} + ], + "props": [ + {"prop": "id"}, + {"prop": "model"}, + {"prop": "vendor"}, + {"prop": "serial"}, + {"prop": "fw_version"}] + }, + "pci": { + "class_id": "PciEquipSlot", + "props": [ + {"label": "Server", "prop": "dn"}, + {"prop": "id"}, + {"prop": "model"}, + {"prop": "vendor"}, + {"prop": "version"}] + }, + "vic": { + "class_id": "AdaptorUnit", + "props": [ + {"label": "Server", "prop": "dn"}, + {"prop": "id"}, + {"prop": "model"}, + {"prop": "vendor"}, + {"prop": "serial"}, + {"label": "PCI-slot", "prop": "pci_slot"}] + }, + "lom": { + "class_id": "NetworkAdapterUnit", + "props": [ + {"prop": "model"}, + {"label": "PCI-Slot", "prop": "slot"}, + {"label": "Num-Interfaces", "prop": "num_intf"}], + }, + "tpm": { + "class_id": "EquipmentTpm", + "props": [ + {"prop": "dn"}, + {"prop": "model"}, + {"prop": "vendor"}, + {"prop": "serial"}, + {"label": "Revision", "prop": "tpm_revision"}] + }, + "storage": { + "class_id": "StorageController", + "props": [ + {"label": "Server", "prop": "dn"}, + {"prop": "model"}, + {"prop": "vendor"}, + {"prop": "serial"}, + {"prop": "type"}, + {"label": "PCI-slot", "prop": "pci_slot"}, + {"label": "Firmware", "prop": "firmware_package_build", "class": "StorageControllerProps", "method": "query_children"}] + }, + "disks": { + "class_id": "StorageLocalDisk", + "props": [ + {"prop": "id"}, + {"label": "Model", "prop": "product_id"}, + {"prop": "vendor"}, + {"label": "Serial Number", "prop": "drive_serial_number"}, + {"label": "PD Status", "prop": "pd_status"}, + {"prop": "health"}, + {"label": "Link Speed", "prop": "link_speed"}, + {"label": "Interface Type", "prop": "interface_type"}, + {"label": "Media Type", "prop": "media_type"}, + {"label": "Size", "prop": "coerced_size"}, + {"label": "Firmware", "prop": "drive_firmware"}, + {"label": "Drive State", "prop": "drive_state"}, + {"prop": "online"} + ] + }, + "vNICs": { + "class_id": "AdaptorHostEthIf", + "props": [ + {"prop": "dn"}, + {"prop": "name"}, + {"prop": "cdn"}, + {"prop": "mac"}, + {"prop": "mtu"}, + {"prop": "pxe_boot"}, + {"prop": "iscsi_boot"}, + {"prop": "usnic_count"}, + {"prop": "uplink_port"}, + {"prop": "class_of_service"}, + {"prop": "channel_number"}, + {"prop": "port_profile"} + ] + }, + "vHBAs": { + "class_id": "AdaptorHostFcIf", + "props": [ + {"prop": "dn"}, + {"prop": "name"}, + {"prop": "wwnn"}, + {"prop": "wwpn"}, + {"prop": "uplink_port"}, + {"prop": "san_boot"}, + {"prop": "channel_number"}, + {"prop": "port_profile"}, + {"prop": "admin_persistent_bindings"} + ] + } +} + + +def _sanitize_and_store(mo_dict, prop, mo): + value = getattr(mo, prop, None) + if value: + value = value.strip() + mo_dict[prop] = value + + +def _should_ignore(comp, obj): + if "ignore" not in comp: + return False + + for ig in comp["ignore"]: + name, value = ig["prop"], ig["value"] + if getattr(obj, name, None) == value: + return True + return False + + +def _check_and_create_key(ds, key, value={}): + if key in ds: + return + ds[key] = value + + +def _get_inventory(handle, comp, spec, inventory): + component = spec[comp] + class_id = component["class_id"] + mos = handle.query_classid(class_id) + + ip = handle.ip + _check_and_create_key(ds=inventory, key=ip, value={}) + _check_and_create_key(ds=inventory[ip], key=comp, value=[]) + inv_comp = inventory[ip][comp] + for mo in mos: + mo_dict = {} + for each in component["props"]: + prop = each["prop"] + class_id = each["class"] if "class" in each else None + method = each["method"] if "method" in each else None + + if class_id: + if method == "query_children": + sub_mos = handle.query_children(in_dn=mo.dn, + class_id=class_id) + sub_mo = sub_mos[0] + + if sub_mo: + if _should_ignore(component, sub_mo): + continue + _sanitize_and_store(mo_dict, prop, sub_mo) + else: + if _should_ignore(component, mo): + continue + _sanitize_and_store(mo_dict, prop, mo) + if len(mo_dict) > 0: + inv_comp.append(mo_dict) + + +def _get_inventory_csv(inventory, file_name, spec=inventory_spec): + import csv + if file_name is None: + raise ImcOperationError("Inventory collection", + "file_name is a required parameter") + f = csv.writer(open(file_name, "w")) + + x = inventory + for comp in spec: + f.writerow([comp.upper()]) + props = spec[comp]["props"] + keys = [y['prop'] for y in props] + keys.insert(0, "Host") + f.writerow(keys) + + for ip in x: + if comp not in x[ip]: + continue + host_component = x[ip][comp] + if len(host_component) == 0: + continue + for entry in host_component: + row_val = [] + for key in keys: + if key not in entry: + continue + row_val.append(entry[key]) + row_val.insert(0, ip) + f.writerow(row_val) + + f.writerow([]) + f.writerow([]) + + +def _get_search_script(): + script = """ + +""" + return script + + +def _get_inventory_html(inventory, file_name, spec=inventory_spec): + if file_name is None: + raise ImcOperationError("Inventory collection", + "file_name is a required parameter") + f = open(file_name, "w") + + html = "" + html += "\n" + + html += "\n" + html += _get_search_script() + html += "\n" + html += "\n" + html += """ +
+ +
+ """ + + x = inventory + for comp in spec: + html += '' + html += "

" + comp.upper() + + props = spec[comp]["props"] + keys = [y['prop'] for y in props] + keys.insert(0, "Host") + html += '' + for key in keys: + html += "" + html += '' + + for ip in x: + if comp not in x[ip]: + continue + host_component = x[ip][comp] + if len(host_component) == 0: + continue + for entry in host_component: + row_val = [] + for key in keys: + if key not in entry: + continue + row_val.append(entry[key]) + row_val.insert(0, ip) + html += "" + for each in row_val: + if each is None: + each = "" + html += "" + html += "" + + html += "
" + key + "
" + each + "
\n" + html += "" + html += "" + f.write(html) + f.close() + + +def inventory_get(handle, + component="all", + file_format="json", + file_name=None, + spec=inventory_spec): + """ + This method fetches the inventory of the server for various + items like cpus, memory, psu or the entire server. + + Args: + handle (ImcHandle or list of ImcHandle): + Can consume a single handle or a list of handles + types (string): comma separated values for the components + "all" - will get inventory for all components + For individual components use - + "cpu, disk, memory, psu, pci, vic, lom, storage, tpm" + file_format (string): "json", "csv", "html" + file_name (string): file name to save the data to. + spec (dictionary): only for advanced usage + + Returns: + json formatted inventory data. additionally data is also written to + a file, if one is specified. + """ + + inventory = {} + + if isinstance(handle, list): + servers = handle + else: + servers = [handle] + + for server in servers: + components = component + if not isinstance(component, list): + components = [component] + if "all" in components: + for comp in spec.keys(): + _get_inventory(server, comp, spec, inventory) + else: + for comp in components: + if comp not in spec: + raise ImcOperationError("Inventory Collection", + ("Unsupported component type:" + + str(component))) + + _get_inventory(server, comp, spec, inventory) + + if file_format == "csv": + _get_inventory_csv(inventory=inventory, file_name=file_name, spec=spec) + elif file_format == "html": + _get_inventory_html(inventory=inventory, file_name=file_name, spec=spec) + elif file_format == "json" and file_name: + f = open(file_name, 'w') + f.write(json.dumps(inventory)) + f.close() + + return inventory diff --git a/imcsdk/apis/v2/server/kmip.py b/imcsdk/apis/v2/server/kmip.py new file mode 100644 index 00000000..71fb78e5 --- /dev/null +++ b/imcsdk/apis/v2/server/kmip.py @@ -0,0 +1,176 @@ +# Copyright 2017 Cisco Systems, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +""" +This module implements all the kmip related config +""" + +from imcsdk.imcexception import ImcOperationError +from imcsdk.imccoreutils import IMC_PLATFORM + + +def _get_server_dn(handle, server_id="1"): + """ + This method gives the dn for a particular rack server based on + the type of platform + + For classic: "sys/rack-unit-1" + For modular: "sys/chassis-1/server-" + """ + + if handle.platform == IMC_PLATFORM.TYPE_CLASSIC: + return "sys" + elif handle.platform == IMC_PLATFORM.TYPE_MODULAR: + return "sys/chassis-1/server-" + str(server_id) + else: + raise ImcOperationError("Unknown platform", "type:%s detected" % + handle.platform) + + +def _get_dn_kmip_mgmt(handle, server_id=1): + return _get_server_dn(handle, server_id) + "/kmip-mgmt" + + +def kmip_mgmt_get(handle, server_id=1, caller="kmip_mgmt_get"): + parent_dn = _get_server_dn(handle, server_id) + dn = parent_dn + "/kmip-mgmt" + mo = handle.query_dn(dn) + if mo is None: + raise ImcOperationError(caller, + "KMIP management '%s' doesn't exist." % dn) + return mo + + +def kmip_mgmt_enable(handle, server_id=1, **kwargs): + """ + This method will enable kmip management server. + + Args: + handle (ImcHandle) + server_id (int): Server Id to be specified for C3260 platforms + + Returns: + KmipManagement object + """ + + mo = kmip_mgmt_get(handle, server_id=server_id, caller="kmip_mgmt_enable") + params = { + "secure_key_management": "enabled", + } + + mo.set_prop_multiple(**kwargs) + mo.set_prop_multiple(**params) + handle.set_mo(mo) + return mo + + +def kmip_mgmt_exists(handle, server_id=1, **kwargs): + try: + mo = kmip_mgmt_get(handle, server_id=server_id, caller="sol_exists") + except ImcOperationError: + return (False, None) + + kwargs['secure_key_management'] = "enabled" + + mo_exists = mo.check_prop_match(**kwargs) + return mo_exists, mo + + +def kmip_mgmt_disable(handle, server_id=1): + """ + This method will disable kmip management server. + + Args: + handle (ImcHandle) + server_id (int): Server Id to be specified for C3260 platforms + + Returns: + KmipManagement object + """ + + mo = kmip_mgmt_get(handle, server_id=server_id, caller="kmip_mgmt_disable") + params = { + "secure_key_management": "disabled", + } + + mo.set_prop_multiple(**params) + handle.set_mo(mo) + return mo + + +def _get_kmip_servers(handle, server_id=1): + kmip_mgmt = kmip_mgmt_get(handle, server_id) + return handle.query_children(in_mo=kmip_mgmt, class_id="KmipServer") + + +def _get_kmip_server(handle, ip_address, server_id=1): + kmip_servers = _get_kmip_servers(handle, server_id) + for kmip_server in kmip_servers: + if kmip_server.ip_address == ip_address: + return kmip_server + return None + + +def _get_free_kmip_server_id(handle, server_id=1): + kmip_servers = _get_kmip_servers(handle, server_id) + kmip_ids = [] + for kmip_server in kmip_servers: + if not kmip_server.ip_address: + kmip_ids.append(int(kmip_server.id)) + + if not kmip_ids: + raise ImcOperationError("Add KMIP Server", + "Max number of servers already added.") + + return str(min(kmip_ids)) + + +def kmip_server_add(handle, ip_address, port=None, timeout=None, server_id=1): + from imcsdk.mometa.kmip.KmipServer import KmipServer + + args = { + "ip_address": ip_address, + "port": str(port) if port else None, + "timeout": str(timeout) if timeout else None + } + + mo = _get_kmip_server(handle, ip_address, server_id) + if not mo: + id = _get_free_kmip_server_id(handle, server_id) + parent_dn = _get_dn_kmip_mgmt(handle, server_id) + mo = KmipServer(parent_mo_or_dn=parent_dn, id=id) + + mo.set_prop_multiple(**args) + handle.set_mo(mo) + return mo + + +def kmip_server_exists(handle, ip_address, server_id=1, **kwargs): + mo = _get_kmip_server(handle, ip_address, server_id) + if mo is None: + return False, None + + return mo.check_prop_match(**kwargs), mo + + +def kmip_server_remove(handle, ip_address, server_id=1): + from imcsdk.mometa.kmip.KmipServer import KmipServerConsts + + mo = _get_kmip_server(handle, ip_address, server_id) + if mo is None: + raise ImcOperationError("kmip_server_remove", + "KMIP server does not exist.") + + mo.admin_action = KmipServerConsts.ADMIN_ACTION_DELETE + handle.set_mo(mo) diff --git a/imcsdk/apis/v2/server/kvm.py b/imcsdk/apis/v2/server/kvm.py new file mode 100644 index 00000000..1dab8fb5 --- /dev/null +++ b/imcsdk/apis/v2/server/kvm.py @@ -0,0 +1,102 @@ +# Copyright 2016 Cisco Systems, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +This module implements all the kvm and sol related samples +""" + +import re + +from imcsdk.mometa.comm.CommKvm import CommKvm +from imcsdk.apis.admin.ipmi import _get_comm_mo_dn + +CIFS_URI_PATTERN = re.compile('^//\d+\.\d+\.\d+\.\d+\/') +NFS_URI_PATTERN = re.compile('^\d+\.\d+\.\d+\.\d+\:\/') + + +def kvm_enable(handle, total_sessions=None, port=None, encryption_state=None, + local_video_state=None, server_id=1): + """ + This method will setup and enable kvm console access + + Args: + handle (ImcHandle) + total_sessions (int): Max no. of sessions allowed (1-4) + port (int): Port used for kvm communication + encryption_state (str): Encrypt video information sent over kvm + local_video_state (str): Mirror the kvm session on local monitor + server_id (int): Server Id to be specified for C3260 platforms + + Returns: + CommKvm object + + Examples: + kvm_setup(handle, + total_sessions=4, + port=4000, + encryption_state="enabled", + local_video_state="enabled") + """ + + kvm_mo = CommKvm(parent_mo_or_dn=_get_comm_mo_dn(handle, server_id)) + params = { + "admin_state": "enabled", + "total_sessions": str(total_sessions) if total_sessions else None, + "port": str(port) if port else None, + "encryption_state": encryption_state, + "local_video_state": local_video_state, + } + + kvm_mo.set_prop_multiple(**params) + handle.set_mo(kvm_mo) + return kvm_mo + + +def kvm_disable(handle, server_id=1): + """ + This method will disable kvm console access + + Args: + handle (ImcHandle) + server_id (int): Server Id to be specified for C3260 platforms + + Returns: + None + """ + + kvm_mo = CommKvm(parent_mo_or_dn=_get_comm_mo_dn(handle, server_id)) + kvm_mo.admin_state = "disabled" + handle.set_mo(kvm_mo) + + +def kvm_exists(handle, server_id=1, **kwargs): + """ + This method will check if kvm console access is enabled + + Args: + handle (ImcHandle) + server_id (int): Server Id to be specified for C3260 platforms + + Returns: + None + """ + + mo = CommKvm(parent_mo_or_dn=_get_comm_mo_dn(handle, server_id)) + mo = handle.query_dn(mo.dn) + if mo is None: + return False, mo + + kwargs['admin_state'] = "enabled" + + mo_exists = mo.check_prop_match(**kwargs) + return (mo_exists, mo if mo_exists else mo) diff --git a/imcsdk/apis/v2/server/power.py b/imcsdk/apis/v2/server/power.py new file mode 100644 index 00000000..6c73c8a5 --- /dev/null +++ b/imcsdk/apis/v2/server/power.py @@ -0,0 +1,208 @@ +# Copyright 2016 Cisco Systems, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +This module implements apis for power policy and power cap related config +""" + +from imcsdk.imccoreutils import get_server_dn +from imcsdk.imcexception import ImcOperationError +from imcsdk.mometa.power.PowerBudget import PowerBudget, PowerBudgetConsts +from imcsdk.mometa.standard.StandardPowerProfile import StandardPowerProfile + +import logging +log = logging.getLogger('imc') + +# This list is currently maintained manually. +# Ideally all such config/capabilites should come from a capability file +supported_models = ['UCSC-C220-M4', 'UCSC-C240-M4', 'UCSC-C3K-M4'] +supported_variants = ['M4', 'M5'] + + +def is_supported_model(handle): + for variant in supported_variants: + if handle.model.find(variant) != -1: + return True + log.info("Current Model:'%s' does not support Power Cap and Budgeting" % + handle.model) + return False + + +def get_supported_models(): + """ + This api returns the list of rack server models that support power cap and + power budgeting + """ + return supported_models + + +def server_power_budget_get(handle, server_id=1): + """ + This api gets the min and max power limits for the platform, cpu and memory + for this specific server + + Args: + handle (ImcHandle) + server_id (int): Server Id to be specified for C3260 platforms + + Returns: + PowerBudget object + """ + + if not is_supported_model(handle): + return + + power_budget_mo = handle.query_children(in_dn=get_server_dn(handle, server_id), + class_id="PowerBudget") + if not power_budget_mo: + raise ImcOperationError("Get Power Budget", + "Invalid Server Id configured") + + return power_budget_mo[0] + + +def server_power_characterization_enable(handle, server_id=1): + """ + Enables power characterization. + + Args: + handle (ImcHandle) + server_id (int): Server Id to be specified for C3260 platforms + + Returns: + PowerBudget object + """ + if not is_supported_model(handle): + return + power_budget_mo = PowerBudget( + parent_mo_or_dn=get_server_dn(handle, server_id)) + power_budget_mo.pow_char_enable = "enabled" + handle.set_mo(power_budget_mo) + return power_budget_mo + + +def server_power_characterization_start(handle, server_id=1): + """ + Starts a power characterization run. + From 3.0(1c) onwards, server_power_characterization_enable needs to be + explicitly done, before invoking this api. + + Args: + handle (ImcHandle) + server_id (int): Server Id to be specified for C3260 platforms + + Returns: + PowerBudget object + """ + if not is_supported_model(handle): + return + power_budget_mo = PowerBudget(parent_mo_or_dn=get_server_dn(handle, server_id)) + + power_budget_mo.admin_action = \ + PowerBudgetConsts.ADMIN_ACTION_START_POWER_CHAR + handle.set_mo(power_budget_mo) + return power_budget_mo + + +def server_power_capping_enable(handle, server_id=1): + """ + Enables power capping feature on the server. + + Args: + handle (ImcHandle) + server_id (int): Server Id to be specified for C3260 platforms + + Returns: + PowerBudget object + """ + + if not is_supported_model(handle): + return + power_budget_mo = PowerBudget(parent_mo_or_dn=get_server_dn(handle, server_id)) + power_budget_mo.admin_state = "enabled" + handle.set_mo(power_budget_mo) + return power_budget_mo + + +def server_standard_power_cap_set(handle, power_limit, throttle=False, + correction_time=3, corrective_action="alert", + hard_cap=False, server_id=1): + """ + This method sets up the standard power cap configuration profile + + Args: + handle (ImcHandle) + throttle (bool) + power_limit (int): Power limit in Watts. + Range can be retrieved from min_power and max_power + fields of PowerBudget object + correction_time (int): Time in seconds before power_limit is enforced + and corrective action is taken. Range (1-600)s + corrective_action (string): "none","alert","shutdown","alert,shutdown" + hard_cap (bool): Enable hard power cap + server_id (int): Server Id to be specified for C3260 platforms + + Returns: + StandardPowerProfile object + + Examples: + server_standard_power_cap_set(handle, + correction_time=5, + corrective_action="alert") + server_standard_power_cap_set(handle, throttle=True, + power_limit=200, correction_time=5, + corrective_action="alert, shutdown") + """ + + if not is_supported_model(handle): + return + + power_budget_dn = get_server_dn(handle, server_id) + "/budget" + stdpowerprof_mo = StandardPowerProfile( + parent_mo_or_dn=power_budget_dn) + + params = { + "allow_throttle": ("no", "yes")[throttle], + "power_limit": str(power_limit), + "corr_time": str(correction_time), + "corr_action": corrective_action, + "profile_enabled": "yes", + "hard_cap": ("no", "yes")[hard_cap] + } + + stdpowerprof_mo.set_prop_multiple(**params) + handle.set_mo(stdpowerprof_mo) + return stdpowerprof_mo + + +def server_standard_power_cap_disable(handle, server_id=1): + """ + This method disables the standard power profile + + Args: + handle (ImcHandle) + server_id (int): Server Id to be specified for C3260 platforms + + Returns: + None + """ + + if not is_supported_model(handle): + return + server_dn = get_server_dn(handle, server_id) + power_budget_dn = server_dn + "/budget" + stdpowerprof_mo = StandardPowerProfile( + parent_mo_or_dn=power_budget_dn) + + stdpowerprof_mo.profile_enabled = "no" + handle.set_mo(stdpowerprof_mo) diff --git a/imcsdk/apis/v2/server/serveractions.py b/imcsdk/apis/v2/server/serveractions.py new file mode 100644 index 00000000..6b07c713 --- /dev/null +++ b/imcsdk/apis/v2/server/serveractions.py @@ -0,0 +1,367 @@ +# Copyright 2016 Cisco Systems, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +""" +This module implements all the server actions +""" + +import time +from imcsdk.imcexception import ImcOperationError +from imcsdk.mometa.compute.ComputeRackUnit import ComputeRackUnit,\ + ComputeRackUnitConsts +from imcsdk.mometa.equipment.EquipmentChassis import EquipmentChassis +from imcsdk.mometa.compute.ComputeServerNode import ComputeServerNodeConsts +from imcsdk.mometa.equipment.EquipmentLocatorLed \ + import EquipmentLocatorLed, EquipmentLocatorLedConsts +from imcsdk.mometa.equipment.EquipmentChassisLocatorLed \ + import EquipmentChassisLocatorLed, EquipmentChassisLocatorLedConsts +from imcsdk.imccoreutils import get_server_dn, IMC_PLATFORM, _set_server_dn +from imcsdk.apis.utils import _is_valid_arg + + +def _set_power_state(handle, server_dn, state): + server_mo = handle.query_dn(server_dn) + if handle.platform == IMC_PLATFORM.TYPE_CLASSIC: + mo_class = ComputeRackUnitConsts + elif handle.platform == IMC_PLATFORM.TYPE_MODULAR: + mo_class = ComputeServerNodeConsts + else: + raise ImcOperationError("Set Power State", "Unknown platform:%s found" % + handle.platform) + + state_dict = { + "up": mo_class.ADMIN_POWER_UP, + "down": mo_class.ADMIN_POWER_DOWN, + "graceful-down": mo_class.ADMIN_POWER_SOFT_SHUT_DOWN, + "cycle": mo_class.ADMIN_POWER_CYCLE_IMMEDIATE + } + + server_mo.admin_power = state_dict[state] + handle.set_mo(server_mo) + + +def server_power_state_get(handle, server_id=1): + """ + This method will return the oper power status of the rack server + + Args: + handle (ImcHandle) + server_id (int): Server Id to be specified for C3260 platforms + + Examples: + For classic or non-C3260 series servers:- + server_power_state_get(handle) + + For modular or C3260 series servers, server_id should also be passed + in the params:- + server_power_state_get(handle, server_id=1) + If server_id is not specified, this will assume server_id="1" + + Returns: + oper power state(string) + """ + + server_dn = get_server_dn(handle, server_id) + server_mo = handle.query_dn(server_dn) + if server_mo: + return server_mo.oper_power + + raise ImcOperationError("Get Server Power State", + "Managed Object not found for dn:%s" % server_dn) + + +def _wait_for_power_state(handle, state, timeout=60, interval=5, server_id=1): + """ + This method should be called after a power state change has been triggered. + It will poll the server and return when the desired state is achieved. + + Args: + handle(ImcHandle) + state(str) + timeout(int) + interval(int) + server_id (int): Server Id to be specified for C3260 platforms + + Returns: + bool + """ + # Verify desired state is valid + if state not in ("on", "off"): + raise ValueError("ERROR invalid state: {0}".format(state)) + + # Verify interval not set to zero + if interval < 1 or type(interval) is not int: + raise ValueError("ERROR: interval must be positive integer") + + wait_time = 0 + while server_power_state_get(handle, server_id) != state: + # Raise error if we've reached timeout + if wait_time > timeout: + raise ImcOperationError( + 'Power State Change', + '{%s}: ERROR - Power {%s} did not complete within ' + '{%s} sec' % (handle.ip, state, timeout) + ) + # Wait interval sec between checks + time.sleep(interval) + wait_time += interval + + +def server_power_up(handle, timeout=60, interval=5, server_id=1, **kwargs): + """ + This method will send the server the power up signal, and then polls + the server every $interval until either $timeout or it comes online. + + Args: + handle(ImcHandle) + timeout (int) + interval (int) + server_id (int): Server Id to be specified for C3260 platforms + kwargs: key=value paired arguments + + Returns: + ComputeRackUnit object for non-C3260 platform + ComputeServerNode object for C3260 platform + + Example: + server_power_up(handle) + server_power_up(handle, timeout=120, interval=10) + server_power_up(handle, server_id=2, timeout=60) + """ + + server_dn = get_server_dn(handle, server_id) + # Turn power on only if not already powered up + if server_power_state_get(handle, server_id) != "on": + _set_power_state(handle, server_dn, "up") + + # Poll until the server is powered up + _wait_for_power_state(handle, "on", timeout=timeout, + interval=interval, server_id=server_id) + + # Return object with current state + return handle.query_dn(server_dn) + + +def server_power_down(handle, timeout=60, interval=5, server_id=1, **kwargs): + """ + This method will power down the rack server, even if tasks are still + running on it. Then polls the server every $interval until either $timeout + or it comes online. + + Args: + handle(ImcHandle) + timeout(int) + interval(int) + server_id (int): Server Id to be specified for C3260 platforms + kwargs: key=value paired arguments + + Returns: + ComputeRackUnit object for non-C3260 platform + ComputeServerNode object for C3260 platform + + Example: + server_power_down(handle) + server_power_down(handle, timeout=120, interval=10) + server_power_down(handle, server_id=2, timeout=60) + """ + + server_dn = get_server_dn(handle, server_id) + # Turn power off only if not already powered down + if server_power_state_get(handle, server_id) != "off": + _set_power_state(handle, server_dn, "down") + + # Poll until the server is powered up + _wait_for_power_state(handle, "off", timeout=timeout, + interval=interval, server_id=server_id) + + return handle.query_dn(server_dn) + + +def server_power_down_gracefully(handle, timeout=120, interval=5, + server_id=1, **kwargs): + """ + This method will power down the rack server gracefully + + Args: + handle(ImcHandle) + server_id (int): Server Id to be specified for C3260 platforms + kwargs: key=value paired arguments + + Returns: + ComputeRackUnit object for non-C3260 platform + ComputeServerNode object for C3260 platform + + Example: + server_power_down_gracefully(handle) + server_power_down_gracefully(handle, timeout=120, interval=10) + server_power_down_gracefully(handle, server_id=2, timeout=60) + """ + + server_dn = get_server_dn(handle, server_id) + # Gracefully power off only if not already powered down + if server_power_state_get(handle, server_id) != "off": + _set_power_state(handle, server_dn, "graceful-down") + + # Poll until the server is powered up + _wait_for_power_state(handle, "off", timeout=timeout, + interval=interval, server_id=server_id) + + return handle.query_dn(server_dn) + + +def server_power_cycle(handle, timeout=120, interval=5, server_id=1, **kwargs): + """ + This method will power cycle the rack server immediately. + + Args: + handle(ImcHandle) + server_id (int): Server Id to be specified for C3260 platforms + kwargs: key=value paired arguments + + + Returns: + ComputeRackUnit object for non-C3260 platform + ComputeServerNode object for C3260 platform + + Example: + server_power_cycle(handle) for non-C3260 platforms + server_power_cycle(handle, timeout=120, interval=10) \ + for non-C3260 platforms + server_power_cycle(handle, server_id=2, timeout=60) for C3260 platforms + """ + + server_dn = get_server_dn(handle, server_id) + _set_power_state(handle, server_dn, "cycle") + + # Poll until the server is powered up + _wait_for_power_state(handle, "on", timeout=timeout, + interval=interval, server_id=server_id) + + return handle.query_dn(server_dn) + + +def _set_chassis_locator_led_state(handle, enabled, kwargs): + chassis_id = str(kwargs["chassis_id"]) + chassis_dn = "sys/chassis-" + chassis_id + led_mo = EquipmentChassisLocatorLed(parent_mo_or_dn=chassis_dn) + led_mo.admin_state = (EquipmentChassisLocatorLedConsts.ADMIN_STATE_OFF, + EquipmentChassisLocatorLedConsts.ADMIN_STATE_ON)\ + [enabled] + handle.set_mo(led_mo) + + +def _set_server_locator_led_state(handle, enabled, kwargs): + server_dn = _set_server_dn(handle, kwargs) + led_mo = EquipmentLocatorLed(parent_mo_or_dn=server_dn) + led_mo.admin_state = (EquipmentLocatorLedConsts.ADMIN_STATE_OFF, + EquipmentLocatorLedConsts.ADMIN_STATE_ON)[enabled] + handle.set_mo(led_mo) + + +def locator_led_on(handle, **kwargs): + """ + This method will turn on the locator led on the server or chassis + + Args: + handle(ImcHandle) + kwargs: key=value paired arguments + + Returns: + None + + Example: + locator_led_on(handle) for non-C3260 platforms. + Turns on locator led on the server. + locator_led_on(handle, server_id=1) for C3260 platforms. + Turns on locator led on the specified server. + locator_led_on(handle, chassis_id=1) for C3260 platforms. + Turns on locator led on the chassis. + """ + + if _is_valid_arg("chassis_id", kwargs): + _set_chassis_locator_led_state(handle, True, kwargs) + + if _is_valid_arg("server_id", kwargs) or \ + handle.platform == IMC_PLATFORM.TYPE_CLASSIC: + _set_server_locator_led_state(handle, True, kwargs) + + +def locator_led_off(handle, **kwargs): + """ + This method will turn off the locator led on the server or on the chassis + + Args: + handle(ImcHandle) + kwargs: key=value paired arguments + + Returns: + None + + Example: + locator_led_off(handle) for non-C3260 platforms. + Turns off locator led on the server. + locator_led_off(handle, server_id=1) for C3260 platforms. + Turns off locator led on the specified server. + locator_led_off(handle, chassis_id=1) for C3260 platforms. + Turns off locator led on the chassis. + """ + + if _is_valid_arg("chassis_id", kwargs): + _set_chassis_locator_led_state(handle, False, kwargs) + + if _is_valid_arg("server_id", kwargs) or \ + handle.platform == IMC_PLATFORM.TYPE_CLASSIC: + _set_server_locator_led_state(handle, False, kwargs) + + +def tag_server(handle, tag): + """ + This method will tag the server with the label specified. + Applicable to non-C3260 platforms + + Args: + handle(ImcHandle) + tag (str): Label to be used to tag the asset + + Returns: + ComputeRackUnit object + + Example: + tag_server(handle, tag="Row21-Rack10-Slot5") + """ + mo = ComputeRackUnit(parent_mo_or_dn="sys", server_id='1') + mo.asset_tag = str(tag) + handle.set_mo(mo) + return handle.query_dn(mo.dn) + + +def tag_chassis(handle, tag): + """ + This method will tag the chassis with the label specified. + Applicable to C3260 platforms + + Args: + handle(ImcHandle) + tag (str): Label to be used to tag the asset + + Returns: + EquipmentChassis object + + Example: + tag_server(handle, tag="Row21-Rack10-Slot10") + """ + mo = EquipmentChassis(parent_mo_or_dn="sys") + mo.asset_tag = str(tag) + handle.set_mo(mo) + return handle.query_dn(mo.dn) diff --git a/imcsdk/apis/v2/server/sol.py b/imcsdk/apis/v2/server/sol.py new file mode 100644 index 00000000..808a131e --- /dev/null +++ b/imcsdk/apis/v2/server/sol.py @@ -0,0 +1,98 @@ +# Copyright 2016 Cisco Systems, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +""" +This module implements all the sol related config +""" + +from imcsdk.imcexception import ImcOperationError +from imcsdk.imccoreutils import get_server_dn +from imcsdk.mometa.sol.SolIf import SolIf, SolIfConsts + + +def sol_get(handle, server_id=1, caller="sol_get"): + parent_dn = get_server_dn(handle, server_id) + dn = parent_dn + "/sol-if" + mo = handle.query_dn(dn) + if mo is None: + raise ImcOperationError(caller, + "SOL '%s' doesn't exist." % dn) + return mo + + +def sol_enable(handle, speed=None, comport=None, ssh_port=None, server_id=1, + **kwargs): + """ + This method will setup serial over lan connection + + Args: + handle (ImcHandle) + speed (int): 9600, 19200, 38400, 57600, 115200 + comport (string): "com0", "com1" + ssh_port (int): port for ssh + server_id (int): Server Id to be specified for C3260 platforms + + Returns: + SolIf object + """ + + mo = sol_get(handle, server_id=server_id, caller="sol_enable") + params = { + "admin_state": SolIfConsts.ADMIN_STATE_ENABLE, + "speed": str(speed) if speed else None, + "comport": comport, + "ssh_port": str(ssh_port) if ssh_port else None, + } + + mo.set_prop_multiple(**kwargs) + mo.set_prop_multiple(**params) + handle.set_mo(mo) + return mo + + +def sol_exists(handle, server_id=1, **kwargs): + try: + mo = sol_get(handle, server_id=server_id, caller="sol_enable") + except ImcOperationError: + return (False, None) + + kwargs['admin_state'] = SolIfConsts.ADMIN_STATE_ENABLE + + mo_exists = mo.check_prop_match(**kwargs) + return mo_exists, mo + + +def sol_disable(handle, server_id=1): + """ + This method will disable Serial over Lan connection + + Args: + handle (ImcHandle) + server_id (int): Server Id to be specified for C3260 platforms + + Returns: + None + """ + + mo = sol_get(handle, server_id=server_id, caller="sol_enable") + mo.admin_state = SolIfConsts.ADMIN_STATE_DISABLE + handle.set_mo(mo) + return mo + + +def sol_validate_inputs(**kwargs): + """ + This method will check if the input parameters are valid + """ + return SolIf(parent_mo_or_dn=None).validate_inputs(**kwargs) diff --git a/imcsdk/apis/v2/server/storage.py b/imcsdk/apis/v2/server/storage.py new file mode 100644 index 00000000..d37ce350 --- /dev/null +++ b/imcsdk/apis/v2/server/storage.py @@ -0,0 +1,1071 @@ +# Copyright 2016 Cisco Systems, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +""" +This module provides APIs for storage configuration like virtual drives and +disk groups. +""" + +import math +import logging +import imcsdk.imccoreutils as imccoreutils +from imcsdk.imcexception import ImcOperationError +from imcsdk.mometa.storage.StorageVirtualDriveCreatorUsingUnusedPhysicalDrive \ + import StorageVirtualDriveCreatorUsingUnusedPhysicalDrive as vd_creator +from imcsdk.mometa.self.SelfEncryptStorageController import \ + SelfEncryptStorageController, SelfEncryptStorageControllerConsts +from imcsdk.mometa.storage.StorageLocalDisk import StorageLocalDiskConsts +from imcsdk.mometa.storage.StorageController import StorageControllerConsts + +log = logging.getLogger('imc') + + +def _list_to_string(drive_list): + # convert to format imc expects + # list to string + # [[1]] => '[1]' + # [[1,2],[3,4]] => '[1,2][3,4]' + # imc fails to parse '[1, 2]' + # it needs to be '[1,2]' + # and hence the replace(' ', '') + dg_str = "" + for each in drive_list: + dg_str += str(each) + return dg_str.replace(' ', '') + + +def _flatten_list(drive_list): + # convert to format imc expects + # [[1]] => [1] + # [[1,2],[3,4]] => [1, 2, 3, 4] + if not (isinstance(drive_list, list) and isinstance(drive_list[0], list)): + raise "drive_list needs a list of list(s). i.e [[1,2],[3,4]]" + dg_list = [] + for each in drive_list: + for sub_each in each: + dg_list.append(sub_each) + return dg_list + + +def _flatten_to_string(drive_list): + # convert to format imc expects + # [[1]] => '1' + # [[1,2],[3,4]] => '1234' + return ''.join([''.join(str(x)) for x in _flatten_list(drive_list)]) + + +def vd_name_derive(raid_level, drive_list): + return "RAID" + str(raid_level) + "_" + _flatten_to_string(drive_list) + + +def _human_to_bytes(size_str): + """ + returns the size in bytes + supported formats KB, MB, GB, TB, PB, EB, ZB, YB + + KB to bytes = (* 1024) == << 10 + MB to bytes = (* 1024 * 1024) == << 20 + GB to bytes = (* 1024 * 1024 * 1024) == << 30 + """ + convert = {'KB': 1, 'MB': 2, 'GB': 3, 'TB': 4, + 'PB': 5, 'EB': 6, 'ZB': 7, 'YB': 8} + s = size_str.split() + if s[1] not in convert: + raise "unknown size format" + size_str + return int(s[0]) << (10 * convert[s[1]]) + + +def _bytes_to_human(size, output_format=None): + """ + converts bytes to human readable format. + The return is in output_format. + """ + convert = {'KB': 1, 'MB': 2, 'GB': 3, 'TB': 4, + 'PB': 5, 'EB': 6, 'ZB': 7, 'YB': 8} + unit = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] + if output_format is None: + output_format = unit[int(math.floor(math.log(size, 2))/10)] + if output_format not in convert: + raise "unknown output format" + output_format + return str(size >> (10 * convert[output_format])) + ' ' + output_format + + +def _pd_sizes_get(handle, + controller_type, + controller_slot, + drive_list, + server_id=1): + sizes = [] + for each in drive_list: + for drive in each: + dmo = physical_drive_get(handle=handle, + controller_type=controller_type, + controller_slot=controller_slot, + drive_slot=drive, + server_id=server_id) + sizes.append(_human_to_bytes(dmo.coerced_size)) + return sizes + + +def _pd_min_size_get(sizes): + return min(sizes) + + +def _pd_total_size_get(sizes): + return sum(sizes) + + +def _vd_span_depth_get(drive_list): + return len(drive_list) + + +def _raid_max_size_get(raid_level, total_size, min_size, span_depth): + size = {0: total_size, + 1: total_size/2, + 5: total_size - (span_depth * 1 * min_size), + 6: total_size - (span_depth * 2 * min_size), + 10: total_size/2, + 50: total_size - (span_depth * 1 * min_size), + 60: total_size - (span_depth * 2 * min_size)} + + if raid_level not in size: + raise "Unsupported Raid level" + str(raid_level) + return size[raid_level] + + +def _vd_max_size_get(handle, + controller_type, + controller_slot, + drive_list, + raid_level, + server_id=1): + """ + Returns the usable disk size for the specified virtual_drive + drive_list: + [[1]] + [[1,2],[3,4]] + + min_size = smallest size of all the drives from this disk group + available_size = accumulated size of all the drives together + span_depth = number of sub groups [[1,2,3],[4,5,6]] span_depth = 2 + """ + sizes = _pd_sizes_get(handle=handle, + controller_type=controller_type, + controller_slot=controller_slot, + drive_list=drive_list, + server_id=server_id) + min_size = _pd_min_size_get(sizes) + total_size = _pd_total_size_get(sizes) + span_depth = _vd_span_depth_get(drive_list) + + max_size = _raid_max_size_get(raid_level, total_size, min_size, span_depth) + return _bytes_to_human(max_size, output_format="MB") + + +def _get_controller_dn(handle, controller_type, controller_slot, server_id=1): + server_dn = imccoreutils.get_server_dn(handle, server_id) + return (server_dn + "/board/storage-" + controller_type + "-" + controller_slot) + + +def _get_controller(handle, controller_type, controller_slot, server_id=1): + mo = handle.query_dn(_get_controller_dn(handle, + controller_type, + controller_slot, + server_id)) + if mo is None: + raise ImcOperationError("Get Controller Type:%s Slot:%s" % + (controller_type, controller_slot), + "Managed Object not found") + return mo + + +def virtual_drive_create(handle, + drive_group, + controller_type, + controller_slot, + raid_level=0, + virtual_drive_name=None, + access_policy="read-write", + read_policy="no-read-ahead", + cache_policy="direct-io", + disk_cache_policy="unchanged", + write_policy="Write Through", + strip_size="64k", + size=None, + self_encrypt=False, + server_id=1): + """ + Creates virtual drive from unused physical drives + + Args: + handle (ImcHandle) + drive_group (list of lists): list of drives + [[1]] + [[1,2]] + [[1,2],[3,4]] + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + virtual_drive_name (str): Name of the virtual drive + raid_level (int): raid level + 0, 1, 5, 6, 10, 50, 60 + Raid 0 Simple striping. + Raid 1 Simple mirroring. + Raid 5 Striping with parity. + Raid 6 Striping with two parity drives. + Raid 10 Spanned mirroring. + Raid 50 Spanned striping with parity. + Raid 60 Spanned striping with two parity drives. + access_policy (str): Access-policy for the virtual drive + ['read-write', 'read-only', 'hidden', 'default', 'blocked'] + self_encrypt (bool): Encrypt the virtual drive if the underlying + controller and physical drive support it + + Returns: + StorageVirtualDrive object + + Examples: + virtual_drive_create(handle=imc, + drive_group=[[2]], + controller_slot='MEZZ') + """ + slot_dn = _get_controller_dn(handle, controller_type, + controller_slot, server_id) + + dg_str = _list_to_string(drive_group) + vdn = virtual_drive_name + + params = {} + params["parent_mo_or_dn"] = slot_dn + params["drive_group"] = dg_str + params["raid_level"] = str(raid_level) + params["access_policy"] = access_policy + params["read_policy"] = read_policy + params["cache_policy"] = cache_policy + params["disk_cache_policy"] = disk_cache_policy + params["write_policy"] = write_policy + params["strip_size"] = strip_size + + if self_encrypt: + params["admin_action"] = "enable-self-encrypt" + + params["virtual_drive_name"] = \ + (vd_name_derive(raid_level, drive_group), vdn)[vdn is not None] + + params["size"] = (_vd_max_size_get(handle=handle, + controller_type=controller_type, + controller_slot=controller_slot, + drive_list=drive_group, + raid_level=raid_level, + server_id=server_id), + size)[size is not None] + + mo = vd_creator(**params) + mo.admin_state = "trigger" + handle.add_mo(mo) + return mo + + +def vd_query_by_name(handle, + controller_type, + controller_slot, + name, + server_id=1): + slot_dn = _get_controller_dn(handle, controller_type, controller_slot, server_id) + + mos = handle.query_children(in_dn=slot_dn, class_id="storageVirtualDrive") + for mo in mos: + if mo.name == name: + return mo + return None + + +def virtual_drive_exists(handle, + controller_type, + controller_slot, + virtual_drive_name, + server_id=1): + """ + Checks if a virtual drive by the specified name exists. + + Args: + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + virtual_drive_name (str): Name of the virtual drive + + Returns: + exists(bool), error(str) + """ + mo = vd_query_by_name(handle, controller_type, controller_slot, + virtual_drive_name, server_id) + return mo is not None, None + + +def virtual_drive_delete(handle, + controller_type, + controller_slot, + name, + server_id=1): + """ + Deletes the specified virtual drive + + Args: + handle (ImcHandle) + controller_slot (str): controller slot name/number + "MEZZ","0"-"9" + name (string): name of the virtual drive to delete + server_id (int): server_id for UCS 3260 platform + + Examples: + virtual_drive_delete(handle=imc, + controller_slot='MEZZ', + name="RAID0_1") + """ + vd = vd_query_by_name(handle=handle, + controller_type=controller_type, + controller_slot=controller_slot, + name=name, + server_id=server_id) + handle.remove_mo(vd) + + +def virtual_drive_encryption_enable(handle, controller_type, + controller_slot, name, server_id=1): + """ + Enables encryption on the virtual drive if it is supported by the controller + and the underlying physical drive + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + name (string): name of the virtual drive + server_id (int): server_id for UCS 3260 platform + + Returns: + StorageVirtualDrive object + + Examples: + virtual_drive_encryption_enable(handle, 'SAS', 'HBA', 'test_vd') + """ + from imcsdk.mometa.storage.StorageVirtualDrive import \ + StorageVirtualDriveConsts + vd = vd_query_by_name(handle=handle, + controller_type=controller_type, + controller_slot=controller_slot, + name=name, + server_id=server_id) + vd.admin_action = StorageVirtualDriveConsts.ADMIN_ACTION_ENABLE_SELF_ENCRYPT + handle.set_mo(vd) + return handle.query_dn(vd.dn) + + +def virtual_drive_set_boot_drive(handle, controller_type, controller_slot, name, server_id=1): + """ + Set a virtual drive as boot drive. + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + name (string): name of the virtual drive + server_id (int): server_id for UCS 3260 platform + + Returns: + StorageVirtualDrive object + + Examples: + virtual_drive_set_boot_drive(handle, 'SAS', 'HBA', 'test_vd') + """ + from imcsdk.mometa.storage.StorageVirtualDrive import \ + StorageVirtualDriveConsts + vd = vd_query_by_name(handle=handle, + controller_type=controller_type, + controller_slot=controller_slot, + name=name, + server_id=server_id) + vd.admin_action = StorageVirtualDriveConsts.ADMIN_ACTION_SET_BOOT_DRIVE + handle.set_mo(vd) + return handle.query_dn(vd.dn) + +def _controller_action_set(handle, controller_type, controller_slot, action, + server_id=1): + controller_mo = _get_controller(handle, + controller_type, + controller_slot, + server_id) + controller_mo.admin_action = action + handle.set_mo(controller_mo) + return handle.query_dn(controller_mo.dn) + + +def controller_jbod_mode_enable(handle, controller_type, + controller_slot, server_id=1): + """ + Enables jbod mode on the controller + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + server_id (int): server_id for UCS 3260 platform + + Returns: + StorageController object + + Examples: + controller_jbod_mode_enable(handle, controller_type='SAS', + controller_slot='HBA') + + """ + return _controller_action_set(handle, controller_type, + controller_slot, 'enable-jbod', + server_id=server_id) + + +def controller_jbod_mode_disable(handle, controller_type, + controller_slot, server_id=1): + """ + Disables jbod mode on the controller + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + server_id (int): server_id for UCS 3260 platform + + Returns: + StorageController object + + Examples: + controller_jbod_mode_disable(handle, controller_type='SAS', + controller_slot='HBA') + + """ + return _controller_action_set(handle, controller_type, + controller_slot, 'disable-jbod', + server_id=server_id) + + +def is_controller_jbod_mode_enabled(handle, controller_type, + controller_slot, server_id=1): + """ + Checks if jbod mode is enabled on the controller + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + server_id (int): server_id for UCS 3260 platform + + Returns: + bool + + Examples: + is_controller_jbod_mode_enabled(handle, controller_type='SAS', + controller_slot='HBA') + """ + dn = _get_controller_dn(handle, controller_type, controller_slot, server_id) + settings = handle.query_children(in_dn=dn, class_id='StorageControllerSettings') + return settings[0].enable_jbod.lower() in ['yes', 'true'] if settings else False + + +def controller_encryption_enable(handle, controller_type, + controller_slot, key_id, security_key, + key_management="local", server_id=1, + **kwargs): + """ + Enables encryption on the controller + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9" + key_id (str): Security Key Identifier. + Max Length is 256 characters. + security_key (str): Security key used to enable controller security. + Max Length is 32 characters. + key_management (str): "local" or "remote" + server_id (int): server_id for UCS 3260 platform + + Returns: + StorageController object + + Examples: + controller_encryption_enable(handle, + controller_type='SAS', + controller_slot='HBA'', + key_id='ABCD12345', security_key='12345') + """ + from imcsdk.mometa.self.SelfEncryptStorageController import \ + SelfEncryptStorageController, SelfEncryptStorageControllerConsts + + enabled, mo = controller_encryption_exists(handle, + controller_type, + controller_slot, + server_id) + if enabled: + return mo + dn = mo.dn + mo = SelfEncryptStorageController(parent_mo_or_dn=dn) + params = { + 'key_id': key_id, + 'security_key': security_key, + 'key_management': key_management, + 'admin_action': + SelfEncryptStorageControllerConsts.ADMIN_ACTION_ENABLE_SELF_ENCRYPT, + } + + mo.set_prop_multiple(**params) + handle.set_mo(mo) + return handle.query_dn(dn) + + +def controller_encryption_disable(handle, controller_type, + controller_slot, server_id=1): + """ + Disables encryption on the controller + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + server_id (int): server_id for UCS 3260 platform + + Returns: + StorageController object + + Examples: + controller_encryption_disable(handle, + controller_type='SAS', + controller_slot='HBA'') + """ + dn = _get_controller_dn( + handle, + controller_type, + controller_slot, + server_id) + + mo = SelfEncryptStorageController(parent_mo_or_dn=dn) + mo.admin_action = \ + SelfEncryptStorageControllerConsts.ADMIN_ACTION_DISABLE_SELF_ENCRYPT + handle.set_mo(mo) + return handle.query_dn(dn) + + +def controller_encryption_exists(handle, controller_type, controller_slot, + server_id=1): + """ + Checks if encryption is enabled on the controller + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + server_id (int): server_id for UCS 3260 platform + + Returns: + bool + + Examples: + is_controller_encryption_enabled( + handle, + controller_type='SAS', + controller_slot='HBA'') + """ + + mo = _get_controller(handle, controller_type, controller_slot, server_id) + if mo.self_encrypt_enabled.lower() in ['yes', 'true']: + return True, mo + return False, mo + + +def controller_encryption_modify_security_key(handle, + controller_type, + controller_slot, + existing_security_key, + security_key, + key_management="local", + server_id=1): + """ + Modifies the security key on the controller + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + existing_security_key (str): Existing Security Key + security_key (str): New Security Key + key_management (str): "local" or "remote" + server_id (int): server_id for UCS 3260 platform + + Returns: + StorageController object + + Examples: + controller_encryption_modify_security_key( + handle, + controller_type='SAS', + controller_slot='HBA'', + existing_security_key='Nbv12345', + security_key='Nbv123456') + """ + dn = _get_controller_dn(handle, + controller_type, + controller_slot, + server_id) + mo = SelfEncryptStorageController(parent_mo_or_dn=dn) + params = { + 'existing_security_key': existing_security_key, + 'security_key': security_key, + 'key_management': key_management, + } + mo.set_prop_multiple(**params) + handle.set_mo(mo) + return handle.query_dn(dn) + + +def controller_encryption_key_id_generate(handle, controller_type, + controller_slot, server_id=1): + """ + Generates a random key id for the given controller + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + server_id (int): server_id for UCS 3260 platform + + Returns: + Key-id string + + Examples: + key_id = controller_encryption_key_id_generate( + handle, + controller_type='SAS', + controller_slot='HBA'') + """ + dn = _get_controller_dn(handle, controller_type, controller_slot, server_id) + mos = handle.query_children( + in_dn=dn, + class_id='GeneratedStorageControllerKeyId') + return mos[0].generated_key_id if mos else "" + + +def controller_encryption_key_generate(handle, controller_type, + controller_slot, server_id=1): + """ + Generates a random security key for the given controller + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + server_id (int): server_id for UCS 3260 platform + + Returns: + Security Key string + + Examples: + key = controller_encryption_key_generate( + handle, + controller_type='SAS', + controller_slot='HBA'') + """ + dn = _get_controller_dn(handle, controller_type, controller_slot, server_id) + mos = handle.query_children( + in_dn=dn, + class_id='SuggestedStorageControllerSecurityKey') + return mos[0].suggested_security_key if mos else "" + + +def controller_unlock_foreign_drives(handle, controller_type, + controller_slot, security_key, + server_id=1): + """ + Unlocks on the given controller, drives encrypted on another controller. + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + security_key (str): Security Key used to encrypt the foreign drive + server_id (int): server_id for UCS 3260 platform + + Returns: + StorageController object + + Examples: + key = controller_unlock_foreign_drives( + handle, + controller_type='SAS', + controller_slot='HBA'', + security_key='12345') + """ + dn = _get_controller_dn(handle, controller_type, controller_slot, server_id) + mo = SelfEncryptStorageController(parent_mo_or_dn=dn) + params = { + 'security_key': security_key, + 'admin_action': SelfEncryptStorageControllerConsts.ADMIN_ACTION_UNLOCK_SECURED_DRIVES + } + mo.set_prop_multiple(**params) + return handle.query_dn(dn) + + +def controller_import_foreign_config(handle, controller_type, + controller_slot, server_id=1): + """ + Imports foreign configuration on the given controller + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + server_id (int): server_id for UCS 3260 platform + + Returns: + StorageController object + + Examples: + controller_import_foreign_config(handle, + controller_type='SAS', + controller_slot='HBA'') + """ + return _controller_action_set( + handle, + controller_type, + controller_slot, + action=StorageControllerConsts.ADMIN_ACTION_IMPORT_FOREIGN_CONFIG, + server_id=server_id) + + +def physical_drive_get(handle, + controller_type, + controller_slot, + drive_slot, + server_id=1): + """ + Gets the physical drive + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + drive_slot (int): Slot in which the drive resides + server_id (int): server_id for UCS 3260 platform + + Returns: + StorageLocalDisk object + + Examples: + mo = physical_drive_get(handle, controller_type='SAS', + controller_slot='HBA'', + drive_slot=4') + """ + controller_dn = _get_controller_dn(handle, controller_type, + controller_slot, server_id) + drive_dn = controller_dn + '/pd-' + str(drive_slot) + return handle.query_dn(drive_dn) + + +def _physical_drive_action_set(handle, controller_type, + controller_slot, drive_slot, + action, server_id=1): + mo = physical_drive_get(handle, controller_type, controller_slot, + drive_slot, server_id) + if mo is None: + raise ImcOperationError("Get Physical Drive", + "Managed Object not found") + mo.admin_action = action + handle.set_mo(mo) + return handle.query_dn(mo.dn) + + +def is_physical_drive_encryption_capable(handle, controller_type, + controller_slot, drive_slot, + server_id=1): + """ + Checks if encryption is supported on the physical drive + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + drive_slot (int): Slot in which the drive resides + server_id (int): server_id for UCS 3260 platform + + Returns: + bool + + Examples: + capable = is_physical_drive_encryption_capable( + handle, + controller_type='SAS', + controller_slot='HBA'', + drive_slot=4') + """ + drive = physical_drive_get(handle, controller_type, + controller_slot, drive_slot, + server_id) + if drive is None: + raise ImcOperationError("Get Physical Drive:%s" % drive_slot, + "Managed Object not found") + return drive.fde_capable.lower() in ['yes', 'true'] + + +def is_physical_drive_encryption_enabled(handle, controller_type, + controller_slot, drive_slot, + server_id=1): + """ + Checks if encryption is enabled on the physical drive + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + drive_slot (int): Slot in which the drive resides + server_id (int): server_id for UCS 3260 platform + + Returns: + bool + + Examples: + enabled = is_physical_drive_encryption_enabled( + handle, + controller_type='SAS', + controller_slot='HBA'', + drive_slot=4') + """ + drive = physical_drive_get(handle, controller_type, + controller_slot, drive_slot, + server_id) + if drive is None: + raise ImcOperationError("Get Physical Drive:%s" % drive_slot, + "Managed Object not found") + return drive.fde_enabled.lower() in ['yes', 'true'] + + +def physical_drive_set_jbod_mode(handle, controller_type, + controller_slot, drive_slot, server_id=1): + """ + Sets the physical drive in jbod mode + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + drive_slot (int): Slot in which the drive resides + server_id (int): server_id for UCS 3260 platform + + Returns: + StorageLocalDisk object + + Examples: + physical_drive_set_jbod_mode(handle, + controller_type='SAS', + controller_slot='HBA'', + drive_slot=4') + """ + if not is_controller_jbod_mode_enabled(handle, controller_type, + controller_slot, server_id): + raise ImcOperationError("Physical Drive: %s JBOD Mode Enable", + "Controller JBOD mode is not enabled") + return _physical_drive_action_set( + handle, + controller_type=controller_type, + controller_slot=controller_slot, + drive_slot=drive_slot, + action=StorageLocalDiskConsts.ADMIN_ACTION_MAKE_JBOD, + server_id=server_id + ) + + +def physical_drive_set_unconfigured_good(handle, + controller_type, + controller_slot, + drive_slot, + server_id=1): + """ + Sets the physical drive in unconfigured good state + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + drive_slot (int): Slot in which the drive resides + server_id (int): server_id for UCS 3260 platform + + Returns: + StorageLocalDisk object + + Examples: + physical_drive_set_unconfigured_good( + handle, + controller_type='SAS', + controller_slot='HBA'', + drive_slot=4') + """ + return _physical_drive_action_set( + handle, + controller_type=controller_type, + controller_slot=controller_slot, + drive_slot=drive_slot, + action=StorageLocalDiskConsts.ADMIN_ACTION_MAKE_UNCONFIGURED_GOOD, + server_id=server_id + ) + + +def physical_drive_encryption_enable(handle, controller_type, + controller_slot, drive_slot, server_id=1): + """ + Enables encryption on the physical drive + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + drive_slot (int): Slot in which the drive resides + server_id (int): server_id for UCS 3260 platform + + Returns: + StorageLocalDisk object + + Examples: + physical_drive_encryption_enable( + handle, + controller_type='SAS', + controller_slot='HBA'', + drive_slot=4') + """ + return _physical_drive_action_set( + handle, + controller_type=controller_type, + controller_slot=controller_slot, + drive_slot=drive_slot, + action=StorageLocalDiskConsts.ADMIN_ACTION_ENABLE_SELF_ENCRYPT, + server_id=server_id + ) + + +def physical_drive_encryption_disable(handle, controller_type, + controller_slot, drive_slot, server_id=1): + """ + Disables encryption on the physical drive + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + drive_slot (int): Slot in which the drive resides + server_id (int): server_id for UCS 3260 platform + + Returns: + StorageLocalDisk object + + Examples: + physical_drive_encryption_disable( + handle, + controller_type='SAS', + controller_slot='HBA'', + drive_slot=4') + """ + return _physical_drive_action_set( + handle, + controller_type=controller_type, + controller_slot=controller_slot, + drive_slot=drive_slot, + action=StorageLocalDiskConsts.ADMIN_ACTION_DISABLE_SELF_ENCRYPT, + server_id=server_id + ) + + +def physical_drive_secure_erase_foreign_drives( + handle, + controller_type, + controller_slot, + drive_slot, + server_id=1): + """ + Erases foreign configuration from the physical drive. Drive data is lost. + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + drive_slot (int): Slot in which the drive resides + server_id (int): server_id for UCS 3260 platform + + Returns: + StorageLocalDisk object + + Examples: + physical_drive_secure_erase_foreign_drives( + handle, + controller_type='SAS', + controller_slot='HBA'', + drive_slot=4') + """ + return _physical_drive_action_set( + handle, + controller_type=controller_type, + controller_slot=controller_slot, + drive_slot=drive_slot, + action=StorageLocalDiskConsts.ADMIN_ACTION_DISABLE_SED_FOREIGN_DRIVES, + server_id=server_id + ) diff --git a/imcsdk/apis/v2/server/vmedia.py b/imcsdk/apis/v2/server/vmedia.py new file mode 100644 index 00000000..9416a3bd --- /dev/null +++ b/imcsdk/apis/v2/server/vmedia.py @@ -0,0 +1,461 @@ +# Copyright 2016 Cisco Systems, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +""" +This module implements all the kvm and sol related samples +""" +import os +import time +import urlparse +import re +import logging + +from imcsdk.mometa.comm.CommVMedia import CommVMedia +from imcsdk.mometa.comm.CommVMediaMap import CommVMediaMap +from imcsdk.imcexception import ImcOperationError +from imcsdk.apis.admin.ipmi import _get_comm_mo_dn + +log = logging.getLogger('imc') + +CIFS_URI_PATTERN = re.compile('^//\d+\.\d+\.\d+\.\d+\/') +NFS_URI_PATTERN = re.compile('^\d+\.\d+\.\d+\.\d+\:\/') + + +def _get_vmedia_mo_dn(handle, server_id=1): + return _get_comm_mo_dn(handle, server_id) + "/vmedia-svc" + + +def _vmedia_setup(handle, admin_state, encryption_state, low_power_usb, + server_id): + dn = _get_comm_mo_dn(handle, server_id) + mo = CommVMedia(parent_mo_or_dn=dn) + params = { + "admin_state": admin_state, + "encryption_state": encryption_state, + "low_power_usb_state": low_power_usb, + "low_power_usb": low_power_usb, + } + + mo.set_prop_multiple(**params) + handle.set_mo(mo) + return mo + + +def vmedia_enable(handle, encryption_state=None, low_power_usb=None, + server_id=1): + """ + This method will enable vmedia and setup the properties + + Args: + handle (ImcHandle) + encrypt (bool): Encrypt virtual media communications + low_power_usb (bool): Enable low power usb + server_id (int): Server Id to be specified for C3260 platforms + + Returns: + CommVMedia object + + Examples: + vmedia_enable(handle, "enabled", "enabled") + """ + + admin_state = "enabled" + return _vmedia_setup(handle, admin_state, encryption_state, low_power_usb, + server_id) + + +def vmedia_exists(handle, server_id=1, **kwargs): + dn = _get_vmedia_mo_dn(handle, server_id=server_id) + mo = handle.query_dn(dn) + if mo is None: + return False, None + + low_power_usb = kwargs.pop('low_power_usb', None) + if low_power_usb and mo.low_power_usb_state: + kwargs['low_power_usb_state'] = low_power_usb + + kwargs['admin_state'] = "enabled" + + if not mo.check_prop_match(**kwargs): + return False, mo + + return True, mo + + +def vmedia_disable(handle, server_id=1, **kwargs): + """ + This method will disables vmedia and setup the properties + + Args: + handle (ImcHandle) + encrypt (bool): Encrypt virtual media communications + low_power_usb (bool): Enable low power usb + server_id (int): Server Id to be specified for C3260 platforms + + Returns: + CommVMedia object + + Examples: + vmedia_disable(handle, "enabled", "enabled") + """ + + admin_state = "disabled" + encryption_state = kwargs.pop('encryption_state', None) + low_power_usb = kwargs.pop('low_power_usb', None) + + return _vmedia_setup(handle, admin_state, encryption_state, low_power_usb, + server_id) + + +def vmedia_get_existing_uri(handle, server_id=1): + """ + This method will return list of URIs of existing mountd media + Args: + handle (ImcHandle) + server_id (int): Server Id to be specified for C3260 platforms + + Returns: + List of URIs of currently mounted virtual media + + Examples: + vmedia_get_existing_uri(handle) + """ + # Create list of URIs of all current virtually mapped ISOs + + vmedias = handle.query_children(in_dn=_get_vmedia_mo_dn(handle, server_id)) + return [vmedia.remote_share + vmedia.remote_file for vmedia in vmedias] + + +def vmedia_get_existing_status(handle, server_id=1): + """ + This method will return list of status of existing mountd media + Args: + handle (ImcHandle) + server_id (int): Server Id to be specified for C3260 platforms + + Returns: + List of Status of currently mounted virtual media + + Examples: + vmedia_get_existing_status(handle) + """ + # Create list of URIs of all current virtually mapped ISOs + vmedias = handle.query_children(in_dn=_get_vmedia_mo_dn(handle, server_id)) + return [vmedia.mapping_status for vmedia in vmedias] + + +def vmedia_mount_get(handle, volume_name, server_id=1): + parent_dn = _get_vmedia_mo_dn(handle, server_id) + dn = parent_dn + "/vmmap-" + volume_name + mo = handle.query_dn(dn) + if mo is None: + raise ImcOperationError("vmedia_mount_get", + "vmedia mount '%s' does not exist" % dn) + return mo + + +def vmedia_mount_create(handle, volume_name, remote_share, remote_file, + map="www", mount_options="noauto", username="", + password="", server_id=1, timeout=60): + """ + This method will setup the vmedia mapping + Args: + handle (ImcHandle) + volume_name (string): Name of the volume or identity of the image + map (string): "cifs", "nfs", "www" + mount_options (string): Options to be passed while mounting the image + remote_share (string): URI of the image + remote_file (string): name of the image + username (string): username + password (string): password + server_id (int): Server Id to be specified for C3260 platforms + timeout (int): Waits for the timeout seconds for mapping to finish + default is 60. + + Returns: + CommVMediaMap object + + Examples: + vmedia_mount_add( + handle, + volume_name="c", + map="www", + mount_options="noauto", "nolock" etc. + remote_share="http://1.1.1.1/files", + remote_file="ubuntu-14.04.2-server-amd64.iso", + username="abcd", + password="xyz") + """ + image_type = remote_file.split('.')[-1] + vmedia_mount_remove_image(handle, image_type) + + mo = CommVMediaMap(parent_mo_or_dn=_get_vmedia_mo_dn(handle, server_id), + volume_name=volume_name) + mo.map = map + if mount_options: + mo.mount_options = mount_options + mo.remote_share = remote_share + mo.remote_file = remote_file + mo.username = username + mo.password = password + + handle.add_mo(mo, modify_present="True") + + wait_time = 0 + interval = 10 + while wait_time < timeout: + time.sleep(interval) + mo = handle.query_dn(mo.dn) + existing_mapping_status = mo.mapping_status + if existing_mapping_status == "OK": + return mo + elif re.match(r"ERROR", existing_mapping_status): + raise ImcOperationError("vmedia_mount_create", + mo.mapping_status) + wait_time += interval + + raise ImcOperationError("vmedia_mount_create", + "ERROR - Mapped ISO status stuck at %s" % + existing_mapping_status) + + +def vmedia_mount_exists(handle, volume_name, server_id=1, **kwargs): + import re + + try: + mo = vmedia_mount_get(handle, volume_name) + except ImcOperationError: + return False, None + + kwargs.pop('timeout', None) + kwargs.pop('password', None) + username = kwargs.pop('username', None) + mount_options = kwargs.pop('mount_options', None) + + if not mo.check_prop_match(**kwargs): + return False, mo + + mo_mount_options = [x.strip() for x in mo.mount_options.split(',')] + + if mount_options: + mount_options = [x.strip() for x in mount_options.split(',')][0] + if mount_options not in mo_mount_options: + return False, mo + + if username and mo.map in ['cifs', 'www']: + mo_username = re.search(r'username=(\S*?),', + mo.mount_options).groups()[0] + if username != mo_username: + return False, mo + + if mo.mapping_status != 'OK': + return False, mo + + return True, mo + + +def vmedia_mount_iso_uri(handle, uri, user_id=None, password=None, + timeout=60, interval=5, server_id=1): + """ + This method will setup the vmedia mapping + Args: + handle (ImcHandle) + uri (string): URI of the ISO image + user_id (string): optional username + password (string): optional password + timeout (int): optional timeout to wait for ISO map status to be 'OK' + interval (int): optional interval to query ISO status + server_id (int): Server Id to be specified for C3260 platforms + + Raises: + Exception if invalid protocol in URI + Exception when the mapping doesn't reach 'OK' status + + Returns: + True if mapping succeeded + + Examples: + vmedia_mount_iso_uri( + handle, + uri="http://1.1.1.1/files/ubuntu-14.04.2-server-amd64.iso" + ) + """ + + # Verify interval not set to zero + if interval < 1 or type(interval) is not int: + raise ValueError("ERROR: interval must be positive integer") + + # Parse file/path from URI + remote_file = os.path.basename(uri) + remote_share = os.path.dirname(uri) + "/" + mount_options = "noauto" + + # Set the Map based on the protocol + if urlparse.urlsplit(uri).scheme == 'http': + mount_protocol = "www" + elif urlparse.urlsplit(uri).scheme == 'https': + mount_protocol = "www" + elif CIFS_URI_PATTERN.match(uri): + mount_protocol = "cifs" + elif NFS_URI_PATTERN.match(uri): + mount_protocol = "nfs" + else: + # Raise ValueError and bail + raise ValueError("Unsupported protocol: " + + urlparse.urlsplit(uri).scheme) + + # Convert no user/pass to blank strings + if not user_id: + user_id = '' + if not password: + password = '' + + # Map the ISO + vmedia_mount_create(handle, + volume_name=remote_file, + map=mount_protocol, + mount_options=mount_options, + remote_share=remote_share, + remote_file=remote_file, + username=user_id, + password=password, + server_id=server_id) + + # Verify correct URL was mapped + if uri in vmedia_get_existing_uri(handle, server_id): + # Loop until mapping moves out of 'In Progress' state + wait_time = 0 + status_list = vmedia_get_existing_status(handle, server_id) + while 'In Progress' in status_list: + # Raise error if we've reached timeout + if wait_time > timeout: + raise ImcOperationError( + 'Mount Virtual Media', + '{0}: ERROR - Mapped ISO status stuck at ' + + '[In Progress]'.format(handle.ip) + ) + # Wait interval sec between checks + time.sleep(interval) + status_list = vmedia_get_existing_status(handle, server_id) + wait_time += interval + else: + # Verify mapping transitioned to 'OK' state + if 'OK' in status_list: + return True + else: + raise ImcOperationError( + 'Mount Virtual Media', + '{0}: ERROR - Mapped ISO status ' + + 'is {1}'.format(handle.ip, status_list) + ) + else: + raise ImcOperationError( + 'Mount Virtual Media', + '{0}: ERROR - ISO {1} did not get mapped.'.format(handle.ip, uri) + ) + + +def vmedia_mount_delete(handle, volume_name, server_id=1): + """ + This method will remove the vmedia mapping referred to by the volume name + + Args: + handle (ImcHandle) + volume_name (string): Name of the volume or identity of the image + server_id (int): Server Id to be specified for C3260 platforms + + Returns: + None + + Raises: + Exception when the mapping is not found + + Examples: + vmedia_mount_remove(handle, volume_name="c") + """ + + vmediamap_mo = CommVMediaMap( + parent_mo_or_dn=_get_vmedia_mo_dn(handle, server_id), + volume_name=volume_name) + vmediamap_mo = handle.query_dn(dn=vmediamap_mo.dn) + if vmediamap_mo is None: + raise ValueError("Volume '%s' does not exist" % volume_name) + + handle.remove_mo(vmediamap_mo) + + +def vmedia_mount_remove_all(handle, server_id=1): + """ + This method will remove all the vmedia mappings + + Args: + handle (ImcHandle) + server_id (int): Server Id to be specified for C3260 platforms + + Raises: + Exception if mapping is able to be removed + + Returns: + True + + Examples: + vmedia_mount_remove_all(handle) + """ + + # Get all current virtually mapped ISOs + virt_media_maps = handle.query_children(in_dn=_get_vmedia_mo_dn(handle, + server_id)) + # Loop over each mapped ISO + for virt_media in virt_media_maps: + # Remove the mapped ISO + handle.remove_mo(virt_media) + # Raise error if all mappings not removed + if len(handle.query_children(in_dn="sys/svc-ext/vmedia-svc")) > 0: + raise ImcOperationError('Remove Virtual Media', + '{0}: ERROR - Unable remove all virtual' + + 'media mappings'.format(handle.ip)) + # Return True if all mappings removed + return True + + +def vmedia_mount_remove_image(handle, image_type, server_id=1): + """ + This method will remove the vmedia mapping of specific type + + Args: + handle (ImcHandle) + image_type (str): 'iso' or 'img' + server_id (int): Server Id to be specified for C3260 platforms + + Raises: + Exception if mapping is able to be removed + + Returns: + True + + Examples: + vmedia_mount_remove_image(handle, image_type='iso') + """ + + # Get all current virtually mapped ISOs + virt_media_maps = handle.query_children(in_dn=_get_vmedia_mo_dn(handle, + server_id)) + # Loop over each mapped ISO + for virt_media in virt_media_maps: + # Remove the mapped ISO + virt_media_type = virt_media.remote_file.split('.')[-1] + if virt_media_type == image_type: + handle.remove_mo(virt_media) + log.warning("Removing existing mapping '%s'" % virt_media.dn) + break diff --git a/imcsdk/apis/v2/storage/__init__.py b/imcsdk/apis/v2/storage/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/imcsdk/apis/v2/storage/controller.py b/imcsdk/apis/v2/storage/controller.py new file mode 100644 index 00000000..c347388a --- /dev/null +++ b/imcsdk/apis/v2/storage/controller.py @@ -0,0 +1,546 @@ +# Copyright 2017 Cisco Systems, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +""" +This module provides APIs for storage controller configuration. +""" + +import logging + +import imcsdk.imccoreutils as imccoreutils +from imcsdk.imcexception import ImcOperationError +from imcsdk.mometa.self.SelfEncryptStorageController import \ + SelfEncryptStorageController, SelfEncryptStorageControllerConsts +from imcsdk.mometa.storage.StorageController import StorageControllerConsts + +log = logging.getLogger('imc') + + +def _get_controller_dn(handle, controller_type, controller_slot, server_id=1): + dn = imccoreutils.get_server_dn(handle, server_id) + return dn + "/board/storage-" + controller_type + "-" + controller_slot + + +def _get_controller(handle, controller_type, controller_slot, server_id=1): + mo = handle.query_dn(_get_controller_dn(handle, + controller_type, + controller_slot, + server_id)) + if mo is None: + raise ImcOperationError("Get Controller Type:%s Slot:%s" % + (controller_type, controller_slot), + "Not found") + return mo + + +def _controller_action_set(handle, controller_type, controller_slot, action, + server_id=1): + mo = _get_controller(handle, controller_type, controller_slot, server_id) + mo.admin_action = action + handle.set_mo(mo) + return mo + + +def controller_jbod_enable(handle, controller_type, controller_slot, + server_id=1): + """ + Enables jbod mode on the controller + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + server_id (int): server_id for UCS 3260 platform + + Returns: + StorageController object + + Examples: + controller_jbod_enable(handle, controller_type='SAS', + controller_slot='HBA') + + """ + action = StorageControllerConsts.ADMIN_ACTION_ENABLE_JBOD + return _controller_action_set(handle=handle, + controller_type=controller_type, + controller_slot=controller_slot, + action=action, + server_id=server_id) + + +def controller_jbod_exists(handle, controller_type, controller_slot, + server_id=1): + """ + Checks if jbod mode is enabled on the controller + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + server_id (int): server_id for UCS 3260 platform + + Returns: + bool + + Examples: + controller_jbod_exists(handle, controller_type='SAS', + controller_slot='HBA') + """ + controller_dn = _get_controller_dn(handle, controller_type, + controller_slot, server_id) + dn = controller_dn + "/controller-settings" + mo = handle.query_dn(dn) + + if mo.enable_jbod.lower() not in ['yes', 'true']: + return False + return True + + +def controller_jbod_disable(handle, controller_type, controller_slot, + server_id=1): + """ + Disables jbod mode on the controller + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + server_id (int): server_id for UCS 3260 platform + + Returns: + StorageController object + + Examples: + controller_jbod_disable(handle, controller_type='SAS', + controller_slot='HBA') + + """ + action = StorageControllerConsts.ADMIN_ACTION_DISABLE_JBOD + return _controller_action_set(handle=handle, + controller_type=controller_type, + controller_slot=controller_slot, + action=action, + server_id=server_id) + + +def controller_encryption_enable(handle, controller_type, controller_slot, + security_key=None, key_id=None, + existing_security_key=None, + key_management="local", + server_id=1, **kwargs): + """ + Enables encryption on the controller + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9" + security_key (str): Security key used to enable controller security. + Max Length is 32 characters. + key_id (str): Security Key Identifier. + Max Length is 256 characters. + existing_security_key (str): Existing security key. + Max Length is 32 characters. + key_management (str): "local" or "remote" + server_id (int): server_id for UCS 3260 platform + + Returns: + StorageController object + + Examples: + controller_encryption_enable(handle, controller_type='SAS', + controller_slot='HBA'', + key_id='ABCD12345', security_key='12345') + """ + from imcsdk.mometa.self.SelfEncryptStorageController import \ + SelfEncryptStorageController, SelfEncryptStorageControllerConsts + + controller_mo = _get_controller(handle, controller_type, controller_slot, + server_id) + + enabled = controller_mo.self_encrypt_enabled.lower() in ['yes', 'true'] + if not enabled: + action = \ + SelfEncryptStorageControllerConsts.ADMIN_ACTION_ENABLE_SELF_ENCRYPT + existing_security_key = None + else: + action = \ + SelfEncryptStorageControllerConsts.ADMIN_ACTION_MODIFY_SELF_ENCRYPT + + if not enabled and key_management == "local" and security_key is None: + raise ImcOperationError("Enable encryption on Controller: %s" % controller_slot, + "Missing param 'security_key'.") + + mo = SelfEncryptStorageController(parent_mo_or_dn=controller_mo.dn) + params = { + 'key_id': key_id, + 'existing_security_key': existing_security_key, + 'security_key': security_key, + 'key_management': key_management, + 'admin_action': action, + } + + mo.set_prop_multiple(**params) + mo.set_prop_multiple(**kwargs) + handle.set_mo(mo) + return handle.query_dn(controller_mo.dn) + + +def controller_encryption_exists(handle, controller_type, controller_slot, + server_id=1, **kwargs): + """ + Checks if encryption is enabled on the controller + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + server_id (int): server_id for UCS 3260 platform + + Returns: + True/False, MO + + Examples: + controller_encryption_exists(handle, controller_type='SAS', + controller_slot='HBA'') + """ + try: + mo = _get_controller(handle, controller_type, controller_slot, + server_id) + except: + return False, None + + existing_security_key = kwargs.pop('existing_security_key', None) + if existing_security_key: + return False, mo + + if mo.self_encrypt_enabled.lower() not in ['yes', 'true']: + return False, mo + + kwargs.pop('security_key', None) + kwargs.pop('key_management', None) + dn = mo.dn + "/ctr-self-encrypt" + encryption_mo = handle.query_dn(dn) + return encryption_mo.check_prop_match(**kwargs), mo + + +def controller_encryption_disable(handle, controller_type, + controller_slot, server_id=1): + """ + Disables encryption on the controller + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + server_id (int): server_id for UCS 3260 platform + + Returns: + StorageController object + + Examples: + controller_encryption_disable(handle, controller_type='SAS', + controller_slot='HBA'') + """ + controller_dn = _get_controller_dn(handle, controller_type, + controller_slot, server_id) + + mo = SelfEncryptStorageController(parent_mo_or_dn=controller_dn) + mo.admin_action = \ + SelfEncryptStorageControllerConsts.ADMIN_ACTION_DISABLE_SELF_ENCRYPT + handle.set_mo(mo) + return handle.query_dn(controller_dn) + + +def controller_encryption_key_id_generate(handle, controller_type, + controller_slot, server_id=1): + """ + Generates a random key id for the given controller + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + server_id (int): server_id for UCS 3260 platform + + Returns: + Key-id string + + Examples: + key_id = controller_encryption_key_id_generate( + handle, + controller_type='SAS', + controller_slot='HBA'') + """ + dn = _get_controller_dn(handle, controller_type, controller_slot, + server_id) + mos = handle.query_children( + in_dn=dn, + class_id='GeneratedStorageControllerKeyId') + return mos[0].generated_key_id if mos else "" + + +def controller_encryption_key_generate(handle, controller_type, + controller_slot, server_id=1): + """ + Generates a random security key for the given controller + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + server_id (int): server_id for UCS 3260 platform + + Returns: + Security Key string + + Examples: + key = controller_encryption_key_generate( + handle, + controller_type='SAS', + controller_slot='HBA'') + """ + dn = _get_controller_dn(handle, controller_type, controller_slot, + server_id) + mos = handle.query_children( + in_dn=dn, + class_id='SuggestedStorageControllerSecurityKey') + return mos[0].suggested_security_key if mos else "" + + +def controller_unlock_foreign_drives(handle, controller_type, + controller_slot, security_key, + server_id=1): + """ + Unlocks on the given controller, drives encrypted on another controller. + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + security_key (str): Security Key used to encrypt the foreign drive + server_id (int): server_id for UCS 3260 platform + + Returns: + StorageController object + + Examples: + key = controller_unlock_foreign_drives( + handle, + controller_type='SAS', + controller_slot='HBA'', + security_key='12345') + """ + action = \ + SelfEncryptStorageControllerConsts.ADMIN_ACTION_UNLOCK_SECURED_DRIVES + dn = _get_controller_dn(handle, controller_type, controller_slot, + server_id) + mo = SelfEncryptStorageController(parent_mo_or_dn=dn) + params = { + 'security_key': security_key, + 'admin_action': action + } + mo.set_prop_multiple(**params) + return handle.query_dn(dn) + + +def controller_import_foreign_config(handle, controller_type, + controller_slot, server_id=1): + """ + Imports foreign configuration on the given controller + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + server_id (int): server_id for UCS 3260 platform + + Returns: + StorageController object + + Examples: + controller_import_foreign_config(handle, + controller_type='SAS', + controller_slot='HBA'') + """ + action = StorageControllerConsts.ADMIN_ACTION_IMPORT_FOREIGN_CONFIG + return _controller_action_set( + handle, + controller_type, + controller_slot, + action=action, + server_id=server_id) + + +def controller_clear_boot_drive(handle, controller_type, controller_slot, + server_id=1): + """ + Clears boot drive + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + server_id (int): server_id for UCS 3260 platform + + Returns: + StorageController object + + Examples: + controller_clear_boot_drive(handle, controller_type='SAS', + controller_slot='HBA'') + """ + action = StorageControllerConsts.ADMIN_ACTION_CLEAR_BOOT_DRIVE + return _controller_action_set( + handle, + controller_type, + controller_slot, + action=action, + server_id=server_id) + + +def controller_clear_boot_drive_exists(handle, controller_type, + controller_slot, server_id=1): + """ + checks if boot drive is already cleared. + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + server_id (int): server_id for UCS 3260 platform + + Returns: + StorageController object + + Examples: + controller_clear_boot_drive(handle, controller_type='SAS', + controller_slot='HBA'') + """ + controller_dn = _get_controller_dn(handle, controller_type, + controller_slot, server_id) + dn = controller_dn + "/controller-props" + mo = handle.query_dn(dn) + + if mo.boot_drive.lower() != 'none': + return False + return True + + +def controller_action_apply(handle, controller_type, controller_slot, + action, server_id=1): + """ + Applies the controller action. + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + action (str): ["clear_boot_drive"] + server_id (int): server_id for UCS 3260 platform + + Returns: + StorageController object + + Examples: + controller_action_apply(handle, controller_type='SAS', + controller_slot='HBA', + action='clear_boot_drive') + """ + api_map = { + "clear_boot_drive": controller_clear_boot_drive, + } + + if action not in api_map: + raise ImcOperationError("Configure Controller: %s" % controller_slot, + "Invalid action. Valid values are %s" + % str(", ".join(api_map.keys()))) + + params = { + 'handle': handle, + 'controller_type': controller_type, + 'controller_slot': controller_slot, + 'server_id': server_id + } + + execute_api = api_map[action] + return execute_api(**params) + + +def controller_action_exists(handle, controller_type, controller_slot, + action, server_id=1): + """ + Check if action is already applied on controller. + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + action (str): ["clear_boot_drive"] + server_id (int): server_id for UCS 3260 platform + + Returns: + True/False, MO/None + + Examples: + controller_action_exists(handle, controller_type='SAS', + controller_slot='HBA'', + action='clear_boot_drive') + """ + api_map = { + "clear_boot_drive": controller_clear_boot_drive_exists, + } + + if action not in api_map: + raise ImcOperationError("Configure Controller: %s" % controller_slot, + "Invalid action. Valid values are %s" + % str(", ".join(api_map.keys()))) + + params = { + 'handle': handle, + 'controller_type': controller_type, + 'controller_slot': controller_slot, + 'server_id': server_id + } + + execute_api = api_map[action] + return execute_api(**params) diff --git a/imcsdk/apis/v2/storage/pd.py b/imcsdk/apis/v2/storage/pd.py new file mode 100644 index 00000000..2215c9a4 --- /dev/null +++ b/imcsdk/apis/v2/storage/pd.py @@ -0,0 +1,657 @@ +# Copyright 2017 Cisco Systems, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +""" +This module provides APIs for physical drive configuration. +""" + +import logging + +from imcsdk.imcexception import ImcOperationError +from imcsdk.mometa.storage.StorageLocalDisk import StorageLocalDiskConsts +from imcsdk.apis.storage.controller import _get_controller_dn + +log = logging.getLogger('imc') + + +def pd_get(handle, + controller_type, controller_slot, drive_slot, + server_id=1): + """ + Gets the physical drive + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + drive_slot (int): Slot in which the drive resides + server_id (int): server_id for UCS 3260 platform + + Returns: + StorageLocalDisk object + + Examples: + mo = pd_get(handle, controller_type='SAS', controller_slot='HBA'', + drive_slot=4') + """ + controller_dn = _get_controller_dn(handle, + controller_type, + controller_slot, + server_id) + dn = controller_dn + '/pd-' + str(drive_slot) + return handle.query_dn(dn) + + +def _pd_set_action(handle, controller_type, controller_slot, drive_slot, + action, + server_id=1, **kwargs): + mo = pd_get(handle, controller_type, controller_slot, drive_slot, + server_id) + if mo is None: + raise ImcOperationError("Get Physical drive: %d" % drive_slot, + "Not found") + mo.admin_action = action + mo.set_prop_multiple(**kwargs) + handle.set_mo(mo) + return handle.query_dn(mo.dn) + + +def pd_set_unconfigured_good(handle, + controller_type, controller_slot, drive_slot, + server_id=1): + """ + Sets the physical drive in unconfigured good state + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + drive_slot (int): Slot in which the drive resides + server_id (int): server_id for UCS 3260 platform + + Returns: + StorageLocalDisk object + + Examples: + pd_set_unconfigured_good(handle, controller_type='SAS', + controller_slot='HBA'', drive_slot=4') + """ + action = StorageLocalDiskConsts.ADMIN_ACTION_MAKE_UNCONFIGURED_GOOD + return _pd_set_action( + handle, + controller_type=controller_type, + controller_slot=controller_slot, + drive_slot=drive_slot, + action=action, + server_id=server_id + ) + + +def pd_set_jbod(handle, + controller_type, controller_slot, drive_slot, + server_id=1): + """ + Sets the physical drive in jbod mode + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + drive_slot (int): Slot in which the drive resides + server_id (int): server_id for UCS 3260 platform + + Returns: + StorageLocalDisk object + + Examples: + pd_set_jbod(handle, controller_type='SAS', controller_slot='HBA'', + drive_slot=4') + """ + from imcsdk.apis.storage.controller import controller_jbod_exists + + if not controller_jbod_exists(handle, + controller_type, + controller_slot, + server_id): + raise ImcOperationError("Enable JBOD mode on Physical drive: %d" % drive_slot, + "Controller JBOD mode is not enabled") + + action = StorageLocalDiskConsts.ADMIN_ACTION_MAKE_JBOD + return _pd_set_action( + handle, + controller_type=controller_type, + controller_slot=controller_slot, + drive_slot=drive_slot, + action=action, + server_id=server_id + ) + + +def pd_set_global_hot_spare(handle, + controller_type, controller_slot, drive_slot, + server_id=1): + """ + Sets the physical drive as boot drive + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + drive_slot (int): Slot in which the drive resides + server_id (int): server_id for UCS 3260 platform + + Returns: + StorageLocalDisk object + + Examples: + pd_set_global_hot_spare(handle, controller_type='SAS', + controller_slot='HBA'', drive_slot=4') + """ + action = StorageLocalDiskConsts.ADMIN_ACTION_MAKE_GLOBAL_HOT_SPARE + return _pd_set_action( + handle, + controller_type=controller_type, + controller_slot=controller_slot, + drive_slot=drive_slot, + action=action, + server_id=server_id + ) + + +def pd_set_dedicated_hot_spare(handle, controller_type, controller_slot, + drive_slot, vd_id, server_id=1): + """ + Sets the physical drive as boot drive + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + drive_slot (int): Slot in which the drive resides + server_id (int): server_id for UCS 3260 platform + + Returns: + StorageLocalDisk object + + Examples: + pd_set_dedicated_hot_spare(handle, controller_type='SAS', + controller_slot='HBA'', drive_slot=4') + """ + action = StorageLocalDiskConsts.ADMIN_ACTION_MAKE_DEDICATED_HOT_SPARE + return _pd_set_action( + handle, + controller_type=controller_type, + controller_slot=controller_slot, + drive_slot=drive_slot, + action=action, + server_id=server_id, + dedicated_hot_spare_for_vd_id=vd_id + ) + + +def pd_remove_hot_spare(handle, + controller_type, controller_slot, drive_slot, + server_id=1): + """ + Removes the physical drive from hot spare pool. + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + drive_slot (int): Slot in which the drive resides + server_id (int): server_id for UCS 3260 platform + + Returns: + StorageLocalDisk object + + Examples: + pd_set_remove_hot_spare(handle, controller_type='SAS', + controller_slot='HBA'', drive_slot=4') + """ + action = StorageLocalDiskConsts.ADMIN_ACTION_REMOVE_HOT_SPARE + return _pd_set_action( + handle, + controller_type=controller_type, + controller_slot=controller_slot, + drive_slot=drive_slot, + action=action, + server_id=server_id + ) + + +def pd_set_boot_drive(handle, + controller_type, controller_slot, drive_slot, + server_id=1): + """ + Sets the physical drive as boot drive + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + drive_slot (int): Slot in which the drive resides + server_id (int): server_id for UCS 3260 platform + + Returns: + StorageLocalDisk object + + Examples: + pd_set_boot_drive(handle, controller_type='SAS', + controller_slot='HBA'', drive_slot=4') + """ + action = StorageLocalDiskConsts.ADMIN_ACTION_SET_BOOT_DRIVE + return _pd_set_action( + handle, + controller_type=controller_type, + controller_slot=controller_slot, + drive_slot=drive_slot, + action=action, + server_id=server_id + ) + + +def _pd_exists_boot_drive(handle, pd_dn): + dn = pd_dn + '/general-props' + mo = handle.query_dn(dn) + if mo is None: + return False + if mo.boot_drive.lower() != 'true': + return False + return True + + +def pd_set_removal_prepare(handle, + controller_type, controller_slot, drive_slot, + server_id=1): + """ + Sets the physical drive as boot drive + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + drive_slot (int): Slot in which the drive resides + server_id (int): server_id for UCS 3260 platform + + Returns: + StorageLocalDisk object + + Examples: + pd_prepare_removal(handle, controller_type='SAS', + controller_slot='HBA'', drive_slot=4') + """ + action = StorageLocalDiskConsts.ADMIN_ACTION_PREPARE_FOR_REMOVAL + return _pd_set_action( + handle, + controller_type=controller_type, + controller_slot=controller_slot, + drive_slot=drive_slot, + action=action, + server_id=server_id + ) + + +def pd_set_removal_undo(handle, + controller_type, controller_slot, drive_slot, + server_id=1): + """ + Sets the physical drive as boot drive + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + drive_slot (int): Slot in which the drive resides + server_id (int): server_id for UCS 3260 platform + + Returns: + StorageLocalDisk object + + Examples: + pd_undo_removal(handle, controller_type='SAS', controller_slot='HBA'', + drive_slot=4') + """ + action = StorageLocalDiskConsts.ADMIN_ACTION_UNDO_PREPARE_FOR_REMOVAL + return _pd_set_action( + handle, + controller_type=controller_type, + controller_slot=controller_slot, + drive_slot=drive_slot, + action=action, + server_id=server_id + ) + + +def pd_secure_erase_foreign_drives(handle, controller_type, controller_slot, + drive_slot, server_id=1): + """ + Erases foreign configuration from the physical drive. Drive data is lost. + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + drive_slot (int): Slot in which the drive resides + server_id (int): server_id for UCS 3260 platform + + Returns: + StorageLocalDisk object + + Examples: + physical_drive_secure_erase_foreign_drives( + handle, + controller_type='SAS', + controller_slot='HBA'', + drive_slot=4') + """ + action = StorageLocalDiskConsts.ADMIN_ACTION_DISABLE_SED_FOREIGN_DRIVES, + return _pd_set_action( + handle, + controller_type=controller_type, + controller_slot=controller_slot, + drive_slot=drive_slot, + action=action, + server_id=server_id + ) + + +def pd_state_set(handle, + controller_type, controller_slot, drive_slot, drive_state, + vd_id=None, server_id=1): + """ + Sets the drive state of physical drive + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + drive_slot (int): Slot in which the drive resides + drive_state (str): ["unconfigured_good", "jbod", "boot_drive", + "global_hot_spare", "dedicated_hot_spare", + "remove_hot_spare", "prepare_removal", + "undo_prepare_removal"] + vd_id (int): virtual drive id. + Use only if drive_state == dedicated_hot_spare + server_id (int): server_id for UCS 3260 platform + + Returns: + StorageLocalDisk object + + Examples: + pd_state_set(handle, controller_type='SAS', controller_slot='HBA'', + drive_slot=4'i, drive_state="jbod") + """ + + ds_map = { + "unconfigured_good": pd_set_unconfigured_good, + "jbod": pd_set_jbod, + "boot_drive": pd_set_boot_drive, + "global_hot_spare": pd_set_global_hot_spare, + "dedicated_hot_spare": pd_set_dedicated_hot_spare, + "remove_hot_spare": pd_remove_hot_spare, + "prepare_removal": pd_set_removal_prepare, + "undo_prepare_removal": pd_set_removal_undo, + } + + if drive_state not in ds_map: + raise ImcOperationError("Get Physical drive: %d" % drive_slot, + "Invalid drive state. Valid values are %s" + % str(", ".join(ds_map.keys()))) + + params = { + 'handle': handle, + 'controller_type': controller_type, + 'controller_slot': controller_slot, + 'drive_slot': drive_slot, + 'vd_id': vd_id, + 'server_id': server_id + } + + if not drive_state == "dedicated_hot_spare": + params.pop('vd_id', None) + + execute_api = ds_map[drive_state] + return execute_api(**params) + + +def pd_state_exists(handle, + controller_type, controller_slot, drive_slot, drive_state, + vd_id=None, server_id=1): + """ + Sets the drive state of physical drive + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + drive_slot (int): Slot in which the drive resides + drive_state (str): ["unconfigured_good", "jbod", "boot_drive", + "global_hot_spare", "dedicated_hot_spare", + "remove_hot_spare", "prepare_removal", + "undo_prepare_removal"] + vd_id (int): virtual drive id. + Use only if drive_state == dedicated_hot_spare + server_id (int): server_id for UCS 3260 platform + + Returns: + StorageLocalDisk object + + Examples: + pd_undo_removal(handle, controller_type='SAS', controller_slot='HBA'', + drive_slot=4') + """ + + ds_map = { + "unconfigured_good": "Unconfigured Good", + "jbod": "JBOD", + "boot_drive": None, + "global_hot_spare": "Global Hot Spare", + "dedicated_hot_spare": "Dedicated Hot Spare", + "remove_hot_spare": None, + "prepare_removal": "Ready to Remove", + "undo_prepare_removal": None, + } + + if drive_state not in ds_map: + raise ImcOperationError("Get Physical drive: %d" % drive_slot, + "Invalid drive state. Valid values are %s" + % str(", ".join(ds_map.keys()))) + + mo = pd_get(handle, controller_type, controller_slot, drive_slot, + server_id) + if mo is None: + return False, None + + mo_drive_state = mo.drive_state + expected_drive_state = ds_map[drive_state] + + if drive_state == "boot_drive": + return _pd_exists_boot_drive(handle, mo.dn), mo + + if expected_drive_state is None: + return False, mo + + return mo_drive_state == expected_drive_state, mo + + +def pd_encryption_capable(handle, controller_type, controller_slot, drive_slot, + server_id=1): + """ + Checks if encryption is supported on the physical drive + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + drive_slot (int): Slot in which the drive resides + server_id (int): server_id for UCS 3260 platform + + Returns: + bool + + Examples: + capable = is_physical_drive_encryption_capable( + handle, + controller_type='SAS', + controller_slot='HBA'', + drive_slot=4') + """ + mo = pd_get(handle, controller_type, controller_slot, drive_slot, + server_id) + if mo is None: + raise ImcOperationError("Get Physical drive:%s" % drive_slot, + "Not found") + return mo.fde_capable.lower() in ['yes', 'true'] + + +def pd_encryption_enable(handle, + controller_type, controller_slot, drive_slot, + server_id=1): + """ + Enables encryption on the physical drive + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + drive_slot (int): Slot in which the drive resides + server_id (int): server_id for UCS 3260 platform + + Returns: + StorageLocalDisk object + + Examples: + pd_encryption_enable(handle, controller_type='SAS', + controller_slot='HBA'', drive_slot=4') + """ + # pre-requisite + # 1) disk should be encryption capable + capable = pd_encryption_capable(handle, controller_type, controller_slot, + drive_slot, server_id) + if not capable: + raise ImcOperationError("Enable encryption on the Physical drive: %d" % drive_slot, + "Drive is not FDE capable.") + + # 2) disk should be in JBOD mode. + pd = pd_get(handle, controller_type, controller_slot, drive_slot, + server_id) + if pd.drive_state != "JBOD": + raise ImcOperationError("Enable encryption on the Physical drive: %d" % drive_slot, + "Drive is not in JBOD mode") + + action = StorageLocalDiskConsts.ADMIN_ACTION_ENABLE_SELF_ENCRYPT + return _pd_set_action( + handle, + controller_type=controller_type, + controller_slot=controller_slot, + drive_slot=drive_slot, + action=action, + server_id=server_id + ) + + +def pd_encryption_exists(handle, controller_type, controller_slot, drive_slot, + server_id=1): + """ + Checks if encryption is enabled on the physical drive + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + drive_slot (int): Slot in which the drive resides + server_id (int): server_id for UCS 3260 platform + + Returns: + True/False, MO/None + + Examples: + enabled = pd_encryption_exists(handle, controller_type='SAS', + controller_slot='HBA'', drive_slot=4') + """ + mo = pd_get(handle, controller_type, controller_slot, drive_slot, + server_id) + if mo is None: + return False, None + + if mo.fde_enabled.lower() not in ['yes', 'true']: + return False, mo + + return True, mo + + +def pd_encryption_disable(handle, controller_type, controller_slot, drive_slot, + server_id=1): + """ + Disables encryption on the physical drive + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + drive_slot (int): Slot in which the drive resides + server_id (int): server_id for UCS 3260 platform + + Returns: + StorageLocalDisk object + + Examples: + physical_drive_encryption_disable( + handle, + controller_type='SAS', + controller_slot='HBA'', + drive_slot=4') + """ + action = StorageLocalDiskConsts.ADMIN_ACTION_DISABLE_SELF_ENCRYPT + return _pd_set_action( + handle, + controller_type=controller_type, + controller_slot=controller_slot, + drive_slot=drive_slot, + action=action, + server_id=server_id + ) diff --git a/imcsdk/apis/v2/storage/vd.py b/imcsdk/apis/v2/storage/vd.py new file mode 100644 index 00000000..70c6e276 --- /dev/null +++ b/imcsdk/apis/v2/storage/vd.py @@ -0,0 +1,743 @@ +# Copyright 2017 Cisco Systems, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +""" +This module provides APIs for storage configuration like virtual drives and +disk groups. +""" + +import math +import logging + +from imcsdk.imcexception import ImcOperationError, ImcException, ImcOperationErrorDetail +from imcsdk.apis.storage.controller import _get_controller_dn +from imcsdk.apis.storage.pd import pd_get +from imcsdk.mometa.storage.StorageVirtualDriveCreatorUsingUnusedPhysicalDrive \ + import StorageVirtualDriveCreatorUsingUnusedPhysicalDrive as vd_creator +from imcsdk.mometa.storage.StorageVirtualDriveCreatorUsingVirtualDriveGroup \ + import StorageVirtualDriveCreatorUsingVirtualDriveGroup as vd_carve + +log = logging.getLogger('imc') + + +def _list_to_string(drive_list): + # convert to format imc expects + # list to string + # [[1]] => '[1]' + # [[1,2],[3,4]] => '[1,2][3,4]' + # imc fails to parse '[1, 2]' + # it needs to be '[1,2]' + # and hence the replace(' ', '') + dg_str = "" + for each in drive_list: + dg_str += str(each) + return dg_str.replace(' ', '') + + +def _flatten_list(drive_list): + # convert to format imc expects + # [[1]] => [1] + # [[1,2],[3,4]] => [1, 2, 3, 4] + if not (isinstance(drive_list, list) and isinstance(drive_list[0], list)): + raise "drive_list needs a list of list(s). i.e [[1,2],[3,4]]" + dg_list = [] + for each in drive_list: + for sub_each in each: + dg_list.append(sub_each) + return dg_list + + +def _flatten_to_string(drive_list): + # convert to format imc expects + # [[1]] => '1' + # [[1,2],[3,4]] => '1234' + return ''.join([''.join(str(x)) for x in _flatten_list(drive_list)]) + + +def vd_name_derive(raid_level, drive_list): + return "RAID" + str(raid_level) + "_" + _flatten_to_string(drive_list) + + +def _human_to_bytes(size_str): + """ + returns the size in bytes + supported formats KB, MB, GB, TB, PB, EB, ZB, YB + + KB to bytes = (* 1024) == << 10 + MB to bytes = (* 1024 * 1024) == << 20 + GB to bytes = (* 1024 * 1024 * 1024) == << 30 + """ + convert = {'KB': 1, 'MB': 2, 'GB': 3, 'TB': 4, + 'PB': 5, 'EB': 6, 'ZB': 7, 'YB': 8} + s = size_str.split() + if s[1] not in convert: + raise "unknown size format" + size_str + return int(s[0]) << (10 * convert[s[1]]) + + +def _bytes_to_human(size, output_format=None): + """ + converts bytes to human readable format. + The return is in output_format. + """ + convert = {'KB': 1, 'MB': 2, 'GB': 3, 'TB': 4, + 'PB': 5, 'EB': 6, 'ZB': 7, 'YB': 8} + unit = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] + if output_format is None: + output_format = unit[int(math.floor(math.log(size, 2))/10)] + if output_format not in convert: + raise "unknown output format" + output_format + return str(size >> (10 * convert[output_format])) + ' ' + output_format + + +def _pd_sizes_get(handle, + controller_type, + controller_slot, + drive_list, + server_id=1): + sizes = [] + for each in drive_list: + for drive in each: + dmo = pd_get(handle=handle, + controller_type=controller_type, + controller_slot=controller_slot, + drive_slot=drive, + server_id=server_id) + sizes.append(_human_to_bytes(dmo.coerced_size)) + return sizes + + +def _pd_min_size_get(sizes): + return min(sizes) + + +def _pd_total_size_get(sizes): + return sum(sizes) + + +def _pd_unused_get(handle): + return handle.query_classid(class_id='StorageUnusedLocalDisk') + + +def _vd_span_depth_get(drive_list): + return len(drive_list) + + +def _raid_max_size_get(raid_level, total_size, min_size, span_depth): + size = {0: total_size, + 1: total_size/2, + 5: total_size - (span_depth * 1 * min_size), + 6: total_size - (span_depth * 2 * min_size), + 10: total_size/2, + 50: total_size - (span_depth * 1 * min_size), + 60: total_size - (span_depth * 2 * min_size)} + + if raid_level not in size: + raise ImcOperationError("Create Virtual Drive", + "Unsupported Raid level <%d>" % raid_level) + return size[raid_level] + + +def _vd_max_size_get(handle, + controller_type, + controller_slot, + drive_list, + raid_level, + server_id=1): + """ + Returns the usable disk size for the specified virtual_drive + drive_list: + [[1]] + [[1,2],[3,4]] + + min_size = smallest size of all the drives from this disk group + available_size = accumulated size of all the drives together + span_depth = number of sub groups [[1,2,3],[4,5,6]] span_depth = 2 + """ + sizes = _pd_sizes_get(handle=handle, + controller_type=controller_type, + controller_slot=controller_slot, + drive_list=drive_list, + server_id=server_id) + min_size = _pd_min_size_get(sizes) + total_size = _pd_total_size_get(sizes) + span_depth = _vd_span_depth_get(drive_list) + + max_size = _raid_max_size_get(raid_level, total_size, min_size, span_depth) + return _bytes_to_human(max_size, output_format="MB") + + +def _existing_vd_maxsize_get(handle, vd_carve, id): + dn = vd_carve.dn + "/vd-" + str(id) + + mo = handle.query_dn(dn) + if mo is None: + raise ImcOperationError("Create Virtual drive using existing Virtual drives", + "Existing Drive: %d does not exist OR " \ + "No space is available to create another Virtual " \ + "drive." % id) + return mo.max_available_space + + +def vd_create_using_unused_pds(handle, + drive_group, + controller_type, + controller_slot, + raid_level=0, + virtual_drive_name=None, + access_policy="read-write", + read_policy="no-read-ahead", + cache_policy="direct-io", + disk_cache_policy="unchanged", + write_policy="Write Through", + strip_size="64k", + size=None, + self_encrypt=False, + server_id=1): + """ + Creates virtual drive from unused physical drives + + Args: + handle (ImcHandle) + drive_group (list of lists): list of drives + [[1]] + [[1,2]] + [[1,2],[3,4]] + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + raid_level (int): raid level + 0, 1, 5, 6, 10, 50, 60 + Raid 0 Simple striping. + Raid 1 Simple mirroring. + Raid 5 Striping with parity. + Raid 6 Striping with two parity drives. + Raid 10 Spanned mirroring. + Raid 50 Spanned striping with parity. + Raid 60 Spanned striping with two parity drives. + virtual_drive_name (str): Name of the virtual drive + access_policy (str): Access-policy for the virtual drive + ['read-write', 'read-only', 'hidden', 'default', 'blocked'] + read_policy (str): Read Policy for the virtual drive. + ["always-read-ahead", "no-read-ahead"] + cache_policy (str): Cache policy used for buffering reads. + ["cached-io", "direct-io"] + disk_cache_policy: Disk cache policy. + ["disabled", "enabled", "unchanged"] + write_policy: Write policy + ["always-write-back", "write-back-good-bbu", "write-through"] + strip_size: Size of each strip + ["1024k", "128k", "16k", "256k", "32k", "512k", "64k", "8k"] + size: The size of the virtual drive you want to create. + Enter a value and select one of the following units - MB,GB,TB + e.g. '100 MB' or '20 GB' or '1 TB' + self_encrypt (bool): Encrypt the virtual drive if the underlying + controller and physical drive support it + server_id (int): Specify server id for UCS C3260 modular servers + + Returns: + None + + Examples: + vd_create_using_unused_pds(handle=imc, drive_group=[[2]], + controller_type = 'SAS', + controller_slot='MEZZ') + """ + slot_dn = _get_controller_dn(handle, controller_type, + controller_slot, server_id) + + dg_str = _list_to_string(drive_group) + vdn = virtual_drive_name + + params = {} + params["parent_mo_or_dn"] = slot_dn + params["drive_group"] = dg_str + params["raid_level"] = str(raid_level) + params["access_policy"] = access_policy + params["read_policy"] = read_policy + params["cache_policy"] = cache_policy + params["disk_cache_policy"] = disk_cache_policy + params["write_policy"] = write_policy + params["strip_size"] = strip_size + + if self_encrypt: + params["admin_action"] = "enable-self-encrypt" + + params["virtual_drive_name"] = \ + (vd_name_derive(raid_level, drive_group), vdn)[vdn is not None] + + params["size"] = (_vd_max_size_get(handle=handle, + controller_type=controller_type, + controller_slot=controller_slot, + drive_list=drive_group, + raid_level=raid_level, + server_id=server_id), + size)[size is not None] + + mo = vd_creator(**params) + mo.admin_state = "trigger" + handle.add_mo(mo) + + +def vd_create_using_existing_vd(handle, + controller_type, + controller_slot, + shared_virtual_drive_id, + virtual_drive_name, + access_policy="read-write", + read_policy="no-read-ahead", + cache_policy="direct-io", + disk_cache_policy="unchanged", + write_policy="Write Through", + strip_size="64k", + size=None, + server_id=1): + """ + Creates virtual drive from the existing virtua drives. + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + shared_virtual_drive_id (int): Id of the existing virtual drive + virtual_drive_name (str): Name of the virtual drive + access_policy (str): Access-policy for the virtual drive + ['read-write', 'read-only', 'hidden', 'default', 'blocked'] + read_policy (str): Read Policy for the virtual drive. + ["always-read-ahead", "no-read-ahead"] + cache_policy (str): Cache policy used for buffering reads. + ["cached-io", "direct-io"] + disk_cache_policy: Disk cache policy. + ["disabled", "enabled", "unchanged"] + write_policy: Write policy + ["always-write-back", "write-back-good-bbu", "write-through"] + strip_size: Size of each strip + ["1024k", "128k", "16k", "256k", "32k", "512k", "64k", "8k"] + size: The size of the virtual drive you want to create. + Enter a value and select one of the following units - MB,GB,TB + e.g. '100 MB' or '20 GB' or '1 TB' + server_id (int): Specify server id for UCS C3260 modular servers + + Returns: + None + + Examples: + vd_create_using_existing_vd(handle=imc, + controller_type='SAS', + controller_slot='MEZZ', + shared_virtual_drive_id=1, + virtual_drive_name='newvd') + """ + slot_dn = _get_controller_dn(handle, controller_type, + controller_slot, server_id) + + mo = vd_carve(parent_mo_or_dn=slot_dn) + + params = {} + params["shared_virtual_drive_id"] = str(shared_virtual_drive_id) + params["virtual_drive_name"] = virtual_drive_name + params["access_policy"] = access_policy + params["read_policy"] = read_policy + params["cache_policy"] = cache_policy + params["disk_cache_policy"] = disk_cache_policy + params["write_policy"] = write_policy + params["strip_size"] = strip_size + + if size and size.strip(): + params["size"] = size + else: + params["size"] = _existing_vd_maxsize_get(handle=handle, + vd_carve=mo, id=shared_virtual_drive_id) + + mo.set_prop_multiple(**params) + mo.admin_state = "trigger" + handle.add_mo(mo) + + +def vd_get(handle, controller_type, controller_slot, id, server_id=1): + slot_dn = _get_controller_dn(handle, controller_type, controller_slot, + server_id) + dn = slot_dn + "/vd-" + str(id) + mo = handle.query_dn(dn) + if mo is None: + raise ImcOperationError("Get Virtual drive: %d" % id, + "Not found") + return mo + +def vd_update(handle, + controller_type, + controller_slot, + id, + server_id=1, + access_policy=None, + read_policy=None, + cache_policy=None, + disk_cache_policy=None, + write_policy=None): + mo = vd_get(handle, controller_type, controller_slot, id, server_id) + if mo is None: + raise ImcOperationError("Get Virtual drive", + "Managed Object not found") + + if access_policy is not None: + mo.access_policy = access_policy + + if read_policy is not None: + mo.read_policy = read_policy + + if cache_policy is not None: + mo.cache_policy = cache_policy + + if disk_cache_policy is not None: + mo.disk_cache_policy = disk_cache_policy + + if write_policy is not None: + mo.requested_write_cache_policy = write_policy + + handle.set_mo(mo) + return mo + + +def _vd_set_action(handle, controller_type, controller_slot, id, + action, + server_id=1, **kwargs): + mo = vd_get(handle, controller_type, controller_slot, id, server_id) + if mo is None: + raise ImcOperationError("Configure Virtual drive: %d" % id, + "Not found") + mo.admin_action = action + mo.set_prop_multiple(**kwargs) + handle.set_mo(mo) + return mo + + +def vd_exists(handle, controller_type, controller_slot, id, server_id=1, **kwargs): + """ + Checks if a virtual drive exists. + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + id (int): Id of the virtual drive + + Returns: + exists(bool), error(str) + + Examples: + vd_exists(handle=imc, controller_type='SAS', controller_slot='MEZZ', + id=1) + """ + try: + mo = vd_get(handle, controller_type, controller_slot, id, server_id) + except: + return False, None + + return True, mo + + +def vd_delete(handle, controller_type, controller_slot, id, server_id=1, + delete_boot_drive=False): + """ + Deletes the specified virtual drive + + Args: + handle (ImcHandle) + controller_slot (str): controller slot name/number + "MEZZ","0"-"9" + id (int): id of the virtual drive + server_id (int): server_id for UCS 3260 platform + + Examples: + vd_delete(handle=imc, controller_type='SAS', controller_slot='MEZZ', + id=1) + """ + from imcsdk.apis.storage.controller import controller_clear_boot_drive + + mo = vd_get(handle=handle, + controller_type=controller_type, + controller_slot=controller_slot, + id=id, + server_id=server_id) + + if delete_boot_drive and mo.boot_drive.lower() in ('yes', 'true'): + controller_clear_boot_drive(handle, controller_type, + controller_slot, server_id) + + handle.remove_mo(mo) + + +def vd_delete_all(handle, controller_type, controller_slot, server_id=1, + delete_boot_drive=False): + """ + Delete all the specified virtual drive + + Args: + handle (ImcHandle) + controller_slot (str): controller slot name/number + "MEZZ","0"-"9" + id (int): id of the virtual drive + server_id (int): server_id for UCS 3260 platform + + Examples: + vd_delete_all(handle=imc, controller_type='SAS', + controller_slot='MEZZ', id=1) + """ + from imcsdk.apis.storage.controller import controller_clear_boot_drive + + controller_dn = _get_controller_dn(handle, controller_type, + controller_slot, server_id) + mos = handle.query_children(in_dn=controller_dn, + class_id="storageVirtualDrive") + error_msg = "" + for mo in mos: + if delete_boot_drive and mo.boot_drive.lower() in ('yes', 'true'): + controller_clear_boot_drive(handle, controller_type, + controller_slot, server_id) + try: + handle.remove_mo(mo) + except ImcException as e: + error_msg += str(e) + + if error_msg: + raise ImcOperationError("Delete all Virtual drives on controller: %s" % controller_slot, + error_msg) + + +def vd_exists_any(handle, controller_type, controller_slot, server_id=1, + **kwargs): + """ + Checks if any virtual drive exists + + Args: + handle (ImcHandle) + controller_slot (str): controller slot name/number + "MEZZ","0"-"9" + id (int): id of the virtual drive + server_id (int): server_id for UCS 3260 platform + + Returns: + bool + + Examples: + vd_exists_any(handle=imc, controller_type='SAS', + controller_slot='MEZZ', id=1) + """ + controller_dn = _get_controller_dn(handle, controller_type, + controller_slot, server_id) + mos = handle.query_children(in_dn=controller_dn, + class_id="storageVirtualDrive") + if mos and len(mos) > 0: + return True + return False + + +def vd_boot_drive_enable(handle, controller_type, controller_slot, id, + server_id=1, **kwargs): + """ + Set a virtual drive as boot drive. + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + id (int): id of the virtual drive + server_id (int): server_id for UCS 3260 platform + + Returns: + StorageVirtualDrive object + + Examples: + vd_set_boot_drive(handle, 'SAS', 'HBA', 'test_vd') + """ + from imcsdk.mometa.storage.StorageVirtualDrive import \ + StorageVirtualDriveConsts + + action = StorageVirtualDriveConsts.ADMIN_ACTION_SET_BOOT_DRIVE + return _vd_set_action( + handle, + controller_type=controller_type, + controller_slot=controller_slot, + id=id, + action=action, + server_id=server_id + ) + + +def vd_boot_drive_exists(handle, controller_type, controller_slot, id, + server_id=1): + """ + Checkes if virtual drive is set as boot drive. + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + id (int): id of the virtual drive + server_id (int): server_id for UCS 3260 platform + + Returns: + StorageVirtualDrive object + + Examples: + vd_boot_drive_exists(handle, 'SAS', 'HBA', 1) + """ + try: + mo = vd_get(handle=handle, + controller_type=controller_type, + controller_slot=controller_slot, + id=id, + server_id=server_id) + except: + False, None + + if mo.boot_drive.lower() not in ('yes', 'true'): + return False, mo + return True, mo + + +def vd_boot_drive_disable(handle, controller_type, controller_slot, id, + server_id=1): + """ + Set a virtual drive as boot drive. + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + id (int): id of the virtual drive + server_id (int): server_id for UCS 3260 platform + + Returns: + StorageVirtualDrive object + + Examples: + vd_set_boot_drive(handle, 'SAS', 'HBA', 'test_vd') + """ + from imcsdk.apis.storage.controller import controller_clear_boot_drive + + vd = vd_get(handle, controller_type, controller_slot, id, server_id) + if vd.boot_drive.lower() not in ('true', 'yes'): + return vd + + controller_clear_boot_drive(handle, controller_type, controller_slot, + server_id) + + return handle.query_dn(vd.dn) + + +def vd_encryption_enable(handle, controller_type, controller_slot, id, + server_id=1): + """ + Enables encryption on the virtual drive if it is supported by the + controller and the underlying physical drive + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + id (int): id of the virtual drive + server_id (int): server_id for UCS 3260 platform + + Returns: + StorageVirtualDrive object + + Examples: + vd_encryption_enable(handle, 'SAS', 'HBA', 1) + """ + from imcsdk.mometa.storage.StorageVirtualDrive import \ + StorageVirtualDriveConsts + + action = StorageVirtualDriveConsts.ADMIN_ACTION_ENABLE_SELF_ENCRYPT + return _vd_set_action( + handle, + controller_type=controller_type, + controller_slot=controller_slot, + id=id, + action=action, + server_id=server_id + ) + + +def vd_encryption_exists(handle, controller_type, controller_slot, id, + server_id=1): + """ + Checks if encryption is enabled on the virtual drive + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + id (int): id of the virtual drive + server_id (int): server_id for UCS 3260 platform + + Returns: + boot + + Examples: + vd_encryption_exists(handle, 'SAS', 'HBA', 0) + """ + mo = vd_get(handle, controller_type, controller_slot, id, server_id) + if mo is None: + return False, None + + if mo.fde_enabled.lower() not in ['yes', 'true']: + return False, mo + + return True, mo + + +def vd_get_by_name(handle, controller_type, controller_slot, name, + server_id=1): + """ + Gets a virtual drive by name + + Args: + handle (ImcHandle) + controller_type (str): Controller type + 'SAS' + controller_slot (str): Controller slot name/number + "MEZZ","0"-"9", "HBA" + name (str): name of the virtual drive + server_id (int): server_id for UCS 3260 platform + + Returns: + StorageVirtualDrive object + + Examples: + vd_get_by_name(handle, 'SAS', 'HBA', 'test_vd') + """ + slot_dn = _get_controller_dn(handle, controller_type, controller_slot, + server_id) + + mos = handle.query_children(in_dn=slot_dn, class_id="storageVirtualDrive") + for mo in mos: + if mo.name == name: + return mo + return None