From 6652a960b7976c4084fae0ace20f5b51c1fb8a45 Mon Sep 17 00:00:00 2001 From: Chris Meyers Date: Tue, 11 Jul 2017 16:28:01 -0400 Subject: [PATCH] make insights playbook fetching an Ansible module * Gone are the days of playbook abuse. * Our project update can now call a single Insight Ansible module to loop over the set of maintenance plans to download and save playbooks for use by job templates. --- awx/playbooks/action_plugins/insights.py | 82 ++++++++++++++++++++++++ awx/playbooks/library/insights.py | 44 +++++++++++++ awx/playbooks/project_update.yml | 50 ++++----------- 3 files changed, 138 insertions(+), 38 deletions(-) create mode 100644 awx/playbooks/action_plugins/insights.py create mode 100644 awx/playbooks/library/insights.py diff --git a/awx/playbooks/action_plugins/insights.py b/awx/playbooks/action_plugins/insights.py new file mode 100644 index 0000000000..5e4cb6649f --- /dev/null +++ b/awx/playbooks/action_plugins/insights.py @@ -0,0 +1,82 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import requests + +from ansible.plugins.action import ActionBase + + +class ActionModule(ActionBase): + + def save_playbook(self, proj_path, plan, content): + fname = '{}-{}.yml'.format(plan.get('name', None) or 'insights-plan', plan['maintenance_id']) + file_path = os.path.join(proj_path, fname) + with open(file_path, 'w') as f: + f.write(content) + + def is_stale(self, proj_path, etag): + file_path = os.path.join(proj_path, '.version') + try: + f = open(file_path, 'r') + version = f.read() + f.close() + return version != etag + except IOError: + return True + + def write_version(self, proj_path, etag): + file_path = os.path.join(proj_path, '.version') + with open(file_path, 'w') as f: + f.write(etag) + + def run(self, tmp=None, task_vars=None): + + self._supports_check_mode = False + + result = super(ActionModule, self).run(tmp, task_vars) + + insights_url = self._task.args.get('insights_url', None) + username = self._task.args.get('username', None) + password = self._task.args.get('password', None) + proj_path = self._task.args.get('project_path', None) + + session = requests.Session() + session.auth = requests.auth.HTTPBasicAuth(username, password) + headers = {'Content-Type': 'application/json'} + + url = '{}/r/insights/v1/maintenance?ansible=true'.format(insights_url) + + res = session.get(url, headers=headers, timeout=120) + + if res.status_code != 200: + result['failed'] = True + result['msg'] = 'Expected {} to return a status code of 200 but returned status code "{}" instead with content "{}".'.format(url, res.status_code, res.content) + return result + + if 'ETag' in res.headers: + version = res.headers['ETag'] + if version.startswith('"') and version.endswith('"'): + version = version[1:-1] + else: + version = "ETAG_NOT_FOUND" + + if not self.is_stale(proj_path, version): + result['changed'] = False + result['version'] = version + return result + + for item in res.json(): + url = '{}/r/insights/v3/maintenance/{}/playbook'.format(insights_url, item['maintenance_id']) + res = session.get(url, timeout=120) + if res.status_code != 200: + result['failed'] = True + result['msg'] = 'Expected {} to return a status code of 200 but returned status code "{}" instead with content "{}".'.format(url, res.status_code, res.content) + return result + self.save_playbook(proj_path, item, res.content) + + self.write_version(proj_path, version) + + result['changed'] = True + result['version'] = version + return result diff --git a/awx/playbooks/library/insights.py b/awx/playbooks/library/insights.py new file mode 100644 index 0000000000..7d0759d656 --- /dev/null +++ b/awx/playbooks/library/insights.py @@ -0,0 +1,44 @@ +ANSIBLE_METADATA = {'metadata_version': '1.0', + 'status': ['stableinterface'], + 'supported_by': 'tower'} + + +DOCUMENTATION = ''' +--- +module: insights +short_description: gather all maintenance plan playbooks for an insights account +description: + - Supply insights Credentials to download all playbooks for all maintenance plans. + The totality of the plans are versioned based on the http ETag response. +version_added: "2.3" +options: + insights_url: + description: + - The base url of the insights api. Most likely https://insights.redhat.com + required: true + username: + description: + - Insights basic auth username. + required: true + password: + description: + - Insights basic auth password. + required: true + project_path: + description: + - Directory to write all playbooks and the special .version to. + required: true + +notes: + - Returns version +author: + - "Chris Meyers" +''' + +EXAMPLES = ''' +- insights: + insights_url: "https://insights.redhat.com" + username: "cmoney" + password: "get_paid" + project_path: "/var/lib/awx/projects/_5_insights" +''' diff --git a/awx/playbooks/project_update.yml b/awx/playbooks/project_update.yml index 43a8eb176a..e9b7ad19f7 100644 --- a/awx/playbooks/project_update.yml +++ b/awx/playbooks/project_update.yml @@ -106,52 +106,26 @@ scm_version: "{{ scm_result['after'] }}" when: "'after' in scm_result" - - block: - - name: update project using insights - uri: - url: "{{insights_url}}/r/insights/v1/maintenance?ansible=true" - user: "{{scm_username}}" - password: "{{scm_password}}" - force_basic_auth: yes - register: insights_output - - - name: Set the insights cache version - set_fact: - scm_version: "{{ insights_output.etag|default(scm_revision)|regex_replace('\"(.*)\"$', '\\1') }}" - - when: scm_type == 'insights' - - block: - name: Ensure the project directory is present file: dest: "{{project_path|quote}}" state: directory - - name: Fetch Insights Playbook With Name - get_url: - url: "{{insights_url}}/r/insights/v3/maintenance/{{item.maintenance_id}}/playbook" - dest: "{{project_path|quote}}/{{item.name}}-{{item.maintenance_id}}.yml" - url_username: "{{scm_username}}" - url_password: "{{scm_password}}" - force_basic_auth: yes - force: yes - when: item.name != None and item.name != "" - with_items: "{{ insights_output.json|default([]) }}" - failed_when: false + - name: Fetch Insights Playbook(s) + insights: + insights_url: "{{insights_url}}" + username: "{{scm_username}}" + password: "{{scm_password}}" + project_path: "{{project_path}}" + register: results - - name: Fetch Insights Playbook - get_url: - url: "{{insights_url}}/r/insights/v3/maintenance/{{item.maintenance_id}}/playbook" - dest: "{{project_path|quote}}/insights-plan-{{item.maintenance_id}}.yml" - url_username: "{{scm_username}}" - url_password: "{{scm_password}}" - force_basic_auth: yes - force: yes - when: (item.name == None or item.name == "") - with_items: "{{ insights_output.json|default([]) }}" - failed_when: false + when: scm_type == 'insights' - when: scm_type == 'insights' and scm_version != scm_revision + - name: Save Insights Version + set_fact: + scm_version: "{{results.version}}" + when: scm_type == 'insights' and results is defined - name: detect requirements.yml stat: path={{project_path|quote}}/roles/requirements.yml