{"action":"created","comment":{"url":"https://api.github.com/repos/saltstack/salt/pulls/comments/174893203","pull_request_review_id":104332926,"id":174893203,"diff_hunk":"@@ -0,0 +1,617 @@\n+# coding=utf-8\n+\n+'''\n+An engine that listens for libvirt events and resend them to the salt event bus.\n+\n+Example configuration:\n+\n+.. code-block:: yaml\n+\n+    engines:\n+        - libvirt_events:\n+            uri: lxc:///\n+            tag_prefix: libvirt\n+            filters:\n+                - domain/lifecycle\n+                - domain/reboot\n+                - pool\n+\n+The default URI is ``qemu:///system`` and the default tag prefix is\n+``salt/engines/libvirt_events``.\n+\n+Filters is a list of event types to relay to the event bus. Items in this list\n+can be either one of the main types (``domain``, ``network``, ``pool``,\n+``nodedev``, ``secret``), ``all`` or a more precise filter. These can be done\n+with values like <main_type>/<subtype>, but better read the engine's code for\n+the complete list of sub types. If the filters list contains ``all``, all\n+events will be relayed.\n+\n+Be aware that the list of events increases with libvirt versions, for example\n+network events have been added in libvirt 1.2.1.\n+\n+:depends: libvirt 1.0.0+ python binding\n+'''\n+\n+from __future__ import absolute_import, unicode_literals, print_function\n+import logging\n+\n+# Import salt libs\n+import salt.utils.event\n+\n+# pylint: disable=no-name-in-module,import-error\n+from salt.ext.six.moves.urllib.parse import urlparse\n+# pylint: enable=no-name-in-module,import-error\n+\n+log = logging.getLogger(__name__)\n+\n+\n+# pylint: disable=invalid-name\n+\n+msg = ''\n+try:\n+    import libvirt\n+    if libvirt.getVersion() < 1000000:\n+        msg = 'libvirt >= 1.0.0 required'\n+        libvirt = None\n+except ImportError:\n+    msg = 'libvirt module not found'\n+    libvirt = None\n+\n+# pylint: enable=invalid-name\n+\n+\n+def __virtual__():\n+    '''\n+    Only load if libvirt python binding is present\n+    '''\n+    return bool(libvirt), msg\n+\n+\n+REGISTER_FUNCTIONS = {\n+    'domain': 'domainEventRegisterAny',\n+    'network': 'networkEventRegisterAny',\n+    'pool': 'storagePoolEventRegisterAny',\n+    'nodedev': 'nodeDeviceEventRegisterAny',\n+    'secret': 'secretEventRegisterAny'\n+}\n+\n+# Handle either BLOCK_JOB or BLOCK_JOB_2, but prefer the latter\n+if hasattr(libvirt, 'VIR_DOMAIN_EVENT_ID_BLOCK_JOB_2'):\n+    BLOCK_JOB_ID = 'VIR_DOMAIN_EVENT_ID_BLOCK_JOB_2'\n+else:\n+    BLOCK_JOB_ID = 'VIR_DOMAIN_EVENT_ID_BLOCK_JOB'\n+\n+CALLBACK_DEFS = {\n+    'domain': (('lifecycle', None),\n+               ('reboot', None),\n+               ('rtc_change', None),\n+               ('watchdog', None),\n+               ('graphics', None),\n+               ('io_error', 'VIR_DOMAIN_EVENT_ID_IO_ERROR_REASON'),\n+               ('control_error', None),\n+               ('disk_change', None),\n+               ('tray_change', None),\n+               ('pmwakeup', None),\n+               ('pmsuspend', None),\n+               ('balloon_change', None),\n+               ('pmsuspend_disk', None),\n+               ('device_removed', None),\n+               ('block_job', BLOCK_JOB_ID),\n+               ('tunable', None),\n+               ('agent_lifecycle', None),\n+               ('device_added', None),\n+               ('migration_iteration', None),\n+               ('job_completed', None),\n+               ('device_removal_failed', None),\n+               ('metadata_change', None),\n+               ('block_threshold', None)),\n+    'network': (('lifecycle', None),),\n+    'pool': (('lifecycle', None),\n+             ('refresh', None)),\n+    'nodedev': (('lifecycle', None),\n+                ('update', None)),\n+    'secret': (('lifecycle', None),\n+               ('value_changed', None))\n+}\n+\n+\n+def _compute_subprefix(attr):\n+    '''\n+    Get the part before the first '_' or the end of @attr including\n+    the potential '_'\n+    '''\n+    return ''.join((attr.split('_')[0], '_' if len(attr.split('_')) > 1 else ''))\n+\n+\n+def _get_libvirt_enum_string(prefix, value):\n+    '''\n+    Convert the libvirt enum integer value @value into a human readable string.\n+    @prefix is the start of the libvirt attribute to look for.\n+    '''\n+    attributes = [attr[len(prefix):] for attr in libvirt.__dict__ if attr.startswith(prefix)]\n+\n+    # Filter out the values starting with a common base as they match another enum\n+    prefixes = [_compute_subprefix(p) for p in attributes]\n+    counts = {p: prefixes.count(p) for p in prefixes}\n+    sub_prefixes = [p for p, count in counts.items() if count > 1]\n+    filtered = [attr for attr in attributes if _compute_subprefix(attr) not in sub_prefixes]\n+\n+    for candidate in filtered:\n+        if value == getattr(libvirt, ''.join((prefix, candidate))):\n+            name = candidate.lower().replace('_', ' ')\n+            return name\n+    return 'unknown'\n+\n+\n+def _get_domain_event_detail(event, detail):\n+    '''\n+    Convert @event and @detail numeric values into a tuple of human readable strings\n+    '''\n+    event_name = _get_libvirt_enum_string('VIR_DOMAIN_EVENT_', event)\n+    if event_name == 'unknown':\n+        return event_name, 'unknown'\n+\n+    prefix = 'VIR_DOMAIN_EVENT_{0}_'.format(event_name.upper())\n+    detail_name = _get_libvirt_enum_string(prefix, detail)\n+\n+    return event_name, detail_name\n+\n+\n+def salt_send_event(opaque, conn, data):\n+    '''\n+    Convenience function adding common data to the event and sending it\n+    on the salt event bus.\n+    '''\n+    tag_prefix = opaque['prefix']\n+    object_type = opaque['object']\n+    event_type = opaque['event']\n+\n+    # Prepare the connection URI to fit in the tag\n+    # qemu+ssh://user@host:1234/system -> qemu+ssh/user@host:1234/system\n+    uri = urlparse(conn.getURI())\n+    uri_tag = [uri.scheme]\n+    if uri.netloc:\n+        uri_tag.append(uri.netloc)\n+    path = uri.path.strip('/')\n+    if path:\n+        uri_tag.append(path)\n+    uri_str = \"/\".join(uri_tag)\n+\n+    # Append some common data\n+    all_data = {\n+        'uri': conn.getURI()\n+    }\n+    all_data.update(data)\n+\n+    tag = '/'.join((tag_prefix, uri_str, object_type, event_type))\n+\n+    # Actually send the event in salt\n+    if __opts__.get('__role') == 'master':\n+        salt.utils.event.get_master_event(\n+            __opts__,\n+            __opts__['sock_dir']).fire_event(tag, all_data)\n+    else:\n+        __salt__['event.send'](tag, all_data)\n+\n+\n+def salt_send_domain_event(opaque, conn, domain, event, event_data):\n+    '''\n+    Helper function send a salt event for a libvirt domain\n+    '''","path":"salt/engines/libvirt_events.py","position":200,"original_position":200,"commit_id":"5af636f248620e5d80de054934bda5515f8a4a8f","original_commit_id":"5af636f248620e5d80de054934bda5515f8a4a8f","user":{"login":"rallytime","id":6547906,"avatar_url":"https://avatars2.githubusercontent.com/u/6547906?v=4","gravatar_id":"","url":"https://api.github.com/users/rallytime","html_url":"https://github.com/rallytime","followers_url":"https://api.github.com/users/rallytime/followers","following_url":"https://api.github.com/users/rallytime/following{/other_user}","gists_url":"https://api.github.com/users/rallytime/gists{/gist_id}","starred_url":"https://api.github.com/users/rallytime/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rallytime/subscriptions","organizations_url":"https://api.github.com/users/rallytime/orgs","repos_url":"https://api.github.com/users/rallytime/repos","events_url":"https://api.github.com/users/rallytime/events{/privacy}","received_events_url":"https://api.github.com/users/rallytime/received_events","type":"User","site_admin":false},"body":"Same comment here about docs.","created_at":"2018-03-15T18:50:22Z","updated_at":"2018-03-15T18:55:12Z","html_url":"https://github.com/saltstack/salt/pull/46461#discussion_r174893203","pull_request_url":"https://api.github.com/repos/saltstack/salt/pulls/46461","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/saltstack/salt/pulls/comments/174893203"},"html":{"href":"https://github.com/saltstack/salt/pull/46461#discussion_r174893203"},"pull_request":{"href":"https://api.github.com/repos/saltstack/salt/pulls/46461"}}},"pull_request":{"url":"https://api.github.com/repos/saltstack/salt/pulls/46461","id":174052193,"html_url":"https://github.com/saltstack/salt/pull/46461","diff_url":"https://github.com/saltstack/salt/pull/46461.diff","patch_url":"https://github.com/saltstack/salt/pull/46461.patch","issue_url":"https://api.github.com/repos/saltstack/salt/issues/46461","number":46461,"state":"open","locked":false,"title":"Add engine relaying libvirt events","user":{"login":"cbosdo","id":397931,"avatar_url":"https://avatars3.githubusercontent.com/u/397931?v=4","gravatar_id":"","url":"https://api.github.com/users/cbosdo","html_url":"https://github.com/cbosdo","followers_url":"https://api.github.com/users/cbosdo/followers","following_url":"https://api.github.com/users/cbosdo/following{/other_user}","gists_url":"https://api.github.com/users/cbosdo/gists{/gist_id}","starred_url":"https://api.github.com/users/cbosdo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cbosdo/subscriptions","organizations_url":"https://api.github.com/users/cbosdo/orgs","repos_url":"https://api.github.com/users/cbosdo/repos","events_url":"https://api.github.com/users/cbosdo/events{/privacy}","received_events_url":"https://api.github.com/users/cbosdo/received_events","type":"User","site_admin":false},"body":"### What does this PR do?\r\n\r\nLibvirt API offers clients to register callbacks for various events.\r\nlibvirt_events engine will listen on a libvirt URI (local or remote)\r\nfor events and send them to the salt event bus.\r\n\r\n### What issues does this PR fix or reference?\r\n\r\nNothing that is in the issue tracker to my knowledge\r\n\r\n### Tests written?\r\n\r\n~~No: those would require a running libvirt daemon~~\r\nYes\r\n\r\n### Commits signed with GPG?\r\n\r\nYes","created_at":"2018-03-09T16:42:41Z","updated_at":"2018-03-15T18:55:12Z","closed_at":null,"merged_at":null,"merge_commit_sha":"757427b1def401f772e0b188bcbf3da72651926e","assignee":null,"assignees":[],"requested_reviewers":[{"login":"garethgreenaway","id":718525,"avatar_url":"https://avatars0.githubusercontent.com/u/718525?v=4","gravatar_id":"","url":"https://api.github.com/users/garethgreenaway","html_url":"https://github.com/garethgreenaway","followers_url":"https://api.github.com/users/garethgreenaway/followers","following_url":"https://api.github.com/users/garethgreenaway/following{/other_user}","gists_url":"https://api.github.com/users/garethgreenaway/gists{/gist_id}","starred_url":"https://api.github.com/users/garethgreenaway/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/garethgreenaway/subscriptions","organizations_url":"https://api.github.com/users/garethgreenaway/orgs","repos_url":"https://api.github.com/users/garethgreenaway/repos","events_url":"https://api.github.com/users/garethgreenaway/events{/privacy}","received_events_url":"https://api.github.com/users/garethgreenaway/received_events","type":"User","site_admin":false}],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/saltstack/salt/pulls/46461/commits","review_comments_url":"https://api.github.com/repos/saltstack/salt/pulls/46461/comments","review_comment_url":"https://api.github.com/repos/saltstack/salt/pulls/comments{/number}","comments_url":"https://api.github.com/repos/saltstack/salt/issues/46461/comments","statuses_url":"https://api.github.com/repos/saltstack/salt/statuses/5af636f248620e5d80de054934bda5515f8a4a8f","head":{"label":"cbosdo:develop","ref":"develop","sha":"5af636f248620e5d80de054934bda5515f8a4a8f","user":{"login":"cbosdo","id":397931,"avatar_url":"https://avatars3.githubusercontent.com/u/397931?v=4","gravatar_id":"","url":"https://api.github.com/users/cbosdo","html_url":"https://github.com/cbosdo","followers_url":"https://api.github.com/users/cbosdo/followers","following_url":"https://api.github.com/users/cbosdo/following{/other_user}","gists_url":"https://api.github.com/users/cbosdo/gists{/gist_id}","starred_url":"https://api.github.com/users/cbosdo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cbosdo/subscriptions","organizations_url":"https://api.github.com/users/cbosdo/orgs","repos_url":"https://api.github.com/users/cbosdo/repos","events_url":"https://api.github.com/users/cbosdo/events{/privacy}","received_events_url":"https://api.github.com/users/cbosdo/received_events","type":"User","site_admin":false},"repo":{"id":124550188,"name":"salt","full_name":"cbosdo/salt","owner":{"login":"cbosdo","id":397931,"avatar_url":"https://avatars3.githubusercontent.com/u/397931?v=4","gravatar_id":"","url":"https://api.github.com/users/cbosdo","html_url":"https://github.com/cbosdo","followers_url":"https://api.github.com/users/cbosdo/followers","following_url":"https://api.github.com/users/cbosdo/following{/other_user}","gists_url":"https://api.github.com/users/cbosdo/gists{/gist_id}","starred_url":"https://api.github.com/users/cbosdo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cbosdo/subscriptions","organizations_url":"https://api.github.com/users/cbosdo/orgs","repos_url":"https://api.github.com/users/cbosdo/repos","events_url":"https://api.github.com/users/cbosdo/events{/privacy}","received_events_url":"https://api.github.com/users/cbosdo/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/cbosdo/salt","description":"Software to automate the management and configuration of any infrastructure or application at scale. Get access to the Salt software package repository here: ","fork":true,"url":"https://api.github.com/repos/cbosdo/salt","forks_url":"https://api.github.com/repos/cbosdo/salt/forks","keys_url":"https://api.github.com/repos/cbosdo/salt/keys{/key_id}","collaborators_url":"https://api.github.com/repos/cbosdo/salt/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/cbosdo/salt/teams","hooks_url":"https://api.github.com/repos/cbosdo/salt/hooks","issue_events_url":"https://api.github.com/repos/cbosdo/salt/issues/events{/number}","events_url":"https://api.github.com/repos/cbosdo/salt/events","assignees_url":"https://api.github.com/repos/cbosdo/salt/assignees{/user}","branches_url":"https://api.github.com/repos/cbosdo/salt/branches{/branch}","tags_url":"https://api.github.com/repos/cbosdo/salt/tags","blobs_url":"https://api.github.com/repos/cbosdo/salt/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/cbosdo/salt/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/cbosdo/salt/git/refs{/sha}","trees_url":"https://api.github.com/repos/cbosdo/salt/git/trees{/sha}","statuses_url":"https://api.github.com/repos/cbosdo/salt/statuses/{sha}","languages_url":"https://api.github.com/repos/cbosdo/salt/languages","stargazers_url":"https://api.github.com/repos/cbosdo/salt/stargazers","contributors_url":"https://api.github.com/repos/cbosdo/salt/contributors","subscribers_url":"https://api.github.com/repos/cbosdo/salt/subscribers","subscription_url":"https://api.github.com/repos/cbosdo/salt/subscription","commits_url":"https://api.github.com/repos/cbosdo/salt/commits{/sha}","git_commits_url":"https://api.github.com/repos/cbosdo/salt/git/commits{/sha}","comments_url":"https://api.github.com/repos/cbosdo/salt/comments{/number}","issue_comment_url":"https://api.github.com/repos/cbosdo/salt/issues/comments{/number}","contents_url":"https://api.github.com/repos/cbosdo/salt/contents/{+path}","compare_url":"https://api.github.com/repos/cbosdo/salt/compare/{base}...{head}","merges_url":"https://api.github.com/repos/cbosdo/salt/merges","archive_url":"https://api.github.com/repos/cbosdo/salt/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/cbosdo/salt/downloads","issues_url":"https://api.github.com/repos/cbosdo/salt/issues{/number}","pulls_url":"https://api.github.com/repos/cbosdo/salt/pulls{/number}","milestones_url":"https://api.github.com/repos/cbosdo/salt/milestones{/number}","notifications_url":"https://api.github.com/repos/cbosdo/salt/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/cbosdo/salt/labels{/name}","releases_url":"https://api.github.com/repos/cbosdo/salt/releases{/id}","deployments_url":"https://api.github.com/repos/cbosdo/salt/deployments","created_at":"2018-03-09T14:22:59Z","updated_at":"2018-03-15T16:39:46Z","pushed_at":"2018-03-15T16:39:43Z","git_url":"git://github.com/cbosdo/salt.git","ssh_url":"git@github.com:cbosdo/salt.git","clone_url":"https://github.com/cbosdo/salt.git","svn_url":"https://github.com/cbosdo/salt","homepage":"https://repo.saltstack.com/","size":210255,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":{"key":"other","name":"Other","spdx_id":null,"url":null},"forks":0,"open_issues":0,"watchers":0,"default_branch":"develop"}},"base":{"label":"saltstack:develop","ref":"develop","sha":"c88c20e1be8cec9ba5f4b7390a9eae658862f7a4","user":{"login":"saltstack","id":1147473,"avatar_url":"https://avatars3.githubusercontent.com/u/1147473?v=4","gravatar_id":"","url":"https://api.github.com/users/saltstack","html_url":"https://github.com/saltstack","followers_url":"https://api.github.com/users/saltstack/followers","following_url":"https://api.github.com/users/saltstack/following{/other_user}","gists_url":"https://api.github.com/users/saltstack/gists{/gist_id}","starred_url":"https://api.github.com/users/saltstack/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/saltstack/subscriptions","organizations_url":"https://api.github.com/users/saltstack/orgs","repos_url":"https://api.github.com/users/saltstack/repos","events_url":"https://api.github.com/users/saltstack/events{/privacy}","received_events_url":"https://api.github.com/users/saltstack/received_events","type":"Organization","site_admin":false},"repo":{"id":1390248,"name":"salt","full_name":"saltstack/salt","owner":{"login":"saltstack","id":1147473,"avatar_url":"https://avatars3.githubusercontent.com/u/1147473?v=4","gravatar_id":"","url":"https://api.github.com/users/saltstack","html_url":"https://github.com/saltstack","followers_url":"https://api.github.com/users/saltstack/followers","following_url":"https://api.github.com/users/saltstack/following{/other_user}","gists_url":"https://api.github.com/users/saltstack/gists{/gist_id}","starred_url":"https://api.github.com/users/saltstack/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/saltstack/subscriptions","organizations_url":"https://api.github.com/users/saltstack/orgs","repos_url":"https://api.github.com/users/saltstack/repos","events_url":"https://api.github.com/users/saltstack/events{/privacy}","received_events_url":"https://api.github.com/users/saltstack/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/saltstack/salt","description":"Software to automate the management and configuration of any infrastructure or application at scale. Get access to the Salt software package repository here: ","fork":false,"url":"https://api.github.com/repos/saltstack/salt","forks_url":"https://api.github.com/repos/saltstack/salt/forks","keys_url":"https://api.github.com/repos/saltstack/salt/keys{/key_id}","collaborators_url":"https://api.github.com/repos/saltstack/salt/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/saltstack/salt/teams","hooks_url":"https://api.github.com/repos/saltstack/salt/hooks","issue_events_url":"https://api.github.com/repos/saltstack/salt/issues/events{/number}","events_url":"https://api.github.com/repos/saltstack/salt/events","assignees_url":"https://api.github.com/repos/saltstack/salt/assignees{/user}","branches_url":"https://api.github.com/repos/saltstack/salt/branches{/branch}","tags_url":"https://api.github.com/repos/saltstack/salt/tags","blobs_url":"https://api.github.com/repos/saltstack/salt/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/saltstack/salt/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/saltstack/salt/git/refs{/sha}","trees_url":"https://api.github.com/repos/saltstack/salt/git/trees{/sha}","statuses_url":"https://api.github.com/repos/saltstack/salt/statuses/{sha}","languages_url":"https://api.github.com/repos/saltstack/salt/languages","stargazers_url":"https://api.github.com/repos/saltstack/salt/stargazers","contributors_url":"https://api.github.com/repos/saltstack/salt/contributors","subscribers_url":"https://api.github.com/repos/saltstack/salt/subscribers","subscription_url":"https://api.github.com/repos/saltstack/salt/subscription","commits_url":"https://api.github.com/repos/saltstack/salt/commits{/sha}","git_commits_url":"https://api.github.com/repos/saltstack/salt/git/commits{/sha}","comments_url":"https://api.github.com/repos/saltstack/salt/comments{/number}","issue_comment_url":"https://api.github.com/repos/saltstack/salt/issues/comments{/number}","contents_url":"https://api.github.com/repos/saltstack/salt/contents/{+path}","compare_url":"https://api.github.com/repos/saltstack/salt/compare/{base}...{head}","merges_url":"https://api.github.com/repos/saltstack/salt/merges","archive_url":"https://api.github.com/repos/saltstack/salt/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/saltstack/salt/downloads","issues_url":"https://api.github.com/repos/saltstack/salt/issues{/number}","pulls_url":"https://api.github.com/repos/saltstack/salt/pulls{/number}","milestones_url":"https://api.github.com/repos/saltstack/salt/milestones{/number}","notifications_url":"https://api.github.com/repos/saltstack/salt/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/saltstack/salt/labels{/name}","releases_url":"https://api.github.com/repos/saltstack/salt/releases{/id}","deployments_url":"https://api.github.com/repos/saltstack/salt/deployments","created_at":"2011-02-20T20:16:56Z","updated_at":"2018-03-15T17:25:05Z","pushed_at":"2018-03-15T18:31:34Z","git_url":"git://github.com/saltstack/salt.git","ssh_url":"git@github.com:saltstack/salt.git","clone_url":"https://github.com/saltstack/salt.git","svn_url":"https://github.com/saltstack/salt","homepage":"https://repo.saltstack.com/","size":210949,"stargazers_count":8677,"watchers_count":8677,"language":"Python","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":4053,"mirror_url":null,"archived":false,"open_issues_count":3702,"license":{"key":"other","name":"Other","spdx_id":null,"url":null},"forks":4053,"open_issues":3702,"watchers":8677,"default_branch":"develop"}},"_links":{"self":{"href":"https://api.github.com/repos/saltstack/salt/pulls/46461"},"html":{"href":"https://github.com/saltstack/salt/pull/46461"},"issue":{"href":"https://api.github.com/repos/saltstack/salt/issues/46461"},"comments":{"href":"https://api.github.com/repos/saltstack/salt/issues/46461/comments"},"review_comments":{"href":"https://api.github.com/repos/saltstack/salt/pulls/46461/comments"},"review_comment":{"href":"https://api.github.com/repos/saltstack/salt/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/saltstack/salt/pulls/46461/commits"},"statuses":{"href":"https://api.github.com/repos/saltstack/salt/statuses/5af636f248620e5d80de054934bda5515f8a4a8f"}},"author_association":"NONE"}}
{"actor":{"display_login":"rallytime"}}