diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..beda045779 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,7 @@ +--- +version: 2 +updates: + - package-ecosystem: "pip" + directory: "/requirements" + schedule: + interval: "monthly" diff --git a/CHANGELOG.md b/CHANGELOG.md index 6237a9f54a..76b8427c4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,19 @@ This is a list of high-level changes for each release of AWX. A full list of commits can be found at `https://github.com/ansible/awx/releases/tag/`. +# (TBD) +- Added user interface for the activity stream: https://github.com/ansible/awx/pull/9083 +- Converted many of the top-level list views (Jobs, Teams, Hosts, Inventories, Projects, and more) to a new, permanent table component for substantially increased responsiveness, usability, maintainability, and other 'ility's: https://github.com/ansible/awx/pull/8970, https://github.com/ansible/awx/pull/9182 and many others! +- Added click-to-expand details for job tables +- Add search filtering to job output https://github.com/ansible/awx/pull/9208 +- Added the new migration, update, and "installation in progress" page https://github.com/ansible/awx/pull/9123 +- Added the user interface for job settings https://github.com/ansible/awx/pull/8661 +- Runtime errors from jobs are now displayed, along with an explanation for what went wrong, on the output page https://github.com/ansible/awx/pull/9264 +- You can now cancel a running job from its output and details panel https://github.com/ansible/awx/pull/9199 +- Fixed a bug where launch prompt inputs were unexpectedly deposited in the url: https://github.com/ansible/awx/pull/9231 +- Playbook, credential type, and inventory file inputs now support type-ahead and manual type-in! https://github.com/ansible/awx/pull/9120 +- Added ability to relaunch against failed hosts: https://github.com/ansible/awx/pull/9225 + # 17.0.1 (January 26, 2021) - Fixed pgdocker directory permissions issue with Local Docker installer: https://github.com/ansible/awx/pull/9152 - Fixed a bug in the UI which caused toggle settings to not be changed when clicked: https://github.com/ansible/awx/pull/9093 diff --git a/Makefile b/Makefile index 9cf77e597e..961d99f66b 100644 --- a/Makefile +++ b/Makefile @@ -515,7 +515,7 @@ ui-zuul-lint-and-test: $(NPM_BIN) --prefix awx/ui_next install $(NPM_BIN) run --prefix awx/ui_next lint $(NPM_BIN) run --prefix awx/ui_next prettier-check - $(NPM_BIN) run --prefix awx/ui_next test + $(NPM_BIN) run --prefix awx/ui_next test -- --coverage --watchAll=false # Build a pip-installable package into dist/ with a timestamped version number. diff --git a/awx/main/dispatch/periodic.py b/awx/main/dispatch/periodic.py index c76366a8d4..b3a1b769c0 100644 --- a/awx/main/dispatch/periodic.py +++ b/awx/main/dispatch/periodic.py @@ -6,6 +6,7 @@ from multiprocessing import Process from django.conf import settings from django.db import connections from schedule import Scheduler +from django_guid.middleware import GuidMiddleware from awx.main.dispatch.worker import TaskWorker @@ -35,6 +36,7 @@ class Scheduler(Scheduler): # If the database connection has a hiccup, re-establish a new # connection conn.close_if_unusable_or_obsolete() + GuidMiddleware.set_guid(GuidMiddleware._generate_guid()) self.run_pending() except Exception: logger.exception( diff --git a/awx/main/dispatch/pool.py b/awx/main/dispatch/pool.py index dc97402788..5dbe034547 100644 --- a/awx/main/dispatch/pool.py +++ b/awx/main/dispatch/pool.py @@ -16,6 +16,7 @@ from queue import Full as QueueFull, Empty as QueueEmpty from django.conf import settings from django.db import connection as django_connection, connections from django.core.cache import cache as django_cache +from django_guid.middleware import GuidMiddleware from jinja2 import Template import psutil @@ -445,6 +446,8 @@ class AutoscalePool(WorkerPool): return super(AutoscalePool, self).up() def write(self, preferred_queue, body): + if 'guid' in body: + GuidMiddleware.set_guid(body['guid']) try: # when the cluster heartbeat occurs, clean up internally if isinstance(body, dict) and 'cluster_node_heartbeat' in body['task']: diff --git a/awx/main/dispatch/publish.py b/awx/main/dispatch/publish.py index f7fd7cf4fb..4d75654b5d 100644 --- a/awx/main/dispatch/publish.py +++ b/awx/main/dispatch/publish.py @@ -5,6 +5,7 @@ import json from uuid import uuid4 from django.conf import settings +from django_guid.middleware import GuidMiddleware from . import pg_bus_conn @@ -83,6 +84,9 @@ class task: 'kwargs': kwargs, 'task': cls.name } + guid = GuidMiddleware.get_guid() + if guid: + obj['guid'] = guid obj.update(**kw) if callable(queue): queue = queue() diff --git a/awx/main/dispatch/worker/callback.py b/awx/main/dispatch/worker/callback.py index e61a516094..342280868c 100644 --- a/awx/main/dispatch/worker/callback.py +++ b/awx/main/dispatch/worker/callback.py @@ -9,6 +9,7 @@ from django.conf import settings from django.utils.timezone import now as tz_now from django.db import DatabaseError, OperationalError, connection as django_connection from django.db.utils import InterfaceError, InternalError +from django_guid.middleware import GuidMiddleware import psutil @@ -152,6 +153,8 @@ class CallbackBrokerWorker(BaseWorker): if body.get('event') == 'EOF': try: + if 'guid' in body: + GuidMiddleware.set_guid(body['guid']) final_counter = body.get('final_counter', 0) logger.info('Event processing is finished for Job {}, sending notifications'.format(job_identifier)) # EOF events are sent when stdout for the running task is @@ -176,6 +179,8 @@ class CallbackBrokerWorker(BaseWorker): handle_success_and_failure_notifications.apply_async([uj.id]) except Exception: logger.exception('Worker failed to emit notifications: Job {}'.format(job_identifier)) + finally: + GuidMiddleware.set_guid('') return event = cls.create_from_data(**body) diff --git a/awx/main/dispatch/worker/task.py b/awx/main/dispatch/worker/task.py index 7e7437d445..80a414770b 100644 --- a/awx/main/dispatch/worker/task.py +++ b/awx/main/dispatch/worker/task.py @@ -6,6 +6,8 @@ import traceback from kubernetes.config import kube_config +from django_guid.middleware import GuidMiddleware + from awx.main.tasks import dispatch_startup, inform_cluster_of_shutdown from .base import BaseWorker @@ -52,6 +54,8 @@ class TaskWorker(BaseWorker): uuid = body.get('uuid', '') args = body.get('args', []) kwargs = body.get('kwargs', {}) + if 'guid' in body: + GuidMiddleware.set_guid(body.pop('guid')) _call = TaskWorker.resolve_callable(task) if inspect.isclass(_call): # the callable is a class, e.g., RunJob; instantiate and diff --git a/awx/main/models/credential/injectors.py b/awx/main/models/credential/injectors.py index ef30b91945..90615f2d66 100644 --- a/awx/main/models/credential/injectors.py +++ b/awx/main/models/credential/injectors.py @@ -92,8 +92,8 @@ def _openstack_data(cred): }, } - if cred.has_input('project_region_name'): - openstack_data['clouds']['devstack']['region_name'] = cred.get_input('project_region_name', default='') + if cred.has_input('region'): + openstack_data['clouds']['devstack']['region_name'] = cred.get_input('region', default='') return openstack_data diff --git a/awx/main/models/notifications.py b/awx/main/models/notifications.py index f30c4d2778..53860563e0 100644 --- a/awx/main/models/notifications.py +++ b/awx/main/models/notifications.py @@ -388,12 +388,17 @@ class JobNotificationMixin(object): and a url to the job run.""" job_context = {'host_status_counts': {}} summary = None - if hasattr(self, 'job_host_summaries'): - summary = self.job_host_summaries.first() - if summary: - from awx.api.serializers import JobHostSummarySerializer - summary_data = JobHostSummarySerializer(summary).to_representation(summary) - job_context['host_status_counts'] = summary_data + try: + has_event_property = any([f for f in self.event_class._meta.fields if f.name == 'event']) + except NotImplementedError: + has_event_property = False + if has_event_property: + qs = self.get_event_queryset() + if qs: + event = qs.only('event_data').filter(event='playbook_on_stats').first() + if event: + summary = event.get_host_status_counts() + job_context['host_status_counts'] = summary context = { 'job': job_context, 'job_friendly_name': self.get_notification_friendly_name(), diff --git a/awx/main/tasks.py b/awx/main/tasks.py index 32529c3d21..b6ab905837 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -34,6 +34,7 @@ from django.contrib.auth.models import User from django.utils.translation import ugettext_lazy as _, gettext_noop from django.core.cache import cache from django.core.exceptions import ObjectDoesNotExist +from django_guid.middleware import GuidMiddleware # Kubernetes from kubernetes.client.rest import ApiException @@ -839,6 +840,7 @@ class BaseTask(object): self.cleanup_paths = [] self.parent_workflow_job_id = None self.host_map = {} + self.guid = GuidMiddleware.get_guid() def update_model(self, pk, _attempt=0, **updates): """Reload the model instance from the database and update the @@ -1274,6 +1276,9 @@ class BaseTask(object): except json.JSONDecodeError: pass + if 'event_data' in event_data: + event_data['event_data']['guid'] = self.guid + event_data.setdefault(self.event_data_key, self.instance.id) self.dispatcher.dispatch(event_data) self.event_ct += 1 @@ -1310,6 +1315,7 @@ class BaseTask(object): event_data = { 'event': 'EOF', 'final_counter': self.event_ct, + 'guid': self.guid, } event_data.setdefault(self.event_data_key, self.instance.id) self.dispatcher.dispatch(event_data) diff --git a/awx/main/tests/data/inventory/plugins/openstack/files/file_reference b/awx/main/tests/data/inventory/plugins/openstack/files/file_reference index c578942ca1..4e1d1c6a02 100644 --- a/awx/main/tests/data/inventory/plugins/openstack/files/file_reference +++ b/awx/main/tests/data/inventory/plugins/openstack/files/file_reference @@ -8,4 +8,5 @@ clouds: project_name: fooo username: fooo private: true + region_name: fooo verify: false diff --git a/awx/main/tests/functional/ansible.json b/awx/main/tests/functional/ansible.json deleted file mode 100644 index e877df2ad1..0000000000 --- a/awx/main/tests/functional/ansible.json +++ /dev/null @@ -1,283 +0,0 @@ -{ - "ansible_all_ipv4_addresses": [ - "172.17.0.7" - ], - "ansible_all_ipv6_addresses": [ - "fe80::42:acff:fe11:7" - ], - "ansible_architecture": "x86_64", - "ansible_bios_date": "12/01/2006", - "ansible_bios_version": "VirtualBox", - "ansible_cmdline": { - "BOOT_IMAGE": "/boot/vmlinuz64", - "base": true, - "console": "tty0", - "initrd": "/boot/initrd.img", - "loglevel": "3", - "noembed": true, - "nomodeset": true, - "norestore": true, - "user": "docker", - "waitusb": "10:LABEL=boot2docker-data" - }, - "ansible_date_time": { - "date": "2016-02-02", - "day": "02", - "epoch": "1454424257", - "hour": "14", - "iso8601": "2016-02-02T14:44:17Z", - "iso8601_basic": "20160202T144417348424", - "iso8601_basic_short": "20160202T144417", - "iso8601_micro": "2016-02-02T14:44:17.348496Z", - "minute": "44", - "month": "02", - "second": "17", - "time": "14:44:17", - "tz": "UTC", - "tz_offset": "+0000", - "weekday": "Tuesday", - "weekday_number": "2", - "weeknumber": "05", - "year": "2016" - }, - "ansible_default_ipv4": { - "address": "172.17.0.7", - "alias": "eth0", - "broadcast": "global", - "gateway": "172.17.0.1", - "interface": "eth0", - "macaddress": "02:42:ac:11:00:07", - "mtu": 1500, - "netmask": "255.255.0.0", - "network": "172.17.0.0", - "type": "ether" - }, - "ansible_default_ipv6": {}, - "ansible_devices": { - "sda": { - "holders": [], - "host": "", - "model": "VBOX HARDDISK", - "partitions": { - "sda1": { - "sectors": "510015555", - "sectorsize": 512, - "size": "243.19 GB", - "start": "1975995" - }, - "sda2": { - "sectors": "1975932", - "sectorsize": 512, - "size": "964.81 MB", - "start": "63" - } - }, - "removable": "0", - "rotational": "0", - "scheduler_mode": "deadline", - "sectors": "512000000", - "sectorsize": "512", - "size": "244.14 GB", - "support_discard": "0", - "vendor": "ATA" - }, - "sr0": { - "holders": [], - "host": "", - "model": "CD-ROM", - "partitions": {}, - "removable": "1", - "rotational": "1", - "scheduler_mode": "deadline", - "sectors": "61440", - "sectorsize": "2048", - "size": "120.00 MB", - "support_discard": "0", - "vendor": "VBOX" - } - }, - "ansible_distribution": "Ubuntu", - "ansible_distribution_major_version": "14", - "ansible_distribution_release": "trusty", - "ansible_distribution_version": "14.04", - "ansible_dns": { - "nameservers": [ - "8.8.8.8" - ] - }, - "ansible_domain": "", - "ansible_env": { - "HOME": "/root", - "HOSTNAME": "ede894599989", - "LANG": "en_US.UTF-8", - "LC_ALL": "en_US.UTF-8", - "LC_MESSAGES": "en_US.UTF-8", - "LESSCLOSE": "/usr/bin/lesspipe %s %s", - "LESSOPEN": "| /usr/bin/lesspipe %s", - "LS_COLORS": "", - "OLDPWD": "/ansible", - "PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", - "PWD": "/ansible/examples", - "SHLVL": "1", - "_": "/usr/local/bin/ansible", - "container": "docker" - }, - "ansible_eth0": { - "active": true, - "device": "eth0", - "ipv4": { - "address": "172.17.0.7", - "broadcast": "global", - "netmask": "255.255.0.0", - "network": "172.17.0.0" - }, - "ipv6": [ - { - "address": "fe80::42:acff:fe11:7", - "prefix": "64", - "scope": "link" - } - ], - "macaddress": "02:42:ac:11:00:07", - "mtu": 1500, - "promisc": false, - "type": "ether" - }, - "ansible_fips": false, - "ansible_form_factor": "Other", - "ansible_fqdn": "ede894599989", - "ansible_hostname": "ede894599989", - "ansible_interfaces": [ - "lo", - "eth0" - ], - "ansible_kernel": "4.1.12-boot2docker", - "ansible_lo": { - "active": true, - "device": "lo", - "ipv4": { - "address": "127.0.0.1", - "broadcast": "host", - "netmask": "255.0.0.0", - "network": "127.0.0.0" - }, - "ipv6": [ - { - "address": "::1", - "prefix": "128", - "scope": "host" - } - ], - "mtu": 65536, - "promisc": false, - "type": "loopback" - }, - "ansible_lsb": { - "codename": "trusty", - "description": "Ubuntu 14.04.3 LTS", - "id": "Ubuntu", - "major_release": "14", - "release": "14.04" - }, - "ansible_machine": "x86_64", - "ansible_memfree_mb": 3746, - "ansible_memory_mb": { - "nocache": { - "free": 8896, - "used": 3638 - }, - "real": { - "free": 3746, - "total": 12534, - "used": 8788 - }, - "swap": { - "cached": 0, - "free": 4048, - "total": 4048, - "used": 0 - } - }, - "ansible_memtotal_mb": 12534, - "ansible_mounts": [ - { - "device": "/dev/sda1", - "fstype": "ext4", - "mount": "/etc/resolv.conf", - "options": "rw,relatime,data=ordered", - "size_available": 201281392640, - "size_total": 256895700992, - "uuid": "NA" - }, - { - "device": "/dev/sda1", - "fstype": "ext4", - "mount": "/etc/hostname", - "options": "rw,relatime,data=ordered", - "size_available": 201281392640, - "size_total": 256895700992, - "uuid": "NA" - }, - { - "device": "/dev/sda1", - "fstype": "ext4", - "mount": "/etc/hosts", - "options": "rw,relatime,data=ordered", - "size_available": 201281392640, - "size_total": 256895700992, - "uuid": "NA" - } - ], - "ansible_nodename": "ede894599989", - "ansible_os_family": "Debian", - "ansible_pkg_mgr": "apt", - "ansible_processor": [ - "GenuineIntel", - "Intel(R) Core(TM) i7-4870HQ CPU @ 2.50GHz", - "GenuineIntel", - "Intel(R) Core(TM) i7-4870HQ CPU @ 2.50GHz", - "GenuineIntel", - "Intel(R) Core(TM) i7-4870HQ CPU @ 2.50GHz", - "GenuineIntel", - "Intel(R) Core(TM) i7-4870HQ CPU @ 2.50GHz", - "GenuineIntel", - "Intel(R) Core(TM) i7-4870HQ CPU @ 2.50GHz", - "GenuineIntel", - "Intel(R) Core(TM) i7-4870HQ CPU @ 2.50GHz", - "GenuineIntel", - "Intel(R) Core(TM) i7-4870HQ CPU @ 2.50GHz", - "GenuineIntel", - "Intel(R) Core(TM) i7-4870HQ CPU @ 2.50GHz" - ], - "ansible_processor_cores": 8, - "ansible_processor_count": 1, - "ansible_processor_threads_per_core": 1, - "ansible_processor_vcpus": 8, - "ansible_product_name": "VirtualBox", - "ansible_product_serial": "0", - "ansible_product_uuid": "25C5EA5A-1DF1-48D9-A2C6-81227DA153C0", - "ansible_product_version": "1.2", - "ansible_python_version": "2.7.6", - "ansible_selinux": false, - "ansible_service_mgr": "upstart", - "ansible_ssh_host_key_dsa_public": "AAAAB3NzaC1kc3MAAACBALF0xsM8UMXgSKiWNw4t19wxbxLnxQX742t/dIM0O8YLx+/lIP+Q69Dv5uoVt0zKV39eFziRlCh96qj2KYkGEJ6XfVZFnhpculL2Pv2CPpSwKuQ1vTbDO/xxUrvY+bHpfNJf9Rh69bFEE2pTsjomFPCgp8M0qGaFtwg6czSaeBONAAAAFQCGEfVtj97JiexTVRqgQITYlFp/eQAAAIEAg+S9qWn+AIb3amwVoLL/usQYOPCmZY9RVPzpkjJ6OG+HI4B7cXeauPtNTJwT0f9vGEqzf4mPpmS+aCShj6iwdmJ+cOwR5+SJlNalab3CMBoXKVLbT1J2XWFlK0szKKnoReP96IDbkAkGQ3fkm4jz0z6Wy0u6wOQVNcd4G5cwLZ4AAACAFvBm+H1LwNrwWBjWio+ayhglZ4Y25mLMEn2+dqBz0gLK5szEbft1HMPOWIVHvl6vi3v34pAJHKpxXpkLlNliTn8iw9BzCOrgP4V8sp2/85mxEuCdI1w/QERj9cHu5iS2pZ0cUwDE3pfuuGBB3IEliaJyaapowdrM8lN12jQl11E=", - "ansible_ssh_host_key_ecdsa_public": "AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBHiYp4e9RfXpxDcEWpK4EuXPHW9++xcFI9hiB0TYAZgxEF9RIgwfucpPawFk7HIFoNc7EXQMlryilLSbg155KWM=", - "ansible_ssh_host_key_ed25519_public": "AAAAC3NzaC1lZDI1NTE5AAAAILclD2JaC654azEsAfcHRIOA2Ig9/Qk6MX80i/VCEdSH", - "ansible_ssh_host_key_rsa_public": "AAAAB3NzaC1yc2EAAAADAQABAAABAQDeSUGxZaZsgBsezld0mj3HcbAwx6aykGnejceBjcs6lVwSGMHevofzSXIQDPYBhZoyWNl0PYAHv6AsQ8+3khd2SitUMJAuHSz1ZjgHCCGQP9ijXTKHn+lWCKA8rhLG/dwYwiouoOPZfn1G+erbKO6XiVbELrrf2RadnMGuMinESIOKVj3IunXsaGRMsDOQferOnUf7MvH7xpQnoySyQ1+p4rGruaohWG+Y2cDo7+B2FylPVbrpRDDJkfbt4J96WHx0KOdD0qzOicQP8JqDflqQPJJCWcgrvjQOSe4gXdPB6GZDtBl2qgQRwt1IgizPMm+b7Bwbd2VDe1TeWV2gT/7H", - "ansible_swapfree_mb": 4048, - "ansible_swaptotal_mb": 4048, - "ansible_system": "Linux", - "ansible_system_vendor": "innotek GmbH", - "ansible_uptime_seconds": 178398, - "ansible_user_dir": "/root", - "ansible_user_gecos": "root", - "ansible_user_gid": 0, - "ansible_user_id": "root", - "ansible_user_shell": "/bin/bash", - "ansible_user_uid": 0, - "ansible_userspace_architecture": "x86_64", - "ansible_userspace_bits": "64", - "ansible_virtualization_role": "guest", - "ansible_virtualization_type": "docker", - "module_setup": true -} diff --git a/awx/main/tests/functional/packages.json b/awx/main/tests/functional/packages.json deleted file mode 100644 index 7bc735d06f..0000000000 --- a/awx/main/tests/functional/packages.json +++ /dev/null @@ -1,2922 +0,0 @@ -[ - { - "name": "kbd", - "source": "rpm", - "epoch": null, - "version": "1.15.5", - "release": "10.el7", - "arch": "x86_64" - }, - { - "name": "centos-release", - "source": "rpm", - "epoch": null, - "version": "7", - "release": "0.1406.el7.centos.2.3", - "arch": "x86_64" - }, - { - "name": "postfix", - "source": "rpm", - "epoch": 2, - "version": "2.10.1", - "release": "6.el7", - "arch": "x86_64" - }, - { - "name": "filesystem", - "source": "rpm", - "epoch": null, - "version": "3.2", - "release": "18.el7", - "arch": "x86_64" - }, - { - "name": "tuned", - "source": "rpm", - "epoch": null, - "version": "2.3.0", - "release": "11.el7", - "arch": "noarch" - }, - { - "name": "ncurses-base", - "source": "rpm", - "epoch": null, - "version": "5.9", - "release": "13.20130511.el7", - "arch": "noarch" - }, - { - "name": "aic94xx-firmware", - "source": "rpm", - "epoch": null, - "version": "30", - "release": "6.el7", - "arch": "noarch" - }, - { - "name": "kbd-misc", - "source": "rpm", - "epoch": null, - "version": "1.15.5", - "release": "10.el7", - "arch": "noarch" - }, - { - "name": "irqbalance", - "source": "rpm", - "epoch": 2, - "version": "1.0.6", - "release": "5.el7", - "arch": "x86_64" - }, - { - "name": "tzdata", - "source": "rpm", - "epoch": null, - "version": "2014b", - "release": "1.el7", - "arch": "noarch" - }, - { - "name": "openssh-clients", - "source": "rpm", - "epoch": null, - "version": "6.4p1", - "release": "8.el7", - "arch": "x86_64" - }, - { - "name": "glibc-common", - "source": "rpm", - "epoch": null, - "version": "2.17", - "release": "55.el7", - "arch": "x86_64" - }, - { - "name": "authconfig", - "source": "rpm", - "epoch": null, - "version": "6.2.8", - "release": "8.el7", - "arch": "x86_64" - }, - { - "name": "xz-libs", - "source": "rpm", - "epoch": null, - "version": "5.1.2", - "release": "8alpha.el7", - "arch": "x86_64" - }, - { - "name": "btrfs-progs", - "source": "rpm", - "epoch": null, - "version": "3.12", - "release": "4.el7", - "arch": "x86_64" - }, - { - "name": "ncurses-libs", - "source": "rpm", - "epoch": null, - "version": "5.9", - "release": "13.20130511.el7", - "arch": "x86_64" - }, - { - "name": "sudo", - "source": "rpm", - "epoch": null, - "version": "1.8.6p7", - "release": "11.el7", - "arch": "x86_64" - }, - { - "name": "libsepol", - "source": "rpm", - "epoch": null, - "version": "2.1.9", - "release": "3.el7", - "arch": "x86_64" - }, - { - "name": "iprutils", - "source": "rpm", - "epoch": null, - "version": "2.3.16", - "release": "4.el7", - "arch": "x86_64" - }, - { - "name": "libselinux", - "source": "rpm", - "epoch": null, - "version": "2.2.2", - "release": "6.el7", - "arch": "x86_64" - }, - { - "name": "iwl6000g2b-firmware", - "source": "rpm", - "epoch": null, - "version": "17.168.5.2", - "release": "34.el7", - "arch": "noarch" - }, - { - "name": "info", - "source": "rpm", - "epoch": null, - "version": "5.1", - "release": "4.el7", - "arch": "x86_64" - }, - { - "name": "iwl7260-firmware", - "source": "rpm", - "epoch": null, - "version": "22.0.7.0", - "release": "34.el7", - "arch": "noarch" - }, - { - "name": "dbus-libs", - "source": "rpm", - "epoch": 1, - "version": "1.6.12", - "release": "8.el7", - "arch": "x86_64" - }, - { - "name": "libertas-sd8787-firmware", - "source": "rpm", - "epoch": null, - "version": "20140213", - "release": "0.3.git4164c23.el7", - "arch": "noarch" - }, - { - "name": "sed", - "source": "rpm", - "epoch": null, - "version": "4.2.2", - "release": "5.el7", - "arch": "x86_64" - }, - { - "name": "iwl6050-firmware", - "source": "rpm", - "epoch": null, - "version": "41.28.5.1", - "release": "34.el7", - "arch": "noarch" - }, - { - "name": "chkconfig", - "source": "rpm", - "epoch": null, - "version": "1.3.61", - "release": "4.el7", - "arch": "x86_64" - }, - { - "name": "iwl1000-firmware", - "source": "rpm", - "epoch": 1, - "version": "39.31.5.1", - "release": "34.el7", - "arch": "noarch" - }, - { - "name": "nspr", - "source": "rpm", - "epoch": null, - "version": "4.10.2", - "release": "4.el7", - "arch": "x86_64" - }, - { - "name": "iwl6000-firmware", - "source": "rpm", - "epoch": null, - "version": "9.221.4.1", - "release": "34.el7", - "arch": "noarch" - }, - { - "name": "nss-util", - "source": "rpm", - "epoch": null, - "version": "3.15.4", - "release": "2.el7", - "arch": "x86_64" - }, - { - "name": "iwl2000-firmware", - "source": "rpm", - "epoch": null, - "version": "18.168.6.1", - "release": "34.el7", - "arch": "noarch" - }, - { - "name": "grep", - "source": "rpm", - "epoch": null, - "version": "2.16", - "release": "1.el7", - "arch": "x86_64" - }, - { - "name": "iwl5150-firmware", - "source": "rpm", - "epoch": null, - "version": "8.24.2.2", - "release": "34.el7", - "arch": "noarch" - }, - { - "name": "gawk", - "source": "rpm", - "epoch": null, - "version": "4.0.2", - "release": "4.el7", - "arch": "x86_64" - }, - { - "name": "iwl4965-firmware", - "source": "rpm", - "epoch": null, - "version": "228.61.2.24", - "release": "34.el7", - "arch": "noarch" - }, - { - "name": "expat", - "source": "rpm", - "epoch": null, - "version": "2.1.0", - "release": "8.el7", - "arch": "x86_64" - }, - { - "name": "iwl3160-firmware", - "source": "rpm", - "epoch": null, - "version": "22.0.7.0", - "release": "34.el7", - "arch": "noarch" - }, - { - "name": "libattr", - "source": "rpm", - "epoch": null, - "version": "2.4.46", - "release": "12.el7", - "arch": "x86_64" - }, - { - "name": "iwl3945-firmware", - "source": "rpm", - "epoch": null, - "version": "15.32.2.9", - "release": "34.el7", - "arch": "noarch" - }, - { - "name": "libcap", - "source": "rpm", - "epoch": null, - "version": "2.22", - "release": "8.el7", - "arch": "x86_64" - }, - { - "name": "libsemanage-python", - "source": "rpm", - "epoch": null, - "version": "2.1.10", - "release": "16.el7", - "arch": "x86_64" - }, - { - "name": "libxml2", - "source": "rpm", - "epoch": null, - "version": "2.9.1", - "release": "5.el7", - "arch": "x86_64" - }, - { - "name": "perl-HTTP-Tiny", - "source": "rpm", - "epoch": null, - "version": "0.033", - "release": "3.el7", - "arch": "noarch" - }, - { - "name": "libgcrypt", - "source": "rpm", - "epoch": null, - "version": "1.5.3", - "release": "4.el7", - "arch": "x86_64" - }, - { - "name": "perl-Pod-Perldoc", - "source": "rpm", - "epoch": null, - "version": "3.20", - "release": "4.el7", - "arch": "noarch" - }, - { - "name": "lua", - "source": "rpm", - "epoch": null, - "version": "5.1.4", - "release": "14.el7", - "arch": "x86_64" - }, - { - "name": "perl-Encode", - "source": "rpm", - "epoch": null, - "version": "2.51", - "release": "7.el7", - "arch": "x86_64" - }, - { - "name": "pkgconfig", - "source": "rpm", - "epoch": 1, - "version": "0.27.1", - "release": "4.el7", - "arch": "x86_64" - }, - { - "name": "perl-Pod-Usage", - "source": "rpm", - "epoch": null, - "version": "1.63", - "release": "3.el7", - "arch": "noarch" - }, - { - "name": "shared-mime-info", - "source": "rpm", - "epoch": null, - "version": "1.1", - "release": "7.el7", - "arch": "x86_64" - }, - { - "name": "perl-Exporter", - "source": "rpm", - "epoch": null, - "version": "5.68", - "release": "3.el7", - "arch": "noarch" - }, - { - "name": "libcap-ng", - "source": "rpm", - "epoch": null, - "version": "0.7.3", - "release": "5.el7", - "arch": "x86_64" - }, - { - "name": "perl-Time-Local", - "source": "rpm", - "epoch": null, - "version": "1.2300", - "release": "2.el7", - "arch": "noarch" - }, - { - "name": "libidn", - "source": "rpm", - "epoch": null, - "version": "1.28", - "release": "3.el7", - "arch": "x86_64" - }, - { - "name": "perl-Carp", - "source": "rpm", - "epoch": null, - "version": "1.26", - "release": "244.el7", - "arch": "noarch" - }, - { - "name": "gmp", - "source": "rpm", - "epoch": 1, - "version": "5.1.1", - "release": "5.el7", - "arch": "x86_64" - }, - { - "name": "perl-PathTools", - "source": "rpm", - "epoch": null, - "version": "3.40", - "release": "5.el7", - "arch": "x86_64" - }, - { - "name": "p11-kit", - "source": "rpm", - "epoch": null, - "version": "0.18.7", - "release": "4.el7", - "arch": "x86_64" - }, - { - "name": "perl-macros", - "source": "rpm", - "epoch": 4, - "version": "5.16.3", - "release": "285.el7", - "arch": "x86_64" - }, - { - "name": "libdaemon", - "source": "rpm", - "epoch": null, - "version": "0.14", - "release": "7.el7", - "arch": "x86_64" - }, - { - "name": "perl-File-Temp", - "source": "rpm", - "epoch": null, - "version": "0.23.01", - "release": "3.el7", - "arch": "noarch" - }, - { - "name": "libcroco", - "source": "rpm", - "epoch": null, - "version": "0.6.8", - "release": "5.el7", - "arch": "x86_64" - }, - { - "name": "perl-threads-shared", - "source": "rpm", - "epoch": null, - "version": "1.43", - "release": "6.el7", - "arch": "x86_64" - }, - { - "name": "libnl3-cli", - "source": "rpm", - "epoch": null, - "version": "3.2.21", - "release": "6.el7", - "arch": "x86_64" - }, - { - "name": "perl-Filter", - "source": "rpm", - "epoch": null, - "version": "1.49", - "release": "3.el7", - "arch": "x86_64" - }, - { - "name": "cyrus-sasl-lib", - "source": "rpm", - "epoch": null, - "version": "2.1.26", - "release": "17.el7", - "arch": "x86_64" - }, - { - "name": "perl-Getopt-Long", - "source": "rpm", - "epoch": null, - "version": "2.40", - "release": "2.el7", - "arch": "noarch" - }, - { - "name": "groff-base", - "source": "rpm", - "epoch": null, - "version": "1.22.2", - "release": "8.el7", - "arch": "x86_64" - }, - { - "name": "gpg-pubkey", - "source": "rpm", - "epoch": null, - "version": "04bbaa7b", - "release": "4c881cbf", - "arch": null - }, - { - "name": "libunistring", - "source": "rpm", - "epoch": null, - "version": "0.9.3", - "release": "9.el7", - "arch": "x86_64" - }, - { - "name": "libicu", - "source": "rpm", - "epoch": null, - "version": "50.1.2", - "release": "11.el7", - "arch": "x86_64" - }, - { - "name": "diffutils", - "source": "rpm", - "epoch": null, - "version": "3.3", - "release": "4.el7", - "arch": "x86_64" - }, - { - "name": "libdnet", - "source": "rpm", - "epoch": null, - "version": "1.12", - "release": "13.1.el7", - "arch": "x86_64" - }, - { - "name": "xz", - "source": "rpm", - "epoch": null, - "version": "5.1.2", - "release": "8alpha.el7", - "arch": "x86_64" - }, - { - "name": "open-vm-tools", - "source": "rpm", - "epoch": null, - "version": "9.4.0", - "release": "6.el7", - "arch": "x86_64" - }, - { - "name": "sysvinit-tools", - "source": "rpm", - "epoch": null, - "version": "2.88", - "release": "14.dsf.el7", - "arch": "x86_64" - }, - { - "name": "open-vm-tools-deploypkg", - "source": "rpm", - "epoch": 0, - "version": "9.4.10", - "release": "3", - "arch": "x86_64" - }, - { - "name": "newt", - "source": "rpm", - "epoch": null, - "version": "0.52.15", - "release": "4.el7", - "arch": "x86_64" - }, - { - "name": "python-kitchen", - "source": "rpm", - "epoch": null, - "version": "1.1.1", - "release": "5.el7", - "arch": "noarch" - }, - { - "name": "ethtool", - "source": "rpm", - "epoch": 2, - "version": "3.8", - "release": "3.el7", - "arch": "x86_64" - }, - { - "name": "yum-utils", - "source": "rpm", - "epoch": null, - "version": "1.1.31", - "release": "29.el7", - "arch": "noarch" - }, - { - "name": "hostname", - "source": "rpm", - "epoch": null, - "version": "3.13", - "release": "3.el7", - "arch": "x86_64" - }, - { - "name": "emacs-filesystem", - "source": "rpm", - "epoch": 1, - "version": "24.3", - "release": "11.el7", - "arch": "noarch" - }, - { - "name": "gdbm", - "source": "rpm", - "epoch": null, - "version": "1.10", - "release": "8.el7", - "arch": "x86_64" - }, - { - "name": "emacs-common", - "source": "rpm", - "epoch": 1, - "version": "24.3", - "release": "11.el7", - "arch": "x86_64" - }, - { - "name": "less", - "source": "rpm", - "epoch": null, - "version": "458", - "release": "8.el7", - "arch": "x86_64" - }, - { - "name": "epel-release", - "source": "rpm", - "epoch": null, - "version": "7", - "release": "5", - "arch": "noarch" - }, - { - "name": "p11-kit-trust", - "source": "rpm", - "epoch": null, - "version": "0.18.7", - "release": "4.el7", - "arch": "x86_64" - }, - { - "name": "pkcs11-helper", - "source": "rpm", - "epoch": null, - "version": "1.11", - "release": "3.el7", - "arch": "x86_64" - }, - { - "name": "nettle", - "source": "rpm", - "epoch": null, - "version": "2.7.1", - "release": "2.el7", - "arch": "x86_64" - }, - { - "name": "easy-rsa", - "source": "rpm", - "epoch": null, - "version": "2.2.2", - "release": "1.el7", - "arch": "noarch" - }, - { - "name": "gobject-introspection", - "source": "rpm", - "epoch": null, - "version": "1.36.0", - "release": "4.el7", - "arch": "x86_64" - }, - { - "name": "libevent", - "source": "rpm", - "epoch": null, - "version": "2.0.21", - "release": "4.el7", - "arch": "x86_64" - }, - { - "name": "gsettings-desktop-schemas", - "source": "rpm", - "epoch": null, - "version": "3.8.2", - "release": "3.el7", - "arch": "x86_64" - }, - { - "name": "dhcp-libs", - "source": "rpm", - "epoch": 12, - "version": "4.2.5", - "release": "36.el7.centos", - "arch": "x86_64" - }, - { - "name": "acl", - "source": "rpm", - "epoch": null, - "version": "2.2.51", - "release": "12.el7", - "arch": "x86_64" - }, - { - "name": "dhcp", - "source": "rpm", - "epoch": 12, - "version": "4.2.5", - "release": "36.el7.centos", - "arch": "x86_64" - }, - { - "name": "elfutils-libs", - "source": "rpm", - "epoch": null, - "version": "0.158", - "release": "3.el7", - "arch": "x86_64" - }, - { - "name": "bind-license", - "source": "rpm", - "epoch": 32, - "version": "9.9.4", - "release": "18.el7_1.1", - "arch": "noarch" - }, - { - "name": "mozjs17", - "source": "rpm", - "epoch": null, - "version": "17.0.0", - "release": "10.el7", - "arch": "x86_64" - }, - { - "name": "bind", - "source": "rpm", - "epoch": 32, - "version": "9.9.4", - "release": "18.el7_1.1", - "arch": "x86_64" - }, - { - "name": "pinentry", - "source": "rpm", - "epoch": null, - "version": "0.8.1", - "release": "14.el7", - "arch": "x86_64" - }, - { - "name": "bind-libs-lite", - "source": "rpm", - "epoch": 32, - "version": "9.9.4", - "release": "18.el7_1.1", - "arch": "x86_64" - }, - { - "name": "libselinux-utils", - "source": "rpm", - "epoch": null, - "version": "2.2.2", - "release": "6.el7", - "arch": "x86_64" - }, - { - "name": "audit-libs", - "source": "rpm", - "epoch": null, - "version": "2.4.1", - "release": "5.el7", - "arch": "x86_64" - }, - { - "name": "libedit", - "source": "rpm", - "epoch": null, - "version": "3.0", - "release": "12.20121213cvs.el7", - "arch": "x86_64" - }, - { - "name": "audit-libs-python", - "source": "rpm", - "epoch": null, - "version": "2.4.1", - "release": "5.el7", - "arch": "x86_64" - }, - { - "name": "libmodman", - "source": "rpm", - "epoch": null, - "version": "2.0.1", - "release": "8.el7", - "arch": "x86_64" - }, - { - "name": "checkpolicy", - "source": "rpm", - "epoch": null, - "version": "2.1.12", - "release": "6.el7", - "arch": "x86_64" - }, - { - "name": "glib-networking", - "source": "rpm", - "epoch": null, - "version": "2.36.2", - "release": "3.el7", - "arch": "x86_64" - }, - { - "name": "setools-libs", - "source": "rpm", - "epoch": null, - "version": "3.3.7", - "release": "46.el7", - "arch": "x86_64" - }, - { - "name": "snappy", - "source": "rpm", - "epoch": null, - "version": "1.1.0", - "release": "3.el7", - "arch": "x86_64" - }, - { - "name": "audit", - "source": "rpm", - "epoch": null, - "version": "2.4.1", - "release": "5.el7", - "arch": "x86_64" - }, - { - "name": "numactl-libs", - "source": "rpm", - "epoch": null, - "version": "2.0.9", - "release": "2.el7", - "arch": "x86_64" - }, - { - "name": "autogen-libopts", - "source": "rpm", - "epoch": null, - "version": "5.18", - "release": "5.el7", - "arch": "x86_64" - }, - { - "name": "libverto", - "source": "rpm", - "epoch": null, - "version": "0.2.5", - "release": "4.el7", - "arch": "x86_64" - }, - { - "name": "ntp", - "source": "rpm", - "epoch": null, - "version": "4.2.6p5", - "release": "19.el7.centos.3", - "arch": "x86_64" - }, - { - "name": "libsemanage", - "source": "rpm", - "epoch": null, - "version": "2.1.10", - "release": "16.el7", - "arch": "x86_64" - }, - { - "name": "krb5-libs", - "source": "rpm", - "epoch": null, - "version": "1.11.3", - "release": "49.el7", - "arch": "x86_64" - }, - { - "name": "openldap", - "source": "rpm", - "epoch": null, - "version": "2.4.39", - "release": "3.el7", - "arch": "x86_64" - }, - { - "name": "cracklib", - "source": "rpm", - "epoch": null, - "version": "2.9.0", - "release": "11.el7", - "arch": "x86_64" - }, - { - "name": "libmount", - "source": "rpm", - "epoch": null, - "version": "2.23.2", - "release": "16.el7", - "arch": "x86_64" - }, - { - "name": "systemd-libs", - "source": "rpm", - "epoch": null, - "version": "208", - "release": "11.el7", - "arch": "x86_64" - }, - { - "name": "libpwquality", - "source": "rpm", - "epoch": null, - "version": "1.2.3", - "release": "4.el7", - "arch": "x86_64" - }, - { - "name": "pam", - "source": "rpm", - "epoch": null, - "version": "1.1.8", - "release": "9.el7", - "arch": "x86_64" - }, - { - "name": "shadow-utils", - "source": "rpm", - "epoch": 2, - "version": "4.1.5.1", - "release": "13.el7", - "arch": "x86_64" - }, - { - "name": "util-linux", - "source": "rpm", - "epoch": null, - "version": "2.23.2", - "release": "16.el7", - "arch": "x86_64" - }, - { - "name": "python-libs", - "source": "rpm", - "epoch": null, - "version": "2.7.5", - "release": "16.el7", - "arch": "x86_64" - }, - { - "name": "python-decorator", - "source": "rpm", - "epoch": null, - "version": "3.4.0", - "release": "3.el7", - "arch": "noarch" - }, - { - "name": "gettext", - "source": "rpm", - "epoch": null, - "version": "0.18.2.1", - "release": "4.el7", - "arch": "x86_64" - }, - { - "name": "centos-logos", - "source": "rpm", - "epoch": null, - "version": "70.0.6", - "release": "1.el7.centos", - "arch": "noarch" - }, - { - "name": "libselinux-python", - "source": "rpm", - "epoch": null, - "version": "2.2.2", - "release": "6.el7", - "arch": "x86_64" - }, - { - "name": "python-slip-dbus", - "source": "rpm", - "epoch": null, - "version": "0.4.0", - "release": "2.el7", - "arch": "noarch" - }, - { - "name": "pyliblzma", - "source": "rpm", - "epoch": null, - "version": "0.5.3", - "release": "11.el7", - "arch": "x86_64" - }, - { - "name": "yum-metadata-parser", - "source": "rpm", - "epoch": null, - "version": "1.1.4", - "release": "10.el7", - "arch": "x86_64" - }, - { - "name": "pyxattr", - "source": "rpm", - "epoch": null, - "version": "0.5.1", - "release": "5.el7", - "arch": "x86_64" - }, - { - "name": "python-backports-ssl_match_hostname", - "source": "rpm", - "epoch": null, - "version": "3.4.0.2", - "release": "4.el7", - "arch": "noarch" - }, - { - "name": "python-pyudev", - "source": "rpm", - "epoch": null, - "version": "0.15", - "release": "6.el7", - "arch": "noarch" - }, - { - "name": "binutils", - "source": "rpm", - "epoch": null, - "version": "2.23.52.0.1", - "release": "16.el7", - "arch": "x86_64" - }, - { - "name": "logrotate", - "source": "rpm", - "epoch": null, - "version": "3.8.6", - "release": "4.el7", - "arch": "x86_64" - }, - { - "name": "alsa-lib", - "source": "rpm", - "epoch": null, - "version": "1.0.27.2", - "release": "3.el7", - "arch": "x86_64" - }, - { - "name": "mariadb-libs", - "source": "rpm", - "epoch": 1, - "version": "5.5.35", - "release": "3.el7", - "arch": "x86_64" - }, - { - "name": "libcurl", - "source": "rpm", - "epoch": null, - "version": "7.29.0", - "release": "19.el7", - "arch": "x86_64" - }, - { - "name": "python-urlgrabber", - "source": "rpm", - "epoch": null, - "version": "3.10", - "release": "4.el7", - "arch": "noarch" - }, - { - "name": "rpm-libs", - "source": "rpm", - "epoch": null, - "version": "4.11.1", - "release": "16.el7", - "arch": "x86_64" - }, - { - "name": "fipscheck", - "source": "rpm", - "epoch": null, - "version": "1.4.1", - "release": "5.el7", - "arch": "x86_64" - }, - { - "name": "json-c", - "source": "rpm", - "epoch": null, - "version": "0.11", - "release": "3.el7", - "arch": "x86_64" - }, - { - "name": "virt-what", - "source": "rpm", - "epoch": null, - "version": "1.13", - "release": "5.el7", - "arch": "x86_64" - }, - { - "name": "libnetfilter_conntrack", - "source": "rpm", - "epoch": null, - "version": "1.0.4", - "release": "2.el7", - "arch": "x86_64" - }, - { - "name": "iproute", - "source": "rpm", - "epoch": null, - "version": "3.10.0", - "release": "13.el7", - "arch": "x86_64" - }, - { - "name": "qrencode-libs", - "source": "rpm", - "epoch": null, - "version": "3.4.1", - "release": "3.el7", - "arch": "x86_64" - }, - { - "name": "device-mapper-libs", - "source": "rpm", - "epoch": 7, - "version": "1.02.84", - "release": "14.el7", - "arch": "x86_64" - }, - { - "name": "systemd", - "source": "rpm", - "epoch": null, - "version": "208", - "release": "11.el7", - "arch": "x86_64" - }, - { - "name": "systemd-sysv", - "source": "rpm", - "epoch": null, - "version": "208", - "release": "11.el7", - "arch": "x86_64" - }, - { - "name": "iputils", - "source": "rpm", - "epoch": null, - "version": "20121221", - "release": "6.el7", - "arch": "x86_64" - }, - { - "name": "device-mapper-event-libs", - "source": "rpm", - "epoch": 7, - "version": "1.02.84", - "release": "14.el7", - "arch": "x86_64" - }, - { - "name": "NetworkManager-glib", - "source": "rpm", - "epoch": 1, - "version": "0.9.9.1", - "release": "13.git20140326.4dba720.el7", - "arch": "x86_64" - }, - { - "name": "polkit-pkla-compat", - "source": "rpm", - "epoch": null, - "version": "0.1", - "release": "4.el7", - "arch": "x86_64" - }, - { - "name": "cronie-anacron", - "source": "rpm", - "epoch": null, - "version": "1.4.11", - "release": "11.el7", - "arch": "x86_64" - }, - { - "name": "crontabs", - "source": "rpm", - "epoch": null, - "version": "1.11", - "release": "6.20121102git.el7", - "arch": "noarch" - }, - { - "name": "device-mapper-event", - "source": "rpm", - "epoch": 7, - "version": "1.02.84", - "release": "14.el7", - "arch": "x86_64" - }, - { - "name": "avahi-libs", - "source": "rpm", - "epoch": null, - "version": "0.6.31", - "release": "13.el7", - "arch": "x86_64" - }, - { - "name": "avahi-autoipd", - "source": "rpm", - "epoch": null, - "version": "0.6.31", - "release": "13.el7", - "arch": "x86_64" - }, - { - "name": "dnsmasq", - "source": "rpm", - "epoch": null, - "version": "2.66", - "release": "12.el7", - "arch": "x86_64" - }, - { - "name": "ebtables", - "source": "rpm", - "epoch": null, - "version": "2.0.10", - "release": "13.el7", - "arch": "x86_64" - }, - { - "name": "libpciaccess", - "source": "rpm", - "epoch": null, - "version": "0.13.1", - "release": "4.1.el7", - "arch": "x86_64" - }, - { - "name": "fxload", - "source": "rpm", - "epoch": null, - "version": "2002_04_11", - "release": "16.el7", - "arch": "x86_64" - }, - { - "name": "alsa-tools-firmware", - "source": "rpm", - "epoch": null, - "version": "1.0.27", - "release": "4.el7", - "arch": "x86_64" - }, - { - "name": "libpipeline", - "source": "rpm", - "epoch": null, - "version": "1.2.3", - "release": "3.el7", - "arch": "x86_64" - }, - { - "name": "gnupg2", - "source": "rpm", - "epoch": null, - "version": "2.0.22", - "release": "3.el7", - "arch": "x86_64" - }, - { - "name": "rpm-python", - "source": "rpm", - "epoch": null, - "version": "4.11.1", - "release": "16.el7", - "arch": "x86_64" - }, - { - "name": "pygpgme", - "source": "rpm", - "epoch": null, - "version": "0.3", - "release": "9.el7", - "arch": "x86_64" - }, - { - "name": "hardlink", - "source": "rpm", - "epoch": 1, - "version": "1.0", - "release": "19.el7", - "arch": "x86_64" - }, - { - "name": "dracut-network", - "source": "rpm", - "epoch": null, - "version": "033", - "release": "161.el7", - "arch": "x86_64" - }, - { - "name": "plymouth", - "source": "rpm", - "epoch": null, - "version": "0.8.9", - "release": "0.10.20140113.el7.centos", - "arch": "x86_64" - }, - { - "name": "teamd", - "source": "rpm", - "epoch": null, - "version": "1.9", - "release": "15.el7", - "arch": "x86_64" - }, - { - "name": "libestr", - "source": "rpm", - "epoch": null, - "version": "0.1.9", - "release": "2.el7", - "arch": "x86_64" - }, - { - "name": "NetworkManager-tui", - "source": "rpm", - "epoch": 1, - "version": "0.9.9.1", - "release": "13.git20140326.4dba720.el7", - "arch": "x86_64" - }, - { - "name": "kernel", - "source": "rpm", - "epoch": null, - "version": "3.10.0", - "release": "123.el7", - "arch": "x86_64" - }, - { - "name": "dracut-config-rescue", - "source": "rpm", - "epoch": null, - "version": "033", - "release": "161.el7", - "arch": "x86_64" - }, - { - "name": "man-db", - "source": "rpm", - "epoch": null, - "version": "2.6.3", - "release": "9.el7", - "arch": "x86_64" - }, - { - "name": "lvm2", - "source": "rpm", - "epoch": 7, - "version": "2.02.105", - "release": "14.el7", - "arch": "x86_64" - }, - { - "name": "libgcc", - "source": "rpm", - "epoch": null, - "version": "4.8.2", - "release": "16.el7", - "arch": "x86_64" - }, - { - "name": "setup", - "source": "rpm", - "epoch": null, - "version": "2.8.71", - "release": "4.el7", - "arch": "noarch" - }, - { - "name": "microcode_ctl", - "source": "rpm", - "epoch": 2, - "version": "2.1", - "release": "7.1.el7", - "arch": "x86_64" - }, - { - "name": "basesystem", - "source": "rpm", - "epoch": null, - "version": "10.0", - "release": "7.el7.centos", - "arch": "noarch" - }, - { - "name": "biosdevname", - "source": "rpm", - "epoch": null, - "version": "0.5.0", - "release": "10.el7", - "arch": "x86_64" - }, - { - "name": "linux-firmware", - "source": "rpm", - "epoch": null, - "version": "20140213", - "release": "0.3.git4164c23.el7", - "arch": "noarch" - }, - { - "name": "openssh-server", - "source": "rpm", - "epoch": null, - "version": "6.4p1", - "release": "8.el7", - "arch": "x86_64" - }, - { - "name": "parted", - "source": "rpm", - "epoch": null, - "version": "3.1", - "release": "17.el7", - "arch": "x86_64" - }, - { - "name": "nss-softokn-freebl", - "source": "rpm", - "epoch": null, - "version": "3.15.4", - "release": "2.el7", - "arch": "x86_64" - }, - { - "name": "selinux-policy-targeted", - "source": "rpm", - "epoch": null, - "version": "3.12.1", - "release": "153.el7", - "arch": "noarch" - }, - { - "name": "glibc", - "source": "rpm", - "epoch": null, - "version": "2.17", - "release": "55.el7", - "arch": "x86_64" - }, - { - "name": "xfsprogs", - "source": "rpm", - "epoch": null, - "version": "3.2.0", - "release": "0.10.alpha2.el7", - "arch": "x86_64" - }, - { - "name": "libstdc++", - "source": "rpm", - "epoch": null, - "version": "4.8.2", - "release": "16.el7", - "arch": "x86_64" - }, - { - "name": "e2fsprogs", - "source": "rpm", - "epoch": null, - "version": "1.42.9", - "release": "4.el7", - "arch": "x86_64" - }, - { - "name": "bash", - "source": "rpm", - "epoch": null, - "version": "4.2.45", - "release": "5.el7", - "arch": "x86_64" - }, - { - "name": "passwd", - "source": "rpm", - "epoch": null, - "version": "0.79", - "release": "4.el7", - "arch": "x86_64" - }, - { - "name": "pcre", - "source": "rpm", - "epoch": null, - "version": "8.32", - "release": "12.el7", - "arch": "x86_64" - }, - { - "name": "tar", - "source": "rpm", - "epoch": 2, - "version": "1.26", - "release": "29.el7", - "arch": "x86_64" - }, - { - "name": "zlib", - "source": "rpm", - "epoch": null, - "version": "1.2.7", - "release": "13.el7", - "arch": "x86_64" - }, - { - "name": "rootfiles", - "source": "rpm", - "epoch": null, - "version": "8.1", - "release": "11.el7", - "arch": "noarch" - }, - { - "name": "iwl6000g2a-firmware", - "source": "rpm", - "epoch": null, - "version": "17.168.5.3", - "release": "34.el7", - "arch": "noarch" - }, - { - "name": "libuuid", - "source": "rpm", - "epoch": null, - "version": "2.23.2", - "release": "16.el7", - "arch": "x86_64" - }, - { - "name": "iwl2030-firmware", - "source": "rpm", - "epoch": null, - "version": "18.168.6.1", - "release": "34.el7", - "arch": "noarch" - }, - { - "name": "popt", - "source": "rpm", - "epoch": null, - "version": "1.13", - "release": "16.el7", - "arch": "x86_64" - }, - { - "name": "iwl100-firmware", - "source": "rpm", - "epoch": null, - "version": "39.31.5.1", - "release": "34.el7", - "arch": "noarch" - }, - { - "name": "libcom_err", - "source": "rpm", - "epoch": null, - "version": "1.42.9", - "release": "4.el7", - "arch": "x86_64" - }, - { - "name": "iwl135-firmware", - "source": "rpm", - "epoch": null, - "version": "18.168.6.1", - "release": "34.el7", - "arch": "noarch" - }, - { - "name": "libdb", - "source": "rpm", - "epoch": null, - "version": "5.3.21", - "release": "17.el7", - "arch": "x86_64" - }, - { - "name": "iwl105-firmware", - "source": "rpm", - "epoch": null, - "version": "18.168.6.1", - "release": "34.el7", - "arch": "noarch" - }, - { - "name": "bzip2-libs", - "source": "rpm", - "epoch": null, - "version": "1.0.6", - "release": "12.el7", - "arch": "x86_64" - }, - { - "name": "libertas-sd8686-firmware", - "source": "rpm", - "epoch": null, - "version": "20140213", - "release": "0.3.git4164c23.el7", - "arch": "noarch" - }, - { - "name": "readline", - "source": "rpm", - "epoch": null, - "version": "6.2", - "release": "9.el7", - "arch": "x86_64" - }, - { - "name": "ivtv-firmware", - "source": "rpm", - "epoch": 2, - "version": "20080701", - "release": "26.el7", - "arch": "noarch" - }, - { - "name": "elfutils-libelf", - "source": "rpm", - "epoch": null, - "version": "0.158", - "release": "3.el7", - "arch": "x86_64" - }, - { - "name": "libertas-usb8388-firmware", - "source": "rpm", - "epoch": 2, - "version": "20140213", - "release": "0.3.git4164c23.el7", - "arch": "noarch" - }, - { - "name": "libgpg-error", - "source": "rpm", - "epoch": null, - "version": "1.12", - "release": "3.el7", - "arch": "x86_64" - }, - { - "name": "iwl5000-firmware", - "source": "rpm", - "epoch": null, - "version": "8.83.5.1_1", - "release": "34.el7", - "arch": "noarch" - }, - { - "name": "libacl", - "source": "rpm", - "epoch": null, - "version": "2.2.51", - "release": "12.el7", - "arch": "x86_64" - }, - { - "name": "gpg-pubkey", - "source": "rpm", - "epoch": null, - "version": "f4a80eb5", - "release": "53a7ff4b", - "arch": null - }, - { - "name": "cpio", - "source": "rpm", - "epoch": null, - "version": "2.11", - "release": "22.el7", - "arch": "x86_64" - }, - { - "name": "perl-parent", - "source": "rpm", - "epoch": 1, - "version": "0.225", - "release": "244.el7", - "arch": "noarch" - }, - { - "name": "libnl3", - "source": "rpm", - "epoch": null, - "version": "3.2.21", - "release": "6.el7", - "arch": "x86_64" - }, - { - "name": "perl-podlators", - "source": "rpm", - "epoch": null, - "version": "2.5.1", - "release": "3.el7", - "arch": "noarch" - }, - { - "name": "sqlite", - "source": "rpm", - "epoch": null, - "version": "3.7.17", - "release": "4.el7", - "arch": "x86_64" - }, - { - "name": "perl-Pod-Escapes", - "source": "rpm", - "epoch": 1, - "version": "1.04", - "release": "285.el7", - "arch": "noarch" - }, - { - "name": "libffi", - "source": "rpm", - "epoch": null, - "version": "3.0.13", - "release": "11.el7", - "arch": "x86_64" - }, - { - "name": "perl-Text-ParseWords", - "source": "rpm", - "epoch": null, - "version": "3.29", - "release": "4.el7", - "arch": "noarch" - }, - { - "name": "glib2", - "source": "rpm", - "epoch": null, - "version": "2.36.3", - "release": "5.el7", - "arch": "x86_64" - }, - { - "name": "perl-Storable", - "source": "rpm", - "epoch": null, - "version": "2.45", - "release": "3.el7", - "arch": "x86_64" - }, - { - "name": "dbus-glib", - "source": "rpm", - "epoch": null, - "version": "0.100", - "release": "7.el7", - "arch": "x86_64" - }, - { - "name": "perl-constant", - "source": "rpm", - "epoch": null, - "version": "1.27", - "release": "2.el7", - "arch": "noarch" - }, - { - "name": "findutils", - "source": "rpm", - "epoch": 1, - "version": "4.5.11", - "release": "3.el7", - "arch": "x86_64" - }, - { - "name": "perl-Socket", - "source": "rpm", - "epoch": null, - "version": "2.010", - "release": "3.el7", - "arch": "x86_64" - }, - { - "name": "file-libs", - "source": "rpm", - "epoch": null, - "version": "5.11", - "release": "21.el7", - "arch": "x86_64" - }, - { - "name": "perl-Time-HiRes", - "source": "rpm", - "epoch": 4, - "version": "1.9725", - "release": "3.el7", - "arch": "x86_64" - }, - { - "name": "libtasn1", - "source": "rpm", - "epoch": null, - "version": "3.3", - "release": "3.el7", - "arch": "x86_64" - }, - { - "name": "perl-Scalar-List-Utils", - "source": "rpm", - "epoch": null, - "version": "1.27", - "release": "248.el7", - "arch": "x86_64" - }, - { - "name": "tcp_wrappers-libs", - "source": "rpm", - "epoch": null, - "version": "7.6", - "release": "77.el7", - "arch": "x86_64" - }, - { - "name": "perl-Pod-Simple", - "source": "rpm", - "epoch": 1, - "version": "3.28", - "release": "4.el7", - "arch": "noarch" - }, - { - "name": "file", - "source": "rpm", - "epoch": null, - "version": "5.11", - "release": "21.el7", - "arch": "x86_64" - }, - { - "name": "perl-File-Path", - "source": "rpm", - "epoch": null, - "version": "2.09", - "release": "2.el7", - "arch": "noarch" - }, - { - "name": "nss-softokn", - "source": "rpm", - "epoch": null, - "version": "3.15.4", - "release": "2.el7", - "arch": "x86_64" - }, - { - "name": "perl-threads", - "source": "rpm", - "epoch": null, - "version": "1.87", - "release": "4.el7", - "arch": "x86_64" - }, - { - "name": "libassuan", - "source": "rpm", - "epoch": null, - "version": "2.1.0", - "release": "3.el7", - "arch": "x86_64" - }, - { - "name": "perl-libs", - "source": "rpm", - "epoch": 4, - "version": "5.16.3", - "release": "285.el7", - "arch": "x86_64" - }, - { - "name": "e2fsprogs-libs", - "source": "rpm", - "epoch": null, - "version": "1.42.9", - "release": "4.el7", - "arch": "x86_64" - }, - { - "name": "perl", - "source": "rpm", - "epoch": 4, - "version": "5.16.3", - "release": "285.el7", - "arch": "x86_64" - }, - { - "name": "which", - "source": "rpm", - "epoch": null, - "version": "2.20", - "release": "7.el7", - "arch": "x86_64" - }, - { - "name": "gpg-pubkey", - "source": "rpm", - "epoch": null, - "version": "66fd4949", - "release": "4803fe57", - "arch": null - }, - { - "name": "libgomp", - "source": "rpm", - "epoch": null, - "version": "4.8.2", - "release": "16.el7", - "arch": "x86_64" - }, - { - "name": "procps-ng", - "source": "rpm", - "epoch": null, - "version": "3.3.10", - "release": "3.el7", - "arch": "x86_64" - }, - { - "name": "kmod-libs", - "source": "rpm", - "epoch": null, - "version": "14", - "release": "9.el7", - "arch": "x86_64" - }, - { - "name": "net-tools", - "source": "rpm", - "epoch": null, - "version": "2.0", - "release": "0.17.20131004git.el7", - "arch": "x86_64" - }, - { - "name": "libnfnetlink", - "source": "rpm", - "epoch": null, - "version": "1.0.1", - "release": "4.el7", - "arch": "x86_64" - }, - { - "name": "libmspack", - "source": "rpm", - "epoch": 0, - "version": "0.0.20040308alpha", - "release": "2", - "arch": "x86_64" - }, - { - "name": "slang", - "source": "rpm", - "epoch": null, - "version": "2.2.4", - "release": "11.el7", - "arch": "x86_64" - }, - { - "name": "python-chardet", - "source": "rpm", - "epoch": null, - "version": "2.0.1", - "release": "7.el7", - "arch": "noarch" - }, - { - "name": "lzo", - "source": "rpm", - "epoch": null, - "version": "2.06", - "release": "6.el7", - "arch": "x86_64" - }, - { - "name": "yum", - "source": "rpm", - "epoch": null, - "version": "3.4.3", - "release": "125.el7.centos", - "arch": "noarch" - }, - { - "name": "pciutils-libs", - "source": "rpm", - "epoch": null, - "version": "3.2.1", - "release": "4.el7", - "arch": "x86_64" - }, - { - "name": "gpm-libs", - "source": "rpm", - "epoch": null, - "version": "1.20.7", - "release": "5.el7", - "arch": "x86_64" - }, - { - "name": "keyutils-libs", - "source": "rpm", - "epoch": null, - "version": "1.5.8", - "release": "3.el7", - "arch": "x86_64" - }, - { - "name": "liblockfile", - "source": "rpm", - "epoch": null, - "version": "1.08", - "release": "17.el7", - "arch": "x86_64" - }, - { - "name": "gettext-libs", - "source": "rpm", - "epoch": null, - "version": "0.18.2.1", - "release": "4.el7", - "arch": "x86_64" - }, - { - "name": "emacs-nox", - "source": "rpm", - "epoch": 1, - "version": "24.3", - "release": "11.el7", - "arch": "x86_64" - }, - { - "name": "libteam", - "source": "rpm", - "epoch": null, - "version": "1.9", - "release": "15.el7", - "arch": "x86_64" - }, - { - "name": "gpg-pubkey", - "source": "rpm", - "epoch": null, - "version": "352c64e5", - "release": "52ae6884", - "arch": null - }, - { - "name": "ca-certificates", - "source": "rpm", - "epoch": null, - "version": "2013.1.95", - "release": "71.el7", - "arch": "noarch" - }, - { - "name": "openvpn", - "source": "rpm", - "epoch": null, - "version": "2.3.7", - "release": "1.el7", - "arch": "x86_64" - }, - { - "name": "gnutls", - "source": "rpm", - "epoch": null, - "version": "3.1.18", - "release": "8.el7", - "arch": "x86_64" - }, - { - "name": "strace", - "source": "rpm", - "epoch": null, - "version": "4.8", - "release": "7.el7", - "arch": "x86_64" - }, - { - "name": "ModemManager-glib", - "source": "rpm", - "epoch": null, - "version": "1.1.0", - "release": "6.git20130913.el7", - "arch": "x86_64" - }, - { - "name": "tmux", - "source": "rpm", - "epoch": null, - "version": "1.8", - "release": "4.el7", - "arch": "x86_64" - }, - { - "name": "vim-minimal", - "source": "rpm", - "epoch": 2, - "version": "7.4.160", - "release": "1.el7", - "arch": "x86_64" - }, - { - "name": "dhcp-common", - "source": "rpm", - "epoch": 12, - "version": "4.2.5", - "release": "36.el7.centos", - "arch": "x86_64" - }, - { - "name": "device-mapper-persistent-data", - "source": "rpm", - "epoch": null, - "version": "0.3.2", - "release": "1.el7", - "arch": "x86_64" - }, - { - "name": "dhclient", - "source": "rpm", - "epoch": 12, - "version": "4.2.5", - "release": "36.el7.centos", - "arch": "x86_64" - }, - { - "name": "libdb-utils", - "source": "rpm", - "epoch": null, - "version": "5.3.21", - "release": "17.el7", - "arch": "x86_64" - }, - { - "name": "bind-libs", - "source": "rpm", - "epoch": 32, - "version": "9.9.4", - "release": "18.el7_1.1", - "arch": "x86_64" - }, - { - "name": "libss", - "source": "rpm", - "epoch": null, - "version": "1.42.9", - "release": "4.el7", - "arch": "x86_64" - }, - { - "name": "bind-utils", - "source": "rpm", - "epoch": 32, - "version": "9.9.4", - "release": "18.el7_1.1", - "arch": "x86_64" - }, - { - "name": "make", - "source": "rpm", - "epoch": 1, - "version": "3.82", - "release": "21.el7", - "arch": "x86_64" - }, - { - "name": "nmap-ncat", - "source": "rpm", - "epoch": 2, - "version": "6.40", - "release": "4.el7", - "arch": "x86_64" - }, - { - "name": "freetype", - "source": "rpm", - "epoch": null, - "version": "2.4.11", - "release": "9.el7", - "arch": "x86_64" - }, - { - "name": "policycoreutils", - "source": "rpm", - "epoch": null, - "version": "2.2.5", - "release": "15.el7", - "arch": "x86_64" - }, - { - "name": "ncurses", - "source": "rpm", - "epoch": null, - "version": "5.9", - "release": "13.20130511.el7", - "arch": "x86_64" - }, - { - "name": "python-IPy", - "source": "rpm", - "epoch": null, - "version": "0.75", - "release": "6.el7", - "arch": "noarch" - }, - { - "name": "libproxy", - "source": "rpm", - "epoch": null, - "version": "0.4.11", - "release": "6.el7", - "arch": "x86_64" - }, - { - "name": "libcgroup", - "source": "rpm", - "epoch": null, - "version": "0.41", - "release": "8.el7", - "arch": "x86_64" - }, - { - "name": "libsoup", - "source": "rpm", - "epoch": null, - "version": "2.42.2", - "release": "3.el7", - "arch": "x86_64" - }, - { - "name": "policycoreutils-python", - "source": "rpm", - "epoch": null, - "version": "2.2.5", - "release": "15.el7", - "arch": "x86_64" - }, - { - "name": "libndp", - "source": "rpm", - "epoch": null, - "version": "1.2", - "release": "4.el7", - "arch": "x86_64" - }, - { - "name": "iftop", - "source": "rpm", - "epoch": null, - "version": "1.0", - "release": "0.7.pre4.el7", - "arch": "x86_64" - }, - { - "name": "libsysfs", - "source": "rpm", - "epoch": null, - "version": "2.1.0", - "release": "16.el7", - "arch": "x86_64" - }, - { - "name": "ntpdate", - "source": "rpm", - "epoch": null, - "version": "4.2.6p5", - "release": "19.el7.centos.3", - "arch": "x86_64" - }, - { - "name": "ustr", - "source": "rpm", - "epoch": null, - "version": "1.0.4", - "release": "16.el7", - "arch": "x86_64" - }, - { - "name": "nss-tools", - "source": "rpm", - "epoch": null, - "version": "3.15.4", - "release": "6.el7", - "arch": "x86_64" - }, - { - "name": "openssl-libs", - "source": "rpm", - "epoch": 1, - "version": "1.0.1e", - "release": "34.el7", - "arch": "x86_64" - }, - { - "name": "gzip", - "source": "rpm", - "epoch": null, - "version": "1.5", - "release": "7.el7", - "arch": "x86_64" - }, - { - "name": "cracklib-dicts", - "source": "rpm", - "epoch": null, - "version": "2.9.0", - "release": "11.el7", - "arch": "x86_64" - }, - { - "name": "nss", - "source": "rpm", - "epoch": null, - "version": "3.15.4", - "release": "6.el7", - "arch": "x86_64" - }, - { - "name": "libuser", - "source": "rpm", - "epoch": null, - "version": "0.60", - "release": "5.el7", - "arch": "x86_64" - }, - { - "name": "coreutils", - "source": "rpm", - "epoch": null, - "version": "8.22", - "release": "11.el7", - "arch": "x86_64" - }, - { - "name": "libblkid", - "source": "rpm", - "epoch": null, - "version": "2.23.2", - "release": "16.el7", - "arch": "x86_64" - }, - { - "name": "libutempter", - "source": "rpm", - "epoch": null, - "version": "1.1.6", - "release": "4.el7", - "arch": "x86_64" - }, - { - "name": "nss-sysinit", - "source": "rpm", - "epoch": null, - "version": "3.15.4", - "release": "6.el7", - "arch": "x86_64" - }, - { - "name": "python", - "source": "rpm", - "epoch": null, - "version": "2.7.5", - "release": "16.el7", - "arch": "x86_64" - }, - { - "name": "dbus-python", - "source": "rpm", - "epoch": null, - "version": "1.1.1", - "release": "9.el7", - "arch": "x86_64" - }, - { - "name": "pygobject3-base", - "source": "rpm", - "epoch": null, - "version": "3.8.2", - "release": "4.el7", - "arch": "x86_64" - }, - { - "name": "python-slip", - "source": "rpm", - "epoch": null, - "version": "0.4.0", - "release": "2.el7", - "arch": "noarch" - }, - { - "name": "python-iniparse", - "source": "rpm", - "epoch": null, - "version": "0.4", - "release": "9.el7", - "arch": "noarch" - }, - { - "name": "newt-python", - "source": "rpm", - "epoch": null, - "version": "0.52.15", - "release": "4.el7", - "arch": "x86_64" - }, - { - "name": "python-configobj", - "source": "rpm", - "epoch": null, - "version": "4.7.2", - "release": "7.el7", - "arch": "noarch" - }, - { - "name": "python-backports", - "source": "rpm", - "epoch": null, - "version": "1.0", - "release": "6.el7", - "arch": "noarch" - }, - { - "name": "python-setuptools", - "source": "rpm", - "epoch": null, - "version": "0.9.8", - "release": "3.el7", - "arch": "noarch" - }, - { - "name": "grubby", - "source": "rpm", - "epoch": null, - "version": "8.28", - "release": "8.el7", - "arch": "x86_64" - }, - { - "name": "kmod", - "source": "rpm", - "epoch": null, - "version": "14", - "release": "9.el7", - "arch": "x86_64" - }, - { - "name": "openssl", - "source": "rpm", - "epoch": 1, - "version": "1.0.1e", - "release": "34.el7", - "arch": "x86_64" - }, - { - "name": "plymouth-core-libs", - "source": "rpm", - "epoch": null, - "version": "0.8.9", - "release": "0.10.20140113.el7.centos", - "arch": "x86_64" - }, - { - "name": "libssh2", - "source": "rpm", - "epoch": null, - "version": "1.4.3", - "release": "8.el7", - "arch": "x86_64" - }, - { - "name": "python-pycurl", - "source": "rpm", - "epoch": null, - "version": "7.19.0", - "release": "17.el7", - "arch": "x86_64" - }, - { - "name": "curl", - "source": "rpm", - "epoch": null, - "version": "7.29.0", - "release": "19.el7", - "arch": "x86_64" - }, - { - "name": "rpm", - "source": "rpm", - "epoch": null, - "version": "4.11.1", - "release": "16.el7", - "arch": "x86_64" - }, - { - "name": "selinux-policy", - "source": "rpm", - "epoch": null, - "version": "3.12.1", - "release": "153.el7", - "arch": "noarch" - }, - { - "name": "fipscheck-lib", - "source": "rpm", - "epoch": null, - "version": "1.4.1", - "release": "5.el7", - "arch": "x86_64" - }, - { - "name": "openssh", - "source": "rpm", - "epoch": null, - "version": "6.4p1", - "release": "8.el7", - "arch": "x86_64" - }, - { - "name": "dmidecode", - "source": "rpm", - "epoch": 1, - "version": "2.12", - "release": "5.el7", - "arch": "x86_64" - }, - { - "name": "libmnl", - "source": "rpm", - "epoch": null, - "version": "1.0.3", - "release": "7.el7", - "arch": "x86_64" - }, - { - "name": "iptables", - "source": "rpm", - "epoch": null, - "version": "1.4.21", - "release": "13.el7", - "arch": "x86_64" - }, - { - "name": "libpcap", - "source": "rpm", - "epoch": 14, - "version": "1.5.3", - "release": "3.el7", - "arch": "x86_64" - }, - { - "name": "device-mapper", - "source": "rpm", - "epoch": 7, - "version": "1.02.84", - "release": "14.el7", - "arch": "x86_64" - }, - { - "name": "cryptsetup-libs", - "source": "rpm", - "epoch": null, - "version": "1.6.3", - "release": "2.el7", - "arch": "x86_64" - }, - { - "name": "dbus", - "source": "rpm", - "epoch": 1, - "version": "1.6.12", - "release": "8.el7", - "arch": "x86_64" - }, - { - "name": "libgudev1", - "source": "rpm", - "epoch": null, - "version": "208", - "release": "11.el7", - "arch": "x86_64" - }, - { - "name": "initscripts", - "source": "rpm", - "epoch": null, - "version": "9.49.17", - "release": "1.el7", - "arch": "x86_64" - }, - { - "name": "polkit", - "source": "rpm", - "epoch": null, - "version": "0.112", - "release": "5.el7", - "arch": "x86_64" - }, - { - "name": "os-prober", - "source": "rpm", - "epoch": null, - "version": "1.58", - "release": "5.el7", - "arch": "x86_64" - }, - { - "name": "cronie", - "source": "rpm", - "epoch": null, - "version": "1.4.11", - "release": "11.el7", - "arch": "x86_64" - }, - { - "name": "grub2-tools", - "source": "rpm", - "epoch": 1, - "version": "2.02", - "release": "0.2.10.el7.centos.1", - "arch": "x86_64" - }, - { - "name": "lvm2-libs", - "source": "rpm", - "epoch": 7, - "version": "2.02.105", - "release": "14.el7", - "arch": "x86_64" - }, - { - "name": "avahi", - "source": "rpm", - "epoch": null, - "version": "0.6.31", - "release": "13.el7", - "arch": "x86_64" - }, - { - "name": "wpa_supplicant", - "source": "rpm", - "epoch": 1, - "version": "2.0", - "release": "12.el7", - "arch": "x86_64" - }, - { - "name": "ppp", - "source": "rpm", - "epoch": null, - "version": "2.4.5", - "release": "33.el7", - "arch": "x86_64" - }, - { - "name": "hwdata", - "source": "rpm", - "epoch": null, - "version": "0.252", - "release": "7.3.el7", - "arch": "noarch" - }, - { - "name": "libdrm", - "source": "rpm", - "epoch": null, - "version": "2.4.50", - "release": "1.1.el7", - "arch": "x86_64" - }, - { - "name": "alsa-firmware", - "source": "rpm", - "epoch": null, - "version": "1.0.27", - "release": "2.el7", - "arch": "noarch" - }, - { - "name": "kpartx", - "source": "rpm", - "epoch": null, - "version": "0.4.9", - "release": "66.el7", - "arch": "x86_64" - }, - { - "name": "pth", - "source": "rpm", - "epoch": null, - "version": "2.0.7", - "release": "22.el7", - "arch": "x86_64" - }, - { - "name": "rpm-build-libs", - "source": "rpm", - "epoch": null, - "version": "4.11.1", - "release": "16.el7", - "arch": "x86_64" - }, - { - "name": "gpgme", - "source": "rpm", - "epoch": null, - "version": "1.3.2", - "release": "5.el7", - "arch": "x86_64" - }, - { - "name": "yum-plugin-fastestmirror", - "source": "rpm", - "epoch": null, - "version": "1.1.31", - "release": "24.el7", - "arch": "noarch" - }, - { - "name": "kernel-tools-libs", - "source": "rpm", - "epoch": null, - "version": "3.10.0", - "release": "123.el7", - "arch": "x86_64" - }, - { - "name": "dracut", - "source": "rpm", - "epoch": null, - "version": "033", - "release": "161.el7", - "arch": "x86_64" - }, - { - "name": "plymouth-scripts", - "source": "rpm", - "epoch": null, - "version": "0.8.9", - "release": "0.10.20140113.el7.centos", - "arch": "x86_64" - }, - { - "name": "jansson", - "source": "rpm", - "epoch": null, - "version": "2.4", - "release": "6.el7", - "arch": "x86_64" - }, - { - "name": "NetworkManager", - "source": "rpm", - "epoch": 1, - "version": "0.9.9.1", - "release": "13.git20140326.4dba720.el7", - "arch": "x86_64" - }, - { - "name": "rsyslog", - "source": "rpm", - "epoch": null, - "version": "7.4.7", - "release": "6.el7", - "arch": "x86_64" - }, - { - "name": "kexec-tools", - "source": "rpm", - "epoch": null, - "version": "2.0.4", - "release": "32.el7.centos", - "arch": "x86_64" - }, - { - "name": "grub2", - "source": "rpm", - "epoch": 1, - "version": "2.02", - "release": "0.2.10.el7.centos.1", - "arch": "x86_64" - }, - { - "name": "kernel-tools", - "source": "rpm", - "epoch": null, - "version": "3.10.0", - "release": "123.el7", - "arch": "x86_64" - }, - { - "name": "firewalld", - "source": "rpm", - "epoch": null, - "version": "0.3.9", - "release": "7.el7", - "arch": "noarch" - } -] diff --git a/awx/main/tests/functional/services.json b/awx/main/tests/functional/services.json deleted file mode 100644 index a86bf4a875..0000000000 --- a/awx/main/tests/functional/services.json +++ /dev/null @@ -1,697 +0,0 @@ -[ - { - "source": "sysv", - "state": "running", - "name": "iprdump" - }, - { - "source": "sysv", - "state": "running", - "name": "iprinit" - }, - { - "source": "sysv", - "state": "running", - "name": "iprupdate" - }, - { - "source": "sysv", - "state": "stopped", - "name": "netconsole" - }, - { - "source": "sysv", - "state": "running", - "name": "network" - }, - { - "source": "systemd", - "state": "stopped", - "name": "arp-ethers.service" - }, - { - "source": "systemd", - "state": "running", - "name": "auditd.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "autovt@.service" - }, - { - "source": "systemd", - "state": "running", - "name": "avahi-daemon.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "blk-availability.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "brandbot.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "console-getty.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "console-shell.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "cpupower.service" - }, - { - "source": "systemd", - "state": "running", - "name": "crond.service" - }, - { - "source": "systemd", - "state": "running", - "name": "dbus-org.fedoraproject.FirewallD1.service" - }, - { - "source": "systemd", - "state": "running", - "name": "dbus-org.freedesktop.Avahi.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "dbus-org.freedesktop.hostname1.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "dbus-org.freedesktop.locale1.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "dbus-org.freedesktop.login1.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "dbus-org.freedesktop.machine1.service" - }, - { - "source": "systemd", - "state": "running", - "name": "dbus-org.freedesktop.NetworkManager.service" - }, - { - "source": "systemd", - "state": "running", - "name": "dbus-org.freedesktop.nm-dispatcher.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "dbus-org.freedesktop.timedate1.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "dbus.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "debug-shell.service" - }, - { - "source": "systemd", - "state": "running", - "name": "dhcpd.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "dhcpd6.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "dhcrelay.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "dm-event.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "dnsmasq.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "dracut-cmdline.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "dracut-initqueue.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "dracut-mount.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "dracut-pre-mount.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "dracut-pre-pivot.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "dracut-pre-trigger.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "dracut-pre-udev.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "dracut-shutdown.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "ebtables.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "emergency.service" - }, - { - "source": "systemd", - "state": "running", - "name": "firewalld.service" - }, - { - "source": "systemd", - "state": "running", - "name": "getty@.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "halt-local.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "initrd-cleanup.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "initrd-parse-etc.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "initrd-switch-root.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "initrd-udevadm-cleanup-db.service" - }, - { - "source": "systemd", - "state": "running", - "name": "irqbalance.service" - }, - { - "source": "systemd", - "state": "running", - "name": "kdump.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "kmod-static-nodes.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "lvm2-lvmetad.service" - }, - { - "source": "systemd", - "state": "running", - "name": "lvm2-monitor.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "lvm2-pvscan@.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "messagebus.service" - }, - { - "source": "systemd", - "state": "running", - "name": "microcode.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "named-setup-rndc.service" - }, - { - "source": "systemd", - "state": "running", - "name": "named.service" - }, - { - "source": "systemd", - "state": "running", - "name": "NetworkManager-dispatcher.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "NetworkManager-wait-online.service" - }, - { - "source": "systemd", - "state": "running", - "name": "NetworkManager.service" - }, - { - "source": "systemd", - "state": "running", - "name": "ntpd.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "ntpdate.service" - }, - { - "source": "systemd", - "state": "running", - "name": "openvpn@.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "plymouth-halt.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "plymouth-kexec.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "plymouth-poweroff.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "plymouth-quit-wait.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "plymouth-quit.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "plymouth-read-write.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "plymouth-reboot.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "plymouth-start.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "plymouth-switch-root.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "polkit.service" - }, - { - "source": "systemd", - "state": "running", - "name": "postfix.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "quotaon.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "rc-local.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "rdisc.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "rescue.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "rhel-autorelabel-mark.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "rhel-autorelabel.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "rhel-configure.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "rhel-dmesg.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "rhel-domainname.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "rhel-import-state.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "rhel-loadmodules.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "rhel-readonly.service" - }, - { - "source": "systemd", - "state": "running", - "name": "rsyslog.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "serial-getty@.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "sshd-keygen.service" - }, - { - "source": "systemd", - "state": "running", - "name": "sshd.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "sshd@.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "systemd-ask-password-console.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "systemd-ask-password-plymouth.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "systemd-ask-password-wall.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "systemd-backlight@.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "systemd-binfmt.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "systemd-fsck-root.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "systemd-fsck@.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "systemd-halt.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "systemd-hibernate.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "systemd-hostnamed.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "systemd-hybrid-sleep.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "systemd-initctl.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "systemd-journal-flush.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "systemd-journald.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "systemd-kexec.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "systemd-localed.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "systemd-logind.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "systemd-machined.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "systemd-modules-load.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "systemd-nspawn@.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "systemd-poweroff.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "systemd-quotacheck.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "systemd-random-seed.service" - }, - { - "source": "systemd", - "state": "running", - "name": "systemd-readahead-collect.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "systemd-readahead-done.service" - }, - { - "source": "systemd", - "state": "running", - "name": "systemd-readahead-drop.service" - }, - { - "source": "systemd", - "state": "running", - "name": "systemd-readahead-replay.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "systemd-reboot.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "systemd-remount-fs.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "systemd-shutdownd.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "systemd-suspend.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "systemd-sysctl.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "systemd-timedated.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "systemd-tmpfiles-clean.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "systemd-tmpfiles-setup-dev.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "systemd-tmpfiles-setup.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "systemd-udev-settle.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "systemd-udev-trigger.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "systemd-udevd.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "systemd-update-utmp-runlevel.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "systemd-update-utmp.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "systemd-user-sessions.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "systemd-vconsole-setup.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "teamd@.service" - }, - { - "source": "systemd", - "state": "running", - "name": "tuned.service" - }, - { - "source": "systemd", - "state": "running", - "name": "vmtoolsd.service" - }, - { - "source": "systemd", - "state": "stopped", - "name": "wpa_supplicant.service" - } -] diff --git a/awx/main/tests/unit/test_tasks.py b/awx/main/tests/unit/test_tasks.py index 166ea95f19..053745cc64 100644 --- a/awx/main/tests/unit/test_tasks.py +++ b/awx/main/tests/unit/test_tasks.py @@ -249,7 +249,7 @@ def test_openstack_client_config_generation_with_project_domain_name(mocker, sou @pytest.mark.parametrize("source,expected", [ (None, True), (False, False), (True, True) ]) -def test_openstack_client_config_generation_with_project_region_name(mocker, source, expected, private_data_dir): +def test_openstack_client_config_generation_with_region(mocker, source, expected, private_data_dir): update = tasks.RunInventoryUpdate() credential_type = CredentialType.defaults['openstack']() inputs = { @@ -259,7 +259,7 @@ def test_openstack_client_config_generation_with_project_region_name(mocker, sou 'project': 'demo-project', 'domain': 'my-demo-domain', 'project_domain_name': 'project-domain', - 'project_region_name': 'region-name', + 'region': 'region-name', } if source is not None: inputs['verify_ssl'] = source @@ -527,7 +527,7 @@ class TestGenericRun(): task.instance = Job(pk=1, id=1) task.event_ct = 17 task.finished_callback(None) - task.dispatcher.dispatch.assert_called_with({'event': 'EOF', 'final_counter': 17, 'job_id': 1}) + task.dispatcher.dispatch.assert_called_with({'event': 'EOF', 'final_counter': 17, 'job_id': 1, 'guid': None}) def test_save_job_metadata(self, job, update_model_wrapper): class MockMe(): diff --git a/awx/main/utils/filters.py b/awx/main/utils/filters.py index 5a01599e86..279a6a30e4 100644 --- a/awx/main/utils/filters.py +++ b/awx/main/utils/filters.py @@ -15,6 +15,10 @@ from django.apps import apps from django.db import models from django.conf import settings +from django_guid.log_filters import CorrelationId +from django_guid.middleware import GuidMiddleware + +from awx import MODE from awx.main.constants import LOGGER_BLOCKLIST from awx.main.utils.common import get_search_fields @@ -364,3 +368,14 @@ class SmartFilter(object): return res[0].result raise RuntimeError("Parsing the filter_string %s went terribly wrong" % filter_string) + + + +class DefaultCorrelationId(CorrelationId): + + def filter(self, record): + guid = GuidMiddleware.get_guid() or '-' + if MODE == 'development': + guid = guid[:8] + record.guid = guid + return True diff --git a/awx/main/utils/formatters.py b/awx/main/utils/formatters.py index 6f804f9f1b..8ebd1fb0c4 100644 --- a/awx/main/utils/formatters.py +++ b/awx/main/utils/formatters.py @@ -154,6 +154,9 @@ class LogstashFormatter(LogstashFormatterBase): if kind == 'job_events' and raw_data.get('python_objects', {}).get('job_event'): job_event = raw_data['python_objects']['job_event'] + guid = job_event.event_data.pop('guid', None) + if guid: + data_for_log['guid'] = guid for field_object in job_event._meta.fields: if not field_object.__class__ or not field_object.__class__.__name__: diff --git a/awx/settings/defaults.py b/awx/settings/defaults.py index 0167325730..af1590862c 100644 --- a/awx/settings/defaults.py +++ b/awx/settings/defaults.py @@ -285,6 +285,7 @@ INSTALLED_APPS = [ 'polymorphic', 'taggit', 'social_django', + 'django_guid', 'corsheaders', 'awx.conf', 'awx.main', @@ -828,11 +829,14 @@ LOGGING = { }, 'dynamic_level_filter': { '()': 'awx.main.utils.filters.DynamicLevelFilter' - } + }, + 'guid': { + '()': 'awx.main.utils.filters.DefaultCorrelationId' + }, }, 'formatters': { 'simple': { - 'format': '%(asctime)s %(levelname)-8s %(name)s %(message)s', + 'format': '%(asctime)s %(levelname)-8s [%(guid)s] %(name)s %(message)s', }, 'json': { '()': 'awx.main.utils.formatters.LogstashFormatter' @@ -842,7 +846,7 @@ LOGGING = { 'format': '%(relativeSeconds)9.3f %(levelname)-8s %(message)s' }, 'dispatcher': { - 'format': '%(asctime)s %(levelname)-8s %(name)s PID:%(process)d %(message)s', + 'format': '%(asctime)s %(levelname)-8s [%(guid)s] %(name)s PID:%(process)d %(message)s', }, 'job_lifecycle': { '()': 'awx.main.utils.formatters.JobLifeCycleFormatter', @@ -852,7 +856,7 @@ LOGGING = { 'console': { '()': 'logging.StreamHandler', 'level': 'DEBUG', - 'filters': ['require_debug_true_or_test'], + 'filters': ['require_debug_true_or_test', 'guid'], 'formatter': 'simple', }, 'null': { @@ -872,33 +876,33 @@ LOGGING = { 'class': 'awx.main.utils.handlers.RSysLogHandler', 'formatter': 'json', 'address': '/var/run/awx-rsyslog/rsyslog.sock', - 'filters': ['external_log_enabled', 'dynamic_level_filter'], + 'filters': ['external_log_enabled', 'dynamic_level_filter', 'guid'], }, 'tower_warnings': { # don't define a level here, it's set by settings.LOG_AGGREGATOR_LEVEL 'class': 'logging.handlers.WatchedFileHandler', - 'filters': ['require_debug_false', 'dynamic_level_filter'], + 'filters': ['require_debug_false', 'dynamic_level_filter', 'guid'], 'filename': os.path.join(LOG_ROOT, 'tower.log'), 'formatter':'simple', }, 'callback_receiver': { # don't define a level here, it's set by settings.LOG_AGGREGATOR_LEVEL 'class': 'logging.handlers.WatchedFileHandler', - 'filters': ['require_debug_false', 'dynamic_level_filter'], + 'filters': ['require_debug_false', 'dynamic_level_filter', 'guid'], 'filename': os.path.join(LOG_ROOT, 'callback_receiver.log'), 'formatter':'simple', }, 'dispatcher': { # don't define a level here, it's set by settings.LOG_AGGREGATOR_LEVEL 'class': 'logging.handlers.WatchedFileHandler', - 'filters': ['require_debug_false', 'dynamic_level_filter'], + 'filters': ['require_debug_false', 'dynamic_level_filter', 'guid'], 'filename': os.path.join(LOG_ROOT, 'dispatcher.log'), 'formatter':'dispatcher', }, 'wsbroadcast': { # don't define a level here, it's set by settings.LOG_AGGREGATOR_LEVEL 'class': 'logging.handlers.WatchedFileHandler', - 'filters': ['require_debug_false', 'dynamic_level_filter'], + 'filters': ['require_debug_false', 'dynamic_level_filter', 'guid'], 'filename': os.path.join(LOG_ROOT, 'wsbroadcast.log'), 'formatter':'simple', }, @@ -914,7 +918,7 @@ LOGGING = { 'task_system': { # don't define a level here, it's set by settings.LOG_AGGREGATOR_LEVEL 'class': 'logging.handlers.WatchedFileHandler', - 'filters': ['require_debug_false', 'dynamic_level_filter'], + 'filters': ['require_debug_false', 'dynamic_level_filter', 'guid'], 'filename': os.path.join(LOG_ROOT, 'task_system.log'), 'formatter':'simple', }, @@ -1094,6 +1098,7 @@ AWX_CALLBACK_PROFILE = False AWX_CLEANUP_PATHS = True MIDDLEWARE = [ + 'django_guid.middleware.GuidMiddleware', 'awx.main.middleware.TimingMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'awx.main.middleware.MigrationRanCheckMiddleware', @@ -1134,3 +1139,7 @@ BROADCAST_WEBSOCKET_NEW_INSTANCE_POLL_RATE_SECONDS = 10 # How often websocket process will generate stats BROADCAST_WEBSOCKET_STATS_POLL_RATE_SECONDS = 5 + +DJANGO_GUID = { + 'GUID_HEADER_NAME': 'X-API-Request-Id', +} diff --git a/awx/sso/views.py b/awx/sso/views.py index ddbc2cbd59..1f2dcad0b9 100644 --- a/awx/sso/views.py +++ b/awx/sso/views.py @@ -58,12 +58,12 @@ class MetadataView(View): def get(self, request, *args, **kwargs): from social_django.utils import load_backend, load_strategy complete_url = reverse('social:complete', args=('saml', )) - saml_backend = load_backend( - load_strategy(request), - 'saml', - redirect_uri=complete_url, - ) try: + saml_backend = load_backend( + load_strategy(request), + 'saml', + redirect_uri=complete_url, + ) metadata, errors = saml_backend.generate_metadata_xml() except Exception as e: logger.exception('unable to generate SAML metadata') diff --git a/awx/templates/error.html b/awx/templates/error.html index 81e54fe434..815235ebfc 100644 --- a/awx/templates/error.html +++ b/awx/templates/error.html @@ -18,7 +18,7 @@ div.response-info span.meta {
); } diff --git a/awx/ui_next/src/components/MultiButtonToggle/MultiButtonToggle.jsx b/awx/ui_next/src/components/MultiButtonToggle/MultiButtonToggle.jsx index b76c4c6a61..efc1cb3d92 100644 --- a/awx/ui_next/src/components/MultiButtonToggle/MultiButtonToggle.jsx +++ b/awx/ui_next/src/components/MultiButtonToggle/MultiButtonToggle.jsx @@ -5,8 +5,10 @@ import { Button } from '@patternfly/react-core'; import ButtonGroup from './ButtonGroup'; const SmallButton = styled(Button)` - padding: 3px 8px; - font-size: var(--pf-global--FontSize--xs); + && { + padding: 3px 8px; + font-size: var(--pf-global--FontSize--xs); + } `; function MultiButtonToggle({ buttons, value, onChange }) { diff --git a/awx/ui_next/src/components/PaginatedTable/ActionItem.jsx b/awx/ui_next/src/components/PaginatedTable/ActionItem.jsx index a6c5e2b239..f9c423fee3 100644 --- a/awx/ui_next/src/components/PaginatedTable/ActionItem.jsx +++ b/awx/ui_next/src/components/PaginatedTable/ActionItem.jsx @@ -14,7 +14,7 @@ export default function ActionItem({ column, tooltip, visible, children }) { `} > - {children} +
{children}
); diff --git a/awx/ui_next/src/components/PaginatedTable/ActionItem.test.jsx b/awx/ui_next/src/components/PaginatedTable/ActionItem.test.jsx index 202e556e83..d38653802f 100644 --- a/awx/ui_next/src/components/PaginatedTable/ActionItem.test.jsx +++ b/awx/ui_next/src/components/PaginatedTable/ActionItem.test.jsx @@ -12,7 +12,7 @@ describe('', () => { const tooltip = wrapper.find('Tooltip'); expect(tooltip.prop('content')).toEqual('a tooltip'); - expect(tooltip.prop('children')).toEqual('foo'); + expect(tooltip.prop('children')).toEqual(
foo
); }); test('should render null if not visible', async () => { diff --git a/awx/ui_next/src/components/ResourceAccessList/ResourceAccessList.jsx b/awx/ui_next/src/components/ResourceAccessList/ResourceAccessList.jsx index 0f5f7c1c64..1cc04c5fa3 100644 --- a/awx/ui_next/src/components/ResourceAccessList/ResourceAccessList.jsx +++ b/awx/ui_next/src/components/ResourceAccessList/ResourceAccessList.jsx @@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useState } from 'react'; import { useLocation } from 'react-router-dom'; import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; -import { TeamsAPI, UsersAPI } from '../../api'; +import { RolesAPI, TeamsAPI, UsersAPI } from '../../api'; import AddResourceRole from '../AddRole/AddResourceRole'; import AlertModal from '../AlertModal'; import DataListToolbar from '../DataListToolbar'; @@ -26,7 +26,13 @@ function ResourceAccessList({ i18n, apiModel, resource }) { const location = useLocation(); const { - result: { accessRecords, itemCount, relatedSearchableKeys, searchableKeys }, + result: { + accessRecords, + itemCount, + relatedSearchableKeys, + searchableKeys, + organizationRoles, + }, error: contentError, isLoading, request: fetchAccessRecords, @@ -37,6 +43,41 @@ function ResourceAccessList({ i18n, apiModel, resource }) { apiModel.readAccessList(resource.id, params), apiModel.readAccessOptions(resource.id), ]); + + // Eventually this could be expanded to other access lists. + // We will need to combine the role ids of all the different level + // of resource level roles. + + let orgRoles; + if (location.pathname.includes('/organizations')) { + const { + data: { results: roles }, + } = await RolesAPI.read({ content_type__isnull: true }); + const sysAdmin = roles.filter( + role => role.name === 'System Administrator' + ); + const sysAud = roles.filter(role => { + let auditor; + if (role.name === 'System Auditor') { + auditor = role.id; + } + return auditor; + }); + + orgRoles = Object.values(resource.summary_fields.object_roles).map( + opt => { + let item; + if (opt.name === 'Admin') { + item = [`${opt.id}, ${sysAdmin[0].id}`, opt.name]; + } else if (sysAud[0].id && opt.name === 'Auditor') { + item = [`${sysAud[0].id}, ${opt.id}`, opt.name]; + } else { + item = [`${opt.id}`, opt.name]; + } + return item; + } + ); + } return { accessRecords: response.data.results, itemCount: response.data.count, @@ -46,8 +87,9 @@ function ResourceAccessList({ i18n, apiModel, resource }) { searchableKeys: Object.keys( actionsResponse.data.actions?.GET || {} ).filter(key => actionsResponse.data.actions?.GET[key].filterable), + organizationRoles: orgRoles, }; - }, [apiModel, location, resource.id]), + }, [apiModel, location, resource]), { accessRecords: [], itemCount: 0, @@ -78,6 +120,29 @@ function ResourceAccessList({ i18n, apiModel, resource }) { fetchItems: fetchAccessRecords, } ); + const toolbarSearchColumns = [ + { + name: i18n._(t`Username`), + key: 'username__icontains', + isDefault: true, + }, + { + name: i18n._(t`First Name`), + key: 'first_name__icontains', + }, + { + name: i18n._(t`Last Name`), + key: 'last_name__icontains', + }, + ]; + + if (organizationRoles?.length > 0) { + toolbarSearchColumns.push({ + name: i18n._(t`Roles`), + key: `or__roles__in`, + options: organizationRoles, + }); + } return ( <> @@ -88,21 +153,7 @@ function ResourceAccessList({ i18n, apiModel, resource }) { itemCount={itemCount} pluralizedItemName={i18n._(t`Roles`)} qsConfig={QS_CONFIG} - toolbarSearchColumns={[ - { - name: i18n._(t`Username`), - key: 'username__icontains', - isDefault: true, - }, - { - name: i18n._(t`First Name`), - key: 'first_name__icontains', - }, - { - name: i18n._(t`Last Name`), - key: 'last_name__icontains', - }, - ]} + toolbarSearchColumns={toolbarSearchColumns} toolbarSortColumns={[ { name: i18n._(t`Username`), diff --git a/awx/ui_next/src/components/ResourceAccessList/ResourceAccessList.test.jsx b/awx/ui_next/src/components/ResourceAccessList/ResourceAccessList.test.jsx index 98b6371414..c77a86f49d 100644 --- a/awx/ui_next/src/components/ResourceAccessList/ResourceAccessList.test.jsx +++ b/awx/ui_next/src/components/ResourceAccessList/ResourceAccessList.test.jsx @@ -1,11 +1,12 @@ import React from 'react'; import { act } from 'react-dom/test-utils'; +import { createMemoryHistory } from 'history'; import { mountWithContexts, waitForElement, } from '../../../testUtils/enzymeHelpers'; -import { OrganizationsAPI, TeamsAPI, UsersAPI } from '../../api'; +import { OrganizationsAPI, TeamsAPI, UsersAPI, RolesAPI } from '../../api'; import ResourceAccessList from './ResourceAccessList'; @@ -17,7 +18,24 @@ describe('', () => { id: 1, name: 'Default', summary_fields: { - object_roles: {}, + object_roles: { + admin_role: { + description: 'Can manage all aspects of the organization', + name: 'Admin', + id: 2, + user_only: true, + }, + execute_role: { + description: 'May run any executable resources in the organization', + name: 'Execute', + id: 3, + }, + project_admin_role: { + description: 'Can manage all projects of the organization', + name: 'Project Admin', + id: 4, + }, + }, user_capabilities: { edit: true, }, @@ -87,12 +105,24 @@ describe('', () => { }); TeamsAPI.disassociateRole.mockResolvedValue({}); UsersAPI.disassociateRole.mockResolvedValue({}); + RolesAPI.read.mockResolvedValue({ + data: { + results: [ + { id: 1, name: 'System Administrator' }, + { id: 14, name: 'System Auditor' }, + ], + }, + }); + const history = createMemoryHistory({ + initialEntries: ['/organizations/1/access'], + }); await act(async () => { wrapper = mountWithContexts( + />, + { context: { router: { history } } } ); }); wrapper.update(); @@ -168,4 +198,24 @@ describe('', () => { expect(OrganizationsAPI.readAccessList).toHaveBeenCalledTimes(2); done(); }); + test('should call api to get org details', async () => { + await waitForElement(wrapper, 'ContentLoading', el => el.length === 0); + + expect( + wrapper.find('PaginatedDataList').prop('toolbarSearchColumns') + ).toStrictEqual([ + { isDefault: true, key: 'username__icontains', name: 'Username' }, + { key: 'first_name__icontains', name: 'First Name' }, + { key: 'last_name__icontains', name: 'Last Name' }, + { + key: 'or__roles__in', + name: 'Roles', + options: [ + ['2, 1', 'Admin'], + ['3', 'Execute'], + ['4', 'Project Admin'], + ], + }, + ]); + }); }); diff --git a/awx/ui_next/src/components/ResourceAccessList/ResourceAccessListItem.jsx b/awx/ui_next/src/components/ResourceAccessList/ResourceAccessListItem.jsx index d641e67e01..669c5223ab 100644 --- a/awx/ui_next/src/components/ResourceAccessList/ResourceAccessListItem.jsx +++ b/awx/ui_next/src/components/ResourceAccessList/ResourceAccessListItem.jsx @@ -56,6 +56,8 @@ function ResourceAccessListItem({ accessRecord, onRoleDelete, i18n }) { onRoleDelete(role, accessRecord); }} isReadOnly={!role.user_capabilities.unattach} + ouiaId={`${role.name}-${role.id}`} + closeBtnAriaLabel={i18n._(t`Remove ${role.name} chip`)} > {role.name} diff --git a/awx/ui_next/src/components/ResourceAccessList/__snapshots__/DeleteRoleConfirmationModal.test.jsx.snap b/awx/ui_next/src/components/ResourceAccessList/__snapshots__/DeleteRoleConfirmationModal.test.jsx.snap index caeea4e351..820035723e 100644 --- a/awx/ui_next/src/components/ResourceAccessList/__snapshots__/DeleteRoleConfirmationModal.test.jsx.snap +++ b/awx/ui_next/src/components/ResourceAccessList/__snapshots__/DeleteRoleConfirmationModal.test.jsx.snap @@ -290,6 +290,7 @@ exports[` should render initially 1`] = ` } } paused={false} + preventScrollOnDeactivate={false} >
initially renders succesfully 1`] = ` > Member @@ -164,11 +165,12 @@ exports[` initially renders succesfully 1`] = ` > Member @@ -253,11 +255,12 @@ exports[` initially renders succesfully 1`] = ` > Member @@ -688,11 +691,12 @@ exports[` initially renders succesfully 1`] = ` > Member @@ -840,6 +844,7 @@ exports[` initially renders succesfully 1`] = ` isClosable={false} numChips={5} onClick={[Function]} + onOverflowChipClick={[Function]} tooltipPosition="top" > initially renders succesfully 1`] = ` > initially renders succesfully 1`] = ` >
@@ -888,19 +894,19 @@ exports[` initially renders succesfully 1`] = ` Member - + ); diff --git a/awx/ui_next/src/screens/Credential/CredentialEdit/CredentialEdit.test.jsx b/awx/ui_next/src/screens/Credential/CredentialEdit/CredentialEdit.test.jsx index 17ab9d3ed4..b750742a22 100644 --- a/awx/ui_next/src/screens/Credential/CredentialEdit/CredentialEdit.test.jsx +++ b/awx/ui_next/src/screens/Credential/CredentialEdit/CredentialEdit.test.jsx @@ -10,6 +10,8 @@ import { CredentialsAPI, CredentialInputSourcesAPI, CredentialTypesAPI, + OrganizationsAPI, + UsersAPI, } from '../../../api'; import CredentialEdit from './CredentialEdit'; @@ -114,6 +116,25 @@ const mockCredential = { kubernetes: false, }; +UsersAPI.readAdminOfOrganizations.mockResolvedValue({ + data: { + count: 1, + results: [ + { + id: 1, + name: 'org', + }, + ], + }, +}); + +OrganizationsAPI.read.mockResolvedValue({ + data: { + results: [{ id: 1 }], + count: 1, + }, +}); + CredentialTypesAPI.read.mockResolvedValue({ data: { results: [ @@ -197,6 +218,58 @@ CredentialTypesAPI.read.mockResolvedValue({ }, injectors: {}, }, + { + id: 9, + type: 'credential_type', + url: '/api/v2/credential_types/9/', + related: { + credentials: '/api/v2/credential_types/9/credentials/', + activity_stream: '/api/v2/credential_types/9/activity_stream/', + }, + summary_fields: { + user_capabilities: { + edit: true, + delete: true, + }, + }, + created: '2021-02-12T19:13:22.352791Z', + modified: '2021-02-12T19:14:15.578773Z', + name: 'Google Compute Engine', + description: '', + kind: 'cloud', + namespace: 'gce', + managed_by_tower: true, + inputs: { + fields: [ + { + id: 'username', + label: 'Service Account Email Address', + type: 'string', + help_text: + 'The email address assigned to the Google Compute Engine service account.', + }, + { + id: 'project', + label: 'Project', + type: 'string', + help_text: + 'The Project ID is the GCE assigned identification. It is often constructed as three words or two words followed by a three-digit number. Examples: project-id-000 and another-project-id', + }, + { + id: 'ssh_key_data', + label: 'RSA Private Key', + type: 'string', + format: 'ssh_private_key', + secret: true, + multiline: true, + help_text: + 'Paste the contents of the PEM file associated with the service account email.', + }, + ], + required: ['username', 'ssh_key_data'], + }, + injectors: {}, + }, ], }, }); @@ -258,7 +331,12 @@ describe('', () => { wrapper = mountWithContexts( , { - context: { router: { history } }, + context: { + router: { history }, + me: { + id: 1, + }, + }, } ); }); @@ -325,6 +403,7 @@ describe('', () => { expect(CredentialsAPI.update).toHaveBeenCalledWith(3, { user: 1, name: 'foo', + organization: null, description: 'bar', credential_type: '1', inputs: { @@ -354,7 +433,40 @@ describe('', () => { expect(CredentialInputSourcesAPI.destroy).toHaveBeenCalledWith(34); expect(history.location.pathname).toBe('/credentials/3/details'); }); + test('inputs are properly rendered', async () => { + history = createMemoryHistory({ initialEntries: ['/credentials'] }); + await act(async () => { + wrapper = mountWithContexts( + , + { + context: { router: { history } }, + } + ); + }); + wrapper.update(); + expect(wrapper.find('input#credential-username').prop('value')).toBe( + 'foo@ansible.com' + ); + expect(wrapper.find('input#credential-project').prop('value')).toBe( + 'foo' + ); + expect( + wrapper.find('textarea#credential-ssh_key_data').prop('value') + ).toBe('$encrypted$'); + }); }); + describe('Initial GET request fails', () => { test('shows error when initial GET request fails', async () => { CredentialTypesAPI.read.mockRejectedValue(new Error()); diff --git a/awx/ui_next/src/screens/Credential/CredentialList/CredentialListItem.jsx b/awx/ui_next/src/screens/Credential/CredentialList/CredentialListItem.jsx index 9886cf3572..34662e5ed2 100644 --- a/awx/ui_next/src/screens/Credential/CredentialList/CredentialListItem.jsx +++ b/awx/ui_next/src/screens/Credential/CredentialList/CredentialListItem.jsx @@ -72,16 +72,16 @@ function CredentialListItem({ - + diff --git a/awx/ui_next/src/screens/Credential/shared/CredentialForm.jsx b/awx/ui_next/src/screens/Credential/shared/CredentialForm.jsx index b46ddf8573..0a33342b50 100644 --- a/awx/ui_next/src/screens/Credential/shared/CredentialForm.jsx +++ b/awx/ui_next/src/screens/Credential/shared/CredentialForm.jsx @@ -1,8 +1,8 @@ import React, { useCallback, useState } from 'react'; +import { func, shape } from 'prop-types'; import { Formik, useField, useFormikContext } from 'formik'; import { withI18n } from '@lingui/react'; import { t } from '@lingui/macro'; -import { arrayOf, func, object, shape } from 'prop-types'; import { ActionGroup, Button, @@ -136,6 +136,7 @@ function CredentialFormFields({ i18n, credentialTypes }) { touched={orgMeta.touched} error={orgMeta.error} required={isGalaxyCredential} + isDisabled={initialValues.isOrgLookupDisabled} /> { @@ -311,18 +314,18 @@ function CredentialForm({ ); } -CredentialForm.proptype = { +CredentialForm.propTypes = { handleSubmit: func.isRequired, handleCancel: func.isRequired, credentialTypes: shape({}).isRequired, credential: shape({}), - inputSources: arrayOf(object), + inputSources: shape({}), submitError: shape({}), }; CredentialForm.defaultProps = { credential: {}, - inputSources: [], + inputSources: {}, submitError: null, }; diff --git a/awx/ui_next/src/screens/Credential/shared/CredentialForm.test.jsx b/awx/ui_next/src/screens/Credential/shared/CredentialForm.test.jsx index ef110a7711..0cd1780b5c 100644 --- a/awx/ui_next/src/screens/Credential/shared/CredentialForm.test.jsx +++ b/awx/ui_next/src/screens/Credential/shared/CredentialForm.test.jsx @@ -327,6 +327,27 @@ describe('', () => { machineFieldExpects(); }); + test('organization lookup should be disabled', async () => { + await act(async () => { + wrapper = mountWithContexts( + + ); + }); + + expect( + wrapper + .find('CredentialFormFields') + .find('OrganizationLookup') + .prop('isDisabled') + ).toBe(true); + }); + test('should display form fields for source control credential properly', async () => { await act(async () => { wrapper = mountWithContexts( diff --git a/awx/ui_next/src/screens/Credential/shared/ExternalTestModal.jsx b/awx/ui_next/src/screens/Credential/shared/ExternalTestModal.jsx index b81b73142c..3e80b5e8d2 100644 --- a/awx/ui_next/src/screens/Credential/shared/ExternalTestModal.jsx +++ b/awx/ui_next/src/screens/Credential/shared/ExternalTestModal.jsx @@ -168,7 +168,7 @@ function ExternalTestModal({ ); } -ExternalTestModal.proptype = { +ExternalTestModal.propType = { credential: shape({}), credentialType: shape({}).isRequired, credentialFormValues: shape({}).isRequired, diff --git a/awx/ui_next/src/screens/Credential/shared/data.credentialTypes.json b/awx/ui_next/src/screens/Credential/shared/data.credentialTypes.json index 6281f15024..d3e048dbd7 100644 --- a/awx/ui_next/src/screens/Credential/shared/data.credentialTypes.json +++ b/awx/ui_next/src/screens/Credential/shared/data.credentialTypes.json @@ -276,7 +276,7 @@ "help_text": "OpenStack domains define administrative boundaries. It is only needed for Keystone v3 authentication URLs. Refer to Ansible Tower documentation for common scenarios." }, { - "id": "project_region_name", + "id": "region", "label": "Region Name", "type": "string" }, diff --git a/awx/ui_next/src/screens/Inventory/InventoryEdit/InventoryEdit.jsx b/awx/ui_next/src/screens/Inventory/InventoryEdit/InventoryEdit.jsx index 9e667a5ea6..f038ebf6fa 100644 --- a/awx/ui_next/src/screens/Inventory/InventoryEdit/InventoryEdit.jsx +++ b/awx/ui_next/src/screens/Inventory/InventoryEdit/InventoryEdit.jsx @@ -118,7 +118,7 @@ function InventoryEdit({ inventory }) { ); } -InventoryEdit.proptype = { +InventoryEdit.propType = { inventory: object.isRequired, }; diff --git a/awx/ui_next/src/screens/Inventory/InventoryList/InventoryListItem.jsx b/awx/ui_next/src/screens/Inventory/InventoryList/InventoryListItem.jsx index 099fd78767..2b2f660efd 100644 --- a/awx/ui_next/src/screens/Inventory/InventoryList/InventoryListItem.jsx +++ b/awx/ui_next/src/screens/Inventory/InventoryList/InventoryListItem.jsx @@ -28,7 +28,7 @@ function InventoryListItem({ isSelected: bool.isRequired, onSelect: func.isRequired, }; - const [isDisabled, setIsDisabled] = useState(false); + const [isCopying, setIsCopying] = useState(false); const copyInventory = useCallback(async () => { await InventoriesAPI.copy(inventory.id, { @@ -38,11 +38,11 @@ function InventoryListItem({ }, [inventory.id, inventory.name, fetchInventories]); const handleCopyStart = useCallback(() => { - setIsDisabled(true); + setIsCopying(true); }, []); const handleCopyFinish = useCallback(() => { - setIsDisabled(false); + setIsCopying(false); }, []); const labelId = `check-action-${inventory.id}`; @@ -115,7 +115,7 @@ function InventoryListItem({ tooltip={i18n._(t`Edit Inventory`)} > )} - )} + ))} {job.summary_fields.user_capabilities.delete && ( { {job.type !== 'system_job' && job.summary_fields.user_capabilities?.start && ( - - - {({ handleRelaunch }) => ( - - )} - + + {job.status === 'failed' && job.type === 'job' ? ( + + {({ handleRelaunch }) => ( + + )} + + ) : ( + + {({ handleRelaunch }) => ( + + )} + + )} )} diff --git a/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateList/NotificationTemplateListItem.jsx b/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateList/NotificationTemplateListItem.jsx index eb19b562c8..f3386f12a9 100644 --- a/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateList/NotificationTemplateListItem.jsx +++ b/awx/ui_next/src/screens/NotificationTemplate/NotificationTemplateList/NotificationTemplateListItem.jsx @@ -159,16 +159,15 @@ function NotificationTemplateListItem({
)} {template.summary_fields.user_capabilities.copy && ( - + + + )} diff --git a/awx/ui_next/src/screens/Project/ProjectJobTemplatesList/ProjectJobTemplatesListItem.jsx b/awx/ui_next/src/screens/Project/ProjectJobTemplatesList/ProjectJobTemplatesListItem.jsx index df2fe6630f..2468ca8352 100644 --- a/awx/ui_next/src/screens/Project/ProjectJobTemplatesList/ProjectJobTemplatesListItem.jsx +++ b/awx/ui_next/src/screens/Project/ProjectJobTemplatesList/ProjectJobTemplatesListItem.jsx @@ -20,7 +20,7 @@ import { import styled from 'styled-components'; import DataListCell from '../../../components/DataListCell'; -import LaunchButton from '../../../components/LaunchButton'; +import { LaunchButton } from '../../../components/LaunchButton'; import Sparkline from '../../../components/Sparkline'; import { toTitleCase } from '../../../util/strings'; diff --git a/awx/ui_next/src/screens/Project/ProjectList/ProjectListItem.jsx b/awx/ui_next/src/screens/Project/ProjectList/ProjectListItem.jsx index 88c72e7f89..5b7498a9bc 100644 --- a/awx/ui_next/src/screens/Project/ProjectList/ProjectListItem.jsx +++ b/awx/ui_next/src/screens/Project/ProjectList/ProjectListItem.jsx @@ -143,16 +143,16 @@ function ProjectListItem({ - + diff --git a/awx/ui_next/src/screens/Template/JobTemplateDetail/JobTemplateDetail.jsx b/awx/ui_next/src/screens/Template/JobTemplateDetail/JobTemplateDetail.jsx index db3e45bc91..3feed6bb7d 100644 --- a/awx/ui_next/src/screens/Template/JobTemplateDetail/JobTemplateDetail.jsx +++ b/awx/ui_next/src/screens/Template/JobTemplateDetail/JobTemplateDetail.jsx @@ -26,7 +26,7 @@ import { } from '../../../components/DetailList'; import DeleteButton from '../../../components/DeleteButton'; import ErrorDetail from '../../../components/ErrorDetail'; -import LaunchButton from '../../../components/LaunchButton'; +import { LaunchButton } from '../../../components/LaunchButton'; import { VariablesDetail } from '../../../components/CodeMirrorInput'; import { JobTemplatesAPI } from '../../../api'; import useRequest, { useDismissableError } from '../../../util/useRequest'; diff --git a/awx/ui_next/src/screens/Template/WorkflowJobTemplateDetail/WorkflowJobTemplateDetail.jsx b/awx/ui_next/src/screens/Template/WorkflowJobTemplateDetail/WorkflowJobTemplateDetail.jsx index 2a5242b9a0..0ce65ca092 100644 --- a/awx/ui_next/src/screens/Template/WorkflowJobTemplateDetail/WorkflowJobTemplateDetail.jsx +++ b/awx/ui_next/src/screens/Template/WorkflowJobTemplateDetail/WorkflowJobTemplateDetail.jsx @@ -24,7 +24,7 @@ import { UserDateDetail, } from '../../../components/DetailList'; import ErrorDetail from '../../../components/ErrorDetail'; -import LaunchButton from '../../../components/LaunchButton'; +import { LaunchButton } from '../../../components/LaunchButton'; import Sparkline from '../../../components/Sparkline'; import { toTitleCase } from '../../../util/strings'; import useRequest, { useDismissableError } from '../../../util/useRequest'; diff --git a/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/VisualizerToolbar.jsx b/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/VisualizerToolbar.jsx index 1170858f83..f68453185a 100644 --- a/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/VisualizerToolbar.jsx +++ b/awx/ui_next/src/screens/Template/WorkflowJobTemplateVisualizer/VisualizerToolbar.jsx @@ -18,7 +18,7 @@ import { WrenchIcon, } from '@patternfly/react-icons'; import styled from 'styled-components'; -import LaunchButton from '../../../components/LaunchButton'; +import { LaunchButton } from '../../../components/LaunchButton'; import { WorkflowDispatchContext, WorkflowStateContext, diff --git a/awx_collection/plugins/modules/tower_ad_hoc_command.py b/awx_collection/plugins/modules/tower_ad_hoc_command.py index d952f954cf..00f16d9f13 100644 --- a/awx_collection/plugins/modules/tower_ad_hoc_command.py +++ b/awx_collection/plugins/modules/tower_ad_hoc_command.py @@ -118,7 +118,7 @@ def main(): limit=dict(), credential=dict(required=True), module_name=dict(required=True), - module_args=dict(default=""), + module_args=dict(), forks=dict(type='int'), verbosity=dict(type='int', choices=['0', '1', '2', '3', '4', '5']), extra_vars=dict(type='dict'), diff --git a/awx_collection/plugins/modules/tower_credential_input_source.py b/awx_collection/plugins/modules/tower_credential_input_source.py index cdc55cb1f0..e53c632777 100644 --- a/awx_collection/plugins/modules/tower_credential_input_source.py +++ b/awx_collection/plugins/modules/tower_credential_input_source.py @@ -76,10 +76,10 @@ from ..module_utils.tower_api import TowerAPIModule def main(): # Any additional arguments that are not fields of the item can be added here argument_spec = dict( - description=dict(default=''), + description=dict(), input_field_name=dict(required=True), target_credential=dict(required=True), - source_credential=dict(default=''), + source_credential=dict(), metadata=dict(type="dict"), state=dict(choices=['present', 'absent'], default='present'), ) diff --git a/awx_collection/plugins/modules/tower_job_template.py b/awx_collection/plugins/modules/tower_job_template.py index c99eb73118..787c145a20 100644 --- a/awx_collection/plugins/modules/tower_job_template.py +++ b/awx_collection/plugins/modules/tower_job_template.py @@ -340,24 +340,24 @@ def main(): argument_spec = dict( name=dict(required=True), new_name=dict(), - description=dict(default=''), + description=dict(), organization=dict(), job_type=dict(choices=['run', 'check']), inventory=dict(), project=dict(), playbook=dict(), - credential=dict(default=''), - vault_credential=dict(default=''), + credential=dict(), + vault_credential=dict(), custom_virtualenv=dict(), credentials=dict(type='list', elements='str'), forks=dict(type='int'), - limit=dict(default=''), + limit=dict(), verbosity=dict(type='int', choices=[0, 1, 2, 3, 4], default=0), extra_vars=dict(type='dict'), - job_tags=dict(default=''), + job_tags=dict(), force_handlers=dict(type='bool', default=False, aliases=['force_handlers_enabled']), - skip_tags=dict(default=''), - start_at_task=dict(default=''), + skip_tags=dict(), + start_at_task=dict(), timeout=dict(type='int', default=0), use_fact_cache=dict(type='bool', aliases=['fact_caching_enabled']), host_config_key=dict(), @@ -399,11 +399,11 @@ def main(): credential = module.params.get('credential') vault_credential = module.params.get('vault_credential') credentials = module.params.get('credentials') - if vault_credential != '': + if vault_credential: if credentials is None: credentials = [] credentials.append(vault_credential) - if credential != '': + if credential: if credentials is None: credentials = [] credentials.append(credential) diff --git a/awx_collection/plugins/modules/tower_project.py b/awx_collection/plugins/modules/tower_project.py index 015badc02b..76cef63f10 100644 --- a/awx_collection/plugins/modules/tower_project.py +++ b/awx_collection/plugins/modules/tower_project.py @@ -229,8 +229,8 @@ def main(): scm_type=dict(choices=['manual', 'git', 'svn', 'insights'], default='manual'), scm_url=dict(), local_path=dict(), - scm_branch=dict(default=''), - scm_refspec=dict(default=''), + scm_branch=dict(), + scm_refspec=dict(), credential=dict(aliases=['scm_credential']), scm_clean=dict(type='bool', default=False), scm_delete_on_update=dict(type='bool', default=False), diff --git a/awx_collection/plugins/modules/tower_settings.py b/awx_collection/plugins/modules/tower_settings.py index c2e8ed1ae5..b0f39126c3 100644 --- a/awx_collection/plugins/modules/tower_settings.py +++ b/awx_collection/plugins/modules/tower_settings.py @@ -133,7 +133,7 @@ def main(): existing_settings = module.get_endpoint('settings/all')['json'] # Begin a json response - json_response = {'changed': False, 'old_values': {}} + json_output = {'changed': False, 'old_values': {}, 'new_values': {}} # Check any of the settings to see if anything needs to be updated needs_update = False @@ -141,18 +141,29 @@ def main(): if a_setting not in existing_settings or existing_settings[a_setting] != new_settings[a_setting]: # At least one thing is different so we need to patch needs_update = True - json_response['old_values'][a_setting] = existing_settings[a_setting] + json_output['old_values'][a_setting] = existing_settings[a_setting] + json_output['new_values'][a_setting] = new_settings[a_setting] + + if module._diff: + json_output['diff'] = { + 'before': json_output['old_values'], + 'after': json_output['new_values'] + } # If nothing needs an update we can simply exit with the response (as not changed) if not needs_update: - module.exit_json(**json_response) + module.exit_json(**json_output) + + if module.check_mode and module._diff: + json_output['changed'] = True + module.exit_json(**json_output) # Make the call to update the settings response = module.patch_endpoint('settings/all', **{'data': new_settings}) if response['status_code'] == 200: # Set the changed response to True - json_response['changed'] = True + json_output['changed'] = True # To deal with the old style values we need to return 'value' in the response new_values = {} @@ -161,11 +172,11 @@ def main(): # If we were using a name we will just add a value of a string, otherwise we will return an array in values if name is not None: - json_response['value'] = new_values[name] + json_output['value'] = new_values[name] else: - json_response['values'] = new_values + json_output['values'] = new_values - module.exit_json(**json_response) + module.exit_json(**json_output) elif 'json' in response and '__all__' in response['json']: module.fail_json(msg=response['json']['__all__']) else: diff --git a/awx_collection/tests/integration/targets/tower_ad_hoc_command/tasks/main.yml b/awx_collection/tests/integration/targets/tower_ad_hoc_command/tasks/main.yml index 1c45ea6b1e..8dafa2338d 100644 --- a/awx_collection/tests/integration/targets/tower_ad_hoc_command/tasks/main.yml +++ b/awx_collection/tests/integration/targets/tower_ad_hoc_command/tasks/main.yml @@ -48,6 +48,19 @@ - "result is changed" - "result.status == 'successful'" +- name: Launch an Ad Hoc Command without module argument + tower_ad_hoc_command: + inventory: "Demo Inventory" + credential: "{{ ssh_cred_name }}" + module_name: "ping" + wait: true + register: result + +- assert: + that: + - "result is changed" + - "result.status == 'successful'" + - name: Check module fails with correct msg tower_ad_hoc_command: inventory: "{{ inv_name }}" diff --git a/docs/licenses/django-guid.txt b/docs/licenses/django-guid.txt new file mode 100644 index 0000000000..de842ea1c7 --- /dev/null +++ b/docs/licenses/django-guid.txt @@ -0,0 +1,27 @@ +Copyright (c) 2019 Jonas Krüger Svensson +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the authors nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/requirements/requirements.in b/requirements/requirements.in index 263b95dfbe..93b5b4f72e 100644 --- a/requirements/requirements.in +++ b/requirements/requirements.in @@ -13,6 +13,7 @@ django-auth-ldap django-cors-headers django-crum django-extensions>=2.2.9 # https://github.com/ansible/awx/pull/6441 +django-guid==2.2.0 # pinned to match Django 2.2 django-jsonfield==1.2.0 # see UPGRADE BLOCKERs django-oauth-toolkit==1.1.3 # see UPGRADE BLOCKERs django-polymorphic diff --git a/requirements/requirements.txt b/requirements/requirements.txt index fd1591dc29..5829401c78 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -19,7 +19,7 @@ channels-redis==3.1.0 # via -r /awx_devel/requirements/requirements.in channels==2.4.0 # via -r /awx_devel/requirements/requirements.in, channels-redis chardet==3.0.4 # via aiohttp, requests constantly==15.1.0 # via twisted -cryptography==2.9.2 # via adal, autobahn, azure-keyvault, pyopenssl, service-identity, social-auth-core +cryptography==2.9.2 # via -r /awx_devel/requirements/requirements.in, adal, autobahn, azure-keyvault, pyopenssl, service-identity, social-auth-core daphne==2.4.1 # via -r /awx_devel/requirements/requirements.in, channels defusedxml==0.6.0 # via python3-openid, python3-saml, social-auth-core dictdiffer==0.8.1 # via openshift @@ -27,6 +27,7 @@ django-auth-ldap==2.1.0 # via -r /awx_devel/requirements/requirements.in django-cors-headers==3.2.1 # via -r /awx_devel/requirements/requirements.in django-crum==0.7.5 # via -r /awx_devel/requirements/requirements.in django-extensions==2.2.9 # via -r /awx_devel/requirements/requirements.in +django-guid==2.2.0 # via -r /awx_devel/requirements/requirements.in django-jsonfield==1.2.0 # via -r /awx_devel/requirements/requirements.in django-oauth-toolkit==1.1.3 # via -r /awx_devel/requirements/requirements.in django-pglocks==1.0.4 # via -r /awx_devel/requirements/requirements.in @@ -37,7 +38,7 @@ django-redis==4.5.0 # via -r /awx_devel/requirements/requirements.in django-solo==1.1.3 # via -r /awx_devel/requirements/requirements.in django-split-settings==1.0.0 # via -r /awx_devel/requirements/requirements.in django-taggit==1.2.0 # via -r /awx_devel/requirements/requirements.in -django==2.2.16 # via -r /awx_devel/requirements/requirements.in, channels, django-auth-ldap, django-cors-headers, django-crum, django-jsonfield, django-oauth-toolkit, django-polymorphic, django-taggit, djangorestframework +django==2.2.16 # via -r /awx_devel/requirements/requirements.in, channels, django-auth-ldap, django-cors-headers, django-crum, django-guid, django-jsonfield, django-oauth-toolkit, django-polymorphic, django-taggit, djangorestframework djangorestframework-yaml==1.0.3 # via -r /awx_devel/requirements/requirements.in djangorestframework==3.12.1 # via -r /awx_devel/requirements/requirements.in docutils==0.16 # via python-daemon diff --git a/requirements/requirements_dev.txt b/requirements/requirements_dev.txt index 3dbcc2f97c..fe51fff164 100644 --- a/requirements/requirements_dev.txt +++ b/requirements/requirements_dev.txt @@ -21,4 +21,4 @@ backports.tempfile # support in unit tests for py32+ tempfile.TemporaryDirector mockldap sdb gprof2dot -atomicwrites==1.1.5 +atomicwrites==1.4.0