summaryrefslogtreecommitdiffstats
path: root/sms-client
diff options
context:
space:
mode:
authorKiran Kamineni <kiran.k.kamineni@intel.com>2018-08-17 15:54:05 -0700
committerKiran Kamineni <kiran.k.kamineni@intel.com>2018-08-17 16:07:50 -0700
commit6546bbac701bdff45adb11c7e93236e6736898f6 (patch)
tree966d806f2785a61ad43fa454a0ede39cf2df32fb /sms-client
parent15f771a0cdada988f44b604b9baa30c8b374cab2 (diff)
Add python client for SMS service
Add python client for SMS service. Other ONAP microservices can use this client to connect to SMS to store and get secrets. Issue-ID: AAF-438 Change-Id: I5bb39001e8482b9191512b1422ed7edadbd2ec67 Signed-off-by: Kiran Kamineni <kiran.k.kamineni@intel.com>
Diffstat (limited to 'sms-client')
-rw-r--r--sms-client/.gitignore1
-rw-r--r--sms-client/python/sms/__init__.py248
2 files changed, 249 insertions, 0 deletions
diff --git a/sms-client/.gitignore b/sms-client/.gitignore
new file mode 100644
index 0000000..7e99e36
--- /dev/null
+++ b/sms-client/.gitignore
@@ -0,0 +1 @@
+*.pyc \ No newline at end of file
diff --git a/sms-client/python/sms/__init__.py b/sms-client/python/sms/__init__.py
new file mode 100644
index 0000000..88a7621
--- /dev/null
+++ b/sms-client/python/sms/__init__.py
@@ -0,0 +1,248 @@
+# Copyright 2018 Intel Corporation, Inc
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json
+import requests
+import requests.exceptions
+import urlparse
+
+class InvalidRequestException(Exception):
+ pass
+
+class InternalServerError(Exception):
+ pass
+
+class UnexpectedError(Exception):
+ pass
+
+class Client(object):
+ """Python Client for Secret Management Service"""
+
+ def __init__(self, url='http://localhost:10443', timeout=30, cacert=None):
+ """Creates a new SMS client instance
+
+ Args:
+ url (str): Base URL with port pointing to the SMS service
+ timeout (int): Number of seconds before aborting the connection
+ cacert (str): Path to the cacert that will be used to verify
+ If this is None, verify will be False and the server cert
+ is not verified by the client.
+ Returns:
+ A Client object which can be used to interact with SMS service
+ """
+
+ self.base_url = url
+ self.timeout = timeout
+ self.cacert = cacert
+ self.session = requests.Session()
+
+ self._base_api_url = '/v1/sms'
+
+ def _urlJoin(self, *urls):
+ """Joins given urls into a single url
+
+ Args:
+ urls (str): url fragments to be combined.
+
+ Returns:
+ str: Joined URL
+ """
+
+ return '/'.join(urls)
+
+ def _raiseException(self, statuscode, errors=None):
+ """ Handles Exception Raising based on statusCode
+
+ Args:
+ statuscode (int): status code returned by the server
+ errors (str): list of strings containing error messages
+
+ Returns:
+ exception: An exception is raised based on error message
+ """
+
+ if statuscode == 400:
+ raise InvalidRequestException(errors)
+ if statuscode == 500:
+ raise InternalServerError(errors)
+
+ raise UnexpectedError(errors)
+
+ def _request(self, method, url, headers=None, **kwargs):
+ """Handles request for all the client methods
+
+ Args:
+ method (str): type of HTTP method (get, post or delete).
+ url (str): api URL.
+ headers (dict): custom headers if any.
+ **kwargs: various args supported by requests library
+
+ Returns:
+ requests.Response: An object containing status_code and returned
+ json data is returned here.
+ """
+
+ if headers is None:
+ headers = {
+ 'content-type': "application/json",
+ 'Accept': "application/json"
+ }
+
+ #Verify the server or not based on the cacert argument
+ if self.cacert is None:
+ verify = False
+ else:
+ verify = self.cacert
+
+ url = urlparse.urljoin(self.base_url, url)
+ response = self.session.request(method, url, headers=headers,
+ allow_redirects=False, verify=verify,
+ timeout = self.timeout, **kwargs)
+
+ errors = None
+ if response.status_code >= 400 and response.status_code < 600:
+ #Request Failed. Raise Exception.
+ errors = response.text
+ self._raiseException(response.status_code, errors)
+
+ return response
+
+ def getStatus(self):
+ """Returns Status of SMS Service
+
+ Returns:
+ bool: True or False depending on if SMS Service is ready.
+ """
+
+ url = self._urlJoin(self._base_api_url, 'quorum', 'status')
+
+ response = self._request('get', url)
+ return response.json()['sealstatus']
+
+ def createDomain(self, domainName):
+ """Creates a Secret Domain
+
+ Args:
+ domainName (str): Name of the secret domain to create
+
+ Returns:
+ string: UUID of the created domain name
+ """
+
+
+ domainName = domainName.strip()
+ data = {"name": domainName}
+ url = self._urlJoin(self._base_api_url, 'domain')
+
+ response = self._request('post', url, json = data)
+ return response.json()['uuid']
+
+ def deleteDomain(self, domainName):
+ """Deletes a Secret Domain
+
+ Args:
+ domainName (str): Name of the secret domain to delete
+
+ Returns:
+ bool: True. An exception will be raised if delete failed.
+ """
+
+ domainName = domainName.strip()
+ url = self._urlJoin(self._base_api_url, 'domain', domainName)
+
+ self._request('delete', url)
+ return True
+
+ def getSecretNames(self, domainName):
+ """Get all Secret Names in Domain
+
+ Args:
+ domainName (str): Name of the secret domain
+
+ Returns:
+ string[]: List of strings each corresponding to a
+ Secret's Name in this Domain.
+ """
+
+ domainName = domainName.strip()
+ url = self._urlJoin(self._base_api_url, 'domain', domainName,
+ 'secret')
+
+ response = self._request('get', url)
+ return response.json()['secretnames']
+
+
+ def storeSecret(self, domainName, secretName, values):
+ """Store a Secret in given Domain
+
+ Args:
+ domainName (str): Name of the secret domain
+ secretName (str): Name for the Secret
+ values (dict): A dict containing name-value pairs which
+ form the secret
+
+ Returns:
+ bool: True. An exception will be raised if store failed.
+ """
+
+ domainName = domainName.strip()
+ secretName = secretName.strip()
+ url = self._urlJoin(self._base_api_url, 'domain', domainName,
+ 'secret')
+
+ if not isinstance(values, dict):
+ raise TypeError('Input values is not a dictionary')
+
+ data = {"name": secretName, "values": values}
+ self._request('post', url, json = data)
+ return True
+
+ def getSecret(self, domainName, secretName):
+ """Get a particular Secret from Domain.
+
+ Args:
+ domainName (str): Name of the secret domain
+ secretName (str): Name of the secret
+
+ Returns:
+ dict: dictionary containing the name-value pairs
+ which form the secret
+ """
+
+ domainName = domainName.strip()
+ secretName = secretName.strip()
+ url = self._urlJoin(self._base_api_url, 'domain', domainName,
+ 'secret', secretName)
+
+ response = self._request('get', url)
+ return response.json()['values']
+
+ def deleteSecret(self, domainName, secretName):
+ """Delete a particular Secret from Domain.
+
+ Args:
+ domainName (str): Name of the secret domain
+ secretName (str): Name of the secret
+
+ Returns:
+ bool: True. An exception will be raised if delete failed.
+ """
+
+ domainName = domainName.strip()
+ secretName = secretName.strip()
+ url = self._urlJoin(self._base_api_url, 'domain', domainName,
+ 'secret', secretName)
+
+ self._request('delete', url)
+ return True \ No newline at end of file