diff options
Diffstat (limited to 'ansible_collections/azure/azcollection/plugins/inventory')
-rw-r--r-- | ansible_collections/azure/azcollection/plugins/inventory/azure_rm.py | 99 |
1 files changed, 77 insertions, 22 deletions
diff --git a/ansible_collections/azure/azcollection/plugins/inventory/azure_rm.py b/ansible_collections/azure/azcollection/plugins/inventory/azure_rm.py index 3442aa124..12970dec3 100644 --- a/ansible_collections/azure/azcollection/plugins/inventory/azure_rm.py +++ b/ansible_collections/azure/azcollection/plugins/inventory/azure_rm.py @@ -105,6 +105,14 @@ exclude_host_filters: - tags['tagkey2'] is defined and tags['tagkey2'] == 'tagkey2' # excludes hosts that are powered off - powerstate != 'running' + +# includes a host to the inventory when any of these expressions is true, can refer to any vars defined on the host +include_host_filters: + # includes hosts that in the eastus region and power on + - location in ['eastus'] and powerstate == 'running' + # includes hosts in the eastus region and power on OR includes hosts in the eastus2 region and tagkey is tagkey + - location in ['eastus'] and powerstate == 'running' + - location in ['eastus2'] and tags['tagkey'] is defined and tags['tagkey'] == 'tagkey' ''' # FUTURE: do we need a set of sane default filters, separate from the user-defineable ones? @@ -130,12 +138,15 @@ from ansible.errors import AnsibleParserError, AnsibleError from ansible.module_utils.parsing.convert_bool import boolean from ansible.module_utils._text import to_native, to_bytes, to_text from itertools import chain +from os import environ try: from azure.core._pipeline_client import PipelineClient from azure.core.pipeline.policies import BearerTokenCredentialPolicy from azure.core.configuration import Configuration from azure.mgmt.core.tools import parse_resource_id + from azure.core.pipeline import PipelineResponse + from azure.mgmt.core.polling.arm_polling import ARMPolling except ImportError: Configuration = object parse_resource_id = object @@ -199,7 +210,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable): if re.match(r'.{0,}azure_rm\.y(a)?ml$', path): return True # display.debug("azure_rm inventory filename must end with 'azure_rm.yml' or 'azure_rm.yaml'") - return False + raise AnsibleError("azure_rm inventory filename must end with 'azure_rm.yml' or 'azure_rm.yaml'") def parse(self, inventory, loader, path, cache=True): super(InventoryModule, self).parse(inventory, loader, path) @@ -215,6 +226,8 @@ class InventoryModule(BaseInventoryPlugin, Constructable): self._filters = self.get_option('exclude_host_filters') + self.get_option('default_host_filters') + self._include_filters = self.get_option('include_host_filters') + try: self._credential_setup() self._get_hosts() @@ -222,8 +235,9 @@ class InventoryModule(BaseInventoryPlugin, Constructable): raise def _credential_setup(self): + auth_source = environ.get('ANSIBLE_AZURE_AUTH_SOURCE', None) or self.get_option('auth_source') auth_options = dict( - auth_source=self.get_option('auth_source'), + auth_source=auth_source, profile=self.get_option('profile'), subscription_id=self.get_option('subscription_id'), client_id=self.get_option('client_id'), @@ -238,6 +252,18 @@ class InventoryModule(BaseInventoryPlugin, Constructable): adfs_authority_url=self.get_option('adfs_authority_url') ) + if self.templar.is_template(auth_options["tenant"]): + auth_options["tenant"] = self.templar.template(variable=auth_options["tenant"], disable_lookups=False) + + if self.templar.is_template(auth_options["client_id"]): + auth_options["client_id"] = self.templar.template(variable=auth_options["client_id"], disable_lookups=False) + + if self.templar.is_template(auth_options["secret"]): + auth_options["secret"] = self.templar.template(variable=auth_options["secret"], disable_lookups=False) + + if self.templar.is_template(auth_options["subscription_id"]): + auth_options["subscription_id"] = self.templar.template(variable=auth_options["subscription_id"], disable_lookups=False) + self.azure_auth = AzureRMAuth(**auth_options) self._clientconfig = AzureRMRestConfiguration(self.azure_auth.azure_credential_track2, self.azure_auth.subscription_id, @@ -297,12 +323,14 @@ class InventoryModule(BaseInventoryPlugin, Constructable): for h in self._hosts: # FUTURE: track hostnames to warn if a hostname is repeated (can happen for legacy and for composed inventory_hostname) inventory_hostname = self._get_hostname(h, hostnames=constructable_hostnames, strict=constructable_config_strict) - if self._filter_host(inventory_hostname, h.hostvars): + if self._filter_exclude_host(inventory_hostname, h.hostvars): + continue + if not self._filter_include_host(inventory_hostname, h.hostvars): continue self.inventory.add_host(inventory_hostname) # FUTURE: configurable default IP list? can already do this via hostvar_expressions self.inventory.set_variable(inventory_hostname, "ansible_host", - next(chain(h.hostvars['public_ipv4_addresses'], h.hostvars['private_ipv4_addresses']), None)) + next(chain(h.hostvars['public_ipv4_address'], h.hostvars['private_ipv4_addresses']), None)) for k, v in iteritems(h.hostvars): # FUTURE: configurable hostvar prefix? Makes docs harder... self.inventory.set_variable(inventory_hostname, k, v) @@ -313,10 +341,10 @@ class InventoryModule(BaseInventoryPlugin, Constructable): self._add_host_to_keyed_groups(constructable_config_keyed_groups, h.hostvars, inventory_hostname, strict=constructable_config_strict) # FUTURE: fix underlying inventory stuff to allow us to quickly access known groupvars from reconciled host - def _filter_host(self, inventory_hostname, hostvars): + def _filter_host(self, filter, inventory_hostname, hostvars): self.templar.available_variables = hostvars - for condition in self._filters: + for condition in filter: # FUTURE: should warn/fail if conditional doesn't return True or False conditional = "{{% if {0} %}} True {{% else %}} False {{% endif %}}".format(condition) try: @@ -329,6 +357,12 @@ class InventoryModule(BaseInventoryPlugin, Constructable): return False + def _filter_include_host(self, inventory_hostname, hostvars): + return self._filter_host(self._include_filters, inventory_hostname, hostvars) + + def _filter_exclude_host(self, inventory_hostname, hostvars): + return self._filter_host(self._filters, inventory_hostname, hostvars) + def _get_hostname(self, host, hostnames=None, strict=False): hostname = None errors = [] @@ -441,8 +475,18 @@ class InventoryModule(BaseInventoryPlugin, Constructable): request_new = self.new_client.post(url, query_parameters, header_parameters, body_content) response = self.new_client.send_request(request_new) - - return json.loads(response.body()) + if response.status_code == 202: + try: + poller = ARMPolling(timeout=3) + poller.initialize(client=self.new_client, + initial_response=PipelineResponse(None, response, None), + deserialization_callback=lambda r: r) + poller.run() + return poller.resource().context['deserialized_data'] + except Exception as ec: + raise + else: + return json.loads(response.body()) def send_request(self, url, api_version): query_parameters = {'api-version': api_version} @@ -525,9 +569,11 @@ class AzureHost(object): network_interface_id=[], security_group_id=[], security_group=[], - public_ipv4_addresses=[], + public_ip_address=[], + public_ipv4_address=[], public_dns_hostnames=[], private_ipv4_addresses=[], + subnet=[], id=self._vm_model['id'], location=self._vm_model['location'], name=self._vm_model['name'], @@ -550,25 +596,34 @@ class AzureHost(object): resource_group=parse_resource_id(self._vm_model['id']).get('resource_group').lower(), default_inventory_hostname=self.default_inventory_hostname, creation_time=self._vm_model['properties']['timeCreated'], + license_type=self._vm_model['properties'].get('licenseType', 'Unknown') ) # set nic-related values from the primary NIC first for nic in sorted(self.nics, key=lambda n: n.is_primary, reverse=True): # and from the primary IP config per NIC first for ipc in sorted(nic._nic_model['properties']['ipConfigurations'], key=lambda i: i['properties'].get('primary', False), reverse=True): - private_ip = ipc['properties'].get('privateIPAddress') - if private_ip: - new_hostvars['private_ipv4_addresses'].append(private_ip) - pip_id = ipc['properties'].get('publicIPAddress', {}).get('id') - if pip_id: - new_hostvars['public_ip_id'] = pip_id - - pip = nic.public_ips[pip_id] - new_hostvars['public_ip_name'] = pip._pip_model['name'] - new_hostvars['public_ipv4_addresses'].append(pip._pip_model['properties'].get('ipAddress', None)) - pip_fqdn = pip._pip_model['properties'].get('dnsSettings', {}).get('fqdn') - if pip_fqdn: - new_hostvars['public_dns_hostnames'].append(pip_fqdn) + try: + subnet = ipc['properties'].get('subnet') + if subnet: + new_hostvars['subnet'].append(subnet) + private_ip = ipc['properties'].get('privateIPAddress') + if private_ip: + new_hostvars['private_ipv4_addresses'].append(private_ip) + pip_id = ipc['properties'].get('publicIPAddress', {}).get('id') + if pip_id and pip_id in nic.public_ips: + pip = nic.public_ips[pip_id] + new_hostvars['public_ipv4_address'].append(pip._pip_model['properties'].get('ipAddress', None)) + new_hostvars['public_ip_address'].append({ + 'id': pip_id, + 'name': pip._pip_model['name'], + 'ipv4_address': pip._pip_model['properties'].get('ipAddress', None), + }) + pip_fqdn = pip._pip_model['properties'].get('dnsSettings', {}).get('fqdn') + if pip_fqdn: + new_hostvars['public_dns_hostnames'].append(pip_fqdn) + except Exception: + continue new_hostvars['mac_address'].append(nic._nic_model['properties'].get('macAddress')) new_hostvars['network_interface'].append(nic._nic_model['name']) |