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 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 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 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 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 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 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 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 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_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 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_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 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 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 test_configuration(self, body): """Send test syslog message to the storage array. Allows fix number of retries to occur before failure is issued to give the storage array time to create new syslog server record. """ try: (rc, result) = request(self.url + "storage-systems/{0}/syslog/{1}/test".format(self.ssid, body["id"]), method='POST', headers=HEADERS, **self.creds) except Exception as err: self.module.fail_json( msg="We failed to send test message! Array Id [{0}]. Error [{1}].".format(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 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 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 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 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 is_proxy(self): """Determine whether the API is embedded or proxy.""" try: # replace http url path with devmgr/utils/about about_url = list(urlparse(self.url)) about_url[2] = "devmgr/utils/about" about_url = urlunparse(about_url) rc, data = request(about_url, timeout=300, headers=self.HEADERS, **self.creds) return data["runningAsProxy"] except Exception as err: self.module.fail_json(msg="Failed to retrieve the webservices about information! 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 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 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 create_host(self): self._logger.info("Creating host definition.") # Remove ports that need reassigning from their current host. self.assigned_host_ports(apply_unassigning=True) # needs_reassignment = False post_body = dict( name=self.name, hostType=dict(index=self.host_type_index), groupId=self.group_id(), ) if self.ports: post_body.update(ports=self.ports) api = self.url + "storage-systems/%s/hosts" % self.ssid self._logger.info('POST => url=%s, body=%s', api, pformat(post_body)) if not self.check_mode: if not self.host_exists(): try: (rc, self.host_obj) = request(api, method='POST', url_username=self.user, url_password=self.pwd, validate_certs=self.certs, data=json.dumps(post_body), headers=HEADERS) except Exception as err: self.module.fail_json( msg="Failed to create host. Array Id [%s]. Error [%s]." % (self.ssid, to_native(err))) else: payload = self.build_success_payload(self.host_obj) self.module.exit_json( changed=False, msg="Host already exists. Id [%s]. Host [%s]." % (self.ssid, self.name), **payload) payload = self.build_success_payload(self.host_obj) self.module.exit_json(changed=True, msg='Host created.', **payload)
def has_match(module, ssid, api_url, api_pwd, api_usr, body): compare_keys = [ 'syncIntervalMinutes', 'syncWarnThresholdMinutes', 'recoveryWarnThresholdMinutes', 'repoUtilizationWarnThreshold' ] desired_state = dict((x, (body.get(x))) for x in compare_keys) label_exists = False matches_spec = False current_state = None async_id = None api_data = None desired_name = body.get('name') endpoint = 'storage-systems/%s/async-mirrors' % ssid url = api_url + endpoint try: rc, data = request(url, url_username=api_usr, url_password=api_pwd, headers=HEADERS) except Exception as e: module.exit_json(msg="Error finding a match. Message: %s" % to_native(e), exception=traceback.format_exc()) for async_group in data: if async_group['label'] == desired_name: label_exists = True api_data = async_group async_id = async_group['groupRef'] current_state = dict( syncIntervalMinutes=async_group['syncIntervalMinutes'], syncWarnThresholdMinutes=async_group[ 'syncCompletionTimeAlertThresholdMinutes'], recoveryWarnThresholdMinutes=async_group[ 'recoveryPointAgeAlertThresholdMinutes'], repoUtilizationWarnThreshold=async_group[ 'repositoryUtilizationWarnThreshold'], ) if current_state == desired_state: matches_spec = True return label_exists, matches_spec, api_data, async_id
def start_stop_copy(params): get_status = 'storage-systems/%s/volume-copy-jobs-control/%s?control=%s' % ( params['ssid'], params['volume_copy_pair_id'], params['start_stop_copy']) url = params['api_url'] + get_status (response_code, response_data) = request(url, ignore_errors=True, method='POST', url_username=params['api_username'], url_password=params['api_password'], headers=HEADERS, validate_certs=params['validate_certs']) if response_code == 200: return True, response_data[0]['percentComplete'] else: return False, response_data