From c95724fb0a45352f3ab891fcfd82b9435eddfec7 Mon Sep 17 00:00:00 2001 From: Yuwei Zhou Date: Thu, 24 May 2018 12:32:03 +0800 Subject: [PATCH] Fixes some NIC problems: create without nsg, support loadbalancer (#38643) * add loadbalancer * dict check nullable * add default vallue when get list * create backend addr pool * fix the set * fix to dict * fix ideponement * use param security group name when create * nic can has no nsg * add test * fix * fix * fix * add document * add configuration * fix * fix * remove all resources * fix * fix test * add version added * fix lint * fix lint * Fixes some NIC bugs (#39213) * add loadbalancer * dict check nullable * add default vallue when get list * create backend addr pool * fix the set * fix to dict * fix ideponement * use param security group name when create * nic can has no nsg * add test * fix * fix * fix * fix idemponet * add document * fix test * add configuration * fix * fix * remove all resources * fix * fix test * add version added * fix lint * fix lint * fix lint * remove new feature and only submit bugfix * remove useless test * fix * fix indent * Update azure_rm_networkinterface.py * fix comment * support 3 types to specific name and resource group * avoid test racing * fix test * add sample * add resource id test --- lib/ansible/module_utils/azure_rm_common.py | 13 +- .../cloud/azure/azure_rm_networkinterface.py | 154 ++++++++++++------ .../azure_rm_networkinterface/tasks/main.yml | 89 ++++++++-- 3 files changed, 186 insertions(+), 70 deletions(-) diff --git a/lib/ansible/module_utils/azure_rm_common.py b/lib/ansible/module_utils/azure_rm_common.py index 6e51b480399..012f6f7e066 100644 --- a/lib/ansible/module_utils/azure_rm_common.py +++ b/lib/ansible/module_utils/azure_rm_common.py @@ -132,7 +132,7 @@ try: from msrestazure.azure_active_directory import AADTokenCredentials from msrestazure.azure_exceptions import CloudError from msrestazure.azure_active_directory import MSIAuthentication - from msrestazure.tools import resource_id, is_valid_resource_id + from msrestazure.tools import parse_resource_id, resource_id, is_valid_resource_id from msrestazure import azure_cloud from azure.common.credentials import ServicePrincipalCredentials, UserPassCredentials from azure.mgmt.network.version import VERSION as network_client_version @@ -651,6 +651,17 @@ class AzureRMModuleBase(object): return None + def parse_resource_to_dict(self, resource): + ''' + Return a dict of the give resource, which contains name and resource group. + + :param resource: It can be a resource name, id or a dict contains name and resource group. + ''' + resource_dict = parse_resource_id(resource) if not isinstance(resource, dict) else resource + resource_dict['resource_group'] = resource_dict.get('resource_group', self.resource_group) + resource_dict['subscription_id'] = resource_dict.get('subscription_id', self.subscription_id) + return resource_dict + def serialize_obj(self, obj, class_name, enum_modules=None): ''' Return a JSON representation of an Azure object. diff --git a/lib/ansible/modules/cloud/azure/azure_rm_networkinterface.py b/lib/ansible/modules/cloud/azure/azure_rm_networkinterface.py index 042f9421e2c..d8eb5b91446 100644 --- a/lib/ansible/modules/cloud/azure/azure_rm_networkinterface.py +++ b/lib/ansible/modules/cloud/azure/azure_rm_networkinterface.py @@ -51,25 +51,22 @@ options: description: - Valid azure location. Defaults to location of the resource group. required: false - virtual_network_resource_group: + virtual_network: description: - - The resource group of I(virtual_network_name). - - If not set then this is the same resource group as I(resource_group). - - This can be used to specify the resource group of a virtual network that is in another resource group - than the network interface. - - If I(virtual_network_name) is specified as a virtual network id, this parameter is ignored. - version_added: 2.6 - virtual_network_name: - description: - - Name or id of an existing virtual network with which the network interface will be associated. Required + - An existing virtual network with which the network interface will be associated. Required when creating a network interface. + - It can be the virtual network's name. + - Make sure your virtual network is in the same resource group as NIC when you give only the name. + - It can be the virtual network's resource id. + - It can be a dict which contains C(name) and C(resource_group) of the virtual network. aliases: - - virtual_network + - virtual_network_name required: true subnet_name: description: - Name of an existing subnet within the specified virtual network. Required when creating a network interface + - Use the C(virtual_network)'s resource group. aliases: - subnet required: true @@ -123,8 +120,7 @@ options: 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 + field private_ip_address, private_ip_allocation_method, public_ip_address_name, public_ip, public_ip_allocation_method, name suboptions: name: description: @@ -150,18 +146,34 @@ options: - Dynamic - Static default: Dynamic + load_balancer_backend_address_pools: + description: + - List of an existing load-balancer backend address pool id to associate with the network interface. + - It can be write as a resource id. + - Also can be a dict of I(name) and I(load_balancer). + version_added: 2.6 primary: description: - Whether the ip configuration is the primary one in the list. type: bool default: 'no' version_added: 2.5 - security_group_name: + create_with_security_group: + description: + - Specifies whether a default security group should be be created with the NIC. Only applies when creating a new NIC. + type: bool + version_added: 2.6 + default: True + security_group: description: - - Name of an existing security group with which to associate the network interface. If not provided, a - default security group will be created. + - An existing security group with which to associate the network interface. If not provided, a + default security group will be created when C(create_with_security_group) is true. + - It can be the name of security group. + - Make sure the security group is in the same resource group when you only give its name. + - It can be the resource id. + - It can be a dict contains security_group's C(name) and C(resource_group). aliases: - - security_group + - security_group_name open_ports: description: - When a default security group is created for a Linux host a rule will be added allowing inbound TCP @@ -182,7 +194,7 @@ EXAMPLES = ''' azure_rm_networkinterface: name: nic001 resource_group: Testing - virtual_network_name: vnet001 + virtual_network: vnet001 subnet_name: subnet001 ip_configurations: - name: ipconfig1 @@ -193,8 +205,9 @@ EXAMPLES = ''' azure_rm_networkinterface: name: nic001 resource_group: Testing - virtual_network_name: vnet001 + virtual_network: vnet001 subnet_name: subnet001 + create_with_security_group: False ip_configurations: - name: ipconfig1 primary: True @@ -203,10 +216,11 @@ EXAMPLES = ''' azure_rm_networkinterface: name: nic002 resource_group: Testing - virtual_network_name: vnet001 + virtual_network: vnet001 subnet_name: subnet001 os_type: Windows rdp_port: 3399 + security_group: "/subscriptions/XXXXXXX/resourceGroups/Testing/providers/Microsoft.Network/networkSecurityGroups/nsg001" ip_configurations: - name: ipconfig1 public_ip_address_name: publicip001 @@ -216,9 +230,9 @@ EXAMPLES = ''' azure_rm_networkinterface: name: nic003 resource_group: Testing - virtual_network_name: vnet001 + virtual_network: vnet001 subnet_name: subnet001 - security_group_name: secgroup001 + security_group: secgroup001 ip_configurations: - name: ipconfig1 public_ip_address_name: publicip001 @@ -229,13 +243,19 @@ EXAMPLES = ''' name: nic004 resource_group: Testing subnet_name: subnet001 - virtual_network_name: vnet001 - security_group_name: secgroup001 + virtual_network: vnet001 + security_group: + name: testnic002 + resource_group: Testing1 ip_configurations: - name: ipconfig1 public_ip_address_name: publicip001 primary: True - name: ipconfig2 + load_balancer_backend_address_pools: + - "{{ loadbalancer001.state.backend_address_pools[0].id }}" + - name: backendaddrpool1 + load_balancer: loadbalancer001 - name: Delete network interface azure_rm_networkinterface: @@ -273,7 +293,10 @@ state: "location": "eastus2", "mac_address": null, "name": "nic003", - "network_security_group": {}, + "network_security_group": { + "id": "/subscriptions//XXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXX/resourceGroups/Testing/providers/Microsoft.Network/networkSecurityGroups/nsg001", + "name": "nsg001" + }, "primary": null, "provisioning_state": "Succeeded", "tags": null, @@ -282,7 +305,7 @@ state: ''' try: - from msrestazure.tools import parse_resource_id + from msrestazure.tools import parse_resource_id, resource_id from msrestazure.azure_exceptions import CloudError except ImportError: # This is handled in azure_rm_common @@ -310,6 +333,8 @@ def nic_to_dict(nic): private_ip_allocation_method=config.private_ip_allocation_method, subnet=subnet_to_dict(config.subnet), primary=config.primary, + load_balancer_backend_address_pools=([item.id for item in config.load_balancer_backend_address_pools] + if config.load_balancer_backend_address_pools else None), public_ip_address=dict( id=config.public_ip_address.id, name=azure_id_to_dict(config.public_ip_address.id).get('publicIPAddresses'), @@ -348,6 +373,7 @@ ip_configuration_spec = dict( 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'), + load_balancer_backend_address_pools=dict(type='list'), primary=dict(type='bool', default=False) ) @@ -360,15 +386,15 @@ class AzureRMNetworkInterface(AzureRMModuleBase): resource_group=dict(type='str', required=True), name=dict(type='str', required=True), location=dict(type='str'), - security_group_name=dict(type='str', aliases=['security_group']), + create_with_security_group=dict(type='bool', default=True), + security_group=dict(type='raw', aliases=['security_group_name']), state=dict(default='present', choices=['present', 'absent']), 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=dict(type='bool', default=True), subnet_name=dict(type='str', aliases=['subnet']), - virtual_network_resource_group=dict(type='str'), - virtual_network_name=dict(type='str', aliases=['virtual_network']), + virtual_network=dict(type='raw', aliases=['virtual_network_name']), 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'), @@ -376,24 +402,23 @@ class AzureRMNetworkInterface(AzureRMModuleBase): ) required_if = [ - ('state', 'present', ['subnet_name', 'virtual_network_name']) + ('state', 'present', ['subnet_name', 'virtual_network']) ] self.resource_group = None self.name = None self.location = None - self.security_group_name = None + self.create_with_security_group = None + self.security_group = None self.private_ip_address = None self.private_ip_allocation_method = None self.public_ip_address_name = None self.public_ip = None self.subnet_name = None - self.virtual_network_resource_group = None - self.virtual_network_name = None + self.virtual_network = 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.ip_configurations = None @@ -423,15 +448,10 @@ class AzureRMNetworkInterface(AzureRMModuleBase): self.location = resource_group.location # 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.virtual_network_resource_group) - - if virtual_network_resource_group is None: - virtual_network_resource_group = self.resource_group + self.virtual_network = self.parse_resource_to_dict(self.virtual_network) # if not set the security group name, use nic name for default - self.security_group_name = self.security_group_name or self.name + self.security_group = self.parse_resource_to_dict(self.security_group or self.name) if self.state == 'present' and not self.ip_configurations: # construct the ip_configurations array for compatiable @@ -464,16 +484,21 @@ class AzureRMNetworkInterface(AzureRMModuleBase): if update_tags: changed = True - nsg = self.get_security_group(self.security_group_name) - if nsg and results.get('network_security_group') and results['network_security_group'].get('id') != nsg.id: - self.log("CHANGED: network interface {0} network security group".format(self.name)) + if self.create_with_security_group != bool(results.get('network_security_group')): + self.log("CHANGED: add or remove network interface {0} network security group".format(self.name)) changed = True - if results['ip_configurations'][0]['subnet']['virtual_network_name'] != virtual_network_name: + if not changed: + nsg = self.get_security_group(self.security_group['resource_group'], self.security_group['name']) + if nsg and results.get('network_security_group') and results['network_security_group'].get('id') != nsg.id: + self.log("CHANGED: network interface {0} network security group".format(self.name)) + changed = True + + if results['ip_configurations'][0]['subnet']['virtual_network_name'] != self.virtual_network['name']: self.log("CHANGED: network interface {0} virtual network name".format(self.name)) changed = True - if results['ip_configurations'][0]['subnet']['resource_group'] != virtual_network_resource_group: + if results['ip_configurations'][0]['subnet']['resource_group'] != self.virtual_network['resource_group']: self.log("CHANGED: network interface {0} virtual network resource group".format(self.name)) changed = True @@ -510,9 +535,9 @@ class AzureRMNetworkInterface(AzureRMModuleBase): if self.state == 'present': subnet = self.network_models.SubResource( '/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Network/virtualNetworks/{2}/subnets/{3}'.format( - self.subscription_id, - virtual_network_resource_group, - virtual_network_name, + self.virtual_network['subscription_id'], + self.virtual_network['resource_group'], + self.virtual_network['name'], self.subnet_name)) nic_ip_configurations = [ @@ -522,15 +547,19 @@ class AzureRMNetworkInterface(AzureRMModuleBase): name=ip_config.get('name'), subnet=subnet, public_ip_address=self.get_or_create_public_ip_address(ip_config), + load_balancer_backend_address_pools=([self.network_models.BackendAddressPool(id=self.backend_addr_pool_id(bap_id)) + for bap_id in ip_config.get('load_balancer_backend_address_pools')] + if ip_config.get('load_balancer_backend_address_pools') else None), primary=ip_config.get('primary') ) for ip_config in self.ip_configurations ] - nsg = self.create_default_securitygroup(self.resource_group, + nsg = self.create_default_securitygroup(self.security_group['resource_group'], self.location, - self.security_group_name, + self.security_group['name'], self.os_type, - self.open_ports) + self.open_ports) if self.create_with_security_group else None + self.log('Creating or updating network interface {0}'.format(self.name)) nic = self.network_models.NetworkInterface( id=results['id'] if results else None, @@ -590,19 +619,36 @@ class AzureRMNetworkInterface(AzureRMModuleBase): except Exception as exc: return None - def get_security_group(self, name): + def get_security_group(self, resource_group, name): self.log("Fetching security group {0}".format(name)) try: - return self.network_client.network_security_groups.get(self.resource_group, name) + return self.network_client.network_security_groups.get(resource_group, name) except Exception as exc: return None + def backend_addr_pool_id(self, val): + if isinstance(val, dict): + lb = val.get('load_balancer', None) + name = val.get('name', None) + if lb and name: + return resource_id(subscription=self.subscription_id, + resource_group=self.resource_group, + namespace='Microsoft.Network', + type='loadBalancers', + name=lb, + child_type_1='backendAddressPools', + child_name_1=name) + return val + def construct_ip_configuration_set(self, 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'), + load_balancer_backend_address_pools=(set([to_native(self.backend_addr_pool_id(id)) + for id in item.get('load_balancer_backend_address_pools')]) + if item.get('load_balancer_backend_address_pools') else None), name=to_native(item.get('name')) )) for item in raw] return set(configurations) diff --git a/test/integration/targets/azure_rm_networkinterface/tasks/main.yml b/test/integration/targets/azure_rm_networkinterface/tasks/main.yml index ef96eea4f1e..82b81b69296 100644 --- a/test/integration/targets/azure_rm_networkinterface/tasks/main.yml +++ b/test/integration/targets/azure_rm_networkinterface/tasks/main.yml @@ -17,6 +17,40 @@ name: ansiblepip3 resource_group: '{{ resource_group }}' +- name: create load balancer with multiple parameters + azure_rm_loadbalancer: + resource_group: '{{ resource_group }}' + name: lbtestfromansible + frontend_ip_configurations: + - name: frontendipconf0 + public_ip_address: ansiblepip3 + backend_address_pools: + - name: backendaddrpool0 + - name: backendaddrpool1 + probes: + - name: prob0 + port: 80 + inbound_nat_pools: + - name: inboundnatpool0 + frontend_ip_configuration_name: frontendipconf0 + protocol: Tcp + frontend_port_range_start: 80 + frontend_port_range_end: 81 + backend_port: 8080 + load_balancing_rules: + - name: lbrbalancingrule0 + frontend_ip_configuration: frontendipconf0 + backend_address_pool: backendaddrpool0 + frontend_port: 80 + backend_port: 80 + probe: prob0 + register: lb + +- name: create public ip + azure_rm_publicipaddress: + name: ansiblepip3 + resource_group: '{{ resource_group }}' + - name: Create NIC (check mode) azure_rm_networkinterface: resource_group: "{{ resource_group }}" @@ -33,26 +67,28 @@ that: - output.changed -- name: Create NIC using virtual_network_resource_group parameter +- name: Create NIC using virtual_network resource_group parameter azure_rm_networkinterface: resource_group: "{{ resource_group }}" name: testnic001rg - virtual_network: testnic001 - virtual_network_resource_group: "{{ resource_group_secondary }}" + virtual_network: + name: testnic001 + resource_group: "{{ resource_group_secondary }}" subnet: testnic001 - public_ip_name: testnic001 + public_ip_name: testnic001rg public_ip_allocation_method: Static security_group: testnic001 register: output -- name: Create NIC using virtual_network_resource_group parameter (idempotent) +- name: Create NIC using virtual_network resource_group parameter (idempotent) azure_rm_networkinterface: resource_group: "{{ resource_group }}" name: testnic001rg - virtual_network: testnic001 - virtual_network_resource_group: "{{ resource_group_secondary }}" + virtual_network: + name: testnic001 + resource_group: "{{ resource_group_secondary }}" subnet: testnic001 - public_ip_name: testnic001 + public_ip_name: testnic001rg public_ip_allocation_method: Static security_group: testnic001 register: output @@ -66,7 +102,7 @@ resource_group: "{{ resource_group }}" name: testnic001rg state: absent - + - name: Create NIC azure_rm_networkinterface: resource_group: "{{ resource_group }}" @@ -75,7 +111,9 @@ subnet: testnic001 public_ip_name: testnic001 public_ip_allocation_method: Static - security_group: testnic001 + security_group: + name: testnic002 + resource_group: "{{ resource_group_secondary }}" register: output - assert: @@ -87,7 +125,9 @@ azure_rm_networkinterface: resource_group: "{{ resource_group }}" name: testnic001 - security_group: testnic001 + security_group: + name: testnic002 + resource_group: "{{ resource_group_secondary }}" virtual_network: "{{ vn.state.id }}" subnet: testnic001 ip_configurations: @@ -110,7 +150,9 @@ azure_rm_networkinterface: resource_group: "{{ resource_group }}" name: testnic001 - security_group: testnic001 + security_group: + name: testnic002 + resource_group: "{{ resource_group_secondary }}" virtual_network: "{{ vn.state.id }}" subnet: testnic001 ip_configurations: @@ -122,6 +164,10 @@ public_ip_allocation_method: Static - name: ipconfig1 public_ip_name: testnic003 + load_balancer_backend_address_pools: + - "{{ lb.state.backend_address_pools[0].id }}" + - name: backendaddrpool1 + load_balancer: lbtestfromansible register: output - assert: @@ -129,12 +175,13 @@ - output.changed - not output.state.ip_configuration - output.state.ip_configurations | length == 3 + - output.state.network_security_group.name == 'testnic002' - name: Update the NIC with mutilple ip configurations (idempotent) azure_rm_networkinterface: resource_group: "{{ resource_group }}" name: testnic001 - security_group: testnic001 + security_group: "{{ output.state.network_security_group.id }}" virtual_network: "{{ vn.state.id }}" subnet: testnic001 ip_configurations: @@ -146,6 +193,10 @@ public_ip_allocation_method: Static - name: ipconfig1 public_ip_name: testnic003 + load_balancer_backend_address_pools: + - "{{ lb.state.backend_address_pools[0].id }}" + - name: backendaddrpool1 + load_balancer: lbtestfromansible register: output - assert: @@ -156,12 +207,18 @@ azure_rm_networkinterface: resource_group: "{{ resource_group }}" name: testnic001 - security_group: testnic001 + security_group: + name: testnic002 + resource_group: "{{ resource_group_secondary }}" virtual_network: "{{ vn.state.id }}" subnet: testnic001 ip_configurations: - name: ipconfig1 public_ip_name: testnic003 + load_balancer_backend_address_pools: + - "{{ lb.state.backend_address_pools[0].id }}" + - name: backendaddrpool1 + load_balancer: lbtestfromansible - name: default public_ip_name: testnic001 public_ip_allocation_method: Static @@ -173,10 +230,11 @@ - not output.state.ip_configuration - output.state.ip_configurations | length == 2 -- name: IP configuration without public IP +- name: IP configuration without public IP and NSG azure_rm_networkinterface: resource_group: "{{ resource_group }}" name: testnic001noip + create_with_security_group: False virtual_network: "{{ vn.state.id }}" subnet: testnic001 ip_configurations: @@ -187,6 +245,7 @@ - assert: that: - output.state.ip_configurations[0].public_ip_address == None + - output.state.network_security_group == None - name: Delete the NIC (check mode) azure_rm_networkinterface: