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 += """ +" + key + " | " + html += '
---|
" + each + " | " + html += "