def setUp(self): old_list = [{ 'key': 1, 'value': 'foo1', 'int_value': 101 }, { 'key': 2, 'value': 'foo2', 'int_value': 102 }, { 'key': 3, 'value': 'foo3', 'int_value': 103 }] new_list = [{ 'key': 1, 'value': 'foo1', 'int_value': 101 }, { 'key': 2, 'value': 'foo2', 'int_value': 112 }, { 'key': 5, 'value': 'foo5', 'int_value': 105 }] self.list_diff = list_diff(old_list, new_list, key='key')
def setUp(self): old_list = [ {"key": 1, "value": "foo1", "int_value": 101}, {"key": 2, "value": "foo2", "int_value": 102}, {"key": 3, "value": "foo3", "int_value": 103}, ] new_list = [ {"key": 1, "value": "foo1", "int_value": 101}, {"key": 2, "value": "foo2", "int_value": 112}, {"key": 5, "value": "foo5", "int_value": 105}, ] self.list_diff = list_diff(old_list, new_list, key="key")
def cluster_configured(name, cluster_config): """ Configures a cluster. Creates a new cluster, if it doesn't exist on the vCenter or reconfigures it if configured differently Supported proxies: esxdatacenter, esxcluster name Name of the state. If the state is run in by an ``esxdatacenter`` proxy, it will be the name of the cluster. cluster_config Configuration applied to the cluster. Complex datastructure following the ESXClusterConfigSchema. Valid example is: .. code-block::yaml drs: default_vm_behavior: fullyAutomated enabled: true vmotion_rate: 3 ha: admission_control _enabled: false default_vm_settings: isolation_response: powerOff restart_priority: medium enabled: true hb_ds_candidate_policy: userSelectedDs host_monitoring: enabled options: - key: das.ignoreinsufficienthbdatastore value: 'true' vm_monitoring: vmMonitoringDisabled vm_swap_placement: vmDirectory vsan: auto_claim_storage: false compression_enabled: true dedup_enabled: true enabled: true """ proxy_type = __salt__["vsphere.get_proxy_type"]() if proxy_type == "esxdatacenter": cluster_name, datacenter_name = ( name, __salt__["esxdatacenter.get_details"]()["datacenter"], ) elif proxy_type == "esxcluster": cluster_name, datacenter_name = ( __salt__["esxcluster.get_details"]()["cluster"], __salt__["esxcluster.get_details"]()["datacenter"], ) else: raise salt.exceptions.CommandExecutionError('Unsupported proxy {0}' ''.format(proxy_type)) log.info('Running %s for cluster \'%s\' in datacenter \'%s\'', name, cluster_name, datacenter_name) cluster_dict = cluster_config log.trace('cluster_dict = %s', cluster_dict) changes_required = False ret = {"name": name, "changes": {}, "result": None, "comment": "Default"} comments = [] changes = {} changes_required = False try: log.trace("Validating cluster_configured state input") schema = ESXClusterConfigSchema.serialize() log.trace('schema = %s', schema) try: jsonschema.validate(cluster_dict, schema) except jsonschema.exceptions.ValidationError as exc: raise salt.exceptions.InvalidESXClusterPayloadError(exc) current = None si = __salt__["vsphere.get_service_instance_via_proxy"]() try: current = __salt__["vsphere.list_cluster"]( datacenter_name, cluster_name, service_instance=si ) except salt.exceptions.VMwareObjectRetrievalError: changes_required = True if __opts__["test"]: comments.append( "State {0} will create cluster " "'{1}' in datacenter '{2}'." "".format(name, cluster_name, datacenter_name) ) log.info(comments[-1]) __salt__["vsphere.disconnect"](si) ret.update({"result": None, "comment": "\n".join(comments)}) return ret log.trace('Creating cluster \'%s\' in datacenter \'%s\'. ', cluster_name, datacenter_name) __salt__['vsphere.create_cluster'](cluster_dict, datacenter_name, cluster_name, service_instance=si) comments.append('Created cluster \'{0}\' in datacenter \'{1}\'' ''.format(cluster_name, datacenter_name)) log.info(comments[-1]) changes.update({"new": cluster_dict}) if current: # Cluster already exists # We need to handle lists sepparately ldiff = None if 'ha' in cluster_dict and 'options' in cluster_dict['ha']: ldiff = list_diff(current.get('ha', {}).get('options', []), cluster_dict.get('ha', {}).get('options', []), 'key') log.trace('options diffs = %s', ldiff.diffs) # Remove options if exist del cluster_dict["ha"]["options"] if "ha" in current and "options" in current["ha"]: del current["ha"]["options"] diff = recursive_diff(current, cluster_dict) log.trace('diffs = %s', diff.diffs) if not (diff.diffs or (ldiff and ldiff.diffs)): # No differences comments.append( "Cluster '{0}' in datacenter '{1}' is up " "to date. Nothing to be done." "".format(cluster_name, datacenter_name) ) log.info(comments[-1]) else: changes_required = True changes_str = "" if diff.diffs: changes_str = "{0}{1}".format(changes_str, diff.changes_str) if ldiff and ldiff.diffs: changes_str = "{0}\nha:\n options:\n{1}".format( changes_str, "\n".join( [" {0}".format(l) for l in ldiff.changes_str2.split("\n")] ), ) # Apply the changes if __opts__["test"]: comments.append( "State {0} will update cluster '{1}' " "in datacenter '{2}':\n{3}" "".format(name, cluster_name, datacenter_name, changes_str) ) else: new_values = diff.new_values old_values = diff.old_values if ldiff and ldiff.new_values: dictupdate.update( new_values, {"ha": {"options": ldiff.new_values}} ) if ldiff and ldiff.old_values: dictupdate.update( old_values, {'ha': {'options': ldiff.old_values}}) log.trace('new_values = %s', new_values) __salt__['vsphere.update_cluster'](new_values, datacenter_name, cluster_name, service_instance=si) comments.append('Updated cluster \'{0}\' in datacenter ' '\'{1}\''.format(cluster_name, datacenter_name)) log.info(comments[-1]) changes.update({"new": new_values, "old": old_values}) __salt__["vsphere.disconnect"](si) ret_status = True if __opts__["test"] and changes_required: ret_status = None ret.update( {"result": ret_status, "comment": "\n".join(comments), "changes": changes} ) return ret except salt.exceptions.CommandExecutionError as exc: log.exception('Encountered error') if si: __salt__["vsphere.disconnect"](si) ret.update({"result": False, "comment": six.text_type(exc)}) return ret
def storage_policies_configured(name, policies): """ Configures storage policies on a vCenter. policies List of dict representation of the required storage policies """ comments = [] changes = [] changes_required = False ret = {"name": name, "changes": {}, "result": None, "comment": None} log.trace("policies = {0}".format(policies)) si = None try: proxy_type = __salt__["vsphere.get_proxy_type"]() log.trace("proxy_type = {0}".format(proxy_type)) # All allowed proxies have a shim execution module with the same # name which implementes a get_details function # All allowed proxies have a vcenter detail vcenter = __salt__["{0}.get_details".format(proxy_type)]()["vcenter"] log.info("Running state '{0}' on vCenter " "'{1}'".format(name, vcenter)) si = __salt__["vsphere.get_service_instance_via_proxy"]() current_policies = __salt__["vsphere.list_storage_policies"]( policy_names=[policy["name"] for policy in policies], service_instance=si) log.trace("current_policies = {0}".format(current_policies)) # TODO Refactor when recurse_differ supports list_differ # It's going to make the whole thing much easier for policy in policies: policy_copy = copy.deepcopy(policy) filtered_policies = [ p for p in current_policies if p["name"] == policy["name"] ] current_policy = filtered_policies[0] if filtered_policies else None if not current_policy: changes_required = True if __opts__["test"]: comments.append("State {0} will create the storage policy " "'{1}' on vCenter '{2}'" "".format(name, policy["name"], vcenter)) else: __salt__["vsphere.create_storage_policy"]( policy["name"], policy, service_instance=si) comments.append("Created storage policy '{0}' on " "vCenter '{1}'".format( policy["name"], vcenter)) changes.append({"new": policy, "old": None}) log.trace(comments[-1]) # Continue with next continue # Building all diffs between the current and expected policy # XXX We simplify the comparison by assuming we have at most 1 # sub_profile if policy.get("subprofiles"): if len(policy["subprofiles"]) > 1: raise ArgumentValueError( "Multiple sub_profiles ({0}) are not " "supported in the input policy") subprofile = policy["subprofiles"][0] current_subprofile = current_policy["subprofiles"][0] capabilities_differ = list_diff( current_subprofile["capabilities"], subprofile.get("capabilities", []), key="id", ) del policy["subprofiles"] if subprofile.get("capabilities"): del subprofile["capabilities"] del current_subprofile["capabilities"] # Get the subprofile diffs without the capability keys subprofile_differ = recursive_diff(current_subprofile, dict(subprofile)) del current_policy["subprofiles"] policy_differ = recursive_diff(current_policy, policy) if (policy_differ.diffs or capabilities_differ.diffs or subprofile_differ.diffs): changes_required = True if __opts__["test"]: str_changes = [] if policy_differ.diffs: str_changes.extend([ change for change in policy_differ.changes_str.split("\n") ]) if subprofile_differ.diffs or capabilities_differ.diffs: str_changes.append("subprofiles:") if subprofile_differ.diffs: str_changes.extend([ " {0}".format(change) for change in subprofile_differ.changes_str.split("\n") ]) if capabilities_differ.diffs: str_changes.append(" capabilities:") str_changes.extend([ " {0}".format(change) for change in capabilities_differ.changes_str2.split("\n") ]) comments.append( "State {0} will update the storage policy '{1}'" " on vCenter '{2}':\n{3}" "".format(name, policy["name"], vcenter, "\n".join(str_changes))) else: __salt__["vsphere.update_storage_policy"]( policy=current_policy["name"], policy_dict=policy_copy, service_instance=si, ) comments.append("Updated the storage policy '{0}'" "in vCenter '{1}'" "".format(policy["name"], vcenter)) log.info(comments[-1]) # Build new/old values to report what was changed new_values = policy_differ.new_values new_values["subprofiles"] = [subprofile_differ.new_values] new_values["subprofiles"][0][ "capabilities"] = capabilities_differ.new_values if not new_values["subprofiles"][0]["capabilities"]: del new_values["subprofiles"][0]["capabilities"] if not new_values["subprofiles"][0]: del new_values["subprofiles"] old_values = policy_differ.old_values old_values["subprofiles"] = [subprofile_differ.old_values] old_values["subprofiles"][0][ "capabilities"] = capabilities_differ.old_values if not old_values["subprofiles"][0]["capabilities"]: del old_values["subprofiles"][0]["capabilities"] if not old_values["subprofiles"][0]: del old_values["subprofiles"] changes.append({"new": new_values, "old": old_values}) else: # No diffs found - no updates required comments.append("Storage policy '{0}' is up to date. " "Nothing to be done.".format(policy["name"])) __salt__["vsphere.disconnect"](si) except CommandExecutionError as exc: log.error("Error: {0}".format(exc)) if si: __salt__["vsphere.disconnect"](si) if not __opts__["test"]: ret["result"] = False ret.update({ "comment": exc.strerror, "result": False if not __opts__["test"] else None }) return ret if not changes_required: # We have no changes ret.update({ "comment": ("All storage policy in vCenter " "'{0}' is correctly configured. " "Nothing to be done.".format(vcenter)), "result": True, }) else: ret.update({ "comment": "\n".join(comments), "changes": { "storage_policies": changes }, "result": None if __opts__["test"] else True, }) return ret
def default_vsan_policy_configured(name, policy): """ Configures the default VSAN policy on a vCenter. The state assumes there is only one default VSAN policy on a vCenter. policy Dict representation of a policy """ # TODO Refactor when recurse_differ supports list_differ # It's going to make the whole thing much easier policy_copy = copy.deepcopy(policy) proxy_type = __salt__["vsphere.get_proxy_type"]() log.trace("proxy_type = {0}".format(proxy_type)) # All allowed proxies have a shim execution module with the same # name which implementes a get_details function # All allowed proxies have a vcenter detail vcenter = __salt__["{0}.get_details".format(proxy_type)]()["vcenter"] log.info("Running {0} on vCenter " "'{1}'".format(name, vcenter)) log.trace("policy = {0}".format(policy)) changes_required = False ret = {"name": name, "changes": {}, "result": None, "comment": None} comments = [] changes = {} changes_required = False si = None try: # TODO policy schema validation si = __salt__["vsphere.get_service_instance_via_proxy"]() current_policy = __salt__["vsphere.list_default_vsan_policy"](si) log.trace("current_policy = {0}".format(current_policy)) # Building all diffs between the current and expected policy # XXX We simplify the comparison by assuming we have at most 1 # sub_profile if policy.get("subprofiles"): if len(policy["subprofiles"]) > 1: raise ArgumentValueError("Multiple sub_profiles ({0}) are not " "supported in the input policy") subprofile = policy["subprofiles"][0] current_subprofile = current_policy["subprofiles"][0] capabilities_differ = list_diff( current_subprofile["capabilities"], subprofile.get("capabilities", []), key="id", ) del policy["subprofiles"] if subprofile.get("capabilities"): del subprofile["capabilities"] del current_subprofile["capabilities"] # Get the subprofile diffs without the capability keys subprofile_differ = recursive_diff(current_subprofile, dict(subprofile)) del current_policy["subprofiles"] policy_differ = recursive_diff(current_policy, policy) if policy_differ.diffs or capabilities_differ.diffs or subprofile_differ.diffs: if ("name" in policy_differ.new_values or "description" in policy_differ.new_values): raise ArgumentValueError( "'name' and 'description' of the default VSAN policy " "cannot be updated") changes_required = True if __opts__["test"]: str_changes = [] if policy_differ.diffs: str_changes.extend([ change for change in policy_differ.changes_str.split("\n") ]) if subprofile_differ.diffs or capabilities_differ.diffs: str_changes.append("subprofiles:") if subprofile_differ.diffs: str_changes.extend([ " {0}".format(change) for change in subprofile_differ.changes_str.split("\n") ]) if capabilities_differ.diffs: str_changes.append(" capabilities:") str_changes.extend([ " {0}".format(change) for change in capabilities_differ.changes_str2.split("\n") ]) comments.append( "State {0} will update the default VSAN policy on " "vCenter '{1}':\n{2}" "".format(name, vcenter, "\n".join(str_changes))) else: __salt__["vsphere.update_storage_policy"]( policy=current_policy["name"], policy_dict=policy_copy, service_instance=si, ) comments.append("Updated the default VSAN policy in vCenter " "'{0}'".format(vcenter)) log.info(comments[-1]) new_values = policy_differ.new_values new_values["subprofiles"] = [subprofile_differ.new_values] new_values["subprofiles"][0][ "capabilities"] = capabilities_differ.new_values if not new_values["subprofiles"][0]["capabilities"]: del new_values["subprofiles"][0]["capabilities"] if not new_values["subprofiles"][0]: del new_values["subprofiles"] old_values = policy_differ.old_values old_values["subprofiles"] = [subprofile_differ.old_values] old_values["subprofiles"][0][ "capabilities"] = capabilities_differ.old_values if not old_values["subprofiles"][0]["capabilities"]: del old_values["subprofiles"][0]["capabilities"] if not old_values["subprofiles"][0]: del old_values["subprofiles"] changes.update({ "default_vsan_policy": { "new": new_values, "old": old_values } }) log.trace(changes) __salt__["vsphere.disconnect"](si) except CommandExecutionError as exc: log.error("Error: {}".format(exc)) if si: __salt__["vsphere.disconnect"](si) if not __opts__["test"]: ret["result"] = False ret.update({ "comment": exc.strerror, "result": False if not __opts__["test"] else None }) return ret if not changes_required: # We have no changes ret.update({ "comment": ("Default VSAN policy in vCenter " "'{0}' is correctly configured. " "Nothing to be done.".format(vcenter)), "result": True, }) else: ret.update({ "comment": "\n".join(comments), "changes": changes, "result": None if __opts__["test"] else True, }) return ret
def storage_policies_configured(name, policies): ''' Configures storage policies on a vCenter. policies List of dict representation of the required storage policies ''' comments = [] changes = [] changes_required = False ret = {'name': name, 'changes': {}, 'result': None, 'comment': None} log.trace('policies = %s', policies) si = None try: proxy_type = __salt__['vsphere.get_proxy_type']() log.trace('proxy_type = %s', proxy_type) # All allowed proxies have a shim execution module with the same # name which implementes a get_details function # All allowed proxies have a vcenter detail vcenter = __salt__['{0}.get_details'.format(proxy_type)]()['vcenter'] log.info('Running state \'%s\' on vCenter \'%s\'', name, vcenter) si = __salt__['vsphere.get_service_instance_via_proxy']() current_policies = __salt__['vsphere.list_storage_policies']( policy_names=[policy['name'] for policy in policies], service_instance=si) log.trace('current_policies = %s', current_policies) # TODO Refactor when recurse_differ supports list_differ # It's going to make the whole thing much easier for policy in policies: policy_copy = copy.deepcopy(policy) filtered_policies = [ p for p in current_policies if p['name'] == policy['name'] ] current_policy = filtered_policies[0] \ if filtered_policies else None if not current_policy: changes_required = True if __opts__['test']: comments.append('State {0} will create the storage policy ' '\'{1}\' on vCenter \'{2}\'' ''.format(name, policy['name'], vcenter)) else: __salt__['vsphere.create_storage_policy']( policy['name'], policy, service_instance=si) comments.append('Created storage policy \'{0}\' on ' 'vCenter \'{1}\''.format( policy['name'], vcenter)) changes.append({'new': policy, 'old': None}) log.trace(comments[-1]) # Continue with next continue # Building all diffs between the current and expected policy # XXX We simplify the comparison by assuming we have at most 1 # sub_profile if policy.get('subprofiles'): if len(policy['subprofiles']) > 1: raise ArgumentValueError( 'Multiple sub_profiles ({0}) are not ' 'supported in the input policy') subprofile = policy['subprofiles'][0] current_subprofile = current_policy['subprofiles'][0] capabilities_differ = list_diff( current_subprofile['capabilities'], subprofile.get('capabilities', []), key='id') del policy['subprofiles'] if subprofile.get('capabilities'): del subprofile['capabilities'] del current_subprofile['capabilities'] # Get the subprofile diffs without the capability keys subprofile_differ = recursive_diff(current_subprofile, dict(subprofile)) del current_policy['subprofiles'] policy_differ = recursive_diff(current_policy, policy) if policy_differ.diffs or capabilities_differ.diffs or \ subprofile_differ.diffs: changes_required = True if __opts__['test']: str_changes = [] if policy_differ.diffs: str_changes.extend([ change for change in policy_differ.changes_str.split('\n') ]) if subprofile_differ.diffs or \ capabilities_differ.diffs: str_changes.append('subprofiles:') if subprofile_differ.diffs: str_changes.extend([ ' {0}'.format(change) for change in subprofile_differ.changes_str.split('\n') ]) if capabilities_differ.diffs: str_changes.append(' capabilities:') str_changes.extend([ ' {0}'.format(change) for change in capabilities_differ.changes_str2.split('\n') ]) comments.append( 'State {0} will update the storage policy \'{1}\'' ' on vCenter \'{2}\':\n{3}' ''.format(name, policy['name'], vcenter, '\n'.join(str_changes))) else: __salt__['vsphere.update_storage_policy']( policy=current_policy['name'], policy_dict=policy_copy, service_instance=si) comments.append('Updated the storage policy \'{0}\'' 'in vCenter \'{1}\'' ''.format(policy['name'], vcenter)) log.info(comments[-1]) # Build new/old values to report what was changed new_values = policy_differ.new_values new_values['subprofiles'] = [subprofile_differ.new_values] new_values['subprofiles'][0]['capabilities'] = \ capabilities_differ.new_values if not new_values['subprofiles'][0]['capabilities']: del new_values['subprofiles'][0]['capabilities'] if not new_values['subprofiles'][0]: del new_values['subprofiles'] old_values = policy_differ.old_values old_values['subprofiles'] = [subprofile_differ.old_values] old_values['subprofiles'][0]['capabilities'] = \ capabilities_differ.old_values if not old_values['subprofiles'][0]['capabilities']: del old_values['subprofiles'][0]['capabilities'] if not old_values['subprofiles'][0]: del old_values['subprofiles'] changes.append({'new': new_values, 'old': old_values}) else: # No diffs found - no updates required comments.append('Storage policy \'{0}\' is up to date. ' 'Nothing to be done.'.format(policy['name'])) __salt__['vsphere.disconnect'](si) except CommandExecutionError as exc: log.error('Error: %s', exc) if si: __salt__['vsphere.disconnect'](si) if not __opts__['test']: ret['result'] = False ret.update({ 'comment': exc.strerror, 'result': False if not __opts__['test'] else None }) return ret if not changes_required: # We have no changes ret.update({ 'comment': ('All storage policy in vCenter ' '\'{0}\' is correctly configured. ' 'Nothing to be done.'.format(vcenter)), 'result': True }) else: ret.update({ 'comment': '\n'.join(comments), 'changes': { 'storage_policies': changes }, 'result': None if __opts__['test'] else True, }) return ret
def default_vsan_policy_configured(name, policy): ''' Configures the default VSAN policy on a vCenter. The state assumes there is only one default VSAN policy on a vCenter. policy Dict representation of a policy ''' # TODO Refactor when recurse_differ supports list_differ # It's going to make the whole thing much easier policy_copy = copy.deepcopy(policy) proxy_type = __salt__['vsphere.get_proxy_type']() log.trace('proxy_type = %s', proxy_type) # All allowed proxies have a shim execution module with the same # name which implementes a get_details function # All allowed proxies have a vcenter detail vcenter = __salt__['{0}.get_details'.format(proxy_type)]()['vcenter'] log.info('Running %s on vCenter \'%s\'', name, vcenter) log.trace('policy = %s', policy) changes_required = False ret = {'name': name, 'changes': {}, 'result': None, 'comment': None} comments = [] changes = {} changes_required = False si = None try: #TODO policy schema validation si = __salt__['vsphere.get_service_instance_via_proxy']() current_policy = __salt__['vsphere.list_default_vsan_policy'](si) log.trace('current_policy = %s', current_policy) # Building all diffs between the current and expected policy # XXX We simplify the comparison by assuming we have at most 1 # sub_profile if policy.get('subprofiles'): if len(policy['subprofiles']) > 1: raise ArgumentValueError('Multiple sub_profiles ({0}) are not ' 'supported in the input policy') subprofile = policy['subprofiles'][0] current_subprofile = current_policy['subprofiles'][0] capabilities_differ = list_diff(current_subprofile['capabilities'], subprofile.get('capabilities', []), key='id') del policy['subprofiles'] if subprofile.get('capabilities'): del subprofile['capabilities'] del current_subprofile['capabilities'] # Get the subprofile diffs without the capability keys subprofile_differ = recursive_diff(current_subprofile, dict(subprofile)) del current_policy['subprofiles'] policy_differ = recursive_diff(current_policy, policy) if policy_differ.diffs or capabilities_differ.diffs or \ subprofile_differ.diffs: if 'name' in policy_differ.new_values or \ 'description' in policy_differ.new_values: raise ArgumentValueError( '\'name\' and \'description\' of the default VSAN policy ' 'cannot be updated') changes_required = True if __opts__['test']: str_changes = [] if policy_differ.diffs: str_changes.extend([ change for change in policy_differ.changes_str.split('\n') ]) if subprofile_differ.diffs or capabilities_differ.diffs: str_changes.append('subprofiles:') if subprofile_differ.diffs: str_changes.extend([ ' {0}'.format(change) for change in subprofile_differ.changes_str.split('\n') ]) if capabilities_differ.diffs: str_changes.append(' capabilities:') str_changes.extend([ ' {0}'.format(change) for change in capabilities_differ.changes_str2.split('\n') ]) comments.append( 'State {0} will update the default VSAN policy on ' 'vCenter \'{1}\':\n{2}' ''.format(name, vcenter, '\n'.join(str_changes))) else: __salt__['vsphere.update_storage_policy']( policy=current_policy['name'], policy_dict=policy_copy, service_instance=si) comments.append('Updated the default VSAN policy in vCenter ' '\'{0}\''.format(vcenter)) log.info(comments[-1]) new_values = policy_differ.new_values new_values['subprofiles'] = [subprofile_differ.new_values] new_values['subprofiles'][0]['capabilities'] = \ capabilities_differ.new_values if not new_values['subprofiles'][0]['capabilities']: del new_values['subprofiles'][0]['capabilities'] if not new_values['subprofiles'][0]: del new_values['subprofiles'] old_values = policy_differ.old_values old_values['subprofiles'] = [subprofile_differ.old_values] old_values['subprofiles'][0]['capabilities'] = \ capabilities_differ.old_values if not old_values['subprofiles'][0]['capabilities']: del old_values['subprofiles'][0]['capabilities'] if not old_values['subprofiles'][0]: del old_values['subprofiles'] changes.update({ 'default_vsan_policy': { 'new': new_values, 'old': old_values } }) log.trace(changes) __salt__['vsphere.disconnect'](si) except CommandExecutionError as exc: log.error('Error: %s', exc) if si: __salt__['vsphere.disconnect'](si) if not __opts__['test']: ret['result'] = False ret.update({ 'comment': exc.strerror, 'result': False if not __opts__['test'] else None }) return ret if not changes_required: # We have no changes ret.update({ 'comment': ('Default VSAN policy in vCenter ' '\'{0}\' is correctly configured. ' 'Nothing to be done.'.format(vcenter)), 'result': True }) else: ret.update({ 'comment': '\n'.join(comments), 'changes': changes, 'result': None if __opts__['test'] else True, }) return ret
def cluster_configured(name, cluster_config): ''' Configures a cluster. Creates a new cluster, if it doesn't exist on the vCenter or reconfigures it if configured differently Supported proxies: esxdatacenter, esxcluster name Name of the state. If the state is run in by an ``esxdatacenter`` proxy, it will be the name of the cluster. cluster_config Configuration applied to the cluster. Complex datastructure following the ESXClusterConfigSchema. Valid example is: .. code-block::yaml drs: default_vm_behavior: fullyAutomated enabled: true vmotion_rate: 3 ha: admission_control _enabled: false default_vm_settings: isolation_response: powerOff restart_priority: medium enabled: true hb_ds_candidate_policy: userSelectedDs host_monitoring: enabled options: - key: das.ignoreinsufficienthbdatastore value: 'true' vm_monitoring: vmMonitoringDisabled vm_swap_placement: vmDirectory vsan: auto_claim_storage: false compression_enabled: true dedup_enabled: true enabled: true ''' proxy_type = __salt__['vsphere.get_proxy_type']() if proxy_type == 'esxdatacenter': cluster_name, datacenter_name = \ name, __salt__['esxdatacenter.get_details']()['datacenter'] elif proxy_type == 'esxcluster': cluster_name, datacenter_name = \ __salt__['esxcluster.get_details']()['cluster'], \ __salt__['esxcluster.get_details']()['datacenter'] else: raise salt.exceptions.CommandExecutionError('Unsupported proxy {0}' ''.format(proxy_type)) log.info('Running {0} for cluster \'{1}\' in datacenter ' '\'{2}\''.format(name, cluster_name, datacenter_name)) cluster_dict = cluster_config log.trace('cluster_dict = {0}'.format(cluster_dict)) changes_required = False ret = {'name': name, 'changes': {}, 'result': None, 'comment': 'Default'} comments = [] changes = {} changes_required = False try: log.trace('Validating cluster_configured state input') schema = ESXClusterConfigSchema.serialize() log.trace('schema = {0}'.format(schema)) try: jsonschema.validate(cluster_dict, schema) except jsonschema.exceptions.ValidationError as exc: raise salt.exceptions.InvalidESXClusterPayloadError(exc) current = None si = __salt__['vsphere.get_service_instance_via_proxy']() try: current = __salt__['vsphere.list_cluster'](datacenter_name, cluster_name, service_instance=si) except salt.exceptions.VMwareObjectRetrievalError: changes_required = True if __opts__['test']: comments.append('State {0} will create cluster ' '\'{1}\' in datacenter \'{2}\'.' ''.format(name, cluster_name, datacenter_name)) log.info(comments[-1]) __salt__['vsphere.disconnect'](si) ret.update({'result': None, 'comment': '\n'.join(comments)}) return ret log.trace('Creating cluster \'{0}\' in datacenter \'{1}\'. ' ''.format(cluster_name, datacenter_name)) __salt__['vsphere.create_cluster'](cluster_dict, datacenter_name, cluster_name, service_instance=si) comments.append('Created cluster \'{0}\' in datacenter \'{1}\'' ''.format(cluster_name, datacenter_name)) log.info(comments[-1]) changes.update({'new': cluster_dict}) if current: # Cluster already exists # We need to handle lists sepparately ldiff = None if 'ha' in cluster_dict and 'options' in cluster_dict['ha']: ldiff = list_diff(current.get('ha', {}).get('options', []), cluster_dict.get('ha', {}).get('options', []), 'key') log.trace('options diffs = {0}'.format(ldiff.diffs)) # Remove options if exist del cluster_dict['ha']['options'] if 'ha' in current and 'options' in current['ha']: del current['ha']['options'] diff = recursive_diff(current, cluster_dict) log.trace('diffs = {0}'.format(diff.diffs)) if not (diff.diffs or (ldiff and ldiff.diffs)): # No differences comments.append('Cluster \'{0}\' in datacenter \'{1}\' is up ' 'to date. Nothing to be done.' ''.format(cluster_name, datacenter_name)) log.info(comments[-1]) else: changes_required = True changes_str = '' if diff.diffs: changes_str = '{0}{1}'.format(changes_str, diff.changes_str) if ldiff and ldiff.diffs: changes_str = '{0}\nha:\n options:\n{1}'.format( changes_str, '\n'.join([' {0}'.format(l) for l in ldiff.changes_str2.split('\n')])) # Apply the changes if __opts__['test']: comments.append( 'State {0} will update cluster \'{1}\' ' 'in datacenter \'{2}\':\n{3}' ''.format(name, cluster_name, datacenter_name, changes_str)) else: new_values = diff.new_values old_values = diff.old_values if ldiff and ldiff.new_values: dictupdate.update( new_values, {'ha': {'options': ldiff.new_values}}) if ldiff and ldiff.old_values: dictupdate.update( old_values, {'ha': {'options': ldiff.old_values}}) log.trace('new_values = {0}'.format(new_values)) __salt__['vsphere.update_cluster'](new_values, datacenter_name, cluster_name, service_instance=si) comments.append('Updated cluster \'{0}\' in datacenter ' '\'{1}\''.format(cluster_name, datacenter_name)) log.info(comments[-1]) changes.update({'new': new_values, 'old': old_values}) __salt__['vsphere.disconnect'](si) ret_status = True if __opts__['test'] and changes_required: ret_status = None ret.update({'result': ret_status, 'comment': '\n'.join(comments), 'changes': changes}) return ret except salt.exceptions.CommandExecutionError as exc: log.error('Error: {0}\n{1}'.format(exc, traceback.format_exc())) if si: __salt__['vsphere.disconnect'](si) ret.update({ 'result': False, 'comment': six.text_type(exc)}) return ret