From 98adf1dd42d9cafec9f100609830b05fa640e7c3 Mon Sep 17 00:00:00 2001 From: Arash Ghoreyshi Date: Wed, 5 Jun 2013 10:28:51 -0500 Subject: [PATCH 1/3] Add logging Make all of the parameters in create_order() and create_secret() optional except for mime_type --- barbicanclient/client.py | 90 +++++++++++++++++++++++++++++++--------- openstack-common.conf | 2 +- tools/pip-requires | 1 + 3 files changed, 72 insertions(+), 21 deletions(-) diff --git a/barbicanclient/client.py b/barbicanclient/client.py index c96f644d..764b2493 100644 --- a/barbicanclient/client.py +++ b/barbicanclient/client.py @@ -4,26 +4,38 @@ eventlet.monkey_patch(socket=True, select=True) import json import requests - +from barbicanclient.common import config from barbicanclient.secrets import Secret from barbicanclient.orders import Order -from barbicanclient.common.auth import authenticate +from barbicanclient.common import auth +from barbicanclient.openstack.common import log from barbicanclient.common.exceptions import ClientException +from barbicanclient.openstack.common.gettextutils import _ from openstack.common.timeutils import parse_isotime from urlparse import urljoin +config.parse_args() +log.setup('barbicanclient') +LOG = log.getLogger(__name__) + + class Connection(object): SECRETS_PATH = 'secrets' ORDERS_PATH = 'orders' - def __init__(self, auth_endpoint, user, key, tenant, **kwargs): + def __init__(self, auth_endpoint, user, key, tenant, + authenticate=None, token=None, **kwargs): """ :param auth_endpoint: The auth URL to authenticate against :param user: The user to authenticate as - :param key: The API key or passowrd to auth with + :param key: The API key or password to auth with """ + + LOG.debug(_("Creating Connection object")) + self._auth_endpoint = auth_endpoint + self.authenticate = authenticate or auth.authenticate self._user = user self._key = key self._tenant = tenant @@ -31,7 +43,7 @@ class Connection(object): or 'https://barbican.api.rackspacecloud.com/v1/') self._cacert = kwargs.get('cacert') - self.connect() + self.connect(token=token) @property def _conn(self): @@ -57,6 +69,9 @@ class Connection(object): token will be used for this connection and auth will not happen. """ + + LOG.debug(_("Establishing connection")) + self._session = requests.Session() #headers = {"Client-Id": self._client_id} @@ -64,9 +79,11 @@ class Connection(object): self._session.verify = True if token: + LOG.warn(_("Bypassing authentication - using provided token")) self.auth_token = token else: - self._endpoint, self.auth_token = authenticate( + LOG.debug(_("Authenticating token")) + self._endpoint, self.auth_token = self.authenticate( self._auth_endpoint, self._user, self._key, @@ -91,8 +108,11 @@ class Connection(object): """ Returns the list of secrets for the auth'd tenant """ + LOG.debug(_("Listing secrets")) href = "{0}/{1}?limit=100".format(self._tenant, self.SECRETS_PATH) + LOG.debug("href: {}".format(href)) hdrs, body = self._perform_http(href=href, method='GET') + LOG.debug("Response - headers: {0}\nbody: {1}".format(hdrs, body)) secrets_dict = body['secrets'] secrets = [] @@ -102,44 +122,59 @@ class Connection(object): return secrets def create_secret(self, - name, mime_type, - algorithm, - bit_length, - cypher_type, - plain_text, - expiration): + plain_text=None, + name=None, + algorithm=None, + bit_length=None, + cypher_type=None, + expiration=None): + LOG.debug(_("Creating secret of mime_type {}".format(mime_type))) href = "{0}/{1}".format(self._tenant, self.SECRETS_PATH) + LOG.debug("href: {}".format(href)) secret_dict = {} - secret_dict['name'] = name secret_dict['mime_type'] = mime_type - secret_dict['algorithm'] = algorithm - secret_dict['bit_length'] = int(bit_length) - secret_dict['cypher_type'] = cypher_type secret_dict['plain_text'] = plain_text + secret_dict['name'] = name + secret_dict['algorithm'] = algorithm + secret_dict['cypher_type'] = cypher_type + if bit_length is not None: + secret_dict['bit_length'] = int(bit_length) if expiration is not None: secret_dict['expiration'] = parse_isotime(expiration) + for k in secret_dict.keys(): + if secret_dict[k] is None: + secret_dict.pop(k) + LOG.debug("Request body: ".format(secret_dict)) hdrs, body = self._perform_http(href=href, method='POST', request_body=json.dumps(secret_dict)) + LOG.debug("Response - headers: {0}\nbody: {1}".format(hdrs, body)) + return body['secret_ref'] + #return Secret(self, body) def delete_secret_by_id(self, secret_id): href = "{0}/{1}/{2}".format(self._tenant, self.SECRETS_PATH, secret_id) + LOG.info(_("Deleting secret - Secret ID: {}".format(secret_id))) return self.delete_secret(href) def delete_secret(self, href): hdrs, body = self._perform_http(href=href, method='DELETE') + LOG.debug("Response - headers: {0}\nbody: {1}".format(hdrs, body)) def get_secret_by_id(self, secret_id): + LOG.debug(_("Getting secret - Secret ID: {}".format(secret_id))) href = "{0}/{1}/{2}".format(self._tenant, self.SECRETS_PATH, secret_id) return self.get_secret(href) def get_secret(self, href): hdrs, body = self._perform_http(href=href, method='GET') + LOG.debug("Response - headers: {0}\nbody: {1}".format(hdrs, body)) return Secret(self._conn, body) def get_raw_secret_by_id(self, secret_id, mime_type): + LOG.debug(_("Getting raw secret - Secret ID: {0}".format(secret_id))) href = "{0}/{1}/{2}".format(self._tenant, self.SECRETS_PATH, secret_id) return self.get_raw_secret(href, mime_type) @@ -147,14 +182,18 @@ class Connection(object): hdrs = {"Accept": mime_type} hdrs, body = self._perform_http(href=href, method='GET', headers=hdrs, parse_json=False) + LOG.debug("Response - headers: {0}\nbody: {1}".format(hdrs, body)) return body def list_orders(self): """ Returns the list of orders """ + LOG.debug(_("Listing orders")) href = "{0}/{1}?limit=100".format(self._tenant, self.ORDERS_PATH) + LOG.debug("href: {}".format(href)) hdrs, body = self._perform_http(href=href, method='GET') + LOG.debug("Response - headers: {0}\nbody: {1}".format(hdrs, body)) orders_dict = body['orders'] orders = [] @@ -164,36 +203,46 @@ class Connection(object): return orders def create_order(self, - name, mime_type, - algorithm, - bit_length, - cypher_type): + name=None, + algorithm=None, + bit_length=None, + cypher_type=None): + LOG.debug(_("Creating order of mime_type {}".format(mime_type))) href = "{0}/{1}".format(self._tenant, self.ORDERS_PATH) + LOG.debug("href: {}".format(href)) order_dict = {'secret': {}} order_dict['secret']['name'] = name order_dict['secret']['mime_type'] = mime_type order_dict['secret']['algorithm'] = algorithm order_dict['secret']['bit_length'] = bit_length order_dict['secret']['cypher_type'] = cypher_type + for k in order_dict['secret'].keys(): + if order_dict['secret'][k] is None: + order_dict['secret'].pop(k) + LOG.debug("Request body: {}".format(order_dict['secret'])) hdrs, body = self._perform_http(href=href, method='POST', request_body=json.dumps(order_dict)) return body['order_ref'] def delete_order_by_id(self, order_id): + LOG.info(_("Deleting order - Order ID: {}".format(order_id))) href = "{0}/{1}/{2}".format(self._tenant, self.ORDERS_PATH, order_id) return self.delete_order(href) def delete_order(self, href): hdrs, body = self._perform_http(href=href, method='DELETE') + LOG.debug("Response - headers: {0}\nbody: {1}".format(hdrs, body)) def get_order_by_id(self, order_id): + LOG.debug(_("Getting order - Order ID: {}".format(order_id))) href = "{0}/{1}/{2}".format(self._tenant, self.ORDERS_PATH, order_id) return self.get_order(href) def get_order(self, href): hdrs, body = self._perform_http(href=href, method='GET') + LOG.debug("Response - headers: {0}\nbody: {1}".format(hdrs, body)) return Order(self._conn, body) def _perform_http(self, method, href, request_body='', headers={}, @@ -218,6 +267,7 @@ class Connection(object): # Check if the status code is 2xx class if not response.ok: + LOG.error('Response status code was bad') raise ClientException(href=href, method=method, http_status=response.status_code, http_response_content=response.content) diff --git a/openstack-common.conf b/openstack-common.conf index 9f1c17e0..82811f9a 100644 --- a/openstack-common.conf +++ b/openstack-common.conf @@ -1,7 +1,7 @@ [DEFAULT] # The list of modules to copy from openstack-common -modules=setup,jsonutils,timeutils,version +modules=gettextutils,setup,jsonutils,timeutils,version,log # The base module to hold the copy of openstack.common base=barbicanclient \ No newline at end of file diff --git a/tools/pip-requires b/tools/pip-requires index b296544e..9f26f417 100644 --- a/tools/pip-requires +++ b/tools/pip-requires @@ -2,4 +2,5 @@ httplib2>=0.7.7 argparse>=1.2.1 python-keystoneclient>=0.2.3 eventlet>=0.12.1 +oslo.config>=1.1.0 iso8601>=0.1.4 \ No newline at end of file From fe6b35111c325fe95bff48b95bee3a5d2f31b4c3 Mon Sep 17 00:00:00 2001 From: Arash Ghoreyshi Date: Wed, 5 Jun 2013 13:42:32 -0500 Subject: [PATCH 2/3] Add a test file, a configuration file, and various generated files --- barbicanclient/common/config.py | 92 +++ barbicanclient/openstack/.DS_Store | Bin 0 -> 6148 bytes .../openstack/common/gettextutils.py | 50 ++ .../openstack/common/importutils.py | 67 +++ barbicanclient/openstack/common/local.py | 48 ++ barbicanclient/openstack/common/log.py | 558 ++++++++++++++++++ barbicanclient/version.py | 21 + etc/barbicanclient.conf | 10 + tests/client_test.py | 76 +++ 9 files changed, 922 insertions(+) create mode 100644 barbicanclient/common/config.py create mode 100644 barbicanclient/openstack/.DS_Store create mode 100644 barbicanclient/openstack/common/gettextutils.py create mode 100644 barbicanclient/openstack/common/importutils.py create mode 100644 barbicanclient/openstack/common/local.py create mode 100644 barbicanclient/openstack/common/log.py create mode 100644 barbicanclient/version.py create mode 100644 etc/barbicanclient.conf create mode 100644 tests/client_test.py diff --git a/barbicanclient/common/config.py b/barbicanclient/common/config.py new file mode 100644 index 00000000..e5d9d004 --- /dev/null +++ b/barbicanclient/common/config.py @@ -0,0 +1,92 @@ +# Copyright (c) 2013 Rackspace, 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. + +""" +Configuration setup for Barbican Client. +""" + +import logging +import logging.config +import logging.handlers +import os +import sys + +from barbicanclient.version import __version__ +from oslo.config import cfg + +CONF = cfg.CONF +CONF.import_opt('verbose', 'barbicanclient.openstack.common.log') +CONF.import_opt('debug', 'barbicanclient.openstack.common.log') +CONF.import_opt('log_dir', 'barbicanclient.openstack.common.log') +CONF.import_opt('log_file', 'barbicanclient.openstack.common.log') +CONF.import_opt('log_config', 'barbicanclient.openstack.common.log') +CONF.import_opt('log_format', 'barbicanclient.openstack.common.log') +CONF.import_opt('log_date_format', 'barbicanclient.openstack.common.log') +CONF.import_opt('use_syslog', 'barbicanclient.openstack.common.log') +CONF.import_opt('syslog_log_facility', 'barbicanclient.openstack.common.log') + + +def parse_args(args=None, usage=None, default_config_files=None): + CONF(args=args, + project='barbicanclient', + prog='barbicanclient', + version=__version__, + usage=usage, + default_config_files=default_config_files) + + +def setup_logging(): + """ + Sets up the logging options + """ + + if CONF.log_config: + # Use a logging configuration file for all settings... + if os.path.exists(CONF.log_config): + logging.config.fileConfig(CONF.log_config) + return + else: + raise RuntimeError("Unable to locate specified logging " + "config file: %s" % CONF.log_config) + + root_logger = logging.root + if CONF.debug: + root_logger.setLevel(logging.DEBUG) + elif CONF.verbose: + root_logger.setLevel(logging.INFO) + else: + root_logger.setLevel(logging.WARNING) + + formatter = logging.Formatter(CONF.log_format, CONF.log_date_format) + + if CONF.use_syslog: + try: + facility = getattr(logging.handlers.SysLogHandler, + CONF.syslog_log_facility) + except AttributeError: + raise ValueError(_("Invalid syslog facility")) + + handler = logging.handlers.SysLogHandler(address='/dev/log', + facility=facility) + elif CONF.log_file: + logfile = CONF.log_file + if CONF.log_dir: + logfile = os.path.join(CONF.log_dir, logfile) + handler = logging.handlers.WatchedFileHandler(logfile) + else: + handler = logging.StreamHandler(sys.stdout) + + handler.setFormatter(formatter) + root_logger.addHandler(handler) diff --git a/barbicanclient/openstack/.DS_Store b/barbicanclient/openstack/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..94e8390ae962e15cb3acaeb6fc15ce9a113b765a GIT binary patch literal 6148 zcmeHKOH0E*5Z-NTji}Iz(BtB{NNSaO@DM@;k3tGPSkc6Y4a96|QhTV;AozRyH~t=H zb~lG$&LVau?0&QJ*vALi2V;!8%dpQ_hcRYBLFA~^2%2kM4HJyWRUeWi!77~xu?%WD z`imx zdgf=#)b$t2bLD3<@F;yOWeob%L{FkHo85N0>-F*JpeOo6JMW40aA^0$$>1!{cR)HH z*1_S?`Q_Mu$zR_J%^wG}5_FOGpe51H=F^u)z$NL&4hJ zU=C>M!~ii+&j9WZ0t%vIu+XTs4yf?@jQ$oP3h4NjKokZYgM~)$fN-4(s8hLlVsM=f zc46WigM~(&&bS&G`Y|Jyj~A{+2fI+=j5`{sCI*OsDg#a3?BMx-0l!S`BfpwLEn Date: Wed, 5 Jun 2013 17:02:56 -0500 Subject: [PATCH 3/3] Minor fixes and adjustments --- barbicanclient/client.py | 61 ++++++++++++++++++++++------------------ tests/client_test.py | 11 ++++++-- 2 files changed, 41 insertions(+), 31 deletions(-) diff --git a/barbicanclient/client.py b/barbicanclient/client.py index 764b2493..912a91bc 100644 --- a/barbicanclient/client.py +++ b/barbicanclient/client.py @@ -25,7 +25,7 @@ class Connection(object): ORDERS_PATH = 'orders' def __init__(self, auth_endpoint, user, key, tenant, - authenticate=None, token=None, **kwargs): + token=None, authenticate=None, request=None, **kwargs): """ :param auth_endpoint: The auth URL to authenticate against :param user: The user to authenticate as @@ -36,6 +36,7 @@ class Connection(object): self._auth_endpoint = auth_endpoint self.authenticate = authenticate or auth.authenticate + self.request = request or requests.request self._user = user self._key = key self._tenant = tenant @@ -112,7 +113,7 @@ class Connection(object): href = "{0}/{1}?limit=100".format(self._tenant, self.SECRETS_PATH) LOG.debug("href: {}".format(href)) hdrs, body = self._perform_http(href=href, method='GET') - LOG.debug("Response - headers: {0}\nbody: {1}".format(hdrs, body)) + LOG.debug(_("Response - headers: {0}\nbody: {1}").format(hdrs, body)) secrets_dict = body['secrets'] secrets = [] @@ -129,9 +130,9 @@ class Connection(object): bit_length=None, cypher_type=None, expiration=None): - LOG.debug(_("Creating secret of mime_type {}".format(mime_type))) + LOG.debug(_("Creating secret of mime_type {}").format(mime_type)) href = "{0}/{1}".format(self._tenant, self.SECRETS_PATH) - LOG.debug("href: {}".format(href)) + LOG.debug(_("href: {}").format(href)) secret_dict = {} secret_dict['mime_type'] = mime_type secret_dict['plain_text'] = plain_text @@ -142,39 +143,39 @@ class Connection(object): secret_dict['bit_length'] = int(bit_length) if expiration is not None: secret_dict['expiration'] = parse_isotime(expiration) - for k in secret_dict.keys(): - if secret_dict[k] is None: - secret_dict.pop(k) - LOG.debug("Request body: ".format(secret_dict)) + #(secret_dict.pop(k) for k in secret_dict.keys() if secret_dict[k] is None) + self._remove_empty_keys(secret_dict) + #LOG.critical("DICT: {}".format(secret_dict)) + LOG.debug(_("Request body: {}").format(secret_dict)) hdrs, body = self._perform_http(href=href, method='POST', request_body=json.dumps(secret_dict)) - LOG.debug("Response - headers: {0}\nbody: {1}".format(hdrs, body)) + LOG.debug(_("Response - headers: {0}\nbody: {1}").format(hdrs, body)) return body['secret_ref'] #return Secret(self, body) def delete_secret_by_id(self, secret_id): href = "{0}/{1}/{2}".format(self._tenant, self.SECRETS_PATH, secret_id) - LOG.info(_("Deleting secret - Secret ID: {}".format(secret_id))) + LOG.info(_("Deleting secret - Secret ID: {}").format(secret_id)) return self.delete_secret(href) def delete_secret(self, href): hdrs, body = self._perform_http(href=href, method='DELETE') - LOG.debug("Response - headers: {0}\nbody: {1}".format(hdrs, body)) + LOG.debug(_("Response - headers: {0}\nbody: {1}").format(hdrs, body)) def get_secret_by_id(self, secret_id): - LOG.debug(_("Getting secret - Secret ID: {}".format(secret_id))) + LOG.debug(_("Getting secret - Secret ID: {}").format(secret_id)) href = "{0}/{1}/{2}".format(self._tenant, self.SECRETS_PATH, secret_id) return self.get_secret(href) def get_secret(self, href): hdrs, body = self._perform_http(href=href, method='GET') - LOG.debug("Response - headers: {0}\nbody: {1}".format(hdrs, body)) + LOG.debug(_("Response - headers: {0}\nbody: {1}").format(hdrs, body)) return Secret(self._conn, body) def get_raw_secret_by_id(self, secret_id, mime_type): - LOG.debug(_("Getting raw secret - Secret ID: {0}".format(secret_id))) + LOG.debug(_("Getting raw secret - Secret ID: {0}").format(secret_id)) href = "{0}/{1}/{2}".format(self._tenant, self.SECRETS_PATH, secret_id) return self.get_raw_secret(href, mime_type) @@ -182,7 +183,7 @@ class Connection(object): hdrs = {"Accept": mime_type} hdrs, body = self._perform_http(href=href, method='GET', headers=hdrs, parse_json=False) - LOG.debug("Response - headers: {0}\nbody: {1}".format(hdrs, body)) + LOG.debug(_("Response - headers: {0}\nbody: {1}").format(hdrs, body)) return body def list_orders(self): @@ -193,7 +194,7 @@ class Connection(object): href = "{0}/{1}?limit=100".format(self._tenant, self.ORDERS_PATH) LOG.debug("href: {}".format(href)) hdrs, body = self._perform_http(href=href, method='GET') - LOG.debug("Response - headers: {0}\nbody: {1}".format(hdrs, body)) + LOG.debug(_("Response - headers: {0}\nbody: {1}").format(hdrs, body)) orders_dict = body['orders'] orders = [] @@ -208,7 +209,7 @@ class Connection(object): algorithm=None, bit_length=None, cypher_type=None): - LOG.debug(_("Creating order of mime_type {}".format(mime_type))) + LOG.debug(_("Creating order of mime_type {}").format(mime_type)) href = "{0}/{1}".format(self._tenant, self.ORDERS_PATH) LOG.debug("href: {}".format(href)) order_dict = {'secret': {}} @@ -217,34 +218,37 @@ class Connection(object): order_dict['secret']['algorithm'] = algorithm order_dict['secret']['bit_length'] = bit_length order_dict['secret']['cypher_type'] = cypher_type - for k in order_dict['secret'].keys(): - if order_dict['secret'][k] is None: - order_dict['secret'].pop(k) - LOG.debug("Request body: {}".format(order_dict['secret'])) + self._remove_empty_keys(order_dict['secret']) + LOG.debug(_("Request body: {}").format(order_dict['secret'])) hdrs, body = self._perform_http(href=href, method='POST', request_body=json.dumps(order_dict)) return body['order_ref'] def delete_order_by_id(self, order_id): - LOG.info(_("Deleting order - Order ID: {}".format(order_id))) + LOG.info(_("Deleting order - Order ID: {}").format(order_id)) href = "{0}/{1}/{2}".format(self._tenant, self.ORDERS_PATH, order_id) return self.delete_order(href) def delete_order(self, href): hdrs, body = self._perform_http(href=href, method='DELETE') - LOG.debug("Response - headers: {0}\nbody: {1}".format(hdrs, body)) + LOG.debug(_("Response - headers: {0}\nbody: {1}").format(hdrs, body)) def get_order_by_id(self, order_id): - LOG.debug(_("Getting order - Order ID: {}".format(order_id))) + LOG.debug(_("Getting order - Order ID: {}").format(order_id)) href = "{0}/{1}/{2}".format(self._tenant, self.ORDERS_PATH, order_id) return self.get_order(href) def get_order(self, href): hdrs, body = self._perform_http(href=href, method='GET') - LOG.debug("Response - headers: {0}\nbody: {1}".format(hdrs, body)) + LOG.debug(_("Response - headers: {0}\nbody: {1}").format(hdrs, body)) return Order(self._conn, body) + def _remove_empty_keys(self, dictionary): + for k in dictionary.keys(): + if dictionary[k] is None: + dictionary.pop(k) + def _perform_http(self, method, href, request_body='', headers={}, parse_json=True): """ @@ -262,12 +266,13 @@ class Connection(object): url = urljoin(self._endpoint, href) - response = requests.request(method=method, url=url, data=request_body, - headers=headers) + response = self.request(method=method, url=url, data=request_body, + headers=headers) + LOG.critical("Response: {}".format(response.content)) # Check if the status code is 2xx class if not response.ok: - LOG.error('Response status code was bad') + LOG.error('Bad response: {}'.format(response.status_code)) raise ClientException(href=href, method=method, http_status=response.status_code, http_response_content=response.content) diff --git a/tests/client_test.py b/tests/client_test.py index 35bf019e..058f94e8 100644 --- a/tests/client_test.py +++ b/tests/client_test.py @@ -39,9 +39,14 @@ class WhenTestingConnection(unittest.TestCase): self.authenticate = MagicMock() self.authenticate.return_value = (self.endpoint, self.auth_token) + self.request = MagicMock() + self.request.return_value.content = {"secret_ref": "http://localhost:9" + + "311/v1/None/secrets/ea1bb4e5-" + + "e769-4d2c-8e58-cbbb17f1a3de"} + self.request.return_value.ok = True self.connection = Connection(self.auth_endpoint, self.user, self.key, - self.tenant, self.authenticate, - token=self.auth_token) + self.tenant, token=self.auth_token, + authenticate=self.authenticate,) def test_should_connect_with_token(self): self.assertFalse(self.authenticate.called) @@ -51,7 +56,7 @@ class WhenTestingConnection(unittest.TestCase): self.user, self.key, self.tenant, - self.authenticate, + authenticate=self.authenticate, endpoint=self.endpoint ) self.authenticate\