{"action":"created","comment":{"url":"https://api.github.com/repos/OpenKMIP/PyKMIP/pulls/comments/35539276","id":35539276,"diff_hunk":"@@ -0,0 +1,389 @@\n+# Copyright (c) 2015 The Johns Hopkins University/Applied Physics Laboratory\n+# All Rights Reserved.\n+#\n+# Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n+# not use this file except in compliance with the License. You may obtain\n+# a copy of the License at\n+#\n+#    http://www.apache.org/licenses/LICENSE-2.0\n+#\n+# Unless required by applicable law or agreed to in writing, software\n+# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n+# License for the specific language governing permissions and limitations\n+# under the License.\n+\n+import logging\n+import multiprocessing\n+import six\n+\n+from kmip.core import enums\n+from kmip.core import objects as cobjects\n+\n+from kmip.core.factories import attributes\n+\n+from kmip.pie import api\n+from kmip.pie import exceptions\n+from kmip.pie import factory\n+from kmip.pie import objects as pobjects\n+\n+from kmip.services.kmip_client import KMIPProxy\n+\n+\n+class ProxyKmipClient(api.KmipClient):\n+    \"\"\"\n+    A simplified KMIP client for conducting KMIP operations.\n+\n+    The ProxyKmipClient is a simpler, thread-safe KMIP client supporting\n+    various KMIP operations. It wraps the original KMIPProxy, reducing\n+    the boilerplate needed to deploy PyKMIP in client applications. The\n+    underlying proxy client is responsible for setting up the underlying\n+    socket connection and for writing/reading data to/from the socket.\n+    \"\"\"\n+    def __init__(self,\n+                 hostname=None,\n+                 port=None,\n+                 cert=None,\n+                 key=None,\n+                 ca=None,\n+                 ssl_version=None,\n+                 username=None,\n+                 password=None,\n+                 config='client'):\n+        \"\"\"\n+        Construct a ProxyKmipClient.\n+\n+        Args:\n+            hostname (string): The host or IP address of a KMIP appliance.\n+                Optional, defaults to None.\n+            port (int): The port number used to establish a connection to a\n+                KMIP appliance. Usually 5696 for KMIP applications. Optional,\n+                defaults to None.\n+            cert (string): The path to the client's certificate. Optional,\n+                defaults to None.\n+            key (string): The path to the key for the client's certificate.\n+                Optional, defaults to None.\n+            ca (string): The path to the CA certificate used to verify the\n+                server's certificate. Optional, defaults to None.\n+            ssl_version (string): The name of the ssl version to use for the\n+                connection. Example: 'PROTOCOL_SSLv23'. Optional, defaults to\n+                None.\n+            username (string): The username of the KMIP appliance account to\n+                use for operations. Optional, defaults to None.\n+            password (string): The password of the KMIP appliance account to\n+                use for operations. Optional, defaults to None.\n+            config (string): The name of a section in the PyKMIP configuration\n+                file. Use to load a specific set of configuration settings from\n+                the configuration file, instead of specifying them manually.\n+                Optional, defaults to the default client section, 'client'.\n+        \"\"\"\n+        self.logger = logging.getLogger()\n+\n+        self.attribute_factory = attributes.AttributeFactory()\n+        self.object_factory = factory.ObjectFactory()\n+\n+        # TODO (peter-hamilton) Consider adding validation checks for inputs.\n+        self.proxy = KMIPProxy(\n+            host=hostname,\n+            port=port,\n+            certfile=cert,\n+            keyfile=key,\n+            ca_certs=ca,\n+            ssl_version=ssl_version,\n+            username=username,\n+            password=password,\n+            config=config)\n+\n+        self._is_open = False\n+        self._lock = multiprocessing.RLock()\n+\n+    def _synchronize(f):\n+        \"\"\"\n+        A synchronization decorator for creating thread-safe client methods.\n+\n+        Args:\n+            f (function): The function to be wrapped in synchronization checks.\n+\n+        Returns:\n+            function: The original function protected by a synchronization\n+                lock.\n+        \"\"\"\n+        def wrapper(self, *args, **kwargs):\n+            with self._lock:\n+                return f(self, *args, **kwargs)\n+\n+        return wrapper\n+\n+    @_synchronize\n+    def open(self):\n+        \"\"\"\n+        Open the client connection.\n+\n+        Raises:\n+            ClientConnectionFailure: if the client connection is already open\n+            Exception: if an error occurs while trying to open the connection\n+        \"\"\"\n+        if self._is_open:\n+            raise exceptions.ClientConnectionFailure(\n+                \"client connection already open\")\n+        else:\n+            try:\n+                self.proxy.open()\n+                self._is_open = True\n+            except Exception as e:\n+                self.logger.exception(\"could not open client connection\", e)\n+                raise e\n+\n+    @_synchronize\n+    def close(self):\n+        \"\"\"\n+        Close the client connection.\n+\n+        Raises:\n+            ClientConnectionNotOpen: if the client connection is not open\n+            Exception: if an error occurs while trying to close the connection\n+        \"\"\"\n+        if not self._is_open:\n+            raise exceptions.ClientConnectionNotOpen()\n+        else:\n+            try:\n+                self.proxy.close()\n+                self._is_open = False\n+            except Exception as e:\n+                self.logger.exception(\"could not close client connection\", e)\n+                raise e\n+\n+    @_synchronize\n+    def create(self, algorithm, length):\n+        \"\"\"\n+        Create a symmetric key on a KMIP appliance.\n+\n+        Args:\n+            algorithm (CryptographicAlgorithm): An enumeration defining the\n+                algorithm to use to generate the symmetric key.\n+            length (int): The length in bits for the symmetric key.\n+\n+        Returns:\n+            string: The uid of the newly created symmetric key.\n+\n+        Raises:\n+            ClientConnectionNotOpen: if the client connection is unusable\n+            KmipOperationFailure: if the operation result is a failure\n+            TypeError: if the input arguments are invalid\n+        \"\"\"\n+        # Check inputs\n+        if not isinstance(algorithm, enums.CryptographicAlgorithm):\n+            raise TypeError(\n+                \"algorithm must be a CryptographicAlgorithm enumeration\")\n+        elif not isinstance(length, six.integer_types) or length <= 0:\n+            raise TypeError(\"length must be a positive integer\")\n+\n+        # Verify that operations can be given at this time\n+        if not self._is_open:\n+            raise exceptions.ClientConnectionNotOpen()\n+\n+        # Create the template containing the attributes\n+        attributes = self._build_key_attributes(algorithm, length)\n+        template = cobjects.TemplateAttribute(attributes=attributes)\n+\n+        # Create the symmetric key and handle the results\n+        result = self.proxy.create(enums.ObjectType.SYMMETRIC_KEY, template)\n+\n+        status = result.result_status.enum\n+        if status == enums.ResultStatus.SUCCESS:\n+            uid = result.uuid.value\n+            return uid\n+        else:\n+            reason = result.result_reason.enum\n+            message = result.result_message.value\n+            raise exceptions.KmipOperationFailure(status, reason, message)\n+\n+    @_synchronize\n+    def create_key_pair(self, algorithm, length):\n+        \"\"\"\n+        Create an asymmetric key pair on a KMIP appliance.\n+\n+        Args:\n+            algorithm (CryptographicAlgorithm): An enumeration defining the\n+                algorithm to use to generate the key pair.\n+            length (int): The length in bits for the key pair.\n+\n+        Returns:\n+            string: The uid of the newly created public key.\n+            string: The uid of the newly created private key.\n+\n+        Raises:\n+            ClientConnectionNotOpen: if the client connection is unusable\n+            KmipOperationFailure: if the operation result is a failure\n+            TypeError: if the input arguments are invalid\n+        \"\"\"\n+        # Check inputs\n+        if not isinstance(algorithm, enums.CryptographicAlgorithm):\n+            raise TypeError(\n+                \"algorithm must be a CryptographicAlgorithm enumeration\")\n+        elif not isinstance(length, six.integer_types) or length <= 0:\n+            raise TypeError(\"length must be a positive integer\")\n+\n+        # Verify that operations can be given at this time\n+        if not self._is_open:\n+            raise exceptions.ClientConnectionNotOpen()\n+\n+        # Create the template containing the attributes\n+        attributes = self._build_key_attributes(algorithm, length)\n+        template = cobjects.CommonTemplateAttribute(attributes=attributes)\n+\n+        # Create the asymmetric key pair and handle the results\n+        result = self.proxy.create_key_pair(common_template_attribute=template)\n+\n+        status = result.result_status.enum\n+        if status == enums.ResultStatus.SUCCESS:\n+            public_uid = result.public_key_uuid.value\n+            private_uid = result.private_key_uuid.value\n+            return public_uid, private_uid\n+        else:\n+            reason = result.result_reason.enum\n+            message = result.result_message.value\n+            raise exceptions.KmipOperationFailure(status, reason, message)\n+\n+    @_synchronize\n+    def register(self, managed_object):\n+        \"\"\"\n+        Register a managed object with a KMIP appliance.\n+\n+        Args:\n+            managed_object (ManagedObject): A managed object to register. An\n+                instantiatable subclass of ManagedObject from the Pie API.\n+\n+        Returns:\n+            string: The uid of the newly registered managed object.\n+\n+        Raises:\n+            ClientConnectionNotOpen: if the client connection is unusable\n+            KmipOperationFailure: if the operation result is a failure\n+            TypeError: if the input argument is invalid\n+        \"\"\"\n+        # Check input\n+        if not isinstance(managed_object, pobjects.ManagedObject):\n+            raise TypeError(\"managed object must be a Pie ManagedObject\")\n+\n+        # Verify that operations can be given at this time\n+        if not self._is_open:\n+            raise exceptions.ClientConnectionNotOpen()\n+\n+        # Extract and create attributes\n+        attributes = list()\n+        if hasattr(managed_object, 'cryptographic_usage_masks'):","path":"kmip/pie/client.py","position":275,"original_position":275,"commit_id":"99caad8528cc8829833d24cba4f4f6093dfe9048","original_commit_id":"99caad8528cc8829833d24cba4f4f6093dfe9048","user":{"login":"rellerreller","id":3485617,"avatar_url":"https://avatars.githubusercontent.com/u/3485617?v=3","gravatar_id":"","url":"https://api.github.com/users/rellerreller","html_url":"https://github.com/rellerreller","followers_url":"https://api.github.com/users/rellerreller/followers","following_url":"https://api.github.com/users/rellerreller/following{/other_user}","gists_url":"https://api.github.com/users/rellerreller/gists{/gist_id}","starred_url":"https://api.github.com/users/rellerreller/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rellerreller/subscriptions","organizations_url":"https://api.github.com/users/rellerreller/orgs","repos_url":"https://api.github.com/users/rellerreller/repos","events_url":"https://api.github.com/users/rellerreller/events{/privacy}","received_events_url":"https://api.github.com/users/rellerreller/received_events","type":"User","site_admin":false},"body":"This is ok for now. In the future it would be cooler to not have to check hasattr for each attribute for the request. We can brainstorm this after this has been merged.","created_at":"2015-07-27T14:12:28Z","updated_at":"2015-07-27T14:12:28Z","html_url":"https://github.com/OpenKMIP/PyKMIP/pull/54#discussion_r35539276","pull_request_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/pulls/54","_links":{"self":{"href":"https://api.github.com/repos/OpenKMIP/PyKMIP/pulls/comments/35539276"},"html":{"href":"https://github.com/OpenKMIP/PyKMIP/pull/54#discussion_r35539276"},"pull_request":{"href":"https://api.github.com/repos/OpenKMIP/PyKMIP/pulls/54"}}},"pull_request":{"url":"https://api.github.com/repos/OpenKMIP/PyKMIP/pulls/54","id":40158940,"html_url":"https://github.com/OpenKMIP/PyKMIP/pull/54","diff_url":"https://github.com/OpenKMIP/PyKMIP/pull/54.diff","patch_url":"https://github.com/OpenKMIP/PyKMIP/pull/54.patch","issue_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/issues/54","number":54,"state":"open","locked":false,"title":"Adding the Pie KmipClient","user":{"login":"PeterHamilton","id":8269688,"avatar_url":"https://avatars.githubusercontent.com/u/8269688?v=3","gravatar_id":"","url":"https://api.github.com/users/PeterHamilton","html_url":"https://github.com/PeterHamilton","followers_url":"https://api.github.com/users/PeterHamilton/followers","following_url":"https://api.github.com/users/PeterHamilton/following{/other_user}","gists_url":"https://api.github.com/users/PeterHamilton/gists{/gist_id}","starred_url":"https://api.github.com/users/PeterHamilton/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/PeterHamilton/subscriptions","organizations_url":"https://api.github.com/users/PeterHamilton/orgs","repos_url":"https://api.github.com/users/PeterHamilton/repos","events_url":"https://api.github.com/users/PeterHamilton/events{/privacy}","received_events_url":"https://api.github.com/users/PeterHamilton/received_events","type":"User","site_admin":false},"body":"This change adds the Pie KmipClient, which provides a simplified version of the original KMIP interface and which wraps the original client. The KmipClient support basic CRUD operations for symmetric, public, and private keys. Unit and integration test suites are provided in addition to a new set of unit demos showing how to create and use the KmipClient.\r\n","created_at":"2015-07-16T18:15:33Z","updated_at":"2015-07-27T14:12:28Z","closed_at":null,"merged_at":null,"merge_commit_sha":"3bfaf9b85f96f522b473907913ba11741ae74663","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/pulls/54/commits","review_comments_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/pulls/54/comments","review_comment_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/pulls/comments{/number}","comments_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/issues/54/comments","statuses_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/statuses/99caad8528cc8829833d24cba4f4f6093dfe9048","head":{"label":"OpenKMIP:feat/add-pie-client","ref":"feat/add-pie-client","sha":"99caad8528cc8829833d24cba4f4f6093dfe9048","user":{"login":"OpenKMIP","id":8350747,"avatar_url":"https://avatars.githubusercontent.com/u/8350747?v=3","gravatar_id":"","url":"https://api.github.com/users/OpenKMIP","html_url":"https://github.com/OpenKMIP","followers_url":"https://api.github.com/users/OpenKMIP/followers","following_url":"https://api.github.com/users/OpenKMIP/following{/other_user}","gists_url":"https://api.github.com/users/OpenKMIP/gists{/gist_id}","starred_url":"https://api.github.com/users/OpenKMIP/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/OpenKMIP/subscriptions","organizations_url":"https://api.github.com/users/OpenKMIP/orgs","repos_url":"https://api.github.com/users/OpenKMIP/repos","events_url":"https://api.github.com/users/OpenKMIP/events{/privacy}","received_events_url":"https://api.github.com/users/OpenKMIP/received_events","type":"Organization","site_admin":false},"repo":{"id":22601443,"name":"PyKMIP","full_name":"OpenKMIP/PyKMIP","owner":{"login":"OpenKMIP","id":8350747,"avatar_url":"https://avatars.githubusercontent.com/u/8350747?v=3","gravatar_id":"","url":"https://api.github.com/users/OpenKMIP","html_url":"https://github.com/OpenKMIP","followers_url":"https://api.github.com/users/OpenKMIP/followers","following_url":"https://api.github.com/users/OpenKMIP/following{/other_user}","gists_url":"https://api.github.com/users/OpenKMIP/gists{/gist_id}","starred_url":"https://api.github.com/users/OpenKMIP/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/OpenKMIP/subscriptions","organizations_url":"https://api.github.com/users/OpenKMIP/orgs","repos_url":"https://api.github.com/users/OpenKMIP/repos","events_url":"https://api.github.com/users/OpenKMIP/events{/privacy}","received_events_url":"https://api.github.com/users/OpenKMIP/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/OpenKMIP/PyKMIP","description":"Open source Python implementation of the KMIP 1.1 specification.","fork":false,"url":"https://api.github.com/repos/OpenKMIP/PyKMIP","forks_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/forks","keys_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/keys{/key_id}","collaborators_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/teams","hooks_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/hooks","issue_events_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/issues/events{/number}","events_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/events","assignees_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/assignees{/user}","branches_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/branches{/branch}","tags_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/tags","blobs_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/git/refs{/sha}","trees_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/git/trees{/sha}","statuses_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/statuses/{sha}","languages_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/languages","stargazers_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/stargazers","contributors_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/contributors","subscribers_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/subscribers","subscription_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/subscription","commits_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/commits{/sha}","git_commits_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/git/commits{/sha}","comments_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/comments{/number}","issue_comment_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/issues/comments{/number}","contents_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/contents/{+path}","compare_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/compare/{base}...{head}","merges_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/merges","archive_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/downloads","issues_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/issues{/number}","pulls_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/pulls{/number}","milestones_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/milestones{/number}","notifications_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/labels{/name}","releases_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/releases{/id}","created_at":"2014-08-04T11:14:51Z","updated_at":"2015-06-16T07:15:48Z","pushed_at":"2015-07-27T12:06:24Z","git_url":"git://github.com/OpenKMIP/PyKMIP.git","ssh_url":"git@github.com:OpenKMIP/PyKMIP.git","clone_url":"https://github.com/OpenKMIP/PyKMIP.git","svn_url":"https://github.com/OpenKMIP/PyKMIP","homepage":null,"size":1865,"stargazers_count":7,"watchers_count":7,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":8,"mirror_url":null,"open_issues_count":8,"forks":8,"open_issues":8,"watchers":7,"default_branch":"master"}},"base":{"label":"OpenKMIP:master","ref":"master","sha":"66cb329f4ae271c982b067f2ddabe33c1a198dbd","user":{"login":"OpenKMIP","id":8350747,"avatar_url":"https://avatars.githubusercontent.com/u/8350747?v=3","gravatar_id":"","url":"https://api.github.com/users/OpenKMIP","html_url":"https://github.com/OpenKMIP","followers_url":"https://api.github.com/users/OpenKMIP/followers","following_url":"https://api.github.com/users/OpenKMIP/following{/other_user}","gists_url":"https://api.github.com/users/OpenKMIP/gists{/gist_id}","starred_url":"https://api.github.com/users/OpenKMIP/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/OpenKMIP/subscriptions","organizations_url":"https://api.github.com/users/OpenKMIP/orgs","repos_url":"https://api.github.com/users/OpenKMIP/repos","events_url":"https://api.github.com/users/OpenKMIP/events{/privacy}","received_events_url":"https://api.github.com/users/OpenKMIP/received_events","type":"Organization","site_admin":false},"repo":{"id":22601443,"name":"PyKMIP","full_name":"OpenKMIP/PyKMIP","owner":{"login":"OpenKMIP","id":8350747,"avatar_url":"https://avatars.githubusercontent.com/u/8350747?v=3","gravatar_id":"","url":"https://api.github.com/users/OpenKMIP","html_url":"https://github.com/OpenKMIP","followers_url":"https://api.github.com/users/OpenKMIP/followers","following_url":"https://api.github.com/users/OpenKMIP/following{/other_user}","gists_url":"https://api.github.com/users/OpenKMIP/gists{/gist_id}","starred_url":"https://api.github.com/users/OpenKMIP/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/OpenKMIP/subscriptions","organizations_url":"https://api.github.com/users/OpenKMIP/orgs","repos_url":"https://api.github.com/users/OpenKMIP/repos","events_url":"https://api.github.com/users/OpenKMIP/events{/privacy}","received_events_url":"https://api.github.com/users/OpenKMIP/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/OpenKMIP/PyKMIP","description":"Open source Python implementation of the KMIP 1.1 specification.","fork":false,"url":"https://api.github.com/repos/OpenKMIP/PyKMIP","forks_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/forks","keys_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/keys{/key_id}","collaborators_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/teams","hooks_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/hooks","issue_events_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/issues/events{/number}","events_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/events","assignees_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/assignees{/user}","branches_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/branches{/branch}","tags_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/tags","blobs_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/git/refs{/sha}","trees_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/git/trees{/sha}","statuses_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/statuses/{sha}","languages_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/languages","stargazers_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/stargazers","contributors_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/contributors","subscribers_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/subscribers","subscription_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/subscription","commits_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/commits{/sha}","git_commits_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/git/commits{/sha}","comments_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/comments{/number}","issue_comment_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/issues/comments{/number}","contents_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/contents/{+path}","compare_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/compare/{base}...{head}","merges_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/merges","archive_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/downloads","issues_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/issues{/number}","pulls_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/pulls{/number}","milestones_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/milestones{/number}","notifications_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/labels{/name}","releases_url":"https://api.github.com/repos/OpenKMIP/PyKMIP/releases{/id}","created_at":"2014-08-04T11:14:51Z","updated_at":"2015-06-16T07:15:48Z","pushed_at":"2015-07-27T12:06:24Z","git_url":"git://github.com/OpenKMIP/PyKMIP.git","ssh_url":"git@github.com:OpenKMIP/PyKMIP.git","clone_url":"https://github.com/OpenKMIP/PyKMIP.git","svn_url":"https://github.com/OpenKMIP/PyKMIP","homepage":null,"size":1865,"stargazers_count":7,"watchers_count":7,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":8,"mirror_url":null,"open_issues_count":8,"forks":8,"open_issues":8,"watchers":7,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/OpenKMIP/PyKMIP/pulls/54"},"html":{"href":"https://github.com/OpenKMIP/PyKMIP/pull/54"},"issue":{"href":"https://api.github.com/repos/OpenKMIP/PyKMIP/issues/54"},"comments":{"href":"https://api.github.com/repos/OpenKMIP/PyKMIP/issues/54/comments"},"review_comments":{"href":"https://api.github.com/repos/OpenKMIP/PyKMIP/pulls/54/comments"},"review_comment":{"href":"https://api.github.com/repos/OpenKMIP/PyKMIP/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/OpenKMIP/PyKMIP/pulls/54/commits"},"statuses":{"href":"https://api.github.com/repos/OpenKMIP/PyKMIP/statuses/99caad8528cc8829833d24cba4f4f6093dfe9048"}}}}