diff --git a/changelogs/fragments/58824-vmware_dvs_portgroup-implement-portgroup-updates.yml b/changelogs/fragments/58824-vmware_dvs_portgroup-implement-portgroup-updates.yml new file mode 100644 index 00000000000..887306032ef --- /dev/null +++ b/changelogs/fragments/58824-vmware_dvs_portgroup-implement-portgroup-updates.yml @@ -0,0 +1,2 @@ +bugfixes: +- vmware_dvs_portgroup - Implemented configuration changes on an existing Distributed vSwitch portgroup. diff --git a/lib/ansible/modules/cloud/vmware/vmware_dvs_portgroup.py b/lib/ansible/modules/cloud/vmware/vmware_dvs_portgroup.py index 995e4721bcd..9336d4a30ba 100644 --- a/lib/ansible/modules/cloud/vmware/vmware_dvs_portgroup.py +++ b/lib/ansible/modules/cloud/vmware/vmware_dvs_portgroup.py @@ -234,28 +234,36 @@ class VMwareDvsPortgroup(PyVmomi): self.dvs_portgroup = None self.dv_switch = None - def process_state(self): - dvspg_states = { - 'absent': { - 'present': self.state_destroy_dvspg, - 'absent': self.state_exit_unchanged, - }, - 'present': { - 'update': self.state_update_dvspg, - 'present': self.state_exit_unchanged, - 'absent': self.state_create_dvspg, - } - } - try: - dvspg_states[self.module.params['state']][self.check_dvspg_state()]() - except vmodl.RuntimeFault as runtime_fault: - self.module.fail_json(msg=runtime_fault.msg) - except vmodl.MethodFault as method_fault: - self.module.fail_json(msg=method_fault.msg) - except Exception as e: - self.module.fail_json(msg=str(e)) - - def create_port_group(self): + def create_vlan_list(self): + vlan_id_list = [] + for vlan_id_splitted in self.module.params['vlan_id'].split(','): + vlans = vlan_id_splitted.split('-') + if len(vlans) > 2: + self.module.fail_json(msg="Invalid VLAN range %s." % vlan_id_splitted) + if len(vlans) == 2: + vlan_id_start = vlans[0].strip() + vlan_id_end = vlans[1].strip() + if not vlan_id_start.isdigit(): + self.module.fail_json(msg="Invalid VLAN %s." % vlan_id_start) + if not vlan_id_end.isdigit(): + self.module.fail_json(msg="Invalid VLAN %s." % vlan_id_end) + vlan_id_start = int(vlan_id_start) + vlan_id_end = int(vlan_id_end) + if vlan_id_start not in range(0, 4095) or vlan_id_end not in range(0, 4095): + self.module.fail_json(msg="vlan_id range %s specified is incorrect. The valid vlan_id range is from 0 to 4094." % vlan_id_splitted) + vlan_id_list.append((vlan_id_start, vlan_id_end)) + else: + vlan_id = vlans[0].strip() + if not vlan_id.isdigit(): + self.module.fail_json(msg="Invalid VLAN %s." % vlan_id) + vlan_id = int(vlan_id) + vlan_id_list.append((vlan_id, vlan_id)) + + vlan_id_list.sort() + + return vlan_id_list + + def build_config(self): config = vim.dvs.DistributedVirtualPortgroup.ConfigSpec() # Basic config @@ -266,16 +274,7 @@ class VMwareDvsPortgroup(PyVmomi): config.defaultPortConfig = vim.dvs.VmwareDistributedVirtualSwitch.VmwarePortConfigPolicy() if self.module.params['vlan_trunk']: config.defaultPortConfig.vlan = vim.dvs.VmwareDistributedVirtualSwitch.TrunkVlanSpec() - vlan_id_list = [] - for vlan_id_splitted in self.module.params['vlan_id'].split(','): - try: - vlan_id_start, vlan_id_end = map(int, vlan_id_splitted.split('-')) - if vlan_id_start not in range(0, 4095) or vlan_id_end not in range(0, 4095): - self.module.fail_json(msg="vlan_id range %s specified is incorrect. The valid vlan_id range is from 0 to 4094." % vlan_id_splitted) - vlan_id_list.append(vim.NumericRange(start=vlan_id_start, end=vlan_id_end)) - except ValueError: - vlan_id_list.append(vim.NumericRange(start=int(vlan_id_splitted.strip()), end=int(vlan_id_splitted.strip()))) - config.defaultPortConfig.vlan.vlanId = vlan_id_list + config.defaultPortConfig.vlan.vlanId = list(map(lambda x: vim.NumericRange(start=x[0], end=x[1]), self.create_vlan_list())) else: config.defaultPortConfig.vlan = vim.dvs.VmwareDistributedVirtualSwitch.VlanIdSpec() config.defaultPortConfig.vlan.vlanId = int(self.module.params['vlan_id']) @@ -310,6 +309,38 @@ class VMwareDvsPortgroup(PyVmomi): # PG Type config.type = self.module.params['portgroup_type'] + return config + + def process_state(self): + dvspg_states = { + 'absent': { + 'present': self.state_destroy_dvspg, + 'absent': self.state_exit_unchanged, + }, + 'present': { + 'update': self.state_update_dvspg, + 'present': self.state_exit_unchanged, + 'absent': self.state_create_dvspg, + } + } + try: + dvspg_states[self.module.params['state']][self.check_dvspg_state()]() + except vmodl.RuntimeFault as runtime_fault: + self.module.fail_json(msg=runtime_fault.msg) + except vmodl.MethodFault as method_fault: + self.module.fail_json(msg=method_fault.msg) + except Exception as e: + self.module.fail_json(msg=str(e)) + + def update_port_group(self): + config = self.build_config() + config.configVersion = self.dvs_portgroup.config.configVersion + task = self.dvs_portgroup.ReconfigureDVPortgroup_Task(config) + changed, result = wait_for_task(task) + return changed, result + + def create_port_group(self): + config = self.build_config() task = self.dv_switch.AddDVPortgroup_Task([config]) changed, result = wait_for_task(task) return changed, result @@ -327,7 +358,12 @@ class VMwareDvsPortgroup(PyVmomi): self.module.exit_json(changed=False) def state_update_dvspg(self): - self.module.exit_json(changed=False, msg="Currently not implemented.") + changed = True + result = None + + if not self.module.check_mode: + changed, result = self.update_port_group() + self.module.exit_json(changed=changed, result=str(result)) def state_create_dvspg(self): changed = True @@ -346,8 +382,58 @@ class VMwareDvsPortgroup(PyVmomi): if self.dvs_portgroup is None: return 'absent' + + # Check config + # Basic config + if self.dvs_portgroup.config.numPorts != self.module.params['num_ports']: + return 'update' + + # Default port config + defaultPortConfig = self.dvs_portgroup.config.defaultPortConfig + if self.module.params['vlan_trunk']: + if not isinstance(defaultPortConfig.vlan, vim.dvs.VmwareDistributedVirtualSwitch.TrunkVlanSpec): + return 'update' + if map(lambda x: (x.start, x.end), defaultPortConfig.vlan.vlanId) != self.create_vlan_list(): + return 'update' else: - return 'present' + if not isinstance(defaultPortConfig.vlan, vim.dvs.VmwareDistributedVirtualSwitch.VlanIdSpec): + return 'update' + if defaultPortConfig.vlan.vlanId != int(self.module.params['vlan_id']): + return 'update' + + if defaultPortConfig.securityPolicy.allowPromiscuous.value != self.module.params['network_policy']['promiscuous'] or \ + defaultPortConfig.securityPolicy.forgedTransmits.value != self.module.params['network_policy']['forged_transmits'] or \ + defaultPortConfig.securityPolicy.macChanges.value != self.module.params['network_policy']['mac_changes']: + return 'update' + + # Teaming Policy + teamingPolicy = self.dvs_portgroup.config.defaultPortConfig.uplinkTeamingPolicy + if teamingPolicy.policy.value != self.module.params['teaming_policy']['load_balance_policy'] or \ + teamingPolicy.reversePolicy.value != self.module.params['teaming_policy']['inbound_policy'] or \ + teamingPolicy.notifySwitches.value != self.module.params['teaming_policy']['notify_switches'] or \ + teamingPolicy.rollingOrder.value != self.module.params['teaming_policy']['rolling_order']: + return 'update' + + # PG policy (advanced_policy) + policy = self.dvs_portgroup.config.policy + if policy.blockOverrideAllowed != self.module.params['port_policy']['block_override'] or \ + policy.ipfixOverrideAllowed != self.module.params['port_policy']['ipfix_override'] or \ + policy.livePortMovingAllowed != self.module.params['port_policy']['live_port_move'] or \ + policy.networkResourcePoolOverrideAllowed != self.module.params['port_policy']['network_rp_override'] or \ + policy.portConfigResetAtDisconnect != self.module.params['port_policy']['port_config_reset_at_disconnect'] or \ + policy.securityPolicyOverrideAllowed != self.module.params['port_policy']['security_override'] or \ + policy.shapingOverrideAllowed != self.module.params['port_policy']['shaping_override'] or \ + policy.trafficFilterOverrideAllowed != self.module.params['port_policy']['traffic_filter_override'] or \ + policy.uplinkTeamingOverrideAllowed != self.module.params['port_policy']['uplink_teaming_override'] or \ + policy.vendorConfigOverrideAllowed != self.module.params['port_policy']['vendor_config_override'] or \ + policy.vlanOverrideAllowed != self.module.params['port_policy']['vlan_override']: + return 'update' + + # PG Type + if self.dvs_portgroup.config.type != self.module.params['portgroup_type']: + return 'update' + + return 'present' def main(): diff --git a/test/integration/targets/vmware_dvs_portgroup/tasks/main.yml b/test/integration/targets/vmware_dvs_portgroup/tasks/main.yml index 02db68e7049..00dbefd3045 100644 --- a/test/integration/targets/vmware_dvs_portgroup/tasks/main.yml +++ b/test/integration/targets/vmware_dvs_portgroup/tasks/main.yml @@ -122,7 +122,7 @@ that: - dvs_pg_result_0005.changed -- name: create basic portgroup with all security and policy settings enabled +- name: create basic portgroup with some security and policy settings enabled vmware_dvs_portgroup: validate_certs: False hostname: "{{ vcenter_hostname }}" @@ -147,6 +147,106 @@ that: - dvs_pg_result_0006.changed +- name: Change forged_transmits to no + vmware_dvs_portgroup: + validate_certs: False + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + switch_name: "{{ dvswitch1 }}" + portgroup_name: "basic-some-enabled" + vlan_id: 0 + num_ports: 32 + portgroup_type: earlyBinding + state: present + network_policy: + promiscuous: yes + forged_transmits: no + mac_changes: no + port_policy: + vlan_override: yes + register: dvs_pg_result_0007 + +- name: ensure forged_transmits is changed + assert: + that: + - dvs_pg_result_0007.changed + +- name: Change vlan_override to no + vmware_dvs_portgroup: + validate_certs: False + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + switch_name: "{{ dvswitch1 }}" + portgroup_name: "basic-some-enabled" + vlan_id: 0 + num_ports: 32 + portgroup_type: earlyBinding + state: present + network_policy: + promiscuous: yes + forged_transmits: no + mac_changes: no + port_policy: + vlan_override: no + register: dvs_pg_result_0008 + +- name: ensure vlan_override is changed + assert: + that: + - dvs_pg_result_0008.changed + +- name: Change num_ports to 16 + vmware_dvs_portgroup: + validate_certs: False + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + switch_name: "{{ dvswitch1 }}" + portgroup_name: "basic-some-enabled" + vlan_id: 0 + num_ports: 16 + portgroup_type: earlyBinding + state: present + network_policy: + promiscuous: yes + forged_transmits: no + mac_changes: no + port_policy: + vlan_override: no + register: dvs_pg_result_0009 + +- name: ensure vlan_override is changed + assert: + that: + - dvs_pg_result_0009.changed + +- name: Change portgroup_type to ephemeral + vmware_dvs_portgroup: + validate_certs: False + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + switch_name: "{{ dvswitch1 }}" + portgroup_name: "basic-some-enabled" + vlan_id: 0 + num_ports: 16 + portgroup_type: ephemeral + state: present + network_policy: + promiscuous: yes + forged_transmits: no + mac_changes: no + port_policy: + vlan_override: no + register: dvs_pg_result_0010 + +- name: ensure vlan_override is changed + assert: + that: + - dvs_pg_result_0010.changed + - name: delete basic portgroup vmware_dvs_portgroup: validate_certs: False @@ -159,12 +259,12 @@ num_ports: 32 portgroup_type: earlyBinding state: absent - register: dvs_pg_result_0007 + register: dvs_pg_result_0011 - name: ensure dvs portgroup is removed assert: that: - - dvs_pg_result_0007.changed + - dvs_pg_result_0011.changed - name: delete basic portgroup again vmware_dvs_portgroup: @@ -178,12 +278,12 @@ num_ports: 32 portgroup_type: earlyBinding state: absent - register: dvs_pg_result_0008 + register: dvs_pg_result_0012 - name: ensure dvs portgroup is removed assert: that: - - not dvs_pg_result_0008.changed + - not dvs_pg_result_0012.changed - name: Check valid VLAN id range in DVS Portgroup vmware_dvs_portgroup: @@ -198,11 +298,90 @@ num_ports: 32 portgroup_type: earlyBinding state: present - register: dvs_pg_result_0009 + register: dvs_pg_result_0013 ignore_errors: True - name: Ensure module fails for invalid VLAN id assert: that: - - not dvs_pg_result_0009.changed - - "'vlan_id range 1-4096 specified is incorrect. The valid vlan_id range is from 0 to 4094.' == '{{ dvs_pg_result_0009.msg }}'" + - not dvs_pg_result_0013.changed + - "'vlan_id range 1-4096 specified is incorrect. The valid vlan_id range is from 0 to 4094.' == '{{ dvs_pg_result_0013.msg }}'" + +- name: Change VLAN on basic VLAN portgroup + vmware_dvs_portgroup: + validate_certs: False + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + switch_name: "{{ dvswitch1 }}" + portgroup_name: "basic-vlan10" + vlan_id: 20 + num_ports: 32 + portgroup_type: earlyBinding + state: present + register: dvs_pg_result_0014 + +- name: ensure dvs portgroup is changed + assert: + that: + - dvs_pg_result_0014.changed + +- name: Change VLAN range on basic trunk portgroup + vmware_dvs_portgroup: + validate_certs: False + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + switch_name: "{{ dvswitch1 }}" + portgroup_name: "basic-trunk" + vlan_id: 1000-2000 + vlan_trunk: True + num_ports: 32 + portgroup_type: earlyBinding + state: present + register: dvs_pg_result_0015 + +- name: ensure dvs portgroup is changed + assert: + that: + - dvs_pg_result_0015.changed + +- name: create complex trunk portgroup + vmware_dvs_portgroup: + validate_certs: False + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + switch_name: "{{ dvswitch1 }}" + portgroup_name: "complex-trunk" + vlan_id: 1-1000, 1005, 1100-1200 + vlan_trunk: True + num_ports: 32 + portgroup_type: earlyBinding + state: present + register: dvs_pg_result_0016 + +- name: ensure dvs portgroup is present + assert: + that: + - dvs_pg_result_0016.changed + +- name: change complex trunk portgroup + vmware_dvs_portgroup: + validate_certs: False + hostname: "{{ vcenter_hostname }}" + username: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + switch_name: "{{ dvswitch1 }}" + portgroup_name: "complex-trunk" + vlan_id: 1-1000, 1006, 1100-1200 + vlan_trunk: True + num_ports: 32 + portgroup_type: earlyBinding + state: present + register: dvs_pg_result_0017 + +- name: ensure dvs portgroup is changed + assert: + that: + - dvs_pg_result_0017.changed