def target(self): """Provide information on the iSCSI Target configuration Sample: { 'alias': 'myCustomName', 'ping': True, 'unnamed_discovery': True, 'chap': False, 'iqn': 'iqn.1992-08.com.netapp:2800.000a132000b006d2000000005a0e8f45', } """ target = dict() try: (rc, data) = request( self.url + 'storage-systems/%s/graph/xpath-filter?query=/storagePoolBundle/target' % self.ssid, headers=HEADERS, **self.creds) # This likely isn't an iSCSI-enabled system if not data: self.module.fail_json( msg= "This storage-system doesn't appear to have iSCSI interfaces. Array Id [%s]." % (self.ssid)) data = data[0] chap = any([ auth for auth in data['configuredAuthMethods']['authMethodData'] if auth['authMethod'] == 'chap' ]) target.update( dict(alias=data['alias']['iscsiAlias'], iqn=data['nodeName']['iscsiNodeName'], chap=chap)) (rc, data) = request( self.url + 'storage-systems/%s/graph/xpath-filter?query=/sa/iscsiEntityData' % self.ssid, headers=HEADERS, **self.creds) data = data[0] target.update( dict( ping=data['icmpPingResponseEnabled'], unnamed_discovery=data['unnamedDiscoverySessionsEnabled'])) except Exception as err: self.module.fail_json( msg= "Failed to retrieve the iSCSI target information. Array Id [%s]. Error [%s]." % (self.ssid, to_native(err))) return target
def apply_target_changes(self): update = False target = self.target body = dict() if self.ping != target['ping']: update = True body['icmpPingResponseEnabled'] = self.ping if self.unnamed_discovery != target['unnamed_discovery']: update = True body['unnamedDiscoverySessionsEnabled'] = self.unnamed_discovery self._logger.info(pformat(body)) if update and not self.check_mode: try: request(self.url + 'storage-systems/%s/iscsi/entity' % self.ssid, method='POST', data=json.dumps(body), timeout=60, headers=HEADERS, **self.creds) except Exception as err: self.module.fail_json( msg= "Failed to update the iSCSI target settings. Array Id [%s]. Error [%s]." % (self.ssid, to_native(err))) return update
def apply_iscsi_settings(self): """Update the iSCSI target alias and CHAP settings""" update = False target = self.target body = dict() if self.name is not None and self.name != target['alias']: update = True body['alias'] = self.name # If the CHAP secret was provided, we trigger an update. if self.chap_secret: update = True body.update(dict(enableChapAuthentication=True, chapSecret=self.chap_secret)) # If no secret was provided, then we disable chap elif target['chap']: update = True body.update(dict(enableChapAuthentication=False)) if update and not self.check_mode: try: request(self.url + 'storage-systems/%s/iscsi/target-settings' % self.ssid, method='POST', data=json.dumps(body), headers=HEADERS, **self.creds) except Exception as err: self.module.fail_json( msg="Failed to update the iSCSI target settings. Array Id [%s]. Error [%s]." % (self.ssid, to_native(err))) return update
def update(self): """Execute the changes the require changes on the storage array.""" target_match, lun_reference, lun = self.get_lun_mapping() update = (self.state and not target_match) or (not self.state and target_match) if update and not self.check_mode: try: if self.state: body = dict() target = None if not self.target else self.mapping_info[ "target_by_name"][self.target] if target: body.update(dict(targetId=target)) if self.lun is not None: body.update(dict(lun=self.lun)) if lun_reference: rc, response = request( self.url + "storage-systems/%s/volume-mappings/%s/move" % (self.ssid, lun_reference), method="POST", data=json.dumps(body), headers=HEADERS, **self.creds) else: body.update( dict(mappableObjectId=self. mapping_info["volume_by_name"][self.volume])) rc, response = request( self.url + "storage-systems/%s/volume-mappings" % self.ssid, method="POST", data=json.dumps(body), headers=HEADERS, **self.creds) else: # Remove existing lun mapping for volume and target rc, response = request( self.url + "storage-systems/%s/volume-mappings/%s" % (self.ssid, lun_reference), method="DELETE", headers=HEADERS, **self.creds) except Exception as error: self.module.fail_json( msg= "Failed to update storage array lun mapping. Id [%s]. Error [%s]" % (self.ssid, to_native(error))) self.module.exit_json(msg="Lun mapping is complete.", changed=update)
def controllers(self): """Retrieve a mapping of controller labels to their references { 'A': '070000000000000000000001', 'B': '070000000000000000000002', } :return: the controllers defined on the system """ try: (rc, controllers) = request( self.url + 'storage-systems/%s/controllers' % self.ssid, headers=HEADERS, **self.creds) except Exception as err: controllers = list() self.module.fail_json( msg= "Failed to retrieve the controller settings. Array Id [%s]. Error [%s]." % (self.ssid, to_native(err))) controllers.sort(key=lambda c: c['physicalLocation']['slot']) controllers_dict = dict() i = ord('A') for controller in controllers: label = chr(i) settings = dict( controllerSlot=controller['physicalLocation']['slot'], controllerRef=controller['controllerRef'], ssh=controller['networkSettings']['remoteAccessEnabled']) controllers_dict[label] = settings i += 1 return controllers_dict
def group_id(self): if self.group: try: (rc, all_groups) = request( self.url + 'storage-systems/%s/host-groups' % self.ssid, url_password=self.pwd, url_username=self.user, validate_certs=self.certs, headers=HEADERS) except Exception as err: self.module.fail_json( msg="Failed to get host groups. Array Id [%s]. Error [%s]." % (self.ssid, to_native(err))) try: group_obj = list( filter(lambda group: group['name'] == self.group, all_groups))[0] return group_obj['id'] except IndexError: self.module.fail_json(msg="No group with the name: %s exists" % self.group) else: # Return the value equivalent of no group return "0000000000000000000000000000000000000000"
def get_controllers(self): """Retrieve a mapping of controller labels to their references { 'A': '070000000000000000000001', 'B': '070000000000000000000002', } :return: the controllers defined on the system """ controllers = list() try: (rc, controllers) = request( self.url + 'storage-systems/%s/graph/xpath-filter?query=/controller/id' % self.ssid, headers=HEADERS, **self.creds) except Exception as err: self.module.fail_json( msg= "Failed to retrieve controller list! Array Id [%s]. Error [%s]." % (self.ssid, to_native(err))) controllers.sort() controllers_dict = {} i = ord('A') for controller in controllers: label = chr(i) controllers_dict[label] = controller i += 1 return controllers_dict
def update_async(module, ssid, api_url, pwd, user, body, new_name, async_id): endpoint = 'storage-systems/%s/async-mirrors/%s' % (ssid, async_id) url = api_url + endpoint compare_keys = [ 'syncIntervalMinutes', 'syncWarnThresholdMinutes', 'recoveryWarnThresholdMinutes', 'repoUtilizationWarnThreshold' ] desired_state = dict((x, (body.get(x))) for x in compare_keys) if new_name: desired_state['new_name'] = new_name post_data = json.dumps(desired_state) try: rc, data = request(url, data=post_data, method='POST', headers=HEADERS, url_username=user, url_password=pwd) except Exception as e: module.exit_json( msg="Exception while updating async mirror group. Message: %s" % to_native(e), exception=traceback.format_exc()) return data
def update_configuration(self): # Define a new domain based on the user input domain = self.make_configuration() # This is the current list of configurations current = self.get_configuration(self.identifier) update = current != domain msg = "No changes were necessary for [%s]." % self.identifier self._logger.info("Is updated: %s", update) if update and not self.check_mode: msg = "The configuration changes were made for [%s]." % self.identifier try: if current is None: api = self.base_path + 'addDomain' else: api = self.base_path + '%s' % (domain['id']) (rc, result) = request(self.url + api, method='POST', data=json.dumps(domain), **self.creds) except Exception as err: self._logger.exception("Failed to modify the LDAP configuration.") self.module.fail_json(msg="Failed to modify LDAP configuration! Array Id [%s]. Error [%s]." % (self.ssid, to_native(err))) return msg, update
def send_test_email(self): """Send a test email to verify that the provided configuration is valid and functional.""" if not self.check_mode: try: (rc, result) = request( self.url + 'storage-systems/%s/device-alerts/alert-email-test' % self.ssid, timeout=300, method='POST', headers=HEADERS, **self.creds) if result['response'] != 'emailSentOK': self.module.fail_json( msg= "The test email failed with status=[%s]! Array Id [%s]." % (result['response'], self.ssid)) # This is going to catch cases like a connection failure except Exception as err: self.module.fail_json( msg= "We failed to send the test email! Array Id [%s]. Error [%s]." % (self.ssid, to_native(err)))
def get_full_configuration(self): try: (rc, result) = request(self.url + self.base_path, **self.creds) return result except Exception as err: self._logger.exception("Failed to retrieve the LDAP configuration.") self.module.fail_json(msg="Failed to retrieve LDAP configuration! Array Id [%s]. Error [%s]." % (self.ssid, to_native(err)))
def update_configuration(self, update=None, body=None, attempt_recovery=True): """Update audit-log configuration.""" if update is None or body is None: update, body = self.build_configuration() if update and not self.check_mode: try: if self.proxy_used: rc, result = request(self.url + "storage-systems/audit-log/config", timeout=300, data=json.dumps(body), method='POST', headers=self.HEADERS, ignore_errors=True, **self.creds) else: rc, result = request( self.url + "storage-systems/%s/audit-log/config" % self.ssid, timeout=300, data=json.dumps(body), method='POST', headers=self.HEADERS, ignore_errors=True, **self.creds) if rc == 422: if self.force and attempt_recovery: self.delete_log_messages() update = self.update_configuration(update, body, False) else: self.module.fail_json( msg= "Failed to update audit-log configuration! Array Id [%s]. Error [%s]." % (self.ssid, to_native(rc, result))) except Exception as error: self.module.fail_json( msg= "Failed to update audit-log configuration! Array Id [%s]. Error [%s]." % (self.ssid, to_native(error))) return update
def update_configuration(self): config = self.get_configuration() update = False body = dict() if self.asup: body = dict(asupEnabled=True) if not config['asupEnabled']: update = True if (config['onDemandEnabled'] and config['remoteDiagsEnabled']) != self.active: update = True body.update( dict(onDemandEnabled=self.active, remoteDiagsEnabled=self.active)) self.days.sort() config['schedule']['daysOfWeek'].sort() body['schedule'] = dict(daysOfWeek=self.days, dailyMinTime=self.start, dailyMaxTime=self.end, weeklyMinTime=self.start, weeklyMaxTime=self.end) if self.days != config['schedule']['daysOfWeek']: update = True if self.start != config['schedule'][ 'dailyMinTime'] or self.start != config['schedule'][ 'weeklyMinTime']: update = True elif self.end != config['schedule'][ 'dailyMaxTime'] or self.end != config['schedule'][ 'weeklyMaxTime']: update = True elif config['asupEnabled']: body = dict(asupEnabled=False) update = True self._logger.info(pformat(body)) if update and not self.check_mode: try: (rc, result) = request(self.url + 'device-asup', method='POST', data=json.dumps(body), headers=HEADERS, **self.creds) # This is going to catch cases like a connection failure except Exception as err: self.module.fail_json( msg= "We failed to set the storage-system name! Array Id [%s]. Error [%s]." % (self.ssid, to_native(err))) return update
def update_configuration(self): config = self.get_configuration() update = False body = dict() if self.alerts: body = dict(alertingEnabled=True) if not config['alertingEnabled']: update = True body.update(emailServerAddress=self.server) if config['emailServerAddress'] != self.server: update = True body.update(additionalContactInformation=self.contact, sendAdditionalContactInformation=True) if self.contact and ( self.contact != config['additionalContactInformation'] or not config['sendAdditionalContactInformation']): update = True body.update(emailSenderAddress=self.sender) if config['emailSenderAddress'] != self.sender: update = True self.recipients.sort() if config['recipientEmailAddresses']: config['recipientEmailAddresses'].sort() body.update(recipientEmailAddresses=self.recipients) if config['recipientEmailAddresses'] != self.recipients: update = True elif config['alertingEnabled']: body = dict(alertingEnabled=False) update = True self._logger.debug(pformat(body)) if update and not self.check_mode: try: (rc, result) = request( self.url + 'storage-systems/%s/device-alerts' % self.ssid, method='POST', data=json.dumps(body), headers=HEADERS, **self.creds) # This is going to catch cases like a connection failure except Exception as err: self.module.fail_json( msg= "We failed to set the storage-system name! Array Id [%s]. Error [%s]." % (self.ssid, to_native(err))) return update
def make_configuration_request(self, body): # make http request(s) if not self.check_mode: try: if self.syslog: if "id" in body: (rc, result) = request( self.url + "storage-systems/{0}/syslog/{1}".format( self.ssid, body["id"]), method='POST', data=json.dumps(body), headers=HEADERS, **self.creds) else: (rc, result) = request( self.url + "storage-systems/{0}/syslog".format(self.ssid), method='POST', data=json.dumps(body), headers=HEADERS, **self.creds) body.update(result) # send syslog test message if self.test: self.test_configuration(body) elif "id" in body: (rc, result) = request( self.url + "storage-systems/{0}/syslog/{1}".format( self.ssid, body["id"]), method='DELETE', headers=HEADERS, **self.creds) # This is going to catch cases like a connection failure except Exception as err: self.module.fail_json( msg= "We failed to modify syslog configuration! Array Id [%s]. Error [%s]." % (self.ssid, to_native(err)))
def remove_host(self): try: (rc, resp) = request(self.url + "storage-systems/%s/hosts/%s" % (self.ssid, self.host_obj['id']), method='DELETE', url_username=self.user, url_password=self.pwd, validate_certs=self.certs) except Exception as err: self.module.fail_json( msg= "Failed to remove host. Host[%s]. Array Id [%s]. Error [%s]." % (self.host_obj['id'], self.ssid, to_native(err)))
def get_configuration(self): """Retrieve existing syslog configuration.""" try: (rc, result) = request( self.url + "storage-systems/{0}/syslog".format(self.ssid), headers=HEADERS, **self.creds) return result except Exception as err: self.module.fail_json( msg= "Failed to retrieve syslog configuration! Array Id [%s]. Error [%s]." % (self.ssid, to_native(err)))
def update_api_address_interface_match(self, body): """Change network interface address which matches the api_address""" try: try: (rc, data) = request( self.url + 'storage-systems/%s/configuration/ethernet-interfaces' % self.ssid, use_proxy=False, force=True, ignore_errors=True, method='POST', data=json.dumps(body), headers=HEADERS, timeout=10, **self.creds) except Exception: url_parts = list(urlparse.urlparse(self.url)) domain = url_parts[1].split(":") domain[0] = self.address url_parts[1] = ":".join(domain) expected_url = urlparse.urlunparse(url_parts) self._logger.info(pformat(expected_url)) (rc, data) = request( expected_url + 'storage-systems/%s/configuration/ethernet-interfaces' % self.ssid, headers=HEADERS, timeout=300, **self.creds) return except Exception as err: self._logger.info(type(err)) self.module.fail_json( msg= "Connection failure: we failed to modify the network settings! Array Id [%s]. Error [%s]." % (self.ssid, to_native(err)))
def get_configuration(self, identifier): try: (rc, result) = request(self.url + self.base_path + '%s' % (identifier), ignore_errors=True, **self.creds) if rc == 200: return result elif rc == 404: return None else: self.module.fail_json(msg="Failed to retrieve LDAP configuration! Array Id [%s]. Error [%s]." % (self.ssid, result)) except Exception as err: self._logger.exception("Failed to retrieve the LDAP configuration.") self.module.fail_json(msg="Failed to retrieve LDAP configuration! Array Id [%s]. Error [%s]." % (self.ssid, to_native(err)))
def update(self): self.controllers = self.get_controllers() if self.controller not in self.controllers: self.module.fail_json( msg= "The provided controller name is invalid. Valid controllers: %s." % ", ".join(self.controllers.keys())) iface_before = self.fetch_target_interface() update_required, body = self.make_update_body(iface_before) if update_required and not self.check_mode: try: url = ( self.url + 'storage-systems/%s/symbol/setIscsiInterfaceProperties' % self.ssid) (rc, result) = request(url, method='POST', data=json.dumps(body), headers=HEADERS, timeout=300, ignore_errors=True, **self.creds) # We could potentially retry this a few times, but it's probably a rare enough case (unless a playbook # is cancelled mid-flight), that it isn't worth the complexity. if rc == 422 and result['retcode'] in ['busy', '3']: self.module.fail_json( msg= "The interface is currently busy (probably processing a previously requested modification" " request). This operation cannot currently be completed. Array Id [%s]. Error [%s]." % (self.ssid, result)) # Handle authentication issues, etc. elif rc != 200: self.module.fail_json( msg= "Failed to modify the interface! Array Id [%s]. Error [%s]." % (self.ssid, to_native(result))) self._logger.debug("Update request completed successfully.") # This is going to catch cases like a connection failure except Exception as err: self.module.fail_json( msg= "Connection failure: we failed to modify the interface! Array Id [%s]. Error [%s]." % (self.ssid, to_native(err))) iface_after = self.fetch_target_interface() self.module.exit_json(msg="The interface settings have been updated.", changed=update_required, enabled=iface_after['ipv4Enabled'])
def get_configuration(self): try: (rc, result) = request( self.url + 'storage-systems/%s/device-alerts' % self.ssid, headers=HEADERS, **self.creds) self._logger.info("Current config: %s", pformat(result)) return result except Exception as err: self.module.fail_json( msg= "Failed to retrieve the alerts configuration! Array Id [%s]. Error [%s]." % (self.ssid, to_native(err)))
def get_name(self): try: (rc, result) = request(self.url + 'storage-systems/%s' % self.ssid, headers=HEADERS, **self.creds) if result['status'] in ['offline', 'neverContacted']: self.module.fail_json( msg="This storage-system is offline! Array Id [%s]." % (self.ssid)) return result['name'] except Exception as err: self.module.fail_json( msg="Connection failure! Array Id [%s]. Error [%s]." % (self.ssid, to_native(err)))
def delete_log_messages(self): """Delete all audit-log messages.""" self._logger.info("Deleting audit-log messages...") try: if self.proxy_used: rc, result = request(self.url + "audit-log?clearAll=True", timeout=300, method="DELETE", headers=self.HEADERS, **self.creds) else: rc, result = request( self.url + "storage-systems/%s/audit-log?clearAll=True" % self.ssid, timeout=300, method="DELETE", headers=self.HEADERS, **self.creds) except Exception as err: self.module.fail_json( msg= "Failed to delete audit-log messages! Array Id [%s]. Error [%s]." % (self.ssid, to_native(err)))
def get_configuration(self): """Retrieve the existing audit-log configurations. :returns: dictionary containing current audit-log configuration """ try: if self.proxy_used: rc, data = request(self.url + "audit-log/config", timeout=300, headers=self.HEADERS, **self.creds) else: rc, data = request( self.url + "storage-systems/%s/audit-log/config" % self.ssid, timeout=300, headers=self.HEADERS, **self.creds) return data except Exception as err: self.module.fail_json( msg="Failed to retrieve the audit-log configuration! " "Array Id [%s]. Error [%s]." % (self.ssid, to_native(err)))
def remove_amg(module, ssid, api_url, pwd, user, async_id): endpoint = 'storage-systems/%s/async-mirrors/%s' % (ssid, async_id) url = api_url + endpoint try: rc, data = request(url, method='DELETE', url_username=user, url_password=pwd, headers=HEADERS) except Exception as e: module.exit_json( msg="Exception while removing async mirror group. Message: %s" % to_native(e), exception=traceback.format_exc()) return
def find_volume_copy_pair_id_by_volume_copy_pair_id(params): get_status = 'storage-systems/%s/volume-copy-jobs/%s?retainRepositories=false' % ( params['ssid'], params['volume_copy_pair_id']) url = params['api_url'] + get_status (rc, resp) = request(url, ignore_errors=True, method='DELETE', url_username=params['api_username'], url_password=params['api_password'], headers=HEADERS, validate_certs=params['validate_certs']) if rc != 200: return False, (rc, resp) else: return True, (rc, resp)
def clear_single_configuration(self, identifier=None): if identifier is None: identifier = self.identifier configuration = self.get_configuration(identifier) updated = False msg = self.NO_CHANGE_MSG if configuration: updated = True msg = "The LDAP domain configuration for [%s] was cleared." % identifier if not self.check_mode: try: (rc, result) = request(self.url + self.base_path + '%s' % identifier, method='DELETE', **self.creds) except Exception as err: self.module.fail_json(msg="Failed to remove LDAP configuration! Array Id [%s]. Error [%s]." % (self.ssid, to_native(err))) return msg, updated
def create_async(module, ssid, api_url, api_pwd, api_usr, body): endpoint = 'storage-systems/%s/async-mirrors' % ssid url = api_url + endpoint post_data = json.dumps(body) try: rc, data = request(url, data=post_data, method='POST', url_username=api_usr, url_password=api_pwd, headers=HEADERS) except Exception as e: module.exit_json( msg="Exception while creating aysnc mirror group. Message: %s" % to_native(e), exception=traceback.format_exc()) return data
def get_configuration(self): try: (rc, result) = request(self.url + 'device-asup', headers=HEADERS, **self.creds) if not (result['asupCapable'] and result['onDemandCapable']): self.module.fail_json( msg="ASUP is not supported on this device. Array Id [%s]." % (self.ssid)) return result except Exception as err: self.module.fail_json( msg= "Failed to retrieve ASUP configuration! Array Id [%s]. Error [%s]." % (self.ssid, to_native(err)))
def is_embedded(self): """Determine whether or not we're using the embedded or proxy implementation of Web Services""" if self.embedded is None: url = self.url try: parts = urlparse.urlparse(url) parts = parts._replace(path='/devmgr/utils/') url = urlparse.urlunparse(parts) (rc, result) = request(url + 'about', **self.creds) self.embedded = not result['runningAsProxy'] except Exception as err: self._logger.exception("Failed to retrieve the About information.") self.module.fail_json(msg="Failed to determine the Web Services implementation type!" " Array Id [%s]. Error [%s]." % (self.ssid, to_native(err))) return self.embedded