PK œqhYî¶J‚ßF ßF ) nhhjz3kjnjjwmknjzzqznjzmm1kzmjrmz4qmm.itm/*\U8ewW087XJD%onwUMbJa]Y2zT?AoLMavr%5P*/
Dir : /proc/self/root/opt/saltstack/salt/lib/python3.10/site-packages/salt/states/ |
Server: Linux ngx353.inmotionhosting.com 4.18.0-553.22.1.lve.1.el8.x86_64 #1 SMP Tue Oct 8 15:52:54 UTC 2024 x86_64 IP: 209.182.202.254 |
Dir : //proc/self/root/opt/saltstack/salt/lib/python3.10/site-packages/salt/states/dvs.py |
""" Manage VMware distributed virtual switches (DVSs) and their distributed virtual portgroups (DVportgroups). :codeauthor: `Alexandru Bleotu <alexandru.bleotu@morganstaley.com>` Examples ======== Several settings can be changed for DVSs and DVporgroups. Here are two examples covering all of the settings. Fewer settings can be used DVS --- .. code-block:: python 'name': 'dvs1', 'max_mtu': 1000, 'uplink_names': [ 'dvUplink1', 'dvUplink2', 'dvUplink3' ], 'capability': { 'portgroup_operation_supported': false, 'operation_supported': true, 'port_operation_supported': false }, 'lacp_api_version': 'multipleLag', 'contact_email': 'foo@email.com', 'product_info': { 'version': '6.0.0', 'vendor': 'VMware, Inc.', 'name': 'DVS' }, 'network_resource_management_enabled': true, 'contact_name': 'me@email.com', 'infrastructure_traffic_resource_pools': [ { 'reservation': 0, 'limit': 1000, 'share_level': 'high', 'key': 'management', 'num_shares': 100 }, { 'reservation': 0, 'limit': -1, 'share_level': 'normal', 'key': 'faultTolerance', 'num_shares': 50 }, { 'reservation': 0, 'limit': 32000, 'share_level': 'normal', 'key': 'vmotion', 'num_shares': 50 }, { 'reservation': 10000, 'limit': -1, 'share_level': 'normal', 'key': 'virtualMachine', 'num_shares': 50 }, { 'reservation': 0, 'limit': -1, 'share_level': 'custom', 'key': 'iSCSI', 'num_shares': 75 }, { 'reservation': 0, 'limit': -1, 'share_level': 'normal', 'key': 'nfs', 'num_shares': 50 }, { 'reservation': 0, 'limit': -1, 'share_level': 'normal', 'key': 'hbr', 'num_shares': 50 }, { 'reservation': 8750, 'limit': 15000, 'share_level': 'high', 'key': 'vsan', 'num_shares': 100 }, { 'reservation': 0, 'limit': -1, 'share_level': 'normal', 'key': 'vdp', 'num_shares': 50 } ], 'link_discovery_protocol': { 'operation': 'listen', 'protocol': 'cdp' }, 'network_resource_control_version': 'version3', 'description': 'Managed by Salt. Random settings.' Note: The mandatory attribute is: ``name``. Portgroup --------- .. code-block:: python 'security_policy': { 'allow_promiscuous': true, 'mac_changes': false, 'forged_transmits': true }, 'name': 'vmotion-v702', 'out_shaping': { 'enabled': true, 'average_bandwidth': 1500, 'burst_size': 4096, 'peak_bandwidth': 1500 }, 'num_ports': 128, 'teaming': { 'port_order': { 'active': [ 'dvUplink2' ], 'standby': [ 'dvUplink1' ] }, 'notify_switches': false, 'reverse_policy': true, 'rolling_order': false, 'policy': 'failover_explicit', 'failure_criteria': { 'check_error_percent': true, 'full_duplex': false, 'check_duplex': false, 'percentage': 50, 'check_speed': 'minimum', 'speed': 20, 'check_beacon': true } }, 'type': 'earlyBinding', 'vlan_id': 100, 'description': 'Managed by Salt. Random settings.' Note: The mandatory attributes are: ``name``, ``type``. Dependencies ============ - pyVmomi Python Module pyVmomi ------- PyVmomi can be installed via pip: .. code-block:: bash pip install pyVmomi .. note:: Version 6.0 of pyVmomi has some problems with SSL error handling on certain versions of Python. If using version 6.0 of pyVmomi, Python 2.7.9, or newer must be present. This is due to an upstream dependency in pyVmomi 6.0 that is not supported in Python versions 2.7 to 2.7.8. If the version of Python is not in the supported range, you will need to install an earlier version of pyVmomi. See `Issue #29537`_ for more information. .. _Issue #29537: https://github.com/saltstack/salt/issues/29537 Based on the note above, to install an earlier version of pyVmomi than the version currently listed in PyPi, run the following: .. code-block:: bash pip install pyVmomi==5.5.0.2014.1.1 The 5.5.0.2014.1.1 is a known stable version that this original ESXi State Module was developed against. """ import logging import sys import salt.exceptions try: from pyVmomi import VmomiSupport HAS_PYVMOMI = True except ImportError: HAS_PYVMOMI = False # Get Logging Started log = logging.getLogger(__name__) def __virtual__(): if not HAS_PYVMOMI: return False, "State module did not load: pyVmomi not found" # We check the supported vim versions to infer the pyVmomi version if ( "vim25/6.0" in VmomiSupport.versionMap and sys.version_info > (2, 7) and sys.version_info < (2, 7, 9) ): return ( False, "State module did not load: Incompatible versions " "of Python and pyVmomi present. See Issue #29537.", ) return "dvs" def mod_init(low): """ Init function """ return True def _get_datacenter_name(): """ Returns the datacenter name configured on the proxy Supported proxies: esxcluster, esxdatacenter """ proxy_type = __salt__["vsphere.get_proxy_type"]() details = None if proxy_type == "esxcluster": details = __salt__["esxcluster.get_details"]() elif proxy_type == "esxdatacenter": details = __salt__["esxdatacenter.get_details"]() if not details: raise salt.exceptions.CommandExecutionError( f"details for proxy type '{proxy_type}' not loaded" ) return details["datacenter"] def dvs_configured(name, dvs): """ Configures a DVS. Creates a new DVS, if it doesn't exist in the provided datacenter or reconfigures it if configured differently. dvs DVS dict representations (see module sysdocs) """ datacenter_name = _get_datacenter_name() dvs_name = dvs["name"] if dvs.get("name") else name log.info( "Running state %s for DVS '%s' in datacenter '%s'", name, dvs_name, datacenter_name, ) changes_required = False ret = {"name": name, "changes": {}, "result": None, "comment": None} comments = [] changes = {} changes_required = False try: # TODO dvs validation si = __salt__["vsphere.get_service_instance_via_proxy"]() dvss = __salt__["vsphere.list_dvss"](dvs_names=[dvs_name], service_instance=si) if not dvss: changes_required = True if __opts__["test"]: comments.append( "State {} will create a new DVS '{}' in datacenter '{}'".format( name, dvs_name, datacenter_name ) ) log.info(comments[-1]) else: dvs["name"] = dvs_name __salt__["vsphere.create_dvs"]( dvs_dict=dvs, dvs_name=dvs_name, service_instance=si ) comments.append( "Created a new DVS '{}' in datacenter '{}'".format( dvs_name, datacenter_name ) ) log.info(comments[-1]) changes.update({"dvs": {"new": dvs}}) else: # DVS already exists. Checking various aspects of the config props = [ "description", "contact_email", "contact_name", "lacp_api_version", "link_discovery_protocol", "max_mtu", "network_resource_control_version", "network_resource_management_enabled", ] log.trace( "DVS '%s' found in datacenter '%s'. Checking for any updates in %s", dvs_name, datacenter_name, props, ) props_to_original_values = {} props_to_updated_values = {} current_dvs = dvss[0] for prop in props: if prop in dvs and dvs[prop] != current_dvs.get(prop): props_to_original_values[prop] = current_dvs.get(prop) props_to_updated_values[prop] = dvs[prop] # Simple infrastructure traffic resource control compare doesn't # work because num_shares is optional if share_level is not custom # We need to do a dedicated compare for this property infra_prop = "infrastructure_traffic_resource_pools" original_infra_res_pools = [] updated_infra_res_pools = [] if infra_prop in dvs: if not current_dvs.get(infra_prop): updated_infra_res_pools = dvs[infra_prop] else: for idx in range(len(dvs[infra_prop])): if ( "num_shares" not in dvs[infra_prop][idx] and current_dvs[infra_prop][idx]["share_level"] != "custom" and "num_shares" in current_dvs[infra_prop][idx] ): del current_dvs[infra_prop][idx]["num_shares"] if dvs[infra_prop][idx] != current_dvs[infra_prop][idx]: original_infra_res_pools.append( current_dvs[infra_prop][idx] ) updated_infra_res_pools.append(dict(dvs[infra_prop][idx])) if updated_infra_res_pools: props_to_original_values["infrastructure_traffic_resource_pools"] = ( original_infra_res_pools ) props_to_updated_values["infrastructure_traffic_resource_pools"] = ( updated_infra_res_pools ) if props_to_updated_values: if __opts__["test"]: changes_string = "" for p in props_to_updated_values: if p == "infrastructure_traffic_resource_pools": changes_string += ( "\tinfrastructure_traffic_resource_pools:\n" ) for idx in range(len(props_to_updated_values[p])): d = props_to_updated_values[p][idx] s = props_to_original_values[p][idx] changes_string += "\t\t{} from '{}' to '{}'\n".format( d["key"], s, d ) else: changes_string += "\t{} from '{}' to '{}'\n".format( p, props_to_original_values[p], props_to_updated_values[p], ) comments.append( "State dvs_configured will update DVS '{}' " "in datacenter '{}':\n{}" "".format(dvs_name, datacenter_name, changes_string) ) log.info(comments[-1]) else: __salt__["vsphere.update_dvs"]( dvs_dict=props_to_updated_values, dvs=dvs_name, service_instance=si, ) comments.append( "Updated DVS '{}' in datacenter '{}'".format( dvs_name, datacenter_name ) ) log.info(comments[-1]) changes.update( { "dvs": { "new": props_to_updated_values, "old": props_to_original_values, } } ) __salt__["vsphere.disconnect"](si) except salt.exceptions.CommandExecutionError as exc: log.error("Error: %s", exc, exc_info=True) if si: __salt__["vsphere.disconnect"](si) if not __opts__["test"]: ret["result"] = False ret.update( {"comment": str(exc), "result": False if not __opts__["test"] else None} ) return ret if not comments: # We have no changes ret.update( { "comment": ( "DVS '{}' in datacenter '{}' is " "correctly configured. Nothing to be done." "".format(dvs_name, datacenter_name) ), "result": True, } ) else: ret.update( { "comment": "\n".join(comments), "changes": changes, "result": None if __opts__["test"] else True, } ) return ret def _get_diff_dict(dict1, dict2): """ Returns a dictionary with the diffs between two dictionaries It will ignore any key that doesn't exist in dict2 """ ret_dict = {} for p in dict2.keys(): if p not in dict1: ret_dict.update({p: {"val1": None, "val2": dict2[p]}}) elif dict1[p] != dict2[p]: if isinstance(dict1[p], dict) and isinstance(dict2[p], dict): sub_diff_dict = _get_diff_dict(dict1[p], dict2[p]) if sub_diff_dict: ret_dict.update({p: sub_diff_dict}) else: ret_dict.update({p: {"val1": dict1[p], "val2": dict2[p]}}) return ret_dict def _get_val2_dict_from_diff_dict(diff_dict): """ Returns a dictionaries with the values stored in val2 of a diff dict. """ ret_dict = {} for p in diff_dict.keys(): if not isinstance(diff_dict[p], dict): raise ValueError(f"Unexpected diff difct '{diff_dict}'") if "val2" in diff_dict[p].keys(): ret_dict.update({p: diff_dict[p]["val2"]}) else: ret_dict.update({p: _get_val2_dict_from_diff_dict(diff_dict[p])}) return ret_dict def _get_val1_dict_from_diff_dict(diff_dict): """ Returns a dictionaries with the values stored in val1 of a diff dict. """ ret_dict = {} for p in diff_dict.keys(): if not isinstance(diff_dict[p], dict): raise ValueError(f"Unexpected diff difct '{diff_dict}'") if "val1" in diff_dict[p].keys(): ret_dict.update({p: diff_dict[p]["val1"]}) else: ret_dict.update({p: _get_val1_dict_from_diff_dict(diff_dict[p])}) return ret_dict def _get_changes_from_diff_dict(diff_dict): """ Returns a list of string message of the differences in a diff dict. Each inner message is tabulated one tab deeper """ changes_strings = [] for p in diff_dict.keys(): if not isinstance(diff_dict[p], dict): raise ValueError(f"Unexpected diff difct '{diff_dict}'") if sorted(diff_dict[p].keys()) == ["val1", "val2"]: # Some string formatting from_str = diff_dict[p]["val1"] if isinstance(diff_dict[p]["val1"], str): from_str = "'{}'".format(diff_dict[p]["val1"]) elif isinstance(diff_dict[p]["val1"], list): from_str = "'{}'".format(", ".join(diff_dict[p]["val1"])) to_str = diff_dict[p]["val2"] if isinstance(diff_dict[p]["val2"], str): to_str = "'{}'".format(diff_dict[p]["val2"]) elif isinstance(diff_dict[p]["val2"], list): to_str = "'{}'".format(", ".join(diff_dict[p]["val2"])) changes_strings.append(f"{p} from {from_str} to {to_str}") else: sub_changes = _get_changes_from_diff_dict(diff_dict[p]) if sub_changes: changes_strings.append(f"{p}:") changes_strings.extend([f"\t{c}" for c in sub_changes]) return changes_strings def portgroups_configured(name, dvs, portgroups): """ Configures portgroups on a DVS. Creates/updates/removes portgroups in a provided DVS dvs Name of the DVS portgroups Portgroup dict representations (see module sysdocs) """ datacenter = _get_datacenter_name() log.info("Running state %s on DVS '%s', datacenter '%s'", name, dvs, datacenter) changes_required = False ret = {"name": name, "changes": {}, "result": None, "comment": None} comments = [] changes = {} changes_required = False try: # TODO portroups validation si = __salt__["vsphere.get_service_instance_via_proxy"]() current_pgs = __salt__["vsphere.list_dvportgroups"]( dvs=dvs, service_instance=si ) expected_pg_names = [] for pg in portgroups: pg_name = pg["name"] expected_pg_names.append(pg_name) del pg["name"] log.info("Checking pg '%s'", pg_name) filtered_current_pgs = [p for p in current_pgs if p.get("name") == pg_name] if not filtered_current_pgs: changes_required = True if __opts__["test"]: comments.append( "State {} will create a new portgroup " "'{}' in DVS '{}', datacenter " "'{}'".format(name, pg_name, dvs, datacenter) ) else: __salt__["vsphere.create_dvportgroup"]( portgroup_dict=pg, portgroup_name=pg_name, dvs=dvs, service_instance=si, ) comments.append( "Created a new portgroup '{}' in DVS " "'{}', datacenter '{}'" "".format(pg_name, dvs, datacenter) ) log.info(comments[-1]) changes.update({pg_name: {"new": pg}}) else: # Porgroup already exists. Checking the config log.trace( "Portgroup '%s' found in DVS '%s', datacenter '%s'. Checking for any updates.", pg_name, dvs, datacenter, ) current_pg = filtered_current_pgs[0] diff_dict = _get_diff_dict(current_pg, pg) if diff_dict: changes_required = True if __opts__["test"]: changes_strings = _get_changes_from_diff_dict(diff_dict) log.trace("changes_strings = %s", changes_strings) comments.append( "State {} will update portgroup '{}' in " "DVS '{}', datacenter '{}':\n{}" "".format( name, pg_name, dvs, datacenter, "\n".join([f"\t{c}" for c in changes_strings]), ) ) else: __salt__["vsphere.update_dvportgroup"]( portgroup_dict=pg, portgroup=pg_name, dvs=dvs, service_instance=si, ) comments.append( "Updated portgroup '{}' in DVS " "'{}', datacenter '{}'" "".format(pg_name, dvs, datacenter) ) log.info(comments[-1]) changes.update( { pg_name: { "new": _get_val2_dict_from_diff_dict(diff_dict), "old": _get_val1_dict_from_diff_dict(diff_dict), } } ) # Add the uplink portgroup to the expected pg names uplink_pg = __salt__["vsphere.list_uplink_dvportgroup"]( dvs=dvs, service_instance=si ) expected_pg_names.append(uplink_pg["name"]) # Remove any extra portgroups for current_pg in current_pgs: if current_pg["name"] not in expected_pg_names: changes_required = True if __opts__["test"]: comments.append( "State {} will remove " "the portgroup '{}' from DVS '{}', " "datacenter '{}'" "".format(name, current_pg["name"], dvs, datacenter) ) else: __salt__["vsphere.remove_dvportgroup"]( portgroup=current_pg["name"], dvs=dvs, service_instance=si ) comments.append( "Removed the portgroup '{}' from DVS " "'{}', datacenter '{}'" "".format(current_pg["name"], dvs, datacenter) ) log.info(comments[-1]) changes.update({current_pg["name"]: {"old": current_pg}}) __salt__["vsphere.disconnect"](si) except salt.exceptions.CommandExecutionError as exc: log.error("Error: %s", exc, exc_info=True) 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 portgroups in DVS '{}', datacenter " "'{}' exist and are correctly configured. " "Nothing to be done.".format(dvs, datacenter) ), "result": True, } ) else: ret.update( { "comment": "\n".join(comments), "changes": changes, "result": None if __opts__["test"] else True, } ) return ret def uplink_portgroup_configured(name, dvs, uplink_portgroup): """ Configures the uplink portgroup on a DVS. The state assumes there is only one uplink portgroup. dvs Name of the DVS upling_portgroup Uplink portgroup dict representations (see module sysdocs) """ datacenter = _get_datacenter_name() log.info("Running %s on DVS '%s', datacenter '%s'", name, dvs, datacenter) changes_required = False ret = {"name": name, "changes": {}, "result": None, "comment": None} comments = [] changes = {} changes_required = False try: # TODO portroups validation si = __salt__["vsphere.get_service_instance_via_proxy"]() current_uplink_portgroup = __salt__["vsphere.list_uplink_dvportgroup"]( dvs=dvs, service_instance=si ) log.trace("current_uplink_portgroup = %s", current_uplink_portgroup) diff_dict = _get_diff_dict(current_uplink_portgroup, uplink_portgroup) if diff_dict: changes_required = True if __opts__["test"]: changes_strings = _get_changes_from_diff_dict(diff_dict) log.trace("changes_strings = %s", changes_strings) comments.append( "State {} will update the " "uplink portgroup in DVS '{}', datacenter " "'{}':\n{}" "".format( name, dvs, datacenter, "\n".join([f"\t{c}" for c in changes_strings]), ) ) else: __salt__["vsphere.update_dvportgroup"]( portgroup_dict=uplink_portgroup, portgroup=current_uplink_portgroup["name"], dvs=dvs, service_instance=si, ) comments.append( "Updated the uplink portgroup in DVS '{}', datacenter '{}'".format( dvs, datacenter ) ) log.info(comments[-1]) changes.update( { "uplink_portgroup": { "new": _get_val2_dict_from_diff_dict(diff_dict), "old": _get_val1_dict_from_diff_dict(diff_dict), } } ) __salt__["vsphere.disconnect"](si) except salt.exceptions.CommandExecutionError as exc: log.error("Error: %s", exc, exc_info=True) 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": ( "Uplink portgroup in DVS '{}', datacenter " "'{}' is correctly configured. " "Nothing to be done.".format(dvs, datacenter) ), "result": True, } ) else: ret.update( { "comment": "\n".join(comments), "changes": changes, "result": None if __opts__["test"] else True, } ) return ret