From 75d733afd2a22995fe18714777d895c81711be39 Mon Sep 17 00:00:00 2001 From: Adam Miller Date: Wed, 27 Mar 2019 16:14:20 -0500 Subject: [PATCH] Bugfix/52688 gcp compute missing image (#54468) * adding (optionally) image information to inventory var * add boot image mapping to gcp_compute instance data for all disk image data in the configured zones Signed-off-by: Adam Miller --- changelogs/gcp-compute-add-image.yaml | 3 + lib/ansible/plugins/inventory/gcp_compute.py | 99 ++++++++++++++++++-- 2 files changed, 93 insertions(+), 9 deletions(-) create mode 100644 changelogs/gcp-compute-add-image.yaml diff --git a/changelogs/gcp-compute-add-image.yaml b/changelogs/gcp-compute-add-image.yaml new file mode 100644 index 0000000000..bb9c40a9e2 --- /dev/null +++ b/changelogs/gcp-compute-add-image.yaml @@ -0,0 +1,3 @@ +--- +minor_changes: + - gcp_compute - add the image field to map to disk source iamges in the configured zones bringing it in line with old gce inventory script data diff --git a/lib/ansible/plugins/inventory/gcp_compute.py b/lib/ansible/plugins/inventory/gcp_compute.py index 8cde3e2b7f..59809ad6a1 100644 --- a/lib/ansible/plugins/inventory/gcp_compute.py +++ b/lib/ansible/plugins/inventory/gcp_compute.py @@ -75,6 +75,15 @@ DOCUMENTATION = ''' type: bool default: False version_added: '2.8' + retrieve_image_info: + description: + - Populate the C(image) host fact for the instances returned with the GCP image name + - By default this plugin does not attempt to resolve the boot image of an instance to the image name cataloged in GCP + because of the performance overhead of the task. + - Unless this option is enabled, the C(image) host variable will be C(null) + type: bool + default: False + version_added: '2.8' ''' EXAMPLES = ''' @@ -165,10 +174,8 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): :param query: a formatted query string :return the JSON response containing a list of instances. ''' - module = GcpMockModule(params) - auth = GcpSession(module, 'compute') - response = auth.get(link, params={'filter': query}) - return self._return_if_object(module, response) + response = self.auth_session.get(link, params={'filter': query}) + return self._return_if_object(self.fake_module, response) def _get_zones(self, project, config_data): ''' @@ -232,7 +239,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): return result - def _format_items(self, items): + def _format_items(self, items, project_disks): ''' :param items: A list of hosts ''' @@ -252,9 +259,10 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): network['subnetwork'] = self._format_network_info(network['subnetwork']) host['project'] = host['selfLink'].split('/')[6] + host['image'] = self._get_image(host, project_disks) return items - def _add_hosts(self, items, config_data, format_items=True): + def _add_hosts(self, items, config_data, format_items=True, project_disks=None): ''' :param items: A list of hosts :param config_data: configuration data @@ -263,7 +271,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): if not items: return if format_items: - items = self._format_items(items) + items = self._format_items(items, project_disks) for host in items: self._populate_host(host) @@ -328,6 +336,71 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): return accessConfig[u'natIP'] return None + def _get_image(self, instance, project_disks): + ''' + :param instance: A instance response from GCP + :return the image of this instance or None + ''' + image = None + if project_disks and 'disks' in instance: + for disk in instance['disks']: + if disk.get('boot'): + image = project_disks[disk["source"]] + return image + + def _get_project_disks(self, config_data, query): + ''' + project space disk images + ''' + + try: + self._project_disks + except AttributeError: + self._project_disks = {} + request_params = {'maxResults': 500, 'filter': query} + + for project in config_data['projects']: + session_responses = [] + page_token = True + while page_token: + response = self.auth_session.get( + 'https://www.googleapis.com/compute/v1/projects/{0}/aggregated/disks'.format(project), + params=request_params + ) + response_json = response.json() + if 'nextPageToken' in response_json: + request_params['pageToken'] = response_json['nextPageToken'] + elif 'pageToken' in request_params: + del request_params['pageToken'] + + if 'items' in response_json: + session_responses.append(response_json) + page_token = 'pageToken' in request_params + + for response in session_responses: + if 'items' in response: + # example k would be a zone or region name + # example v would be { "disks" : [], "otherkey" : "..." } + for zone_or_region, aggregate in response['items'].items(): + if 'zones' in zone_or_region: + if 'disks' in aggregate: + zone = zone_or_region.replace('zones/', '') + for disk in aggregate['disks']: + if 'zones' in config_data and zone in config_data['zones']: + # If zones specified, only store those zones' data + if 'sourceImage' in disk: + self._project_disks[disk['selfLink']] = disk['sourceImage'].split('/')[-1] + else: + self._project_disks[disk['selfLink']] = disk['selfLink'].split('/')[-1] + + else: + if 'sourceImage' in disk: + self._project_disks[disk['selfLink']] = disk['sourceImage'].split('/')[-1] + else: + self._project_disks[disk['selfLink']] = disk['selfLink'].split('/')[-1] + + return self._project_disks + def _get_privateip(self, item): ''' :param item: A host response from GCP @@ -358,8 +431,16 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): 'service_account_email': self.get_option('service_account_email'), } + self.fake_module = GcpMockModule(params) + self.auth_session = GcpSession(self.fake_module, 'compute') + query = self._get_query_options(params['filters']) + if self.get_option('retrieve_image_info'): + project_disks = self._get_project_disks(config_data, query) + else: + project_disks = None + # Cache logic if cache: cache = self.get_option('cache') @@ -373,7 +454,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): results = self._cache[cache_key] for project in results: for zone in results[project]: - self._add_hosts(results[project][zone], config_data, False) + self._add_hosts(results[project][zone], config_data, False, project_disks=project_disks) except KeyError: cache_needs_update = True @@ -390,7 +471,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): link = self._instances % (project, zone) params['zone'] = zone resp = self.fetch_list(params, link, query) - self._add_hosts(resp.get('items'), config_data) + self._add_hosts(resp.get('items'), config_data, project_disks=project_disks) cached_data[project][zone] = resp.get('items') if cache_needs_update: