{"action":"created","comment":{"url":"https://api.github.com/repos/ceph/ceph-deploy/pulls/comments/59376268","id":59376268,"diff_hunk":"@@ -1,84 +1,262 @@\n import os.path\n import logging\n+import json\n+import tempfile\n+import shutil\n+import time\n \n from ceph_deploy import hosts, exc\n from ceph_deploy.cliutil import priority\n+from ceph_deploy.lib import remoto\n \n \n LOG = logging.getLogger(__name__)\n \n \n-def fetch_file(args, frompath, topath, _hosts):\n-    if os.path.exists(topath):\n-        LOG.debug('Have %s', topath)\n-        return True\n-    else:\n-        for hostname in _hosts:\n-            filepath = frompath.format(hostname=hostname)\n-            LOG.debug('Checking %s for %s', hostname, filepath)\n-            distro = hosts.get(hostname, username=args.username)\n-            key = distro.conn.remote_module.get_file(filepath)\n+def _keyring_equivilent(keyring_one, keyring_two):\n+    \"\"\"\n+    Check two keyrings are identical\n+    \"\"\"\n+    def keyring_extract_key(file_path):\n+        \"\"\"\n+        Cephx keyring files may or may not have white space before some lines.\n+        They may have some values in quotes, so a safe way to compare is to\n+        extract the key.\n+        \"\"\"\n+        with file(file_path, 'r') as f:\n+            for line in f:\n+                content = line.strip()\n+                if len(content) == 0:\n+                    continue\n+                split_line = content.split('=')\n+                if split_line[0].strip() == 'key':\n+                    return \"=\".join(split_line[1:]).strip()\n+        raise RuntimeError(\"File '%s' is not a keyring\" % file_path)\n+    key_one = keyring_extract_key(keyring_one)\n+    key_two = keyring_extract_key(keyring_two)\n+    return key_one == key_two\n+\n \n-            if key is not None:\n-                LOG.debug('Got %s key from %s.', topath, hostname)\n-                with file(topath, 'w') as f:\n-                    f.write(key)\n-                    return True\n-            distro.conn.exit()\n-            LOG.warning('Unable to find %s on %s', filepath, hostname)\n-    return False\n+def keytype_path_to(args, keytype):\n+    \"\"\"\n+    Get the local filename for a keyring type\n+    \"\"\"\n+    if keytype == \"admin\":\n+        return '{cluster}.client.admin.keyring'.format(\n+            cluster=args.cluster)\n+    if keytype == \"mon\":\n+        return '{cluster}.mon.keyring'.format(\n+            cluster=args.cluster)\n+    return '{cluster}.bootstrap-{what}.keyring'.format(\n+            cluster=args.cluster,\n+            what=keytype)\n \n \n-def gatherkeys(args):\n-    oldmask = os.umask(077)\n-    try:\n-        # client.admin\n-        keyring = '/etc/ceph/{cluster}.client.admin.keyring'.format(\n+def keytype_path_from(args, keytype):\n+    \"\"\"\n+    Get the remote file path for a keyring type\n+    \"\"\"\n+    if keytype == \"admin\":\n+        return '/etc/ceph/{cluster}.client.admin.keyring'.format(\n             cluster=args.cluster)\n-        r = fetch_file(\n-            args=args,\n-            frompath=keyring,\n-            topath='{cluster}.client.admin.keyring'.format(\n-                cluster=args.cluster),\n-            _hosts=args.mon,\n-            )\n-        if not r:\n-            raise exc.KeyNotFoundError(keyring, args.mon)\n-\n-        # mon.\n-        keyring = '/var/lib/ceph/mon/{cluster}-{{hostname}}/keyring'.format(\n+    if keytype == \"mon\":\n+        return '/var/lib/ceph/mon/{cluster}-{{hostname}}/keyring'.format(\n             cluster=args.cluster)\n-        r = fetch_file(\n-            args=args,\n-            frompath=keyring,\n-            topath='{cluster}.mon.keyring'.format(cluster=args.cluster),\n-            _hosts=args.mon,\n-            )\n-        if not r:\n-            raise exc.KeyNotFoundError(keyring, args.mon)\n-\n-        # bootstrap\n-        for what in ['osd', 'mds', 'rgw']:\n-            keyring = '/var/lib/ceph/bootstrap-{what}/{cluster}.keyring'.format(\n-                what=what,\n+    return '/var/lib/ceph/bootstrap-{what}/{cluster}.keyring'.format(\n+                what=keytype,\n                 cluster=args.cluster)\n-            r = fetch_file(\n-                args=args,\n-                frompath=keyring,\n-                topath='{cluster}.bootstrap-{what}.keyring'.format(\n-                    cluster=args.cluster,\n-                    what=what),\n-                _hosts=args.mon,\n-                )\n-            if not r:\n-                if what in ['osd', 'mds']:\n-                    raise exc.KeyNotFoundError(keyring, args.mon)\n-                else:\n-                    LOG.warning((\"No RGW bootstrap key found. Will not be able to \"\n-                                 \"deploy RGW daemons\"))\n+\n+\n+def keytype_identity(keytype):\n+    \"\"\"\n+    Get the keyring identity from keyring type.\n+\n+    This is used in authentication with keyrings and generating keyrings.\n+    \"\"\"\n+    if keytype is 'admin':\n+         return 'client.admin'\n+    if keytype is 'mds':\n+         return 'client.bootstrap-mds'\n+    if keytype is 'osd':\n+         return 'client.bootstrap-osd'\n+    if keytype is 'rgw':\n+         return 'client.bootstrap-rgw'\n+    if keytype is 'mon':\n+         return 'mon.'\n+    raise RuntimeError('Unexpected keytype %s' % keytype)\n+\n+\n+def keytype_capabilities(keytype):\n+    \"\"\"\n+    Get the capabilities of a keyring from keyring type.\n+    \"\"\"\n+    if keytype is 'admin':\n+        return [\n+            'osd', 'allow *',\n+            'mds', 'allow *',\n+            'mon', 'allow *'\n+        ]\n+    if keytype is 'mds':\n+        return [\n+            'mon', 'allow profile bootstrap-mds'\n+        ]\n+    if keytype is 'osd':\n+        return [\n+            'mon', 'allow profile bootstrap-osd'\n+        ]\n+    if keytype is 'rgw':\n+        return [\n+            'mon', 'allow profile bootstrap-rgw'\n+        ]\n+    raise RuntimeError('Unexpected keytype %s' % keytype)\n+\n+\n+def gatherkeys_missing(args, distro, rlogger, keypath, keytype, dest_dir):\n+    \"\"\"\n+    Get or create the keyring from the mon using the mon keyring by keytype and\n+    copy to dest_dir\n+    \"\"\"\n+    arguments = [\n+        '/usr/bin/ceph',\n+        '--connect-timeout=25',\n+        '--cluster={cluster}'.format(\n+            cluster=args.cluster),\n+        '--name', 'mon.',\n+        '--keyring={keypath}'.format(\n+            keypath=keypath),\n+        'auth', 'get-or-create',\n+        ]\n+    identity = keytype_identity(keytype)\n+    arguments.append(identity)\n+    capabilites = keytype_capabilities(keytype)\n+    arguments.extend(capabilites)\n+    out, err, code = remoto.process.check(\n+        distro.conn,\n+        arguments\n+        )\n+    if code != 0:\n+        rlogger.error('\"ceph auth get-or-create for keytype %s returned %s' % (keytype, code))\n+        for line in err:\n+            rlogger.debug(line)\n+        return False\n+    keyring_name_local = keytype_path_to(args, keytype)\n+    keyring_path_local = os.path.join(dest_dir, keyring_name_local)\n+    with file(keyring_path_local, 'w') as f:\n+        for line in out:\n+            f.write(line + '\\n')\n+    return True\n+\n+\n+def gatherkeys_with_mon(args, host, dest_dir):\n+    \"\"\"\n+    Connect to mon and gather keys if mon is in quorum.\n+    \"\"\"\n+    distro = hosts.get(host, username=args.username)\n+    path_keytype_mon_template = keytype_path_from(args, 'mon')\n+    path_keytype_mon = path_keytype_mon_template.format(\n+            hostname=host)\n+    mon_key = distro.conn.remote_module.get_file(path_keytype_mon)\n+    if mon_key is None:\n+        LOG.warning(\"No mon key found. Is '%s' a mon node\" % (host))\n+        return False\n+    mon_name_local = keytype_path_to(args, \"mon\")\n+    mon_path_local = os.path.join(dest_dir, mon_name_local)\n+    with file(mon_path_local, 'w') as f:\n+        f.write(mon_key)\n+    rlogger = logging.getLogger(host)\n+    out, err, code = remoto.process.check(\n+        distro.conn,\n+            [\n+                \"/usr/bin/ceph\",\n+                \"--connect-timeout=25\",\n+                \"--cluster={cluster}\".format(\n+                    cluster=args.cluster),\n+                \"--admin-daemon=/var/run/ceph/{cluster}-mon.{hostname}.asok\".format(\n+                    hostname=host,\n+                    cluster=args.cluster),\n+                \"mon_status\"\n+            ]\n+        )\n+    if code != 0:\n+        rlogger.error('\"ceph mon_status %s\" returned %s' % (host, code))\n+        for line in err:\n+            rlogger.debug(line)\n+        return False\n+    mon_status = json.loads(\"\".join(out))\n+    mon_number = None\n+    mon_map = mon_status.get('monmap')\n+    if mon_map is None:\n+        rlogger.error(\"could not find mon map for mons on '%s'\" % (host))\n+        return False\n+    mon_quorum = mon_status.get('quorum')\n+    if mon_quorum is None:\n+        rlogger.error(\"could not find quorum for mons on '%s'\" % (host))\n+        return False","path":"ceph_deploy/gatherkeys.py","position":256,"original_position":256,"commit_id":"c7c3b07d5dfec781139d1e54ccf7d99622989b8d","original_commit_id":"c7c3b07d5dfec781139d1e54ccf7d99622989b8d","user":{"login":"oms4suse","id":14092619,"avatar_url":"https://avatars.githubusercontent.com/u/14092619?v=3","gravatar_id":"","url":"https://api.github.com/users/oms4suse","html_url":"https://github.com/oms4suse","followers_url":"https://api.github.com/users/oms4suse/followers","following_url":"https://api.github.com/users/oms4suse/following{/other_user}","gists_url":"https://api.github.com/users/oms4suse/gists{/gist_id}","starred_url":"https://api.github.com/users/oms4suse/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/oms4suse/subscriptions","organizations_url":"https://api.github.com/users/oms4suse/orgs","repos_url":"https://api.github.com/users/oms4suse/repos","events_url":"https://api.github.com/users/oms4suse/events{/privacy}","received_events_url":"https://api.github.com/users/oms4suse/received_events","type":"User","site_admin":false},"body":"Can you be more specific what you want tested here?","created_at":"2016-04-12T13:36:16Z","updated_at":"2016-04-12T13:36:16Z","html_url":"https://github.com/ceph/ceph-deploy/pull/393#discussion_r59376268","pull_request_url":"https://api.github.com/repos/ceph/ceph-deploy/pulls/393","_links":{"self":{"href":"https://api.github.com/repos/ceph/ceph-deploy/pulls/comments/59376268"},"html":{"href":"https://github.com/ceph/ceph-deploy/pull/393#discussion_r59376268"},"pull_request":{"href":"https://api.github.com/repos/ceph/ceph-deploy/pulls/393"}}},"pull_request":{"url":"https://api.github.com/repos/ceph/ceph-deploy/pulls/393","id":65971496,"html_url":"https://github.com/ceph/ceph-deploy/pull/393","diff_url":"https://github.com/ceph/ceph-deploy/pull/393.diff","patch_url":"https://github.com/ceph/ceph-deploy/pull/393.patch","issue_url":"https://api.github.com/repos/ceph/ceph-deploy/issues/393","number":393,"state":"open","locked":false,"title":"[RM-15451] Gather keys directly from the mon","user":{"login":"oms4suse","id":14092619,"avatar_url":"https://avatars.githubusercontent.com/u/14092619?v=3","gravatar_id":"","url":"https://api.github.com/users/oms4suse","html_url":"https://github.com/oms4suse","followers_url":"https://api.github.com/users/oms4suse/followers","following_url":"https://api.github.com/users/oms4suse/following{/other_user}","gists_url":"https://api.github.com/users/oms4suse/gists{/gist_id}","starred_url":"https://api.github.com/users/oms4suse/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/oms4suse/subscriptions","organizations_url":"https://api.github.com/users/oms4suse/orgs","repos_url":"https://api.github.com/users/oms4suse/repos","events_url":"https://api.github.com/users/oms4suse/events{/privacy}","received_events_url":"https://api.github.com/users/oms4suse/received_events","type":"User","site_admin":false},"body":"Use the mon keyring to gather keys directly from the mon.\r\nThis is better than relying on:\r\n\r\n  ceph-create-keys --cluster ${cluster_name} --id ${mon_id}\r\n\r\nWhich is started as a side effect of booting a mon. This side effect has\r\nnumerous issues as stated in this thread on ceph-devel:\r\n\r\n    http://comments.gmane.org/gmane.comp.file-systems.ceph.devel/30552\r\n\r\nThe conclusion is that ceph will remove this side effect, after ceph-deploy\r\nhas a fix. This fix removes that dependency.\r\n\r\nSigned-off-by: Owen Synge <osynge@suse.com>","created_at":"2016-04-11T10:30:47Z","updated_at":"2016-04-12T13:36:16Z","closed_at":null,"merged_at":null,"merge_commit_sha":"0bd0e60d103dae549246d7f529350476d1763ede","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/ceph/ceph-deploy/pulls/393/commits","review_comments_url":"https://api.github.com/repos/ceph/ceph-deploy/pulls/393/comments","review_comment_url":"https://api.github.com/repos/ceph/ceph-deploy/pulls/comments{/number}","comments_url":"https://api.github.com/repos/ceph/ceph-deploy/issues/393/comments","statuses_url":"https://api.github.com/repos/ceph/ceph-deploy/statuses/c7c3b07d5dfec781139d1e54ccf7d99622989b8d","head":{"label":"SUSE:fix-remove-ceph-create-keys-dep","ref":"fix-remove-ceph-create-keys-dep","sha":"c7c3b07d5dfec781139d1e54ccf7d99622989b8d","user":{"login":"SUSE","id":1067733,"avatar_url":"https://avatars.githubusercontent.com/u/1067733?v=3","gravatar_id":"","url":"https://api.github.com/users/SUSE","html_url":"https://github.com/SUSE","followers_url":"https://api.github.com/users/SUSE/followers","following_url":"https://api.github.com/users/SUSE/following{/other_user}","gists_url":"https://api.github.com/users/SUSE/gists{/gist_id}","starred_url":"https://api.github.com/users/SUSE/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/SUSE/subscriptions","organizations_url":"https://api.github.com/users/SUSE/orgs","repos_url":"https://api.github.com/users/SUSE/repos","events_url":"https://api.github.com/users/SUSE/events{/privacy}","received_events_url":"https://api.github.com/users/SUSE/received_events","type":"Organization","site_admin":false},"repo":{"id":38686248,"name":"ceph-deploy","full_name":"SUSE/ceph-deploy","owner":{"login":"SUSE","id":1067733,"avatar_url":"https://avatars.githubusercontent.com/u/1067733?v=3","gravatar_id":"","url":"https://api.github.com/users/SUSE","html_url":"https://github.com/SUSE","followers_url":"https://api.github.com/users/SUSE/followers","following_url":"https://api.github.com/users/SUSE/following{/other_user}","gists_url":"https://api.github.com/users/SUSE/gists{/gist_id}","starred_url":"https://api.github.com/users/SUSE/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/SUSE/subscriptions","organizations_url":"https://api.github.com/users/SUSE/orgs","repos_url":"https://api.github.com/users/SUSE/repos","events_url":"https://api.github.com/users/SUSE/events{/privacy}","received_events_url":"https://api.github.com/users/SUSE/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/SUSE/ceph-deploy","description":"Deploy Ceph with minimal infrastructure, using just SSH access","fork":true,"url":"https://api.github.com/repos/SUSE/ceph-deploy","forks_url":"https://api.github.com/repos/SUSE/ceph-deploy/forks","keys_url":"https://api.github.com/repos/SUSE/ceph-deploy/keys{/key_id}","collaborators_url":"https://api.github.com/repos/SUSE/ceph-deploy/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/SUSE/ceph-deploy/teams","hooks_url":"https://api.github.com/repos/SUSE/ceph-deploy/hooks","issue_events_url":"https://api.github.com/repos/SUSE/ceph-deploy/issues/events{/number}","events_url":"https://api.github.com/repos/SUSE/ceph-deploy/events","assignees_url":"https://api.github.com/repos/SUSE/ceph-deploy/assignees{/user}","branches_url":"https://api.github.com/repos/SUSE/ceph-deploy/branches{/branch}","tags_url":"https://api.github.com/repos/SUSE/ceph-deploy/tags","blobs_url":"https://api.github.com/repos/SUSE/ceph-deploy/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/SUSE/ceph-deploy/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/SUSE/ceph-deploy/git/refs{/sha}","trees_url":"https://api.github.com/repos/SUSE/ceph-deploy/git/trees{/sha}","statuses_url":"https://api.github.com/repos/SUSE/ceph-deploy/statuses/{sha}","languages_url":"https://api.github.com/repos/SUSE/ceph-deploy/languages","stargazers_url":"https://api.github.com/repos/SUSE/ceph-deploy/stargazers","contributors_url":"https://api.github.com/repos/SUSE/ceph-deploy/contributors","subscribers_url":"https://api.github.com/repos/SUSE/ceph-deploy/subscribers","subscription_url":"https://api.github.com/repos/SUSE/ceph-deploy/subscription","commits_url":"https://api.github.com/repos/SUSE/ceph-deploy/commits{/sha}","git_commits_url":"https://api.github.com/repos/SUSE/ceph-deploy/git/commits{/sha}","comments_url":"https://api.github.com/repos/SUSE/ceph-deploy/comments{/number}","issue_comment_url":"https://api.github.com/repos/SUSE/ceph-deploy/issues/comments{/number}","contents_url":"https://api.github.com/repos/SUSE/ceph-deploy/contents/{+path}","compare_url":"https://api.github.com/repos/SUSE/ceph-deploy/compare/{base}...{head}","merges_url":"https://api.github.com/repos/SUSE/ceph-deploy/merges","archive_url":"https://api.github.com/repos/SUSE/ceph-deploy/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/SUSE/ceph-deploy/downloads","issues_url":"https://api.github.com/repos/SUSE/ceph-deploy/issues{/number}","pulls_url":"https://api.github.com/repos/SUSE/ceph-deploy/pulls{/number}","milestones_url":"https://api.github.com/repos/SUSE/ceph-deploy/milestones{/number}","notifications_url":"https://api.github.com/repos/SUSE/ceph-deploy/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/SUSE/ceph-deploy/labels{/name}","releases_url":"https://api.github.com/repos/SUSE/ceph-deploy/releases{/id}","deployments_url":"https://api.github.com/repos/SUSE/ceph-deploy/deployments","created_at":"2015-07-07T12:30:28Z","updated_at":"2016-02-11T16:02:34Z","pushed_at":"2016-04-12T13:21:24Z","git_url":"git://github.com/SUSE/ceph-deploy.git","ssh_url":"git@github.com:SUSE/ceph-deploy.git","clone_url":"https://github.com/SUSE/ceph-deploy.git","svn_url":"https://github.com/SUSE/ceph-deploy","homepage":null,"size":2353,"stargazers_count":1,"watchers_count":1,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":2,"mirror_url":null,"open_issues_count":0,"forks":2,"open_issues":0,"watchers":1,"default_branch":"master"}},"base":{"label":"ceph:master","ref":"master","sha":"6763f3347a73c979a609004386fb581556d3d9d1","user":{"login":"ceph","id":1015767,"avatar_url":"https://avatars.githubusercontent.com/u/1015767?v=3","gravatar_id":"","url":"https://api.github.com/users/ceph","html_url":"https://github.com/ceph","followers_url":"https://api.github.com/users/ceph/followers","following_url":"https://api.github.com/users/ceph/following{/other_user}","gists_url":"https://api.github.com/users/ceph/gists{/gist_id}","starred_url":"https://api.github.com/users/ceph/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ceph/subscriptions","organizations_url":"https://api.github.com/users/ceph/orgs","repos_url":"https://api.github.com/users/ceph/repos","events_url":"https://api.github.com/users/ceph/events{/privacy}","received_events_url":"https://api.github.com/users/ceph/received_events","type":"Organization","site_admin":false},"repo":{"id":5786856,"name":"ceph-deploy","full_name":"ceph/ceph-deploy","owner":{"login":"ceph","id":1015767,"avatar_url":"https://avatars.githubusercontent.com/u/1015767?v=3","gravatar_id":"","url":"https://api.github.com/users/ceph","html_url":"https://github.com/ceph","followers_url":"https://api.github.com/users/ceph/followers","following_url":"https://api.github.com/users/ceph/following{/other_user}","gists_url":"https://api.github.com/users/ceph/gists{/gist_id}","starred_url":"https://api.github.com/users/ceph/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ceph/subscriptions","organizations_url":"https://api.github.com/users/ceph/orgs","repos_url":"https://api.github.com/users/ceph/repos","events_url":"https://api.github.com/users/ceph/events{/privacy}","received_events_url":"https://api.github.com/users/ceph/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/ceph/ceph-deploy","description":"Deploy Ceph with minimal infrastructure, using just SSH access","fork":false,"url":"https://api.github.com/repos/ceph/ceph-deploy","forks_url":"https://api.github.com/repos/ceph/ceph-deploy/forks","keys_url":"https://api.github.com/repos/ceph/ceph-deploy/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ceph/ceph-deploy/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ceph/ceph-deploy/teams","hooks_url":"https://api.github.com/repos/ceph/ceph-deploy/hooks","issue_events_url":"https://api.github.com/repos/ceph/ceph-deploy/issues/events{/number}","events_url":"https://api.github.com/repos/ceph/ceph-deploy/events","assignees_url":"https://api.github.com/repos/ceph/ceph-deploy/assignees{/user}","branches_url":"https://api.github.com/repos/ceph/ceph-deploy/branches{/branch}","tags_url":"https://api.github.com/repos/ceph/ceph-deploy/tags","blobs_url":"https://api.github.com/repos/ceph/ceph-deploy/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ceph/ceph-deploy/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ceph/ceph-deploy/git/refs{/sha}","trees_url":"https://api.github.com/repos/ceph/ceph-deploy/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ceph/ceph-deploy/statuses/{sha}","languages_url":"https://api.github.com/repos/ceph/ceph-deploy/languages","stargazers_url":"https://api.github.com/repos/ceph/ceph-deploy/stargazers","contributors_url":"https://api.github.com/repos/ceph/ceph-deploy/contributors","subscribers_url":"https://api.github.com/repos/ceph/ceph-deploy/subscribers","subscription_url":"https://api.github.com/repos/ceph/ceph-deploy/subscription","commits_url":"https://api.github.com/repos/ceph/ceph-deploy/commits{/sha}","git_commits_url":"https://api.github.com/repos/ceph/ceph-deploy/git/commits{/sha}","comments_url":"https://api.github.com/repos/ceph/ceph-deploy/comments{/number}","issue_comment_url":"https://api.github.com/repos/ceph/ceph-deploy/issues/comments{/number}","contents_url":"https://api.github.com/repos/ceph/ceph-deploy/contents/{+path}","compare_url":"https://api.github.com/repos/ceph/ceph-deploy/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ceph/ceph-deploy/merges","archive_url":"https://api.github.com/repos/ceph/ceph-deploy/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ceph/ceph-deploy/downloads","issues_url":"https://api.github.com/repos/ceph/ceph-deploy/issues{/number}","pulls_url":"https://api.github.com/repos/ceph/ceph-deploy/pulls{/number}","milestones_url":"https://api.github.com/repos/ceph/ceph-deploy/milestones{/number}","notifications_url":"https://api.github.com/repos/ceph/ceph-deploy/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ceph/ceph-deploy/labels{/name}","releases_url":"https://api.github.com/repos/ceph/ceph-deploy/releases{/id}","deployments_url":"https://api.github.com/repos/ceph/ceph-deploy/deployments","created_at":"2012-09-12T22:30:24Z","updated_at":"2016-04-06T07:02:41Z","pushed_at":"2016-04-12T13:12:45Z","git_url":"git://github.com/ceph/ceph-deploy.git","ssh_url":"git@github.com:ceph/ceph-deploy.git","clone_url":"https://github.com/ceph/ceph-deploy.git","svn_url":"https://github.com/ceph/ceph-deploy","homepage":null,"size":3100,"stargazers_count":137,"watchers_count":137,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":139,"mirror_url":null,"open_issues_count":16,"forks":139,"open_issues":16,"watchers":137,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/ceph/ceph-deploy/pulls/393"},"html":{"href":"https://github.com/ceph/ceph-deploy/pull/393"},"issue":{"href":"https://api.github.com/repos/ceph/ceph-deploy/issues/393"},"comments":{"href":"https://api.github.com/repos/ceph/ceph-deploy/issues/393/comments"},"review_comments":{"href":"https://api.github.com/repos/ceph/ceph-deploy/pulls/393/comments"},"review_comment":{"href":"https://api.github.com/repos/ceph/ceph-deploy/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/ceph/ceph-deploy/pulls/393/commits"},"statuses":{"href":"https://api.github.com/repos/ceph/ceph-deploy/statuses/c7c3b07d5dfec781139d1e54ccf7d99622989b8d"}}}}