From 7a32bd8080e5a5cffd6a32d72a1de007803335a6 Mon Sep 17 00:00:00 2001 From: Abhijeet Kasurde Date: Tue, 14 May 2019 14:34:43 +0530 Subject: [PATCH] VMware: Improve vmware_portgroup (#56382) * Rewrite vmware_portgroup module * support check mode * VLAN ID isn't required anymore * VLAN ID 0 (no tagging) is the default * Options match values in vSphere Client and vmware_vswitch module * Policy override is configured properly * VMware: vmware_portgroup updates --- .../modules/cloud/vmware/vmware_portgroup.py | 1111 ++++++++++++----- .../targets/vmware_portgroup/aliases | 3 + .../targets/vmware_portgroup/tasks/main.yml | 68 + 3 files changed, 900 insertions(+), 282 deletions(-) create mode 100644 test/integration/targets/vmware_portgroup/aliases create mode 100644 test/integration/targets/vmware_portgroup/tasks/main.yml diff --git a/lib/ansible/modules/cloud/vmware/vmware_portgroup.py b/lib/ansible/modules/cloud/vmware/vmware_portgroup.py index ff9afd5ec0..f34a249cb9 100644 --- a/lib/ansible/modules/cloud/vmware/vmware_portgroup.py +++ b/lib/ansible/modules/cloud/vmware/vmware_portgroup.py @@ -2,8 +2,9 @@ # -*- coding: utf-8 -*- # Copyright: (c) 2015, Joseph Callen -# Copyright: (c) 2017-18, Ansible Project -# Copyright: (c) 2017-18, Abhijeet Kasurde +# Copyright: (c) 2017, Ansible Project +# Copyright: (c) 2017, Abhijeet Kasurde +# Copyright: (c) 2018, Christian Kotte # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function @@ -20,70 +21,87 @@ DOCUMENTATION = ''' module: vmware_portgroup short_description: Create a VMware portgroup description: - - Create a VMware portgroup on given host/s or hosts of given cluster + - Create a VMware Port Group on a VMware Standard Switch (vSS) for given ESXi host(s) or hosts of given cluster. version_added: 2.0 author: - Joseph Callen (@jcpowermac) - Russell Teague (@mtnbikenc) -- Abhijeet Kasurde (@Akasurde) +- Abhijeet Kasurde (@Akasurde) +- Christian Kotte (@ckotte) notes: - - Tested on vSphere 5.5, 6.5 + - Tested on vSphere 5.5 and 6.5 + - Complete configuration only tested on vSphere 6.5 + - 'C(inbound_policy) and C(rolling_order) are deprecated and will be removed in 2.11' + - Those two options are only used during portgroup creation. Updating isn't supported with those options. requirements: - "python >= 2.6" - PyVmomi options: - switch_name: + switch: description: - vSwitch to modify. required: True - portgroup_name: + aliases: [ 'switch_name', 'vswitch' ] + portgroup: description: - Portgroup name to add. required: True + aliases: [ 'portgroup_name' ] vlan_id: description: - VLAN ID to assign to portgroup. - required: True - network_policy: + - Set to 0 (no VLAN tagging) by default. + required: False + default: 0 + aliases: [ 'vlan' ] + security: description: - Network policy specifies layer 2 security settings for a portgroup such as promiscuous mode, where guest adapter listens to all the packets, MAC address changes and forged transmits. - Dict which configures the different security values for portgroup. - 'Valid attributes are:' - - '- C(promiscuous_mode) (bool): indicates whether promiscuous mode is allowed. (default: false)' - - '- C(forged_transmits) (bool): indicates whether forged transmits are allowed. (default: false)' - - '- C(mac_changes) (bool): indicates whether mac changes are allowed. (default: false)' + - '- C(promiscuous_mode) (bool): indicates whether promiscuous mode is allowed. (default: None)' + - '- C(forged_transmits) (bool): indicates whether forged transmits are allowed. (default: None)' + - '- C(mac_changes) (bool): indicates whether mac changes are allowed. (default: None)' required: False version_added: "2.2" - default: { - mac_changes: false, - promiscuous_mode: false, - forged_transmits: false, - } - teaming_policy: + aliases: [ 'security_policy', 'network_policy' ] + teaming: description: - Dictionary which configures the different teaming values for portgroup. - 'Valid attributes are:' - - '- C(load_balance_policy) (string): Network adapter teaming policy. (default: loadbalance_srcid)' + - '- C(load_balancing) (string): Network adapter teaming policy. C(load_balance_policy) is also alias to this option. (default: loadbalance_srcid)' - ' - choices: [ loadbalance_ip, loadbalance_srcmac, loadbalance_srcid, failover_explicit ]' - - '- C(inbound_policy) (bool): Indicate whether or not the teaming policy is applied to inbound frames as well. (default: False)' - - '- C(notify_switches) (bool): Indicate whether or not to notify the physical switch if a link fails. (default: True)' - - '- C(rolling_order) (bool): Indicate whether or not to use a rolling policy when restoring links. (default: False)' + - '- C(network_failure_detection) (string): Network failure detection. (default: link_status_only)' + - ' - choices: [ link_status_only, beacon_probing ]' + - '- C(notify_switches) (bool): Indicate whether or not to notify the physical switch if a link fails. (default: None)' + - '- C(failback) (bool): Indicate whether or not to use a failback when restoring links. (default: None)' + - '- C(active_adapters) (list): List of active adapters used for load balancing.' + - '- C(standby_adapters) (list): List of standby adapters used for failover.' + - '- All vmnics are used as active adapters if C(active_adapters) and C(standby_adapters) are not defined.' + - '- C(inbound_policy) (bool): Indicate whether or not the teaming policy is applied to inbound frames as well. Deprecated. (default: False)' + - '- C(rolling_order) (bool): Indicate whether or not to use a rolling policy when restoring links. Deprecated. (default: False)' required: False version_added: '2.6' - default: { - 'notify_switches': True, - 'load_balance_policy': 'loadbalance_srcid', - 'inbound_policy': False, - 'rolling_order': False - } + aliases: [ 'teaming_policy' ] + traffic_shaping: + description: + - Dictionary which configures traffic shaping for the switch. + - 'Valid attributes are:' + - '- C(enabled) (bool): Status of Traffic Shaping Policy. (default: None)' + - '- C(average_bandwidth) (int): Average bandwidth (kbit/s). (default: None)' + - '- C(peak_bandwidth) (int): Peak bandwidth (kbit/s). (default: None)' + - '- C(burst_size) (int): Burst size (KB). (default: None)' + required: False + version_added: '2.9' cluster_name: description: - Name of cluster name for host membership. - Portgroup will be created on all hosts of the given cluster. - This option is required if C(hosts) is not specified. version_added: "2.5" + aliases: [ 'cluster' ] hosts: description: - List of name of host or hosts on which portgroup needs to be added. @@ -107,8 +125,8 @@ EXAMPLES = r''' hostname: "{{ esxi_hostname }}" username: "{{ esxi_username }}" password: "{{ esxi_password }}" - switch_name: "{{ vswitch_name }}" - portgroup_name: "{{ portgroup_name }}" + switch: "{{ vswitch_name }}" + portgroup: "{{ portgroup_name }}" vlan_id: "{{ vlan_id }}" delegate_to: localhost @@ -117,9 +135,9 @@ EXAMPLES = r''' hostname: "{{ esxi_hostname }}" username: "{{ esxi_username }}" password: "{{ esxi_password }}" - switch_name: "{{ vswitch_name }}" - portgroup_name: "{{ portgroup_name }}" - network_policy: + switch: "{{ vswitch_name }}" + portgroup: "{{ portgroup_name }}" + security: promiscuous_mode: True delegate_to: localhost @@ -129,8 +147,8 @@ EXAMPLES = r''' username: "{{ vcenter_username }}" password: "{{ vcenter_password }}" hosts: [esxi_hostname_one] - switch_name: "{{ vswitch_name }}" - portgroup_name: "{{ portgroup_name }}" + switch: "{{ vswitch_name }}" + portgroup: "{{ portgroup_name }}" vlan_id: "{{ vlan_id }}" delegate_to: localhost @@ -140,8 +158,8 @@ EXAMPLES = r''' username: "{{ vcenter_username }}" password: "{{ vcenter_password }}" cluster_name: "{{ cluster_name }}" - switch_name: "{{ vswitch_name }}" - portgroup_name: "{{ portgroup_name }}" + switch: "{{ vswitch_name }}" + portgroup: "{{ portgroup_name }}" vlan_id: "{{ vlan_id }}" delegate_to: localhost @@ -151,22 +169,39 @@ EXAMPLES = r''' username: "{{ vcenter_username }}" password: "{{ vcenter_password }}" cluster_name: "{{ cluster_name }}" - switch_name: "{{ vswitch_name }}" - portgroup_name: "{{ portgroup_name }}" + switch: "{{ vswitch_name }}" + portgroup: "{{ portgroup_name }}" vlan_id: "{{ vlan_id }}" state: absent delegate_to: localhost -- name: Add Portgroup with teaming policy +- name: Add Portgroup with all settings defined vmware_portgroup: - hostname: "{{ esxi_hostname }}" - username: "{{ esxi_username }}" - password: "{{ esxi_password }}" - switch_name: "{{ vswitch_name }}" - portgroup_name: "{{ portgroup_name }}" - teaming_policy: - load_balance_policy: 'failover_explicit' - inbound_policy: True + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + esxi_hostname: "{{ inventory_hostname }}" + switch: "{{ vswitch_name }}" + portgroup: "{{ portgroup_name }}" + vlan_id: 10 + security: + promiscuous_mode: False + mac_changes: False + forged_transmits: False + traffic_shaping: + enabled: True + average_bandwidth: 100000 + peak_bandwidth: 100000 + burst_size: 102400 + teaming: + load_balancing: failover_explicit + network_failure_detection: link_status_only + notify_switches: true + failback: true + active_adapters: + - vmnic0 + standby_adapters: + - vmnic1 delegate_to: localhost register: teaming_result ''' @@ -178,9 +213,21 @@ result: type: dict sample: { "esxi01.example.com": { - "portgroup_name": "pg0010", - "switch_name": "vswitch_0001", - "vlan_id": 1 + "changed": true, + "failback": "No override", + "failover_active": "No override", + "failover_standby": "No override", + "failure_detection": "No override", + "load_balancing": "No override", + "msg": "Port Group added", + "notify_switches": "No override", + "portgroup": "vMotion", + "sec_forged_transmits": false, + "sec_mac_changes": false, + "sec_promiscuous_mode": false, + "traffic_shaping": "No override", + "vlan_id": 33, + "vswitch": "vSwitch1" } } ''' @@ -196,297 +243,797 @@ from ansible.module_utils.vmware import PyVmomi, vmware_argument_spec from ansible.module_utils._text import to_native -class PyVmomiHelper(PyVmomi): - def __init__(self, module): - super(PyVmomiHelper, self).__init__(module) +class VMwareHostPortGroup(PyVmomi): + """Manage portgroup""" + def __init__(self, module): + super(VMwareHostPortGroup, self).__init__(module) + self.switch_object = None + self.portgroup_object = None hosts = self.params['hosts'] cluster = self.params['cluster_name'] - self.portgroup_name = self.params['portgroup_name'] - self.switch_name = self.params['switch_name'] + self.portgroup = self.params['portgroup'] + self.switch = self.params['switch'] self.vlan_id = self.params['vlan_id'] - self.promiscuous_mode = self.params['network_policy'].get('promiscuous_mode') - self.forged_transmits = self.params['network_policy'].get('forged_transmits') - self.mac_changes = self.params['network_policy'].get('mac_changes') - self.network_policy = self.create_network_policy() + if self.params['security']: + self.sec_promiscuous_mode = self.params['security'].get('promiscuous_mode') + self.sec_forged_transmits = self.params['security'].get('forged_transmits') + self.sec_mac_changes = self.params['security'].get('mac_changes') + else: + self.sec_promiscuous_mode = None + self.sec_forged_transmits = None + self.sec_mac_changes = None + if self.params['traffic_shaping']: + self.ts_enabled = self.params['traffic_shaping'].get('enabled') + for value in ['average_bandwidth', 'peak_bandwidth', 'burst_size']: + if not self.params['traffic_shaping'].get(value): + self.module.fail_json(msg="traffic_shaping.%s is a required parameter if traffic_shaping is enabled." % value) + self.ts_average_bandwidth = self.params['traffic_shaping'].get('average_bandwidth') + self.ts_peak_bandwidth = self.params['traffic_shaping'].get('peak_bandwidth') + self.ts_burst_size = self.params['traffic_shaping'].get('burst_size') + else: + self.ts_enabled = None + self.ts_average_bandwidth = None + self.ts_peak_bandwidth = None + self.ts_burst_size = None + if self.params['teaming']: + self.teaming_load_balancing = self.params['teaming'].get('load_balancing') + self.teaming_failure_detection = self.params['teaming'].get('network_failure_detection') + self.teaming_notify_switches = self.params['teaming'].get('notify_switches') + self.teaming_failback = self.params['teaming'].get('failback') + self.teaming_failover_order_active = self.params['teaming'].get('active_adapters') + self.teaming_failover_order_standby = self.params['teaming'].get('standby_adapters') + if self.teaming_failover_order_active is None: + self.teaming_failover_order_active = [] + if self.teaming_failover_order_standby is None: + self.teaming_failover_order_standby = [] + # NOTE: the following options are deprecated and should be removed in 2.11 + self.teaming_inbound_policy = self.module.params['teaming']['inbound_policy'] + self.teaming_rolling_order = self.module.params['teaming']['rolling_order'] + else: + self.teaming_load_balancing = None + self.teaming_failure_detection = None + self.teaming_notify_switches = None + self.teaming_failback = None + self.teaming_failover_order_active = None + self.teaming_failover_order_standby = None + # NOTE: the following options are deprecated and should be removed in 2.11 + self.teaming_inbound_policy = None + self.teaming_rolling_order = None self.state = self.params['state'] - self.host_obj_list = self.get_all_host_objs(cluster_name=cluster, esxi_host_name=hosts) + + self.hosts = self.get_all_host_objs(cluster_name=cluster, esxi_host_name=hosts) + if not self.hosts: + self.module.fail_json(msg="Failed to find host system with given configuration.") def process_state(self): - """ - Function to manage state - """ - if self.state == 'present': - self.add_hosts_port_group() - elif self.state == 'absent': - self.remove_hosts_port_group() - - # Get - def get_port_group_by_name(self, host_system, portgroup_name, vswitch_name): - """ - Function to get specific port group by given name - Args: - host_system: Name of Host System - portgroup_name: Name of Port Group - vswitch_name: Name of vSwitch - - Returns: List of port groups by given specifications - - """ - pgs_list = self.get_all_port_groups_by_host(host_system=host_system) - desired_pgs = [] - for pg in pgs_list: - if pg.spec.name == portgroup_name and pg.spec.vswitchName == vswitch_name: - desired_pgs.append(pg) - return desired_pgs - - @staticmethod - def check_network_policy_diff(current_policy, desired_policy): - """ - Function to find difference between existing network policy and user given network policy - Args: - current_policy: Current network policy - desired_policy: User defined network policy - - Returns: True if difference found, False if not. - - """ - ret = False - if (current_policy.security.allowPromiscuous != desired_policy.security.allowPromiscuous) or \ - (current_policy.security.forgedTransmits != desired_policy.security.forgedTransmits) or \ - (current_policy.security.macChanges != desired_policy.security.macChanges) or \ - (current_policy.nicTeaming.policy != desired_policy.nicTeaming.policy) or \ - (current_policy.nicTeaming.reversePolicy != desired_policy.nicTeaming.reversePolicy) or \ - (current_policy.nicTeaming.notifySwitches != desired_policy.nicTeaming.notifySwitches) or \ - (current_policy.nicTeaming.rollingOrder != desired_policy.nicTeaming.rollingOrder): - ret = True - return ret - - # Add - def add_hosts_port_group(self): - """ - Function to add port group to given hosts - """ + """Manage internal state of the portgroup""" results = dict(changed=False, result=dict()) host_change_list = [] - for host in self.host_obj_list: - change = False - results['result'][host.name] = dict(portgroup_name=self.portgroup_name, - vlan_id=self.vlan_id, - switch_name=self.switch_name) - change = self.create_host_port_group(host, self.portgroup_name, self.vlan_id, self.switch_name, self.network_policy) - host_change_list.append(change) + for host in self.hosts: + changed = False + results['result'][host.name] = dict() + switch_state = self.check_if_vswitch_exists(host_system=host) + if switch_state == 'absent': + self.module.fail_json(msg="The vSwitch '%s' doesn't exist on host '%s'" % (self.switch, host.name)) + portgroup_state = self.check_if_portgroup_exists(host_system=host) + if self.state == 'present' and portgroup_state == 'present': + changed, host_results = self.update_host_port_group( + host_system=host, + portgroup_object=self.portgroup_object + ) + elif self.state == 'present' and portgroup_state == 'absent': + changed, host_results = self.create_host_port_group(host_system=host) + elif self.state == 'absent' and portgroup_state == 'present': + changed, host_results = self.remove_host_port_group(host_system=host) + else: + host_results = dict() + host_results['changed'] = False + host_results['msg'] = "Port Group already deleted" + host_results['portgroup'] = self.portgroup + results['result'][host.name] = host_results + + host_change_list.append(changed) + if any(host_change_list): results['changed'] = True self.module.exit_json(**results) - def create_host_port_group(self, host_system, portgroup_name, vlan_id, vswitch_name, network_policy): + def check_if_portgroup_exists(self, host_system): """ - Function to create/update portgroup on given host using portgroup specifications + Check if portgroup exists + Returns: 'present' if portgroup exists or 'absent' if not + """ + self.portgroup_object = self.find_portgroup_by_name( + host_system=host_system, + portgroup_name=self.portgroup, + vswitch_name=self.switch + ) + if self.portgroup_object is None: + return 'absent' + return 'present' + + def find_portgroup_by_name(self, host_system, portgroup_name, vswitch_name): + """ + Find and return port group managed object Args: host_system: Name of Host System - portgroup_name: Name of Portgroup - vlan_id: The VLAN ID for ports using this port group. - vswitch_name: Name of vSwitch Name - network_policy: Network policy object + portgroup_name: Name of the Port Group + vswitch_name: Name of the vSwitch + + Returns: Port Group managed object if found, else None """ - desired_pgs = self.get_port_group_by_name(host_system=host_system, - portgroup_name=portgroup_name, - vswitch_name=vswitch_name) + portgroups = self.get_all_port_groups_by_host(host_system=host_system) + for portgroup in portgroups: + if portgroup.spec.name == portgroup_name and portgroup.spec.vswitchName != vswitch_name: + # portgroup names are unique; there can be only one portgroup with the same name per host + self.module.fail_json(msg="The portgroup already exists on vSwitch '%s'" % portgroup.spec.vswitchName) + if portgroup.spec.name == portgroup_name and portgroup.spec.vswitchName == vswitch_name: + return portgroup + return None - port_group = vim.host.PortGroup.Config() - port_group.spec = vim.host.PortGroup.Specification() - port_changed = False + def check_if_vswitch_exists(self, host_system): + """ + Check if vSwitch exists + Returns: 'present' if vSwitch exists or 'absent' if not + """ + self.switch_object = self.find_vswitch_by_name( + host_system=host_system, + vswitch_name=self.switch + ) + if self.switch_object is None: + return 'absent' + return 'present' - if not desired_pgs: - # Add new portgroup - port_group.spec.name = portgroup_name - port_group.spec.vlanId = vlan_id - port_group.spec.vswitchName = vswitch_name - port_group.spec.policy = network_policy + @staticmethod + def find_vswitch_by_name(host_system, vswitch_name): + """ + Find and return vSwitch managed object + Args: + host: Host system managed object + vswitch_name: Name of vSwitch to find + + Returns: vSwitch managed object if found, else None + """ + for vss in host_system.configManager.networkSystem.networkInfo.vswitch: + if vss.name == vswitch_name: + return vss + return None + + def remove_host_port_group(self, host_system): + """ + Remove a Port Group from a given host + Args: + host_system: Name of Host System + """ + host_results = dict(changed=False, msg="") + + if self.module.check_mode: + host_results['msg'] = "Port Group would be removed" + else: + try: + host_system.configManager.networkSystem.RemovePortGroup(pgName=self.portgroup) + host_results['msg'] = "Port Group removed" + except vim.fault.NotFound as not_found: + self.module.fail_json( + msg="Failed to remove Portgroup as it was not found: %s" % to_native(not_found.msg) + ) + except vim.fault.ResourceInUse as resource_in_use: + self.module.fail_json( + msg="Failed to remove Portgroup as it is in use: %s" % to_native(resource_in_use.msg) + ) + except vim.fault.HostConfigFault as host_config_fault: + self.module.fail_json( + msg="Failed to remove Portgroup due to configuration failures: %s" % to_native(host_config_fault.msg) + ) + host_results['changed'] = True + host_results['portgroup'] = self.portgroup + host_results['vswitch'] = self.switch + + return True, host_results + + def create_host_port_group(self, host_system): + """Create Port Group on a given host + Args: + host_system: Name of Host System + """ + host_results = dict(changed=False, msg="") + + if self.module.check_mode: + host_results['msg'] = "Port Group would be added" + else: + port_group = vim.host.PortGroup.Config() + port_group.spec = vim.host.PortGroup.Specification() + port_group.spec.vswitchName = self.switch + port_group.spec.name = self.portgroup + port_group.spec.vlanId = self.vlan_id + port_group.spec.policy = self.create_network_policy() try: host_system.configManager.networkSystem.AddPortGroup(portgrp=port_group.spec) - port_changed = True - except vim.fault.AlreadyExists as e: - # To match with other vmware_* modules if portgroup - # exists, we proceed, as user may want idempotency - pass + host_results['changed'] = True + host_results['msg'] = "Port Group added" + except vim.fault.AlreadyExists as already_exists: + self.module.fail_json( + msg="Failed to add Portgroup as it already exists: %s" % to_native(already_exists.msg) + ) except vim.fault.NotFound as not_found: - self.module.fail_json(msg="Failed to add Portgroup as vSwitch" - " was not found: %s" % to_native(not_found.msg)) + self.module.fail_json( + msg="Failed to add Portgroup as vSwitch was not found: %s" % to_native(not_found.msg) + ) except vim.fault.HostConfigFault as host_config_fault: - self.module.fail_json(msg="Failed to add Portgroup due to host system" - " configuration failure : %s" % to_native(host_config_fault.msg)) + self.module.fail_json( + msg="Failed to add Portgroup due to host system configuration failure : %s" % + to_native(host_config_fault.msg) + ) except vmodl.fault.InvalidArgument as invalid_argument: - self.module.fail_json(msg="Failed to add Portgroup as VLAN id was not" - " correct as per specifications: %s" % to_native(invalid_argument.msg)) - except Exception as generic_exception: - self.module.fail_json(msg="Failed to add Portgroup due to generic" - " exception : %s" % to_native(generic_exception)) + self.module.fail_json( + msg="Failed to add Portgroup as VLAN id was not correct as per specifications: %s" % + to_native(invalid_argument.msg) + ) + host_results['changed'] = True + host_results['portgroup'] = self.portgroup + host_results['vswitch'] = self.switch + host_results['vlan_id'] = self.vlan_id + if self.sec_promiscuous_mode is None: + host_results['sec_promiscuous_mode'] = "No override" else: - # Change portgroup - port_group.spec.name = portgroup_name - port_group.spec.vlanId = vlan_id - port_group.spec.policy = network_policy - port_group.spec.vswitchName = desired_pgs[0].spec.vswitchName - if self.check_network_policy_diff(desired_pgs[0].spec.policy, network_policy) or \ - desired_pgs[0].spec.vlanId != vlan_id: - port_changed = True + host_results['sec_promiscuous_mode'] = self.sec_promiscuous_mode + if self.sec_mac_changes is None: + host_results['sec_mac_changes'] = "No override" + else: + host_results['sec_mac_changes'] = self.sec_mac_changes + if self.sec_forged_transmits is None: + host_results['sec_forged_transmits'] = "No override" + else: + host_results['sec_forged_transmits'] = self.sec_forged_transmits + host_results['traffic_shaping'] = "No override" if self.ts_enabled is None else self.ts_enabled + host_results['load_balancing'] = "No override" if self.teaming_load_balancing is None \ + else self.teaming_load_balancing + host_results['notify_switches'] = "No override" if self.teaming_notify_switches is None \ + else self.teaming_notify_switches + host_results['failback'] = "No override" if self.teaming_failback is None else self.teaming_failback + host_results['failover_active'] = "No override" if self.teaming_failover_order_active is None \ + else self.teaming_failover_order_active + host_results['failover_standby'] = "No override" if self.teaming_failover_order_standby is None \ + else self.teaming_failover_order_standby + host_results['failure_detection'] = "No override" if self.teaming_failure_detection is None \ + else self.teaming_failure_detection + + return True, host_results + + def update_host_port_group(self, host_system, portgroup_object): + """Update a Port Group on a given host + Args: + host_system: Name of Host System + """ + changed = changed_security = False + changed_list = [] + host_results = dict(changed=False, msg="") + spec = portgroup_object.spec + + # Check VLAN ID + host_results['vlan_id'] = self.vlan_id + if spec.vlanId != self.vlan_id: + changed = True + changed_list.append("VLAN ID") + host_results['vlan_id_previous'] = spec.vlanId + spec.vlanId = self.vlan_id + + # Check security settings + if self.sec_promiscuous_mode is None: + host_results['sec_promiscuous_mode'] = "No override" + else: + host_results['sec_promiscuous_mode'] = self.sec_promiscuous_mode + if self.sec_mac_changes is None: + host_results['sec_mac_changes'] = "No override" + else: + host_results['sec_mac_changes'] = self.sec_mac_changes + if self.sec_forged_transmits is None: + host_results['sec_forged_transmits'] = "No override" + else: + host_results['sec_forged_transmits'] = self.sec_forged_transmits + if spec.policy.security: + promiscuous_mode_previous = spec.policy.security.allowPromiscuous + mac_changes_previous = spec.policy.security.macChanges + forged_transmits_previous = spec.policy.security.forgedTransmits + if promiscuous_mode_previous is not self.sec_promiscuous_mode: + spec.policy.security.allowPromiscuous = self.sec_promiscuous_mode + changed = changed_security = True + changed_list.append("Promiscuous mode") + if mac_changes_previous is not self.sec_mac_changes: + spec.policy.security.macChanges = self.sec_mac_changes + changed = changed_security = True + changed_list.append("MAC address changes") + if forged_transmits_previous is not self.sec_forged_transmits: + spec.policy.security.forgedTransmits = self.sec_forged_transmits + changed = changed_security = True + changed_list.append("Forged transmits") + if changed_security: + if self.sec_promiscuous_mode is None: + host_results['sec_promiscuous_mode_previous'] = "No override" + else: + host_results['sec_promiscuous_mode_previous'] = promiscuous_mode_previous + if self.sec_mac_changes is None: + host_results['sec_mac_changes_previous'] = "No override" + else: + host_results['sec_mac_changes'] = mac_changes_previous + if self.sec_forged_transmits is None: + host_results['sec_forged_transmits_previous'] = "No override" + else: + host_results['sec_forged_transmits_previous'] = forged_transmits_previous + else: + spec.policy.security = self.create_security_policy() + changed = True + changed_list.append("Security") + host_results['sec_promiscuous_mode_previous'] = "No override" + host_results['sec_mac_changes_previous'] = "No override" + host_results['sec_forged_transmits_previous'] = "No override" + + # Check traffic shaping + if self.ts_enabled is None: + host_results['traffic_shaping'] = "No override" + else: + host_results['traffic_shaping'] = self.ts_enabled + if self.ts_enabled: + ts_average_bandwidth = self.ts_average_bandwidth * 1000 + ts_peak_bandwidth = self.ts_peak_bandwidth * 1000 + ts_burst_size = self.ts_burst_size * 1024 + host_results['traffic_shaping_avg_bandw'] = ts_average_bandwidth + host_results['traffic_shaping_peak_bandw'] = ts_peak_bandwidth + host_results['traffic_shaping_burst'] = ts_burst_size + if spec.policy.shapingPolicy and spec.policy.shapingPolicy.enabled is not None: + if spec.policy.shapingPolicy.enabled: + if self.ts_enabled: + if spec.policy.shapingPolicy.averageBandwidth != ts_average_bandwidth: + changed = True + changed_list.append("Average bandwidth") + host_results['traffic_shaping_avg_bandw_previous'] = spec.policy.shapingPolicy.averageBandwidth + spec.policy.shapingPolicy.averageBandwidth = ts_average_bandwidth + if spec.policy.shapingPolicy.peakBandwidth != ts_peak_bandwidth: + changed = True + changed_list.append("Peak bandwidth") + host_results['traffic_shaping_peak_bandw_previous'] = spec.policy.shapingPolicy.peakBandwidth + spec.policy.shapingPolicy.peakBandwidth = ts_peak_bandwidth + if spec.policy.shapingPolicy.burstSize != ts_burst_size: + changed = True + changed_list.append("Burst size") + host_results['traffic_shaping_burst_previous'] = spec.policy.shapingPolicy.burstSize + spec.policy.shapingPolicy.burstSize = ts_burst_size + elif self.ts_enabled is False: + changed = True + changed_list.append("Traffic shaping") + host_results['traffic_shaping_previous'] = True + spec.policy.shapingPolicy.enabled = False + elif self.ts_enabled is None: + spec.policy.shapingPolicy = None + changed = True + changed_list.append("Traffic shaping") + host_results['traffic_shaping_previous'] = True + else: + if self.ts_enabled: + spec.policy.shapingPolicy = self.create_shaping_policy() + changed = True + changed_list.append("Traffic shaping") + host_results['traffic_shaping_previous'] = False + elif self.ts_enabled is False: + changed = True + changed_list.append("Traffic shaping") + host_results['traffic_shaping_previous'] = True + spec.policy.shapingPolicy.enabled = False + elif self.ts_enabled is None: + spec.policy.shapingPolicy = None + changed = True + changed_list.append("Traffic shaping") + host_results['traffic_shaping_previous'] = True + else: + if self.ts_enabled: + spec.policy.shapingPolicy = self.create_shaping_policy() + changed = True + changed_list.append("Traffic shaping") + host_results['traffic_shaping_previous'] = "No override" + elif self.ts_enabled is False: + changed = True + changed_list.append("Traffic shaping") + host_results['traffic_shaping_previous'] = "No override" + spec.policy.shapingPolicy.enabled = False + + # Check teaming + if spec.policy.nicTeaming: + # Check teaming policy + if self.teaming_load_balancing is None: + host_results['load_balancing'] = "No override" + else: + host_results['load_balancing'] = self.teaming_load_balancing + if spec.policy.nicTeaming.policy: + if spec.policy.nicTeaming.policy != self.teaming_load_balancing: + changed = True + changed_list.append("Load balancing") + host_results['load_balancing_previous'] = spec.policy.nicTeaming.policy + spec.policy.nicTeaming.policy = self.teaming_load_balancing + else: + if self.teaming_load_balancing: + changed = True + changed_list.append("Load balancing") + host_results['load_balancing_previous'] = "No override" + spec.policy.nicTeaming.policy = self.teaming_load_balancing + # Check teaming notify switches + if spec.policy.nicTeaming.notifySwitches is None: + host_results['notify_switches'] = "No override" + else: + host_results['notify_switches'] = self.teaming_notify_switches + if spec.policy.nicTeaming.notifySwitches is not None: + if self.teaming_notify_switches is not None: + if spec.policy.nicTeaming.notifySwitches is not self.teaming_notify_switches: + changed = True + changed_list.append("Notify switches") + host_results['notify_switches_previous'] = spec.policy.nicTeaming.notifySwitches + spec.policy.nicTeaming.notifySwitches = self.teaming_notify_switches + else: + changed = True + changed_list.append("Notify switches") + host_results['notify_switches_previous'] = spec.policy.nicTeaming.notifySwitches + spec.policy.nicTeaming.notifySwitches = None + else: + if self.teaming_notify_switches is not None: + changed = True + changed_list.append("Notify switches") + host_results['notify_switches_previous'] = "No override" + spec.policy.nicTeaming.notifySwitches = self.teaming_notify_switches + # Check failback + if spec.policy.nicTeaming.rollingOrder is None: + host_results['failback'] = "No override" + else: + host_results['failback'] = self.teaming_failback + if spec.policy.nicTeaming.rollingOrder is not None: + if self.teaming_failback is not None: + # this option is called 'failback' in the vSphere Client + # rollingOrder also uses the opposite value displayed in the client + if spec.policy.nicTeaming.rollingOrder is self.teaming_failback: + changed = True + changed_list.append("Failback") + host_results['failback_previous'] = not spec.policy.nicTeaming.rollingOrder + spec.policy.nicTeaming.rollingOrder = not self.teaming_failback + else: + changed = True + changed_list.append("Failback") + host_results['failback_previous'] = spec.policy.nicTeaming.rollingOrder + spec.policy.nicTeaming.rollingOrder = None + else: + if self.teaming_failback is not None: + changed = True + changed_list.append("Failback") + host_results['failback_previous'] = "No override" + spec.policy.nicTeaming.rollingOrder = not self.teaming_failback + # Check teaming failover order + if self.teaming_failover_order_active is None and self.teaming_failover_order_standby is None: + host_results['failover_active'] = "No override" + host_results['failover_standby'] = "No override" + else: + host_results['failover_active'] = self.teaming_failover_order_active + host_results['failover_standby'] = self.teaming_failover_order_standby + if spec.policy.nicTeaming.nicOrder: + if self.teaming_failover_order_active or self.teaming_failover_order_standby: + if spec.policy.nicTeaming.nicOrder.activeNic != self.teaming_failover_order_active: + changed = True + changed_list.append("Failover order active") + host_results['failover_active_previous'] = spec.policy.nicTeaming.nicOrder.activeNic + spec.policy.nicTeaming.nicOrder.activeNic = self.teaming_failover_order_active + if spec.policy.nicTeaming.nicOrder.standbyNic != self.teaming_failover_order_standby: + changed = True + changed_list.append("Failover order standby") + host_results['failover_standby_previous'] = spec.policy.nicTeaming.nicOrder.standbyNic + spec.policy.nicTeaming.nicOrder.standbyNic = self.teaming_failover_order_standby + else: + spec.policy.nicTeaming.nicOrder = None + changed = True + changed_list.append("Failover order") + if hasattr(spec.policy.nicTeaming.nicOrder, 'activeNic'): + host_results['failover_active_previous'] = spec.policy.nicTeaming.nicOrder.activeNic + else: + host_results['failover_active_previous'] = [] + if hasattr(spec.policy.nicTeaming.nicOrder, 'standbyNic'): + host_results['failover_standby_previous'] = spec.policy.nicTeaming.nicOrder.standbyNic + else: + host_results['failover_standby_previous'] = [] + else: + if self.teaming_failover_order_active or self.teaming_failover_order_standby: + changed = True + changed_list.append("Failover order") + host_results['failover_active_previous'] = "No override" + host_results['failover_standby_previous'] = "No override" + spec.policy.nicTeaming.nicOrder = self.create_nic_order_policy() + # Check teaming failure detection + if self.teaming_failure_detection is None: + host_results['failure_detection'] = "No override" + else: + host_results['failure_detection'] = self.teaming_failure_detection + if spec.policy.nicTeaming.failureCriteria and spec.policy.nicTeaming.failureCriteria.checkBeacon is not None: + if self.teaming_failure_detection == "link_status_only": + if spec.policy.nicTeaming.failureCriteria.checkBeacon is True: + changed = True + changed_list.append("Network failure detection") + host_results['failure_detection_previous'] = "beacon_probing" + spec.policy.nicTeaming.failureCriteria.checkBeacon = False + elif self.teaming_failure_detection == "beacon_probing": + if spec.policy.nicTeaming.failureCriteria.checkBeacon is False: + changed = True + changed_list.append("Network failure detection") + host_results['failure_detection_previous'] = "link_status_only" + spec.policy.nicTeaming.failureCriteria.checkBeacon = True + elif spec.policy.nicTeaming.failureCriteria.checkBeacon is not None: + changed = True + changed_list.append("Network failure detection") + host_results['failure_detection_previous'] = spec.policy.nicTeaming.failureCriteria.checkBeacon + spec.policy.nicTeaming.failureCriteria = None + else: + if self.teaming_failure_detection: + spec.policy.nicTeaming.failureCriteria = self.create_nic_failure_policy() + changed = True + changed_list.append("Network failure detection") + host_results['failure_detection_previous'] = "No override" + else: + spec.policy.nicTeaming = self.create_teaming_policy() + if spec.policy.nicTeaming: + changed = True + changed_list.append("Teaming and failover") + host_results['load_balancing_previous'] = "No override" + host_results['notify_switches_previous'] = "No override" + host_results['failback_previous'] = "No override" + host_results['failover_active_previous'] = "No override" + host_results['failover_standby_previous'] = "No override" + host_results['failure_detection_previous'] = "No override" + + if changed: + if self.module.check_mode: + changed_suffix = ' would be changed' + else: + changed_suffix = ' changed' + if len(changed_list) > 2: + message = ', '.join(changed_list[:-1]) + ', and ' + str(changed_list[-1]) + elif len(changed_list) == 2: + message = ' and '.join(changed_list) + elif len(changed_list) == 1: + message = changed_list[0] + message += changed_suffix + if not self.module.check_mode: try: - host_system.configManager.networkSystem.UpdatePortGroup(pgName=self.portgroup_name, - portgrp=port_group.spec) - except vim.fault.AlreadyExists as e: - # To match with other vmware_* modules if portgroup - # exists, we proceed, as user may want idempotency - pass + host_system.configManager.networkSystem.UpdatePortGroup( + pgName=self.portgroup, + portgrp=spec + ) + except vim.fault.AlreadyExists as already_exists: + self.module.fail_json( + msg="Failed to update Portgroup as it would conflict with an existing port group: %s" % + to_native(already_exists.msg) + ) except vim.fault.NotFound as not_found: - self.module.fail_json(msg="Failed to update Portgroup as" - " vSwitch was not found: %s" % to_native(not_found.msg)) + self.module.fail_json( + msg="Failed to update Portgroup as vSwitch was not found: %s" % + to_native(not_found.msg) + ) except vim.fault.HostConfigFault as host_config_fault: - self.module.fail_json(msg="Failed to update Portgroup due to host" - " system configuration failure : %s" % to_native(host_config_fault.msg)) + self.module.fail_json( + msg="Failed to update Portgroup due to host system configuration failure : %s" % + to_native(host_config_fault.msg) + ) except vmodl.fault.InvalidArgument as invalid_argument: - self.module.fail_json(msg="Failed to update Portgroup as VLAN id was not" - " correct as per specifications: %s" % to_native(invalid_argument.msg)) - except Exception as generic_exception: - self.module.fail_json(msg="Failed to update Portgroup due to generic" - " exception : %s" % to_native(generic_exception)) - return port_changed + self.module.fail_json( + msg="Failed to update Port Group '%s', this can be due to either of following :" + " 1. VLAN id was not correct as per specifications, 2. Network policy is invalid : %s" % + (self.portgroup, to_native(invalid_argument.msg)) + ) + else: + message = "Port Group already configured properly" + host_results['changed'] = changed + host_results['msg'] = message + host_results['portgroup'] = self.portgroup + host_results['vswitch'] = self.switch + + return changed, host_results def create_network_policy(self): """ - Function to create Network policy - Returns: Network policy object + Create a Network Policy + Returns: Network Policy object """ - security_policy = vim.host.NetworkPolicy.SecurityPolicy() - if self.promiscuous_mode: - security_policy.allowPromiscuous = self.promiscuous_mode - if self.forged_transmits: - security_policy.forgedTransmits = self.forged_transmits - if self.mac_changes: - security_policy.macChanges = self.mac_changes + security_policy = None + shaping_policy = None + teaming_policy = None - # Teaming Policy - teaming_policy = vim.host.NetworkPolicy.NicTeamingPolicy() - teaming_policy.policy = self.module.params['teaming_policy']['load_balance_policy'] - teaming_policy.reversePolicy = self.module.params['teaming_policy']['inbound_policy'] - teaming_policy.notifySwitches = self.module.params['teaming_policy']['notify_switches'] - teaming_policy.rollingOrder = self.module.params['teaming_policy']['rolling_order'] + # Only configure security policy if an option is defined + if not all(option is None for option in [self.sec_promiscuous_mode, + self.sec_mac_changes, + self.sec_forged_transmits]): + security_policy = self.create_security_policy() + if self.ts_enabled: + shaping_policy = self.create_shaping_policy() + teaming_policy = self.create_teaming_policy() - network_policy = vim.host.NetworkPolicy(nicTeaming=teaming_policy, - security=security_policy) + network_policy = vim.host.NetworkPolicy( + security=security_policy, + nicTeaming=teaming_policy, + shapingPolicy=shaping_policy + ) return network_policy - def remove_hosts_port_group(self): + def create_security_policy(self): """ - Function to remove port group from given host + Create a Security Policy + Returns: Security Policy object """ - results = dict(changed=False, result=dict()) - host_change_list = [] - for host in self.host_obj_list: - change = False - results['result'][host.name] = dict(portgroup_name=self.portgroup_name, - vlan_id=self.vlan_id, - switch_name=self.switch_name) - change = self.remove_host_port_group(host_system=host, - portgroup_name=self.portgroup_name, - vswitch_name=self.switch_name) - host_change_list.append(change) - if any(host_change_list): - results['changed'] = True - self.module.exit_json(**results) + security_policy = vim.host.NetworkPolicy.SecurityPolicy() + security_policy.allowPromiscuous = self.sec_promiscuous_mode + security_policy.macChanges = self.sec_mac_changes + security_policy.forgedTransmits = self.sec_forged_transmits + return security_policy - def remove_host_port_group(self, host_system, portgroup_name, vswitch_name): + def create_shaping_policy(self): """ - Function to remove port group depending upon host system, port group name and vswitch name - Args: - host_system: Name of Host System - portgroup_name: Name of Portgroup - vswitch_name: Name of vSwitch + Create a Traffic Shaping Policy + Returns: Traffic Shaping Policy object + """ + shaping_policy = vim.host.NetworkPolicy.TrafficShapingPolicy() + shaping_policy.enabled = self.ts_enabled + shaping_policy.averageBandwidth = self.ts_average_bandwidth * 1000 + shaping_policy.peakBandwidth = self.ts_peak_bandwidth * 1000 + shaping_policy.burstSize = self.ts_burst_size * 1024 + return shaping_policy + def create_teaming_policy(self): """ - changed = False - desired_pgs = self.get_port_group_by_name(host_system=host_system, - portgroup_name=portgroup_name, - vswitch_name=vswitch_name) - if desired_pgs: - try: - host_system.configManager.networkSystem.RemovePortGroup(pgName=self.portgroup_name) - changed = True - except vim.fault.NotFound as not_found: - self.module.fail_json(msg="Failed to remove Portgroup as it was" - " not found: %s" % to_native(not_found.msg)) - except vim.fault.ResourceInUse as resource_in_use: - self.module.fail_json(msg="Failed to remove Portgroup as it is" - " in use: %s" % to_native(resource_in_use.msg)) - except vim.fault.HostConfigFault as host_config_fault: - self.module.fail_json(msg="Failed to remove Portgroup due to configuration" - " failures: %s" % to_native(host_config_fault.msg)) - except Exception as generic_exception: - self.module.fail_json(msg="Failed to remove Portgroup due to generic" - " exception : %s" % to_native(generic_exception)) - return changed + Create a NIC Teaming Policy + Returns: NIC Teaming Policy object + """ + # Only configure teaming policy if an option is defined + if not all(option is None for option in [self.teaming_load_balancing, + self.teaming_failure_detection, + self.teaming_notify_switches, + self.teaming_failback, + self.teaming_failover_order_active, + self.teaming_failover_order_standby]): + teaming_policy = vim.host.NetworkPolicy.NicTeamingPolicy() + teaming_policy.policy = self.teaming_load_balancing + # NOTE: 'teaming_inbound_policy' is deprecated and the following if statement should be removed in 2.11 + if self.teaming_inbound_policy: + teaming_policy.reversePolicy = self.teaming_inbound_policy + else: + # Deprecated since VI API 5.1. The system default (true) will be used + teaming_policy.reversePolicy = True + teaming_policy.notifySwitches = self.teaming_notify_switches + # NOTE: 'teaming_rolling_order' is deprecated and the following if statement should be removed in 2.11 + if self.teaming_rolling_order: + teaming_policy.rollingOrder = self.teaming_rolling_order + else: + # this option is called 'failback' in the vSphere Client + # rollingOrder also uses the opposite value displayed in the client + if self.teaming_failback is None: + teaming_policy.rollingOrder = None + else: + teaming_policy.rollingOrder = not self.teaming_failback + if self.teaming_failover_order_active is None and self.teaming_failover_order_standby is None: + teaming_policy.nicOrder = None + else: + teaming_policy.nicOrder = self.create_nic_order_policy() + if self.teaming_failure_detection is None: + teaming_policy.failureCriteria = None + else: + teaming_policy.failureCriteria = self.create_nic_failure_policy() + return teaming_policy + return None + + def create_nic_order_policy(self): + """ + Create a NIC order Policy + Returns: NIC order Policy object + """ + for active_nic in self.teaming_failover_order_active: + if active_nic not in self.switch_object.spec.bridge.nicDevice: + self.module.fail_json( + msg="NIC '%s' (active) is not configured on vSwitch '%s'" % (active_nic, self.switch) + ) + for standby_nic in self.teaming_failover_order_standby: + if standby_nic not in self.switch_object.spec.bridge.nicDevice: + self.module.fail_json( + msg="NIC '%s' (standby) is not configured on vSwitch '%s'" % (standby_nic, self.switch) + ) + nic_order = vim.host.NetworkPolicy.NicOrderPolicy() + nic_order.activeNic = self.teaming_failover_order_active + nic_order.standbyNic = self.teaming_failover_order_standby + return nic_order + + def create_nic_failure_policy(self): + """ + Create a NIC Failure Criteria Policy + Returns: NIC Failure Criteria Policy object + """ + failure_criteria = vim.host.NetworkPolicy.NicFailureCriteria() + if self.teaming_failure_detection == "link_status_only": + failure_criteria.checkBeacon = False + elif self.teaming_failure_detection == "beacon_probing": + failure_criteria.checkBeacon = True + elif self.teaming_failure_detection is None: + failure_criteria = None + # The following properties are deprecated since VI API 5.1. Default values are used + failure_criteria.fullDuplex = False + failure_criteria.percentage = 0 + failure_criteria.checkErrorPercent = False + failure_criteria.checkDuplex = False + failure_criteria.speed = 10 + failure_criteria.checkSpeed = 'minimum' + return failure_criteria def main(): + """Main""" argument_spec = vmware_argument_spec() argument_spec.update(dict( - portgroup_name=dict(required=True, type='str'), - switch_name=dict(required=True, type='str'), - vlan_id=dict(required=True, type='int'), + portgroup=dict(type='str', required=True, aliases=['portgroup_name']), + switch=dict(type='str', required=True, aliases=['switch_name', 'vswitch']), + vlan_id=dict(type='int', required=False, default=0, aliases=['vlan']), hosts=dict(type='list', aliases=['esxi_hostname']), - cluster_name=dict(type='str'), + cluster_name=dict(type='str', aliases=['cluster']), state=dict(type='str', choices=['present', 'absent'], default='present'), - network_policy=dict(type='dict', - options=dict( - promiscuous_mode=dict(type='bool'), - forged_transmits=dict(type='bool'), - mac_changes=dict(type='bool'), - ), - default=dict( - promiscuous_mode=False, - forged_transmits=False, - mac_changes=False, - ) - ), - teaming_policy=dict( + security=dict( type='dict', options=dict( - inbound_policy=dict(type='bool', default=False), - notify_switches=dict(type='bool', default=True), - rolling_order=dict(type='bool', default=False), - load_balance_policy=dict(type='str', - default='loadbalance_srcid', - choices=[ - 'loadbalance_ip', - 'loadbalance_srcmac', - 'loadbalance_srcid', - 'failover_explicit', - ], - ) + promiscuous_mode=dict(type='bool'), + forged_transmits=dict(type='bool'), + mac_changes=dict(type='bool'), ), - default=dict( - inbound_policy=False, - notify_switches=True, - rolling_order=False, - load_balance_policy='loadbalance_srcid', + aliases=['security_policy', 'network_policy'] + ), + traffic_shaping=dict( + type='dict', + options=dict( + enabled=dict(type='bool'), + average_bandwidth=dict(type='int'), + peak_bandwidth=dict(type='int'), + burst_size=dict(type='int'), ), ), - ) - ) + teaming=dict( + type='dict', + options=dict( + load_balancing=dict( + type='str', + choices=[ + None, + 'loadbalance_ip', + 'loadbalance_srcmac', + 'loadbalance_srcid', + 'failover_explicit', + ], + aliases=['load_balance_policy'], + ), + network_failure_detection=dict( + type='str', + choices=['link_status_only', 'beacon_probing'] + ), + notify_switches=dict(type='bool'), + failback=dict(type='bool'), + active_adapters=dict(type='list'), + standby_adapters=dict(type='list'), + # NOTE: Deprecated from 2.11 onwards + inbound_policy=dict(type='bool'), + rolling_order=dict(type='bool'), + ), + aliases=['teaming_policy'] + ), + )) - module = AnsibleModule(argument_spec=argument_spec, - supports_check_mode=False, - required_one_of=[ - ['cluster_name', 'hosts'], - ] - ) + module = AnsibleModule( + argument_spec=argument_spec, + required_one_of=[ + ['cluster_name', 'hosts'], + ], + supports_check_mode=True + ) try: - pyv = PyVmomiHelper(module) - pyv.process_state() + host_portgroup = VMwareHostPortGroup(module) + host_portgroup.process_state() except vmodl.RuntimeFault as runtime_fault: module.fail_json(msg=to_native(runtime_fault.msg)) except vmodl.MethodFault as method_fault: diff --git a/test/integration/targets/vmware_portgroup/aliases b/test/integration/targets/vmware_portgroup/aliases new file mode 100644 index 0000000000..3eede2cbf0 --- /dev/null +++ b/test/integration/targets/vmware_portgroup/aliases @@ -0,0 +1,3 @@ +cloud/vcenter +shippable/vcenter/group1 +needs/target/prepare_vmware_tests diff --git a/test/integration/targets/vmware_portgroup/tasks/main.yml b/test/integration/targets/vmware_portgroup/tasks/main.yml new file mode 100644 index 0000000000..341dadce5f --- /dev/null +++ b/test/integration/targets/vmware_portgroup/tasks/main.yml @@ -0,0 +1,68 @@ +# Test code for the vmware_portgroup module. +# Copyright: (c) 2018, Abhijeet Kasurde +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +- when: vcsim is not defined + block: + - debug: var=ccr1 + + - debug: var=host1 + + - name: Create portgroup without Portgroup and vSwitch + vmware_portgroup: + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + validate_certs: no + ignore_errors: yes + register: no_pg_vs + + - assert: + that: + - '"missing" in no_pg_vs.msg' + + - name: Create portgroup without Portgroup + vmware_portgroup: + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + vswitch: '' + validate_certs: no + ignore_errors: yes + register: no_vs + + - assert: + that: + - '"missing" in no_vs.msg' + + - name: Create portgroup without cluster + vmware_portgroup: + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + vswitch: 'vSwitch_03' + portgroup: 'pg_03' + validate_certs: no + ignore_errors: yes + register: no_cluster + + - assert: + that: + - '"cluster_name" in no_cluster.msg' + + - name: Create portgroup + vmware_portgroup: + hostname: '{{ vcenter_hostname }}' + username: '{{ vcenter_username }}' + password: '{{ vcenter_password }}' + vswitch: 'vSwitch_03' + portgroup: 'pg_03' + cluster_name: "{{ ccr1 }}" + validate_certs: no + state: present + ignore_errors: yes + register: pg_info + + - assert: + that: + - pg_info.changed