diff --git a/lib/ansible/module_utils/vmware.py b/lib/ansible/module_utils/vmware.py index 305c26a04c..ffca309ef0 100644 --- a/lib/ansible/module_utils/vmware.py +++ b/lib/ansible/module_utils/vmware.py @@ -223,14 +223,6 @@ def find_vm_by_name(content, vm_name, folder=None, recurse=True): return None -def find_host_portgroup_by_name(host, portgroup_name): - - for portgroup in host.config.network.portgroup: - if portgroup.spec.name == portgroup_name: - return portgroup - return None - - def compile_folder_path_for_object(vobj): """ make a /vm/foo/bar/baz like folder path for an object """ @@ -813,6 +805,7 @@ class PyVmomi(object): self.current_vm_obj = None self.content = connect_to_api(self.module) + # Virtual Machine related functions def get_vm(self): vm = None match_first = (self.params['name_match'] == 'first') @@ -830,3 +823,60 @@ class PyVmomi(object): def gather_facts(self, vm): return gather_vm_facts(self.content, vm) + + # Cluster related functions + def find_cluster_by_name(self, cluster_name, datacenter_name=None): + """ + Find Cluster by name in given datacenter + Args: + cluster_name: Name of cluster name to find + datacenter_name: (optional) Name of datacenter + + Returns: True if found + + """ + return find_cluster_by_name(self.content, cluster_name, datacenter=datacenter_name) + + def get_all_hosts_by_cluster(self, cluster_name): + """ + Get all hosts from cluster by cluster name + Args: + cluster_name: Name of cluster + + Returns: List of hosts + + """ + cluster_obj = self.find_cluster_by_name(cluster_name=cluster_name) + if cluster_obj: + return [host for host in cluster_obj.host] + else: + return [] + + # Hosts related functions + def find_hostsystem_by_name(self, host_name): + """ + Find Host by name + Args: + host_name: Name of ESXi host + + Returns: True if found + + """ + return find_hostsystem_by_name(self.content, hostname=host_name) + + # Network related functions + @staticmethod + def find_host_portgroup_by_name(host, portgroup_name): + """ + Find Portgroup on given host + Args: + host: Host config object + portgroup_name: Name of portgroup + + Returns: True if found else False + + """ + for portgroup in host.config.network.portgroup: + if portgroup.spec.name == portgroup_name: + return portgroup + return False diff --git a/lib/ansible/modules/cloud/vmware/vmware_portgroup.py b/lib/ansible/modules/cloud/vmware/vmware_portgroup.py index 6dd5f1fb84..2090c4e162 100644 --- a/lib/ansible/modules/cloud/vmware/vmware_portgroup.py +++ b/lib/ansible/modules/cloud/vmware/vmware_portgroup.py @@ -1,16 +1,20 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -# (c) 2015, Joseph Callen +# Copyright: (c) 2015, Joseph Callen +# Copyright: (c) 2017, Ansible Project +# Copyright: (c) 2017, Abhijeet Kasurde # 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 __metaclass__ = type -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} +ANSIBLE_METADATA = { + 'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community' +} DOCUMENTATION = ''' @@ -18,138 +22,196 @@ DOCUMENTATION = ''' module: vmware_portgroup short_description: Create a VMware portgroup description: - - Create a VMware portgroup + - Create a VMware portgroup on given host/s or hosts of given cluster version_added: 2.0 -author: "Joseph Callen (@jcpowermac), Russell Teague (@mtnbikenc)" +author: +- Joseph Callen (@jcpowermac) +- Russell Teague (@mtnbikenc) +- Abhijeet Kasurde (@akasurde) notes: - - Tested on vSphere 5.5 + - Tested on vSphere 5.5, 6.5 requirements: - "python >= 2.6" - PyVmomi options: switch_name: description: - - vSwitch to modify + - vSwitch to modify. required: True portgroup_name: description: - - Portgroup name to add + - Portgroup name to add. required: True vlan_id: description: - - VLAN ID to assign to portgroup + - VLAN ID to assign to portgroup. required: True network_policy: 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. - Settings are promiscuous_mode, forged_transmits, mac_changes + - 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)' required: False version_added: "2.2" + 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 hosts is not specified. + version_added: "2.5" + hosts: + description: + - List of name of host or hosts on which portgroup needs to be added. + - This option is required if cluster_name is not specified. + version_added: "2.5" extends_documentation_fragment: vmware.documentation ''' -EXAMPLES = ''' -# Example from Ansible playbook +EXAMPLES = r''' +- name: Add Management Network VM Portgroup + vmware_portgroup: + hostname: esxi_hostname + username: esxi_username + password: esxi_password + switch_name: vswitch_name + portgroup_name: portgroup_name + vlan_id: vlan_id - - name: Add Management Network VM Portgroup - local_action: - module: vmware_portgroup - hostname: esxi_hostname - username: esxi_username - password: esxi_password - switch_name: vswitch_name - portgroup_name: portgroup_name - vlan_id: vlan_id +- name: Add Portgroup with Promiscuous Mode Enabled + vmware_portgroup: + hostname: esxi_hostname + username: esxi_username + password: esxi_password + switch_name: vswitch_name + portgroup_name: portgroup_name + network_policy: + promiscuous_mode: True + +- name: Add Management Network VM Portgroup to specific hosts + vmware_portgroup: + hostname: vCenter_hostname + username: esxi_username + password: esxi_password + hosts: [esxi_hostname_one] + switch_name: vswitch_name + portgroup_name: portgroup_name + vlan_id: vlan_id + +- name: Add Management Network VM Portgroup to all hosts in a cluster + vmware_portgroup: + hostname: vCenter_hostname + username: esxi_username + password: esxi_password + cluster_name: rh_engineering + switch_name: vswitch_name + portgroup_name: portgroup_name + vlan_id: vlan_id - - name: Add Portgroup with Promiscuous Mode Enabled - local_action: - module: vmware_portgroup - hostname: esxi_hostname - username: esxi_username - password: esxi_password - switch_name: vswitch_name - portgroup_name: portgroup_name - network_policy: - promiscuous_mode: True ''' try: from pyVmomi import vim, vmodl - HAS_PYVMOMI = True except ImportError: - HAS_PYVMOMI = False + pass from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.vmware import (HAS_PYVMOMI, - connect_to_api, - find_host_portgroup_by_name, - get_all_objs, - vmware_argument_spec, - ) +from ansible.module_utils.vmware import PyVmomi, vmware_argument_spec -def create_network_policy(promiscuous_mode, forged_transmits, mac_changes): +class PyVmomiHelper(PyVmomi): + def __init__(self, module): + super(PyVmomiHelper, self).__init__(module) - security_policy = vim.host.NetworkPolicy.SecurityPolicy() - if promiscuous_mode: - security_policy.allowPromiscuous = promiscuous_mode - if forged_transmits: - security_policy.forgedTransmits = forged_transmits - if mac_changes: - security_policy.macChanges = mac_changes - network_policy = vim.host.NetworkPolicy(security=security_policy) - return network_policy + self.hosts = self.params['hosts'] + self.cluster = self.params['cluster_name'] + self.portgroup_name = self.params['portgroup_name'] + self.switch_name = self.params['switch_name'] + 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() + self.changed = False + def add_hosts_port_group(self, hosts): + for host in hosts: + self.changed = self.create_port_group(host, self.portgroup_name, self.vlan_id, + self.switch_name, self.network_policy) -def create_port_group(host_system, portgroup_name, vlan_id, vswitch_name, network_policy): + def create_port_group(self, host_system, portgroup_name, vlan_id, vswitch_name, network_policy): + config = vim.host.NetworkConfig() + config.portgroup = [vim.host.PortGroup.Config()] + config.portgroup[0].changeOperation = "add" + config.portgroup[0].spec = vim.host.PortGroup.Specification() + config.portgroup[0].spec.name = portgroup_name + config.portgroup[0].spec.vlanId = vlan_id + config.portgroup[0].spec.vswitchName = vswitch_name + config.portgroup[0].spec.policy = network_policy - config = vim.host.NetworkConfig() - config.portgroup = [vim.host.PortGroup.Config()] - config.portgroup[0].changeOperation = "add" - config.portgroup[0].spec = vim.host.PortGroup.Specification() - config.portgroup[0].spec.name = portgroup_name - config.portgroup[0].spec.vlanId = vlan_id - config.portgroup[0].spec.vswitchName = vswitch_name - config.portgroup[0].spec.policy = network_policy + host_system.configManager.networkSystem.UpdateNetworkConfig(config, "modify") + return True - host_system.configManager.networkSystem.UpdateNetworkConfig(config, "modify") - return True + def create_network_policy(self): + 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 + network_policy = vim.host.NetworkPolicy(security=security_policy) + return network_policy + + def add_portgroup(self): + if self.cluster and self.find_cluster_by_name(cluster_name=self.cluster): + hosts = self.get_all_hosts_by_cluster(cluster_name=self.cluster) + self.add_hosts_port_group(hosts=hosts) + elif self.hosts: + for host in self.hosts: + host_system = self.find_hostsystem_by_name(host_name=host) + if host_system: + self.add_hosts_port_group([host_system]) def 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'), - network_policy=dict(required=False, type='dict', default={}))) + 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'), + hosts=dict(type='list'), + cluster_name=dict(type='str'), + 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, + ) + ) + ) + ) - module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False) - - if not HAS_PYVMOMI: - module.fail_json(msg='pyvmomi is required for this module') - - portgroup_name = module.params['portgroup_name'] - switch_name = module.params['switch_name'] - vlan_id = module.params['vlan_id'] - promiscuous_mode = module.params['network_policy'].get('promiscuous_mode', None) - forged_transmits = module.params['network_policy'].get('forged_transmits', None) - mac_changes = module.params['network_policy'].get('mac_changes', None) + module = AnsibleModule(argument_spec=argument_spec, + supports_check_mode=False, + required_one_of=[ + ['cluster_name', 'hosts'], + ] + ) try: - content = connect_to_api(module) - host = get_all_objs(content, [vim.HostSystem]) - if not host: - raise SystemExit("Unable to locate Physical Host.") - host_system = host.keys()[0] - - if find_host_portgroup_by_name(host_system, portgroup_name): - module.exit_json(changed=False) - - network_policy = create_network_policy(promiscuous_mode, forged_transmits, mac_changes) - changed = create_port_group(host_system, portgroup_name, vlan_id, switch_name, network_policy) + pyv = PyVmomiHelper(module) + pyv.add_portgroup() + changed = pyv.changed module.exit_json(changed=changed) except vmodl.RuntimeFault as runtime_fault: