From 6f67d68f5ac2f27040ee8e852e9aecc2ba8618fe Mon Sep 17 00:00:00 2001 From: Yuwei Zhou Date: Wed, 24 Jan 2018 12:57:08 +0800 Subject: [PATCH] azure_rm_networkinterface: support the multiple ip configuration (#32882) * support the mutilple ip configuration * Update azure_rm_networkinterface.py * add test * fix spell * make the virtual network name more flexiable * add test * fix * fix lint * add test * fix parameter * deprecate the flatten ip configuration * fix lint * fix encoding * fix mirror * fix * load model from common --- lib/ansible/module_utils/azure_rm_common.py | 21 +- .../cloud/azure/azure_rm_networkinterface.py | 430 ++++++++++-------- .../cloud/azure/azure_rm_virtualmachine.py | 4 +- .../targets/azure_rm_networkinterface/aliases | 3 + .../azure_rm_networkinterface/meta/main.yml | 2 + .../azure_rm_networkinterface/tasks/main.yml | 169 +++++++ 6 files changed, 416 insertions(+), 213 deletions(-) create mode 100644 test/integration/targets/azure_rm_networkinterface/aliases create mode 100644 test/integration/targets/azure_rm_networkinterface/meta/main.yml create mode 100644 test/integration/targets/azure_rm_networkinterface/tasks/main.yml diff --git a/lib/ansible/module_utils/azure_rm_common.py b/lib/ansible/module_utils/azure_rm_common.py index bd52f8558e..d420c00174 100644 --- a/lib/ansible/module_utils/azure_rm_common.py +++ b/lib/ansible/module_utils/azure_rm_common.py @@ -356,6 +356,9 @@ class AzureRMModuleBase(object): ''' self.module.fail_json(msg=msg, **kwargs) + def deprecate(self, msg, version=None): + self.module.deprecate(msg, version) + def log(self, msg, pretty_print=False): pass # Use only during module development @@ -654,18 +657,17 @@ class AzureRMModuleBase(object): self.fail("Error creating blob service client for storage account {0} - {1}".format(storage_account_name, str(exc))) - def create_default_pip(self, resource_group, location, name, allocation_method='Dynamic'): + def create_default_pip(self, resource_group, location, public_ip_name, allocation_method='Dynamic'): ''' - Create a default public IP address 01 to associate with a network interface. - If a PIP address matching 01 exists, return it. Otherwise, create one. + Create a default public IP address to associate with a network interface. + If a PIP address matching exists, return it. Otherwise, create one. :param resource_group: name of an existing resource group :param location: a valid azure location - :param name: base name to assign the public IP address + :param public_ip_name: base name to assign the public IP address :param allocation_method: one of 'Static' or 'Dynamic' :return: PIP object ''' - public_ip_name = name + '01' pip = None self.log("Starting create_default_pip {0}".format(public_ip_name)) @@ -692,20 +694,19 @@ class AzureRMModuleBase(object): return self.get_poller_result(poller) - def create_default_securitygroup(self, resource_group, location, name, os_type, open_ports): + def create_default_securitygroup(self, resource_group, location, security_group_name, os_type, open_ports): ''' - Create a default security group 01 to associate with a network interface. If a security group matching - 01 exists, return it. Otherwise, create one. + Create a default security group to associate with a network interface. If a security group matching + exists, return it. Otherwise, create one. :param resource_group: Resource group name :param location: azure location name - :param name: base name to use for the security group + :param security_group_name: base name to use for the security group :param os_type: one of 'Windows' or 'Linux'. Determins any default rules added to the security group. :param ssh_port: for os_type 'Linux' port used in rule allowing SSH access. :param rdp_port: for os_type 'Windows' port used in rule allowing RDP access. :return: security_group object ''' - security_group_name = name + '01' group = None self.log("Create security group {0}".format(security_group_name)) diff --git a/lib/ansible/modules/cloud/azure/azure_rm_networkinterface.py b/lib/ansible/modules/cloud/azure/azure_rm_networkinterface.py index a4e0ff83ba..13ac61a373 100644 --- a/lib/ansible/modules/cloud/azure/azure_rm_networkinterface.py +++ b/lib/ansible/modules/cloud/azure/azure_rm_networkinterface.py @@ -53,12 +53,11 @@ options: required: false virtual_network_name: description: - - Name of an existing virtual network with which the network interface will be associated. Required + - Name or id of an existing virtual network with which the network interface will be associated. Required when creating a network interface. aliases: - virtual_network required: true - default: null subnet_name: description: - Name of an existing subnet within the specified virtual network. Required when creating a network @@ -77,58 +76,90 @@ options: - Windows - Linux default: Linux - required: false private_ip_address: description: - - Valid IPv4 address that falls within the specified subnet. - required: false + - (Deprecate) Valid IPv4 address that falls within the specified subnet. + - This option will be deprecated in 2.9, use I(ip_configurations) instead. private_ip_allocation_method: description: - - "Specify whether or not the assigned IP address is permanent. NOTE: when creating a network interface + - "(Deprecate) Specify whether or not the assigned IP address is permanent. NOTE: when creating a network interface specifying a value of 'Static' requires that a private_ip_address value be provided. You can update the allocation method to 'Static' after a dynamic private ip address has been assigned." + - This option will be deprecated in 2.9, use I(ip_configurations) instead. default: Dynamic choices: - Dynamic - Static - required: false public_ip: description: - - When creating a network interface, if no public IP address name is provided a default public IP + - (Deprecate) When creating a network interface, if no public IP address name is provided a default public IP address will be created. Set to false, if you do not want a public IP address automatically created. + - This option will be deprecated in 2.9, use I(ip_configurations) instead. default: true - required: false public_ip_address_name: description: - - Name of an existing public IP address object to associate with the security group. + - (Deprecate) Name of an existing public IP address object to associate with the security group. + - This option will be deprecated in 2.9, use I(ip_configurations) instead. aliases: - public_ip_address - public_ip_name - required: false default: null public_ip_allocation_method: description: - - If a public_ip_address_name is not provided, a default public IP address will be created. The allocation + - (Deprecate) If a public_ip_address_name is not provided, a default public IP address will be created. The allocation method determines whether or not the public IP address assigned to the network interface is permanent. + - This option will be deprecated in 2.9, use I(ip_configurations) instead. choices: - Dynamic - Static default: Dynamic - required: false + ip_configurations: + description: + - List of ip configuration if contains mutilple configuration, should contain configuration object include + field private_ip_address, private_ip_allocation_method, public_ip_address_name, public_ip, subnet_name, + virtual_network_name, public_ip_allocation_method, name + suboptions: + name: + description: + - Name of the ip configuration. + required: true + private_ip_address: + description: + - Private ip address for the ip configuration. + private_ip_allocation_method: + description: + - private ip allocation method. + choices: + - Dynamic + - Static + default: Dynamic + public_ip_address_name: + description: + - Name of the public ip address. None for disable ip address. + public_ip_allocation_method: + description: + - public ip allocation method. + choices: + - Dynamic + - Static + default: Dynamic + primary: + description: + - Whether the ip configuration is the primary one in the list. + default: False + version_added: 2.5 security_group_name: description: - Name of an existing security group with which to associate the network interface. If not provided, a default security group will be created. aliases: - security_group - required: false default: null open_ports: description: - When a default security group is created for a Linux host a rule will be added allowing inbound TCP connections to the default SSH port 22, and for a Windows host rules will be added allowing inbound access to RDP ports 3389 and 5986. Override the default ports by providing a list of open ports. - required: false default: null extends_documentation_fragment: - azure @@ -146,6 +177,10 @@ EXAMPLES = ''' resource_group: Testing virtual_network_name: vnet001 subnet_name: subnet001 + ip_configurations: + name: ipconfig1 + public_ip_address_name: publicip001 + primary: True - name: Create a network interface with private IP address only (no Public IP) azure_rm_networkinterface: @@ -153,7 +188,9 @@ EXAMPLES = ''' resource_group: Testing virtual_network_name: vnet001 subnet_name: subnet001 - public_ip: no + ip_configurations: + name: ipconfig1 + primary: True - name: Create a network interface for use in a Windows host (opens RDP port) with custom RDP port azure_rm_networkinterface: @@ -163,6 +200,10 @@ EXAMPLES = ''' subnet_name: subnet001 os_type: Windows rdp_port: 3399 + ip_configurations: + name: ipconfig1 + public_ip_address_name: publicip001 + primary: True - name: Create a network interface using existing security group and public IP azure_rm_networkinterface: @@ -171,7 +212,23 @@ EXAMPLES = ''' virtual_network_name: vnet001 subnet_name: subnet001 security_group_name: secgroup001 - public_ip_address_name: publicip001 + ip_configurations: + name: ipconfig1 + public_ip_address_name: publicip001 + primary: True + + - name: Create a network with mutilple ip configurations + azure_rm_networkinterface: + name: nic004 + resource_group: Testing + subnet_name: subnet001 + virtual_network_name: vnet001 + security_group_name: secgroup001 + ip_configurations: + - name: ipconfig1 + public_ip_address_name: publicip001 + primary: True + - name: ipconfig2 - name: Delete network interface azure_rm_networkinterface: @@ -195,7 +252,7 @@ state: "enable_ip_forwarding": false, "etag": 'W/"be115a43-2148-4545-a324-f33ad444c926"', "id": "/subscriptions/XXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXX/resourceGroups/Testing/providers/Microsoft.Network/networkInterfaces/nic003", - "ip_configuration": { + "ip_configurations": [{ "name": "default", "private_ip_address": "10.1.0.10", "private_ip_allocation_method": "Static", @@ -204,7 +261,7 @@ state: "name": "publicip001" }, "subnet": {} - }, + }], "location": "eastus2", "mac_address": null, "name": "nic003", @@ -217,61 +274,84 @@ state: ''' try: + from msrestazure.tools import parse_resource_id from msrestazure.azure_exceptions import CloudError except ImportError: # This is handled in azure_rm_common pass from ansible.module_utils.azure_rm_common import AzureRMModuleBase, azure_id_to_dict +from ansible.module_utils._text import to_native + + +def subnet_to_dict(subnet): + dic = azure_id_to_dict(subnet.id) + return dict( + id=subnet.id, + virtual_network_name=dic.get('virtualNetworks'), + resource_group=dic.get('resourceGroups'), + name=dic.get('subnets') + ) def nic_to_dict(nic): - result = dict( + ip_configurations = [ + dict( + name=config.name, + private_ip_address=config.private_ip_address, + private_ip_allocation_method=config.private_ip_allocation_method, + subnet=subnet_to_dict(config.subnet), + primary=config.primary, + public_ip_address=dict( + id=config.public_ip_address.id, + name=azure_id_to_dict(config.public_ip_address.id).get('publicIPAddresses'), + public_ip_allocation_method=config.public_ip_address.public_ip_allocation_method + ) if config.public_ip_address else None + ) for config in nic.ip_configurations + ] + return dict( id=nic.id, name=nic.name, type=nic.type, location=nic.location, tags=nic.tags, - network_security_group=dict(), - ip_configuration=dict( - name=nic.ip_configurations[0].name, - private_ip_address=nic.ip_configurations[0].private_ip_address, - private_ip_allocation_method=nic.ip_configurations[0].private_ip_allocation_method, - subnet=dict(), - public_ip_address=dict(), - ), + network_security_group=dict( + id=nic.network_security_group.id, + name=azure_id_to_dict(nic.network_security_group.id).get('networkSecurityGroups') + ) if nic.network_security_group else None, dns_settings=dict( dns_servers=nic.dns_settings.dns_servers, applied_dns_servers=nic.dns_settings.applied_dns_servers, internal_dns_name_label=nic.dns_settings.internal_dns_name_label, internal_fqdn=nic.dns_settings.internal_fqdn ), + ip_configurations=ip_configurations, + ip_configuration=ip_configurations[0] if len(ip_configurations) == 1 else None, # for compatiable issue, keep this field mac_address=nic.mac_address, - primary=nic.primary, enable_ip_forwarding=nic.enable_ip_forwarding, provisioning_state=nic.provisioning_state, etag=nic.etag, ) - if nic.network_security_group: - result['network_security_group']['id'] = nic.network_security_group.id - id_keys = azure_id_to_dict(nic.network_security_group.id) - result['network_security_group']['name'] = id_keys['networkSecurityGroups'] - if nic.ip_configurations[0].subnet: - result['ip_configuration']['subnet']['id'] = \ - nic.ip_configurations[0].subnet.id - id_keys = azure_id_to_dict(nic.ip_configurations[0].subnet.id) - result['ip_configuration']['subnet']['virtual_network_name'] = id_keys['virtualNetworks'] - result['ip_configuration']['subnet']['name'] = id_keys['subnets'] +def construct_ip_configuration_set(raw): + configurations = [str(dict( + private_ip_allocation_method=to_native(item.get('private_ip_allocation_method')), + public_ip_address_name=(to_native(item.get('public_ip_address').get('name')) + if item.get('public_ip_address') else to_native(item.get('public_ip_address_name'))), + primary=item.get('primary'), + name=to_native(item.get('name')) + )) for item in raw] + return set(configurations) - if nic.ip_configurations[0].public_ip_address: - result['ip_configuration']['public_ip_address']['id'] = \ - nic.ip_configurations[0].public_ip_address.id - id_keys = azure_id_to_dict(nic.ip_configurations[0].public_ip_address.id) - result['ip_configuration']['public_ip_address']['name'] = id_keys['publicIPAddresses'] - - return result +ip_configuration_spec = dict( + name=dict(type='str', required=True), + private_ip_address=dict(type='str'), + private_ip_allocation_method=dict(type='str', choices=['Dynamic', 'Static'], default='Dynamic'), + public_ip_address_name=dict(type='str', aliases=['public_ip_address', 'public_ip_name']), + public_ip_allocation_method=dict(type='str', choices=['Dynamic', 'Static'], default='Dynamic'), + primary=dict(type='bool', default=False) +) class AzureRMNetworkInterface(AzureRMModuleBase): @@ -290,11 +370,16 @@ class AzureRMNetworkInterface(AzureRMModuleBase): public_ip=dict(type='bool', default=True), subnet_name=dict(type='str', aliases=['subnet']), virtual_network_name=dict(type='str', aliases=['virtual_network']), + public_ip_allocation_method=dict(type='str', choices=['Dynamic', 'Static'], default='Dynamic'), + ip_configurations=dict(type='list', default=None, elements='dict', options=ip_configuration_spec), os_type=dict(type='str', choices=['Windows', 'Linux'], default='Linux'), open_ports=dict(type='list'), - public_ip_allocation_method=dict(type='str', choices=['Dynamic', 'Static'], default='Dynamic'), ) + required_if = [ + ('state', 'present', ['subnet_name', 'virtual_network_name']) + ] + self.resource_group = None self.name = None self.location = None @@ -302,15 +387,16 @@ class AzureRMNetworkInterface(AzureRMModuleBase): self.private_ip_address = None self.private_ip_allocation_method = None self.public_ip_address_name = None - self.state = None + self.public_ip = None self.subnet_name = None - self.tags = None self.virtual_network_name = None + self.public_ip_allocation_method = None + self.state = None + self.tags = None self.security_group_name = None self.os_type = None self.open_ports = None - self.public_ip_allocation_method = None - self.public_ip = None + self.ip_configurations = None self.results = dict( changed=False, @@ -318,40 +404,42 @@ class AzureRMNetworkInterface(AzureRMModuleBase): ) super(AzureRMNetworkInterface, self).__init__(derived_arg_spec=self.module_arg_spec, - supports_check_mode=True) + supports_check_mode=True, + required_if=required_if) def exec_module(self, **kwargs): for key in list(self.module_arg_spec.keys()) + ['tags']: setattr(self, key, kwargs[key]) - results = dict() + results = None changed = False nic = None - subnet = None nsg = None - pip = None resource_group = self.get_resource_group(self.resource_group) if not self.location: # Set default location self.location = resource_group.location - if self.state == 'present': - if self.virtual_network_name and not self.subnet_name: - self.fail("Parameter error: a subnet is required when passing a virtual_network_name.") + # parse the virtual network resource group and name + virtual_network_dict = parse_resource_id(self.virtual_network_name) + virtual_network_name = virtual_network_dict.get('name') + virtual_network_resource_group = virtual_network_dict.get('resource_group', self.resource_group) - if self.subnet_name and not self.virtual_network_name: - self.fail("Parameter error: virtual_network_name is required when passing a subnet value.") - - if self.virtual_network_name and self.subnet_name: - subnet = self.get_subnet(self.virtual_network_name, self.subnet_name) - - if self.public_ip_address_name: - pip = self.get_public_ip_address(self.public_ip_address_name) - - if self.security_group_name: - nsg = self.get_security_group(self.security_group_name) + if self.state == 'present' and not self.ip_configurations: + # construct the ip_configurations array for compatiable + self.deprecate('Setting ip_configuration flatten is deprecated and will be removed.' + ' Using ip_configurations list to define the ip configuration', version='2.9') + self.ip_configurations = [ + dict( + private_ip_address=self.private_ip_address, + private_ip_allocation_method=self.private_ip_allocation_method, + public_ip_address_name=self.public_ip_address_name if self.public_ip else None, + public_ip_allocation_method=self.public_ip_allocation_method, + name='default' + ) + ] try: self.log('Fetching network interface {0}'.format(self.name)) @@ -362,47 +450,40 @@ class AzureRMNetworkInterface(AzureRMModuleBase): results = nic_to_dict(nic) self.log(results, pretty_print=True) + nsg = None if self.state == 'present': - + # check for update update_tags, results['tags'] = self.update_tags(results['tags']) if update_tags: changed = True - if self.private_ip_address: - if results['ip_configuration']['private_ip_address'] != self.private_ip_address: - self.log("CHANGED: network interface {0} private ip".format(self.name)) - changed = True - results['ip_configuration']['private_ip_address'] = self.private_ip_address - - if self.public_ip_address_name: - if results['ip_configuration']['public_ip_address'].get('id') != pip.id: - self.log("CHANGED: network interface {0} public ip".format(self.name)) - changed = True - results['ip_configuration']['public_ip_address']['id'] = pip.id - results['ip_configuration']['public_ip_address']['name'] = pip.name - if self.security_group_name: - if results['network_security_group'].get('id') != nsg.id: + nsg = self.get_security_group(self.security_group_name) + if nsg and results['network_security_group'].get('id') != nsg.id: self.log("CHANGED: network interface {0} network security group".format(self.name)) changed = True - results['network_security_group']['id'] = nsg.id - results['network_security_group']['name'] = nsg.name - if self.private_ip_allocation_method: - if results['ip_configuration']['private_ip_allocation_method'] != self.private_ip_allocation_method: - self.log("CHANGED: network interface {0} private ip allocation".format(self.name)) - changed = True - results['ip_configuration']['private_ip_allocation_method'] = self.private_ip_allocation_method - if self.private_ip_allocation_method == 'Dynamic': - results['ip_configuration']['private_ip_address'] = None + if results['ip_configurations'][0]['subnet']['virtual_network_name'] != virtual_network_name: + self.log("CHANGED: network interface {0} virtual network name".format(self.name)) + changed = True - if self.subnet_name: - if results['ip_configuration']['subnet'].get('id') != subnet.id: - changed = True - self.log("CHANGED: network interface {0} subnet".format(self.name)) - results['ip_configuration']['subnet']['id'] = subnet.id - results['ip_configuration']['subnet']['name'] = subnet.name - results['ip_configuration']['subnet']['virtual_network_name'] = self.virtual_network_name + if results['ip_configurations'][0]['subnet']['resource_group'] != virtual_network_resource_group: + self.log("CHANGED: network interface {0} virtual network resource group".format(self.name)) + changed = True + + if results['ip_configurations'][0]['subnet']['name'] != self.subnet_name: + self.log("CHANGED: network interface {0} subnet name".format(self.name)) + changed = True + + # check the ip_configuration is changed + # construct two set with the same structure and then compare + # the list should contains: + # name, private_ip_address, public_ip_address_name, private_ip_allocation_method, subnet_name + ip_configuration_result = construct_ip_configuration_set(results['ip_configurations']) + ip_configuration_request = construct_ip_configuration_set(self.ip_configurations) + if ip_configuration_result != ip_configuration_request: + self.log("CHANGED: network interface {0} ip configurations".format(self.name)) + changed = True elif self.state == 'absent': self.log("CHANGED: network interface {0} exists but requested state is 'absent'".format(self.name)) @@ -410,8 +491,7 @@ class AzureRMNetworkInterface(AzureRMModuleBase): except CloudError: self.log('Network interface {0} does not exist'.format(self.name)) if self.state == 'present': - self.log("CHANGED: network interface {0} does not exist but requested state is " - "'present'".format(self.name)) + self.log("CHANGED: network interface {0} does not exist but requested state is 'present'".format(self.name)) changed = True self.results['changed'] = changed @@ -422,141 +502,89 @@ class AzureRMNetworkInterface(AzureRMModuleBase): if changed: if self.state == 'present': - if not nic: - # create network interface - self.log("Creating network interface {0}.".format(self.name)) - - # check required parameters - if not self.subnet_name: - self.fail("parameter error: subnet_name required when creating a network interface.") - if not self.virtual_network_name: - self.fail("parameter error: virtual_network_name required when creating a network interface.") - - if not self.security_group_name: - # create default security group - nsg = self.create_default_securitygroup(self.resource_group, self.location, self.name, - self.os_type, self.open_ports) - - if not pip and self.public_ip: - # create a default public_ip - pip = self.create_default_pip(self.resource_group, self.location, self.name, - self.public_ip_allocation_method) - - nic = self.network_models.NetworkInterface( - location=self.location, - tags=self.tags, - ip_configurations=[ - self.network_models.NetworkInterfaceIPConfiguration( - private_ip_allocation_method=self.private_ip_allocation_method, - ) - ] - ) - # nic.name = self.name - nic.ip_configurations[0].subnet = self.network_models.Subnet(id=subnet.id) - nic.ip_configurations[0].name = 'default' - nic.network_security_group = self.network_models.NetworkSecurityGroup(id=nsg.id, - location=nsg.location, - resource_guid=nsg.resource_guid) - if self.private_ip_address: - nic.ip_configurations[0].private_ip_address = self.private_ip_address - - if pip: - nic.ip_configurations[0].public_ip_address = self.network_models.PublicIPAddress( - id=pip.id, - location=pip.location, - resource_guid=pip.resource_guid) - else: - self.log("Updating network interface {0}.".format(self.name)) - nic = self.network_models.NetworkInterface( - id=results['id'], - location=results['location'], - tags=results['tags'], - ip_configurations=[ - self.network_models.NetworkInterfaceIPConfiguration( - private_ip_allocation_method=results['ip_configuration']['private_ip_allocation_method'] - ) - ] - ) - subnet = self.get_subnet(results['ip_configuration']['subnet']['virtual_network_name'], - results['ip_configuration']['subnet']['name']) - nic.ip_configurations[0].subnet = self.network_models.Subnet(id=subnet.id) - nic.ip_configurations[0].name = results['ip_configuration']['name'] - # nic.name = name=results['name'], - - if results['ip_configuration'].get('private_ip_address'): - nic.ip_configurations[0].private_ip_address = results['ip_configuration']['private_ip_address'] - - if results['ip_configuration']['public_ip_address'].get('id'): - pip = \ - self.get_public_ip_address(results['ip_configuration']['public_ip_address']['name']) - nic.ip_configurations[0].public_ip_address = self.network_models.PublicIPAddress( - id=pip.id, - location=pip.location, - resource_guid=pip.resource_guid) - # name=pip.name, - - if results['network_security_group'].get('id'): - nsg = self.get_security_group(results['network_security_group']['name']) - nic.network_security_group = self.network_models.NetworkSecurityGroup(id=nsg.id, - location=nsg.location, - resource_guid=nsg.resource_guid) - - # See what actually gets sent to the API - request = self.serialize_obj(nic, 'NetworkInterface') - self.log(request, pretty_print=True) + subnet = self.get_subnet(virtual_network_resource_group, virtual_network_name, self.subnet_name) + if not subnet: + self.fail('subnet {0} is not exist'.format(self.subnet_name)) + nic_ip_configurations = [ + self.network_models.NetworkInterfaceIPConfiguration( + private_ip_allocation_method=ip_config.get('private_ip_allocation_method'), + private_ip_address=ip_config.get('private_ip_address'), + name=ip_config.get('name'), + subnet=subnet, + public_ip_address=self.get_or_create_public_ip_address(ip_config), + primary=ip_config.get('primary') + ) for ip_config in self.ip_configurations + ] + nsg = nsg or self.create_default_securitygroup(self.resource_group, self.location, self.name, self.os_type, self.open_ports) + self.log('Creating or updating network interface {0}'.format(self.name)) + nic = self.network_models.NetworkInterface( + id=results['id'] if results else None, + location=self.location, + tags=self.tags, + ip_configurations=nic_ip_configurations, + network_security_group=nsg + ) self.results['state'] = self.create_or_update_nic(nic) - elif self.state == 'absent': self.log('Deleting network interface {0}'.format(self.name)) self.delete_nic() + # Delete doesn't return anything. If we get this far, assume success + self.results['state']['status'] = 'Deleted' return self.results + def get_or_create_public_ip_address(self, ip_config): + name = ip_config.get('public_ip_address_name') + pip = self.get_public_ip_address(name) + if not pip: + params = self.network_models.PublicIPAddress( + location=self.location, + public_ip_allocation_method=ip_config.get('public_ip_allocation_method'), + ) + try: + poller = self.network_client.public_ip_addresses.create_or_update(self.resource_group, name, params) + pip = self.get_poller_result(poller) + except CloudError as exc: + self.fail("Error creating {0} - {1}".format(name, str(exc))) + return pip + def create_or_update_nic(self, nic): try: poller = self.network_client.network_interfaces.create_or_update(self.resource_group, self.name, nic) new_nic = self.get_poller_result(poller) + return nic_to_dict(new_nic) except Exception as exc: self.fail("Error creating or updating network interface {0} - {1}".format(self.name, str(exc))) - return nic_to_dict(new_nic) - def delete_nic(self): try: poller = self.network_client.network_interfaces.delete(self.resource_group, self.name) self.get_poller_result(poller) except Exception as exc: self.fail("Error deleting network interface {0} - {1}".format(self.name, str(exc))) - # Delete doesn't return anything. If we get this far, assume success - self.results['state']['status'] = 'Deleted' return True def get_public_ip_address(self, name): self.log("Fetching public ip address {0}".format(name)) try: - public_ip = self.network_client.public_ip_addresses.get(self.resource_group, name) + return self.network_client.public_ip_addresses.get(self.resource_group, name) except Exception as exc: - self.fail("Error: fetching public ip address {0} - {1}".format(self.name, str(exc))) - return public_ip + return None - def get_subnet(self, vnet_name, subnet_name): + def get_subnet(self, resource_group, vnet_name, subnet_name): self.log("Fetching subnet {0} in virtual network {1}".format(subnet_name, vnet_name)) try: - subnet = self.network_client.subnets.get(self.resource_group, vnet_name, subnet_name) + return self.network_client.subnets.get(resource_group, vnet_name, subnet_name) except Exception as exc: - self.fail("Error: fetching subnet {0} in virtual network {1} - {2}".format(subnet_name, - vnet_name, - str(exc))) - return subnet + return None def get_security_group(self, name): self.log("Fetching security group {0}".format(name)) try: - nsg = self.network_client.network_security_groups.get(self.resource_group, name) + return self.network_client.network_security_groups.get(self.resource_group, name) except Exception as exc: - self.fail("Error: fetching network security group {0} - {1}.".format(name, str(exc))) - return nsg + return None def main(): diff --git a/lib/ansible/modules/cloud/azure/azure_rm_virtualmachine.py b/lib/ansible/modules/cloud/azure/azure_rm_virtualmachine.py index 4997c6954a..4fa82eb98f 100644 --- a/lib/ansible/modules/cloud/azure/azure_rm_virtualmachine.py +++ b/lib/ansible/modules/cloud/azure/azure_rm_virtualmachine.py @@ -1648,10 +1648,10 @@ class AzureRMVirtualMachine(AzureRMModuleBase): self.fail(no_subnets_msg) self.results['actions'].append('Created default public IP {0}'.format(self.name + '01')) - pip = self.create_default_pip(self.resource_group, self.location, self.name, self.public_ip_allocation_method) + pip = self.create_default_pip(self.resource_group, self.location, self.name + '01', self.public_ip_allocation_method) self.results['actions'].append('Created default security group {0}'.format(self.name + '01')) - group = self.create_default_securitygroup(self.resource_group, self.location, self.name, self.os_type, + group = self.create_default_securitygroup(self.resource_group, self.location, self.name + '01', self.os_type, self.open_ports) parameters = self.network_models.NetworkInterface( diff --git a/test/integration/targets/azure_rm_networkinterface/aliases b/test/integration/targets/azure_rm_networkinterface/aliases new file mode 100644 index 0000000000..9e23ddb721 --- /dev/null +++ b/test/integration/targets/azure_rm_networkinterface/aliases @@ -0,0 +1,3 @@ +cloud/azure +posix/ci/cloud/group2/azure +destructive diff --git a/test/integration/targets/azure_rm_networkinterface/meta/main.yml b/test/integration/targets/azure_rm_networkinterface/meta/main.yml new file mode 100644 index 0000000000..95e1952f98 --- /dev/null +++ b/test/integration/targets/azure_rm_networkinterface/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - setup_azure diff --git a/test/integration/targets/azure_rm_networkinterface/tasks/main.yml b/test/integration/targets/azure_rm_networkinterface/tasks/main.yml new file mode 100644 index 0000000000..878ede7987 --- /dev/null +++ b/test/integration/targets/azure_rm_networkinterface/tasks/main.yml @@ -0,0 +1,169 @@ +- name: Create virtual network + azure_rm_virtualnetwork: + resource_group: "{{ resource_group_secondary }}" + name: testnic001 + address_prefixes: "10.10.0.0/16" + register: vn + +- name: Add subnet + azure_rm_subnet: + resource_group: "{{ resource_group_secondary }}" + name: testnic001 + address_prefix: "10.10.0.0/24" + virtual_network: testnic001 + +- name: Create NIC (check mode) + azure_rm_networkinterface: + resource_group: "{{ resource_group }}" + name: testnic001 + virtual_network: "{{ vn.state.id }}" + subnet: testnic001 + public_ip_name: testnic001 + public_ip_allocation_method: Static + security_group: testnic001 + register: output + check_mode: yes + +- assert: + that: + - output.changed + +- name: Create NIC + azure_rm_networkinterface: + resource_group: "{{ resource_group }}" + name: testnic001 + virtual_network: "{{ vn.state.id }}" + subnet: testnic001 + public_ip_name: testnic001 + public_ip_allocation_method: Static + security_group: testnic001 + register: output + +- assert: + that: + - output.changed + - output.state.ip_configuration.subnet.name == 'testnic001' + +- name: Update the NIC with mutilple ip configurations (check mode) + azure_rm_networkinterface: + resource_group: "{{ resource_group }}" + name: testnic001 + security_group: testnic001 + virtual_network: "{{ vn.state.id }}" + subnet: testnic001 + ip_configurations: + - name: ipconfig-add + public_ip_name: testnic002 + - name: default + public_ip_name: testnic001 + primary: True + public_ip_allocation_method: Static + - name: ipconfig1 + public_ip_name: testnic003 + register: output + check_mode: yes + +- assert: + that: + - output.changed + +- name: Update the NIC with mutilple ip configurations + azure_rm_networkinterface: + resource_group: "{{ resource_group }}" + name: testnic001 + security_group: testnic001 + virtual_network: "{{ vn.state.id }}" + subnet: testnic001 + ip_configurations: + - name: ipconfig-add + public_ip_name: testnic002 + - name: default + public_ip_name: testnic001 + primary: True + public_ip_allocation_method: Static + - name: ipconfig1 + public_ip_name: testnic003 + register: output + +- assert: + that: + - output.changed + - not output.state.ip_configuration + - output.state.ip_configurations | length == 3 + +- name: Update the NIC with mutilple ip configurations (idempotent) + azure_rm_networkinterface: + resource_group: "{{ resource_group }}" + name: testnic001 + security_group: testnic001 + virtual_network: "{{ vn.state.id }}" + subnet: testnic001 + ip_configurations: + - name: ipconfig-add + public_ip_name: testnic002 + - name: default + public_ip_name: testnic001 + primary: True + public_ip_allocation_method: Static + - name: ipconfig1 + public_ip_name: testnic003 + register: output + +- assert: + that: + - not output.changed + +- name: Remove one ip configuration + azure_rm_networkinterface: + resource_group: "{{ resource_group }}" + name: testnic001 + security_group: testnic001 + virtual_network: "{{ vn.state.id }}" + subnet: testnic001 + ip_configurations: + - name: ipconfig-add + public_ip_name: testnic002 + - name: default + public_ip_name: testnic001 + public_ip_allocation_method: Static + primary: True + register: output + +- assert: + that: + - not output.state.ip_configuration + - output.state.ip_configurations | length == 2 + +- name: Delete the NIC (check mode) + azure_rm_networkinterface: + resource_group: "{{ resource_group }}" + name: testnic001 + state: absent + check_mode: yes + register: output + +- assert: + that: + - output.changed + +- name: Delete the NIC + azure_rm_networkinterface: + resource_group: "{{ resource_group }}" + name: testnic001 + state: absent + register: output + +- assert: + that: + - output.changed + +- name: Delete the NIC (idempotent) + azure_rm_networkinterface: + resource_group: "{{ resource_group }}" + name: testnic001 + state: absent + register: output + +- assert: + that: + - not output.changed \ No newline at end of file