{"action":"created","comment":{"url":"https://api.github.com/repos/ansible/ansible/pulls/comments/223211455","pull_request_review_id":162288545,"id":223211455,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIyMzIxMTQ1NQ==","diff_hunk":"@@ -0,0 +1,601 @@\n+#!/usr/bin/python\n+#\n+# (c) 2018, Evert Mulder <evertmulder@gmail.com> (base on manageiq_user.py by Daniel Korn <korndaniel1@gmail.com>)\n+#\n+# This file is part of Ansible\n+#\n+# Ansible is free software: you can redistribute it and/or modify\n+# it under the terms of the GNU General Public License as published by\n+# the Free Software Foundation, either version 3 of the License, or\n+# (at your option) any later version.\n+#\n+# Ansible is distributed in the hope that it will be useful,\n+# but WITHOUT ANY WARRANTY; without even the implied warranty of\n+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n+# GNU General Public License for more details.\n+#\n+# You should have received a copy of the GNU General Public License\n+# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.\n+\n+from __future__ import (absolute_import, division, print_function)\n+\n+__metaclass__ = type\n+\n+ANSIBLE_METADATA = {'metadata_version': '1.1',\n+                    'status': ['preview'],\n+                    'supported_by': 'community'}\n+\n+DOCUMENTATION = '''\n+\n+module: manageiq_group\n+\n+short_description: Management of groups in ManageIQ.\n+extends_documentation_fragment: manageiq\n+version_added: '2.8'\n+author: Evert Mulder\n+description:\n+  - The manageiq_group module supports adding, updating and deleting groups in ManageIQ.\n+\n+options:\n+  state:\n+    description:\n+    - absent - group should not exist, present - group should be.\n+    choices: ['absent', 'present']\n+    default: 'present'\n+  description:\n+    description:\n+    - The group description.\n+    required: true\n+    default: null\n+  role_id:\n+    description:\n+    - The the group role id\n+    required: false\n+    default: null\n+  role:\n+    description:\n+    - The the group role name\n+    - The C(role_id) has precedence over the C(role) when supplied.\n+    required: false\n+    default: null\n+  tenant_id:\n+    description:\n+    - The tenant for the group identified by the tenant id.\n+    required: false\n+    default: null\n+  tenant:\n+    description:\n+    - The tenant for the group identified by the tenant name.\n+    - The C(tenant_id) has precedence over the C(tenant) when supplied.\n+    - Tenant names are case sensitive.\n+    required: false\n+    default: null\n+  managed_filters:\n+    description: A list of list of tag categories\n+    type: list\n+    required: false\n+    default: null\n+  managed_filters_merge_mode:\n+    description:\n+    - In merge mode existing categories are kept or updated, new categories are added.\n+    - In replace mode all categories will be replaced with the supplied C(managed_filters).\n+    choices: [ merge, replace ]\n+    default: replace\n+  belongsto_filters:\n+    description: A of strings with a reference to the allowed host, cluster or folder\n+    type: list\n+    required: false\n+    default: null\n+  belongsto_filters_merge_mode:\n+    description:\n+    - In merge mode existing settings are merged with the supplied C(belongsto_filters).\n+    - In replace mode current values are replaced with the supplied C(belongsto_filters).\n+    choices: [ merge, replace ]\n+    default: replace\n+'''\n+\n+EXAMPLES = '''\n+- name: Create a group in ManageIQ with the role EvmRole-user and tenant 'my_tenant'\n+  manageiq_group:\n+    description: 'MyGroup-user'\n+    role: 'EvmRole-user'\n+    tenant: 'my_tenant'\n+    manageiq_connection:\n+      url: 'https://manageiq_server'\n+      username: 'admin'\n+      password: 'smartvm'\n+      verify_ssl: False\n+\n+- name: Create a group in ManageIQ with the role EvmRole-user and tenant with tenant_id 4\n+  manageiq_group:\n+    description: 'MyGroup-user'\n+    role: 'EvmRole-user'\n+    tenant_id: 4\n+    manageiq_connection:\n+      url: 'https://manageiq_server'\n+      username: 'admin'\n+      password: 'smartvm'\n+      verify_ssl: False\n+\n+- name:\n+  - Create or update a group in ManageIQ with the role EvmRole-user and tenant my_tenant.\n+  - Apply 3 prov_max_cpu and 2 department tags to the group.\n+  - Limit access to a cluster for the group.\n+  manageiq_group:\n+    description: 'MyGroup-user'\n+    role: 'EvmRole-user'\n+    tenant: my_tenant\n+    managed_filters:\n+    - - \"/managed/prov_max_cpu/1\"\n+      - \"/managed/prov_max_cpu/3\"\n+      - \"/managed/prov_max_cpu/4\"\n+    - - \"/managed/department/defense\"\n+      - \"/managed/department/engineering\"\n+    managed_filters_merge_mode: replace\n+    belongsto_filters:\n+    - \"/belongsto/ExtManagementSystem|ProviderName/EmsFolder|Datacenters/EmsFolder|dc_name/EmsFolder|host/EmsCluster|Cluster name\"\n+    belongsto_filters_merge_mode: merge\n+    manageiq_connection:\n+      url: 'https://manageiq_server'\n+      username: 'admin'\n+      password: 'smartvm'\n+      verify_ssl: False\n+\n+- name: Delete a group in ManageIQ\n+  manageiq_group:\n+    state: 'absent'\n+    description: 'MyGroup-user'\n+    manageiq_connection:\n+      url: 'http://127.0.0.1:3000'\n+      username: 'admin'\n+      password: 'smartvm'\n+\n+- name: Delete a group in ManageIQ using a token\n+  manageiq_group:\n+    state: 'absent'\n+    description: 'MyGroup-user'\n+    manageiq_connection:\n+      url: 'http://127.0.0.1:3000'\n+      token: 'sometoken'\n+'''\n+\n+RETURN = '''\n+group:\n+  description: The group.\n+  returned: success\n+  type: complex\n+  contains:\n+    description:\n+      description: The group description\n+      returned: success\n+      type: string\n+    id:\n+      description: The group id\n+      returned: success\n+      type: int\n+    group_type:\n+      description: The group type, system or user\n+      returned: success\n+      type: string\n+    role_name:\n+      description: The group role name\n+      returned: success\n+      type: string\n+    tenant_name:\n+      description: The group tenant name\n+      returned: success\n+      type: string\n+    created_on:\n+      description: Group creation date\n+      returned: success\n+      type: string\n+      example: 2018-08-12T08:37:55+00:00\n+    updated_on:\n+      description: Group update date\n+      returned: success\n+      type: int\n+      example: 2018-08-12T08:37:55+00:00\n+'''\n+\n+from ansible.module_utils.basic import AnsibleModule\n+from ansible.module_utils.manageiq import ManageIQ, manageiq_argument_spec\n+\n+\n+class ManageIQgroup(object):\n+    \"\"\"\n+        Object to execute group management operations in manageiq.\n+    \"\"\"\n+\n+    def __init__(self, manageiq):\n+        self.manageiq = manageiq\n+\n+        self.module = self.manageiq.module\n+        self.api_url = self.manageiq.api_url\n+        self.client = self.manageiq.client\n+\n+    def group(self, description):\n+        \"\"\" Search for group object by description.\n+        Returns:\n+            the group, or None if group was not found.\n+        \"\"\"\n+        groups = self.client.collections.groups.find_by(description=description)\n+        if len(groups) == 0:\n+            return None\n+        else:\n+            return groups[0]\n+\n+    def tenant(self, tenant_id, tenant_name):\n+        \"\"\" Search for tenant entity by name or id\n+        Returns:\n+            the tenant entity, None no id or name was supplied\n+        \"\"\"\n+\n+        if tenant_id:\n+            tenant = self.client.get_entity('tenants', tenant_id)\n+            if not tenant:\n+                self.module.fail_json(msg=\"Tenant with id '%s' not found in manageiq\" % str(tenant_id))\n+            return tenant\n+        else:\n+            if tenant_name:\n+                tenant_res = self.client.collections.tenants.find_by(name=tenant_name)\n+                if not tenant_res:\n+                    self.module.fail_json(msg=\"Tenant '%s' not found in manageiq\" % tenant_name)\n+                if len(tenant_res) > 1:\n+                    self.module.fail_json(msg=\"Multiple tenants found in manageiq with name '%s\" % tenant_name)\n+                tenant = tenant_res[0]\n+                return tenant\n+            else:\n+                # No tenant name or tenant id supplied\n+                return None\n+\n+    def role(self, role_id, role_name):\n+        \"\"\" Search for a role object by name or id.\n+        Returns:\n+            the role entity, None no id or name was supplied\n+\n+            the role, or send a module Fail signal if role not found.\n+        \"\"\"\n+        if role_id:\n+            role = self.client.get_entity('roles', role_id)\n+            if not role:\n+                self.module.fail_json(msg=\"Role with id '%s' not found in manageiq\" % str(role_id))\n+            return role\n+        else:\n+            if role_name:\n+                role_res = self.client.collections.roles.find_by(name=role_name)\n+                if not role_res:\n+                    self.module.fail_json(msg=\"Role '%s' not found in manageiq\" % role_name)\n+                if len(role_res) > 1:\n+                    self.module.fail_json(msg=\"Multiple roles found in manageiq with name '%s\" % role_name)\n+                return role_res[0]\n+            else:\n+                # No role name or role id supplied\n+                return None\n+\n+    @staticmethod\n+    def have_same_values_nested_array(current_values, updated_values):\n+        if (not current_values) and (not updated_values):\n+            return True\n+        if current_values and (not updated_values):\n+            return False\n+        if (not current_values) and updated_values:\n+            return False\n+        if len(current_values) != len(updated_values):\n+            return False\n+\n+        # sort subs\n+        for sub in current_values:\n+            sub.sort()\n+        for sub in updated_values:\n+            sub.sort()\n+\n+        current_values.sort()\n+        updated_values.sort()\n+\n+        return current_values == updated_values\n+\n+    @staticmethod\n+    def merge_values_nested_array(current_values, updated_values):\n+        if current_values and (not updated_values):\n+            return current_values\n+        if (not current_values) and updated_values:\n+            return updated_values\n+\n+        current_values_categories = {}\n+        new_values_categories = {}\n+\n+        # organize the values per category name\n+        for sub in current_values:\n+            sub.sort()\n+            key = sub[0].split('/')[2]\n+            current_values_categories[key] = sub\n+\n+        for sub in updated_values:\n+            sub.sort()\n+            key = sub[0].split('/')[2]\n+            new_values_categories[key] = sub\n+\n+        # add and update current categories\n+        for key in new_values_categories:\n+            current_values_categories[key] = new_values_categories[key]\n+\n+        res = []\n+\n+        for key in current_values_categories:\n+            res.append(current_values_categories[key])\n+\n+        return res\n+\n+    def delete_group(self, group):\n+        \"\"\" Deletes a group from manageiq.\n+\n+        Returns:\n+            a dict of:\n+            changed: boolean indicating if the entity was updated.\n+            msg: a short message describing the operation executed.\n+        \"\"\"\n+        try:\n+            url = '%s/groups/%s' % (self.api_url, group['id'])\n+            self.client.post(url, action='delete')\n+        except Exception as e:\n+            self.module.fail_json(msg=\"failed to delete group %s: %s\" % (group['description'], str(e)))\n+\n+        return dict(\n+            changed=True,\n+            msg=\"deleted group %s with id %i\" % (group['description'], group['id']))\n+\n+    def edit_group(self, group, description, role, tenant, managed_filters, managed_filters_merge_mode,\n+                   belongsto_filters, belongsto_filters_merge_mode):\n+        \"\"\" Edit a manageiq group.\n+\n+        Returns:\n+            a dict of:\n+            changed: boolean indicating if the entity was updated.\n+            msg: a short message describing the operation executed.\n+        \"\"\"\n+\n+        if role or managed_filters or belongsto_filters:\n+            group.reload(attributes=['miq_user_role_name', 'entitlement'])\n+\n+        try:\n+            current_role = group['miq_user_role_name']\n+        except AttributeError:\n+            current_role = None\n+\n+        changed = False\n+        resource = {}\n+\n+        if description and group['description'] != description:\n+            resource['description'] = description\n+            changed = True\n+\n+        if tenant and group['tenant_id'] != tenant['id']:\n+            resource['tenant'] = dict(id=tenant['id'])\n+            changed = True\n+\n+        if role and current_role != role['name']:\n+            resource['role'] = dict(id=role['id'])\n+            changed = True\n+\n+        if managed_filters or belongsto_filters:\n+\n+            # Only compare if filters are supplied\n+            entitlement = group['entitlement']\n+\n+            if 'filters' not in entitlement:\n+                # No existing filters exist, use supplied filters\n+                resource['filters'] = {'managed': managed_filters, \"belongsto\": belongsto_filters}\n+                changed = True\n+            else:\n+                current_filters = entitlement['filters']\n+                new_filters = self.edit_group_edit_filters(current_filters,\n+                                                           managed_filters, managed_filters_merge_mode,\n+                                                           belongsto_filters, belongsto_filters_merge_mode)\n+                if new_filters:\n+                    resource['filters'] = new_filters\n+                    changed = True\n+\n+        if not changed:\n+            return dict(\n+                changed=False,\n+                msg=\"group %s is not changed.\" % group['description'])\n+\n+        # try to update group\n+        try:\n+            self.client.post(group['href'], action='edit', resource=resource)\n+            changed = True\n+        except Exception as e:\n+            self.module.fail_json(msg=\"failed to update group %s: %s\" % (group['name'], str(e)))\n+\n+        return dict(\n+            changed=changed,\n+            msg=\"successfully updated the group %s with id %s\" % (group['description'], group['id']))\n+\n+    def edit_group_edit_filters(self, current_filters, managed_filters, managed_filters_merge_mode,\n+                                belongsto_filters, belongsto_filters_merge_mode):\n+        \"\"\" Edit a manageiq group filters.\n+\n+        Returns:\n+            None if no the group was not updated\n+            If the group was updated the post body part for updating the group\n+        \"\"\"\n+        filters_updated = False\n+        new_filters_resource = {}\n+\n+        # Process belongsto filters\n+        if 'belongsto' in current_filters:\n+            current_belongsto_set = set(current_filters['belongsto'])\n+        else:\n+            current_belongsto_set = set()\n+\n+        if belongsto_filters:\n+            new_belongsto_set = set(belongsto_filters)\n+        else:\n+            new_belongsto_set = set()\n+\n+        if current_belongsto_set == new_belongsto_set:\n+            new_filters_resource['belongsto'] = current_filters['belongsto']\n+        else:\n+            if belongsto_filters_merge_mode == 'merge':\n+                current_belongsto_set.update(new_belongsto_set)","path":"lib/ansible/modules/remote_management/manageiq/manageiq_group.py","position":440,"original_position":440,"commit_id":"58dbe5e83dc8e13f1e7db3b0d1cc05dfe077b185","original_commit_id":"58dbe5e83dc8e13f1e7db3b0d1cc05dfe077b185","user":{"login":"cben","id":273688,"node_id":"MDQ6VXNlcjI3MzY4OA==","avatar_url":"https://avatars2.githubusercontent.com/u/273688?v=4","gravatar_id":"","url":"https://api.github.com/users/cben","html_url":"https://github.com/cben","followers_url":"https://api.github.com/users/cben/followers","following_url":"https://api.github.com/users/cben/following{/other_user}","gists_url":"https://api.github.com/users/cben/gists{/gist_id}","starred_url":"https://api.github.com/users/cben/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cben/subscriptions","organizations_url":"https://api.github.com/users/cben/orgs","repos_url":"https://api.github.com/users/cben/repos","events_url":"https://api.github.com/users/cben/events{/privacy}","received_events_url":"https://api.github.com/users/cben/received_events","type":"User","site_admin":false},"body":"Is this deliberately different from `merge_values_nested_array(current_managed, managed_filters)` below?\r\nOuch, I see, belongsto is 1 level while managed is 2 levels:\r\n```\r\n    \"filters\" : {\r\n      \"belongsto\" : [ \"/managed/area/1\", \"/managed/area/2\", \"/managed/area/3\" ],\r\n      \"managed\" : [[ \"/managed/infra/1\", \"/managed/infra/2\"], [\"/managed/other/3\"]]\r\n    }\r\n```\r\nLGTM :+1:","created_at":"2018-10-07T12:27:41Z","updated_at":"2018-10-07T12:27:41Z","html_url":"https://github.com/ansible/ansible/pull/45867#discussion_r223211455","pull_request_url":"https://api.github.com/repos/ansible/ansible/pulls/45867","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/ansible/ansible/pulls/comments/223211455"},"html":{"href":"https://github.com/ansible/ansible/pull/45867#discussion_r223211455"},"pull_request":{"href":"https://api.github.com/repos/ansible/ansible/pulls/45867"}}},"pull_request":{"url":"https://api.github.com/repos/ansible/ansible/pulls/45867","id":216755737,"node_id":"MDExOlB1bGxSZXF1ZXN0MjE2NzU1NzM3","html_url":"https://github.com/ansible/ansible/pull/45867","diff_url":"https://github.com/ansible/ansible/pull/45867.diff","patch_url":"https://github.com/ansible/ansible/pull/45867.patch","issue_url":"https://api.github.com/repos/ansible/ansible/issues/45867","number":45867,"state":"open","locked":false,"title":"Manageiq group module","user":{"login":"evertmulder","id":61044,"node_id":"MDQ6VXNlcjYxMDQ0","avatar_url":"https://avatars0.githubusercontent.com/u/61044?v=4","gravatar_id":"","url":"https://api.github.com/users/evertmulder","html_url":"https://github.com/evertmulder","followers_url":"https://api.github.com/users/evertmulder/followers","following_url":"https://api.github.com/users/evertmulder/following{/other_user}","gists_url":"https://api.github.com/users/evertmulder/gists{/gist_id}","starred_url":"https://api.github.com/users/evertmulder/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/evertmulder/subscriptions","organizations_url":"https://api.github.com/users/evertmulder/orgs","repos_url":"https://api.github.com/users/evertmulder/repos","events_url":"https://api.github.com/users/evertmulder/events{/privacy}","received_events_url":"https://api.github.com/users/evertmulder/received_events","type":"User","site_admin":false},"body":"##### SUMMARY\r\nAdded a module to manage groups in ManageIQ\r\n\r\n##### ISSUE TYPE\r\n - New Module Pull Request\r\n\r\n##### COMPONENT NAME\r\nmanageiq_group\r\n\r\n##### ANSIBLE VERSION\r\n```\r\n ansible --version\r\nansible 2.6.1\r\n  config file = None\r\n  configured module search path = [u'/Users/emulder/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']\r\n  ansible python module location = /usr/local/lib/python2.7/site-packages/ansible\r\n  executable location = /usr/local/bin/ansible\r\n  python version = 2.7.14 (default, Mar 21 2018, 16:34:57) [GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.39.2)]\r\n```\r\n\r\n##### ADDITIONAL INFORMATION\r\nNew module for creating groups in manageiq","created_at":"2018-09-19T19:49:49Z","updated_at":"2018-10-07T12:27:41Z","closed_at":null,"merged_at":null,"merge_commit_sha":"03c09220ced5f04d27ef784a1dd0c4ee5350933d","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[{"id":1043397454,"node_id":"MDU6TGFiZWwxMDQzMzk3NDU0","url":"https://api.github.com/repos/ansible/ansible/labels/affects_2.8","name":"affects_2.8","color":"ededed","default":false},{"id":501518759,"node_id":"MDU6TGFiZWw1MDE1MTg3NTk=","url":"https://api.github.com/repos/ansible/ansible/labels/community_review","name":"community_review","color":"f9d0c4","default":false},{"id":496359346,"node_id":"MDU6TGFiZWw0OTYzNTkzNDY=","url":"https://api.github.com/repos/ansible/ansible/labels/module","name":"module","color":"1d76db","default":false},{"id":473225334,"node_id":"MDU6TGFiZWw0NzMyMjUzMzQ=","url":"https://api.github.com/repos/ansible/ansible/labels/new_module","name":"new_module","color":"c2e0c6","default":false},{"id":153577825,"node_id":"MDU6TGFiZWwxNTM1Nzc4MjU=","url":"https://api.github.com/repos/ansible/ansible/labels/new_plugin","name":"new_plugin","color":"bfe5bf","default":false},{"id":581543918,"node_id":"MDU6TGFiZWw1ODE1NDM5MTg=","url":"https://api.github.com/repos/ansible/ansible/labels/stale_ci","name":"stale_ci","color":"fef2c0","default":false},{"id":636327968,"node_id":"MDU6TGFiZWw2MzYzMjc5Njg=","url":"https://api.github.com/repos/ansible/ansible/labels/support:community","name":"support:community","color":"ededed","default":false}],"milestone":null,"commits_url":"https://api.github.com/repos/ansible/ansible/pulls/45867/commits","review_comments_url":"https://api.github.com/repos/ansible/ansible/pulls/45867/comments","review_comment_url":"https://api.github.com/repos/ansible/ansible/pulls/comments{/number}","comments_url":"https://api.github.com/repos/ansible/ansible/issues/45867/comments","statuses_url":"https://api.github.com/repos/ansible/ansible/statuses/58dbe5e83dc8e13f1e7db3b0d1cc05dfe077b185","head":{"label":"evertmulder:manageiq-group-module","ref":"manageiq-group-module","sha":"58dbe5e83dc8e13f1e7db3b0d1cc05dfe077b185","user":{"login":"evertmulder","id":61044,"node_id":"MDQ6VXNlcjYxMDQ0","avatar_url":"https://avatars0.githubusercontent.com/u/61044?v=4","gravatar_id":"","url":"https://api.github.com/users/evertmulder","html_url":"https://github.com/evertmulder","followers_url":"https://api.github.com/users/evertmulder/followers","following_url":"https://api.github.com/users/evertmulder/following{/other_user}","gists_url":"https://api.github.com/users/evertmulder/gists{/gist_id}","starred_url":"https://api.github.com/users/evertmulder/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/evertmulder/subscriptions","organizations_url":"https://api.github.com/users/evertmulder/orgs","repos_url":"https://api.github.com/users/evertmulder/repos","events_url":"https://api.github.com/users/evertmulder/events{/privacy}","received_events_url":"https://api.github.com/users/evertmulder/received_events","type":"User","site_admin":false},"repo":{"id":130602167,"node_id":"MDEwOlJlcG9zaXRvcnkxMzA2MDIxNjc=","name":"ansible","full_name":"evertmulder/ansible","private":false,"owner":{"login":"evertmulder","id":61044,"node_id":"MDQ6VXNlcjYxMDQ0","avatar_url":"https://avatars0.githubusercontent.com/u/61044?v=4","gravatar_id":"","url":"https://api.github.com/users/evertmulder","html_url":"https://github.com/evertmulder","followers_url":"https://api.github.com/users/evertmulder/followers","following_url":"https://api.github.com/users/evertmulder/following{/other_user}","gists_url":"https://api.github.com/users/evertmulder/gists{/gist_id}","starred_url":"https://api.github.com/users/evertmulder/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/evertmulder/subscriptions","organizations_url":"https://api.github.com/users/evertmulder/orgs","repos_url":"https://api.github.com/users/evertmulder/repos","events_url":"https://api.github.com/users/evertmulder/events{/privacy}","received_events_url":"https://api.github.com/users/evertmulder/received_events","type":"User","site_admin":false},"html_url":"https://github.com/evertmulder/ansible","description":"Ansible is a radically simple IT automation platform that makes your applications and systems easier to deploy. Avoid writing scripts or custom code to deploy and update your applications — automate in a language that approaches plain English, using SSH, with no agents to install on remote systems. https://docs.ansible.com/ansible/","fork":true,"url":"https://api.github.com/repos/evertmulder/ansible","forks_url":"https://api.github.com/repos/evertmulder/ansible/forks","keys_url":"https://api.github.com/repos/evertmulder/ansible/keys{/key_id}","collaborators_url":"https://api.github.com/repos/evertmulder/ansible/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/evertmulder/ansible/teams","hooks_url":"https://api.github.com/repos/evertmulder/ansible/hooks","issue_events_url":"https://api.github.com/repos/evertmulder/ansible/issues/events{/number}","events_url":"https://api.github.com/repos/evertmulder/ansible/events","assignees_url":"https://api.github.com/repos/evertmulder/ansible/assignees{/user}","branches_url":"https://api.github.com/repos/evertmulder/ansible/branches{/branch}","tags_url":"https://api.github.com/repos/evertmulder/ansible/tags","blobs_url":"https://api.github.com/repos/evertmulder/ansible/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/evertmulder/ansible/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/evertmulder/ansible/git/refs{/sha}","trees_url":"https://api.github.com/repos/evertmulder/ansible/git/trees{/sha}","statuses_url":"https://api.github.com/repos/evertmulder/ansible/statuses/{sha}","languages_url":"https://api.github.com/repos/evertmulder/ansible/languages","stargazers_url":"https://api.github.com/repos/evertmulder/ansible/stargazers","contributors_url":"https://api.github.com/repos/evertmulder/ansible/contributors","subscribers_url":"https://api.github.com/repos/evertmulder/ansible/subscribers","subscription_url":"https://api.github.com/repos/evertmulder/ansible/subscription","commits_url":"https://api.github.com/repos/evertmulder/ansible/commits{/sha}","git_commits_url":"https://api.github.com/repos/evertmulder/ansible/git/commits{/sha}","comments_url":"https://api.github.com/repos/evertmulder/ansible/comments{/number}","issue_comment_url":"https://api.github.com/repos/evertmulder/ansible/issues/comments{/number}","contents_url":"https://api.github.com/repos/evertmulder/ansible/contents/{+path}","compare_url":"https://api.github.com/repos/evertmulder/ansible/compare/{base}...{head}","merges_url":"https://api.github.com/repos/evertmulder/ansible/merges","archive_url":"https://api.github.com/repos/evertmulder/ansible/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/evertmulder/ansible/downloads","issues_url":"https://api.github.com/repos/evertmulder/ansible/issues{/number}","pulls_url":"https://api.github.com/repos/evertmulder/ansible/pulls{/number}","milestones_url":"https://api.github.com/repos/evertmulder/ansible/milestones{/number}","notifications_url":"https://api.github.com/repos/evertmulder/ansible/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/evertmulder/ansible/labels{/name}","releases_url":"https://api.github.com/repos/evertmulder/ansible/releases{/id}","deployments_url":"https://api.github.com/repos/evertmulder/ansible/deployments","created_at":"2018-04-22T19:50:51Z","updated_at":"2018-09-20T05:26:53Z","pushed_at":"2018-09-25T14:37:42Z","git_url":"git://github.com/evertmulder/ansible.git","ssh_url":"git@github.com:evertmulder/ansible.git","clone_url":"https://github.com/evertmulder/ansible.git","svn_url":"https://github.com/evertmulder/ansible","homepage":"https://www.ansible.com/","size":133300,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":{"key":"gpl-3.0","name":"GNU General Public License v3.0","spdx_id":"GPL-3.0","url":"https://api.github.com/licenses/gpl-3.0","node_id":"MDc6TGljZW5zZTk="},"forks":0,"open_issues":0,"watchers":0,"default_branch":"devel"}},"base":{"label":"ansible:devel","ref":"devel","sha":"7e8435b96d836c153f8979d38ba3b169f278bbf4","user":{"login":"ansible","id":1507452,"node_id":"MDEyOk9yZ2FuaXphdGlvbjE1MDc0NTI=","avatar_url":"https://avatars2.githubusercontent.com/u/1507452?v=4","gravatar_id":"","url":"https://api.github.com/users/ansible","html_url":"https://github.com/ansible","followers_url":"https://api.github.com/users/ansible/followers","following_url":"https://api.github.com/users/ansible/following{/other_user}","gists_url":"https://api.github.com/users/ansible/gists{/gist_id}","starred_url":"https://api.github.com/users/ansible/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ansible/subscriptions","organizations_url":"https://api.github.com/users/ansible/orgs","repos_url":"https://api.github.com/users/ansible/repos","events_url":"https://api.github.com/users/ansible/events{/privacy}","received_events_url":"https://api.github.com/users/ansible/received_events","type":"Organization","site_admin":false},"repo":{"id":3638964,"node_id":"MDEwOlJlcG9zaXRvcnkzNjM4OTY0","name":"ansible","full_name":"ansible/ansible","private":false,"owner":{"login":"ansible","id":1507452,"node_id":"MDEyOk9yZ2FuaXphdGlvbjE1MDc0NTI=","avatar_url":"https://avatars2.githubusercontent.com/u/1507452?v=4","gravatar_id":"","url":"https://api.github.com/users/ansible","html_url":"https://github.com/ansible","followers_url":"https://api.github.com/users/ansible/followers","following_url":"https://api.github.com/users/ansible/following{/other_user}","gists_url":"https://api.github.com/users/ansible/gists{/gist_id}","starred_url":"https://api.github.com/users/ansible/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ansible/subscriptions","organizations_url":"https://api.github.com/users/ansible/orgs","repos_url":"https://api.github.com/users/ansible/repos","events_url":"https://api.github.com/users/ansible/events{/privacy}","received_events_url":"https://api.github.com/users/ansible/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/ansible/ansible","description":"Ansible is a radically simple IT automation platform that makes your applications and systems easier to deploy. Avoid writing scripts or custom code to deploy and update your applications — automate in a language that approaches plain English, using SSH, with no agents to install on remote systems. https://docs.ansible.com/ansible/","fork":false,"url":"https://api.github.com/repos/ansible/ansible","forks_url":"https://api.github.com/repos/ansible/ansible/forks","keys_url":"https://api.github.com/repos/ansible/ansible/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ansible/ansible/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ansible/ansible/teams","hooks_url":"https://api.github.com/repos/ansible/ansible/hooks","issue_events_url":"https://api.github.com/repos/ansible/ansible/issues/events{/number}","events_url":"https://api.github.com/repos/ansible/ansible/events","assignees_url":"https://api.github.com/repos/ansible/ansible/assignees{/user}","branches_url":"https://api.github.com/repos/ansible/ansible/branches{/branch}","tags_url":"https://api.github.com/repos/ansible/ansible/tags","blobs_url":"https://api.github.com/repos/ansible/ansible/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ansible/ansible/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ansible/ansible/git/refs{/sha}","trees_url":"https://api.github.com/repos/ansible/ansible/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ansible/ansible/statuses/{sha}","languages_url":"https://api.github.com/repos/ansible/ansible/languages","stargazers_url":"https://api.github.com/repos/ansible/ansible/stargazers","contributors_url":"https://api.github.com/repos/ansible/ansible/contributors","subscribers_url":"https://api.github.com/repos/ansible/ansible/subscribers","subscription_url":"https://api.github.com/repos/ansible/ansible/subscription","commits_url":"https://api.github.com/repos/ansible/ansible/commits{/sha}","git_commits_url":"https://api.github.com/repos/ansible/ansible/git/commits{/sha}","comments_url":"https://api.github.com/repos/ansible/ansible/comments{/number}","issue_comment_url":"https://api.github.com/repos/ansible/ansible/issues/comments{/number}","contents_url":"https://api.github.com/repos/ansible/ansible/contents/{+path}","compare_url":"https://api.github.com/repos/ansible/ansible/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ansible/ansible/merges","archive_url":"https://api.github.com/repos/ansible/ansible/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ansible/ansible/downloads","issues_url":"https://api.github.com/repos/ansible/ansible/issues{/number}","pulls_url":"https://api.github.com/repos/ansible/ansible/pulls{/number}","milestones_url":"https://api.github.com/repos/ansible/ansible/milestones{/number}","notifications_url":"https://api.github.com/repos/ansible/ansible/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ansible/ansible/labels{/name}","releases_url":"https://api.github.com/repos/ansible/ansible/releases{/id}","deployments_url":"https://api.github.com/repos/ansible/ansible/deployments","created_at":"2012-03-06T14:58:02Z","updated_at":"2018-10-07T10:19:51Z","pushed_at":"2018-10-07T12:27:15Z","git_url":"git://github.com/ansible/ansible.git","ssh_url":"git@github.com:ansible/ansible.git","clone_url":"https://github.com/ansible/ansible.git","svn_url":"https://github.com/ansible/ansible","homepage":"https://www.ansible.com/","size":142493,"stargazers_count":32955,"watchers_count":32955,"language":"Python","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":13023,"mirror_url":null,"archived":false,"open_issues_count":5516,"license":{"key":"gpl-3.0","name":"GNU General Public License v3.0","spdx_id":"GPL-3.0","url":"https://api.github.com/licenses/gpl-3.0","node_id":"MDc6TGljZW5zZTk="},"forks":13023,"open_issues":5516,"watchers":32955,"default_branch":"devel"}},"_links":{"self":{"href":"https://api.github.com/repos/ansible/ansible/pulls/45867"},"html":{"href":"https://github.com/ansible/ansible/pull/45867"},"issue":{"href":"https://api.github.com/repos/ansible/ansible/issues/45867"},"comments":{"href":"https://api.github.com/repos/ansible/ansible/issues/45867/comments"},"review_comments":{"href":"https://api.github.com/repos/ansible/ansible/pulls/45867/comments"},"review_comment":{"href":"https://api.github.com/repos/ansible/ansible/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/ansible/ansible/pulls/45867/commits"},"statuses":{"href":"https://api.github.com/repos/ansible/ansible/statuses/58dbe5e83dc8e13f1e7db3b0d1cc05dfe077b185"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"cben"}}