diff options
Diffstat (limited to 'openstackclient/common')
| -rw-r--r-- | openstackclient/common/availability_zone.py | 5 | ||||
| -rw-r--r-- | openstackclient/common/clientmanager.py | 39 | ||||
| -rw-r--r-- | openstackclient/common/command.py | 1 | ||||
| -rw-r--r-- | openstackclient/common/exceptions.py | 5 | ||||
| -rw-r--r-- | openstackclient/common/logs.py | 48 | ||||
| -rw-r--r-- | openstackclient/common/quota.py | 4 | ||||
| -rw-r--r-- | openstackclient/common/timing.py | 3 | ||||
| -rw-r--r-- | openstackclient/common/utils.py | 14 |
8 files changed, 89 insertions, 30 deletions
diff --git a/openstackclient/common/availability_zone.py b/openstackclient/common/availability_zone.py index a941418b..a6d11b78 100644 --- a/openstackclient/common/availability_zone.py +++ b/openstackclient/common/availability_zone.py @@ -146,21 +146,20 @@ class ListAvailabilityZone(command.Lister): def _get_network_availability_zones(self, parsed_args): network_client = self.app.client_manager.network - data = [] try: # Verify that the extension exists. network_client.find_extension('Availability Zone', ignore_missing=False) - data = network_client.availability_zones() except Exception as e: self.log.debug('Network availability zone exception: ' + str(e)) if parsed_args.network: message = "Availability zones list not supported by " \ "Network API" self.log.warning(message) + return [] result = [] - for zone in data: + for zone in network_client.availability_zones(): result += _xform_network_availability_zone(zone) return result diff --git a/openstackclient/common/clientmanager.py b/openstackclient/common/clientmanager.py index dce19725..938dd05c 100644 --- a/openstackclient/common/clientmanager.py +++ b/openstackclient/common/clientmanager.py @@ -37,6 +37,7 @@ USER_AGENT = 'python-openstackclient' class ClientCache(object): """Descriptor class for caching created client handles.""" + def __init__(self, factory): self.factory = factory self._handle = None @@ -90,7 +91,7 @@ class ClientManager(object): self._cli_options = cli_options self._api_version = api_version self._pw_callback = pw_func - self._url = self._cli_options.auth.get('url', None) + self._url = self._cli_options.auth.get('url') self._region_name = self._cli_options.region_name self._interface = self._cli_options.interface @@ -113,24 +114,40 @@ class ClientManager(object): root_logger = logging.getLogger('') LOG.setLevel(root_logger.getEffectiveLevel()) - def setup_auth(self): + # NOTE(gyee): use this flag to indicate whether auth setup has already + # been completed. If so, do not perform auth setup again. The reason + # we need this flag is that we want to be able to perform auth setup + # outside of auth_ref as auth_ref itself is a property. We can not + # retrofit auth_ref to optionally skip scope check. Some operations + # do not require a scoped token. In those cases, we call setup_auth + # prior to dereferrencing auth_ref. + self._auth_setup_completed = False + + def setup_auth(self, required_scope=True): """Set up authentication + :param required_scope: indicate whether a scoped token is required + This is deferred until authentication is actually attempted because it gets in the way of things that do not require auth. """ + if self._auth_setup_completed: + return + # If no auth type is named by the user, select one based on # the supplied options self.auth_plugin_name = auth.select_auth_plugin(self._cli_options) # Basic option checking to avoid unhelpful error messages - auth.check_valid_auth_options(self._cli_options, self.auth_plugin_name) + auth.check_valid_auth_options(self._cli_options, + self.auth_plugin_name, + required_scope=required_scope) # Horrible hack alert...must handle prompt for null password if # password auth is requested. if (self.auth_plugin_name.endswith('password') and - not self._cli_options.auth.get('password', None)): + not self._cli_options.auth.get('password')): self._cli_options.auth['password'] = self._pw_callback() (auth_plugin, self._auth_params) = auth.build_auth_params( @@ -146,9 +163,9 @@ class ClientManager(object): # PROJECT_DOMAIN_ID to 'OS_DEFAULT_DOMAIN' for better usability. if (self._api_version.get('identity') == '3' and self.auth_plugin_name.endswith('password') and - not self._auth_params.get('project_domain_id', None) and + not self._auth_params.get('project_domain_id') and not self.auth_plugin_name.startswith('v2') and - not self._auth_params.get('project_domain_name', None)): + not self._auth_params.get('project_domain_name')): self._auth_params['project_domain_id'] = default_domain # NOTE(stevemar): If USER_DOMAIN_ID or USER_DOMAIN_NAME is present, @@ -157,8 +174,8 @@ class ClientManager(object): if (self._api_version.get('identity') == '3' and self.auth_plugin_name.endswith('password') and not self.auth_plugin_name.startswith('v2') and - not self._auth_params.get('user_domain_id', None) and - not self._auth_params.get('user_domain_name', None)): + not self._auth_params.get('user_domain_id') and + not self._auth_params.get('user_domain_name')): self._auth_params['user_domain_id'] = default_domain # For compatibility until all clients can be updated @@ -167,8 +184,8 @@ class ClientManager(object): elif 'tenant_name' in self._auth_params: self._project_name = self._auth_params['tenant_name'] - LOG.info('Using auth plugin: %s' % self.auth_plugin_name) - LOG.debug('Using parameters %s' % + LOG.info('Using auth plugin: %s', self.auth_plugin_name) + LOG.debug('Using parameters %s', strutils.mask_password(self._auth_params)) self.auth = auth_plugin.load_from_options(**self._auth_params) # needed by SAML authentication @@ -180,6 +197,8 @@ class ClientManager(object): user_agent=USER_AGENT, ) + self._auth_setup_completed = True + return @property diff --git a/openstackclient/common/command.py b/openstackclient/common/command.py index 13b0bcc2..fee4559e 100644 --- a/openstackclient/common/command.py +++ b/openstackclient/common/command.py @@ -22,6 +22,7 @@ import six class CommandMeta(abc.ABCMeta): + def __new__(mcs, name, bases, cls_dict): if 'log' not in cls_dict: cls_dict['log'] = logging.getLogger( diff --git a/openstackclient/common/exceptions.py b/openstackclient/common/exceptions.py index ab043db0..5f5f5ab1 100644 --- a/openstackclient/common/exceptions.py +++ b/openstackclient/common/exceptions.py @@ -41,6 +41,7 @@ class UnsupportedVersion(Exception): class ClientException(Exception): """The base exception class for all exceptions this library raises.""" + def __init__(self, code, message=None, details=None): self.code = code self.message = message or self.__class__.message @@ -122,8 +123,8 @@ def from_response(response, body): if body: if hasattr(body, 'keys'): error = body[body.keys()[0]] - message = error.get('message', None) - details = error.get('details', None) + message = error.get('message') + details = error.get('details') else: # If we didn't get back a properly formed error message we # probably couldn't communicate with Keystone at all. diff --git a/openstackclient/common/logs.py b/openstackclient/common/logs.py index 6d1aec13..221c5997 100644 --- a/openstackclient/common/logs.py +++ b/openstackclient/common/logs.py @@ -18,6 +18,13 @@ import sys import warnings +def get_loggers(): + loggers = {} + for logkey in logging.Logger.manager.loggerDict.keys(): + loggers[logkey] = logging.getLevelName(logging.getLogger(logkey).level) + return loggers + + def log_level_from_options(options): # if --debug, --quiet or --verbose is not specified, # the default logging level is warning @@ -34,6 +41,17 @@ def log_level_from_options(options): return log_level +def log_level_from_string(level_string): + log_level = { + 'critical': logging.CRITICAL, + 'error': logging.ERROR, + 'warning': logging.WARNING, + 'info': logging.INFO, + 'debug': logging.DEBUG, + }.get(level_string, logging.WARNING) + return log_level + + def log_level_from_config(config): # Check the command line option verbose_level = config.get('verbose_level') @@ -49,15 +67,7 @@ def log_level_from_config(config): verbose_level = 'info' else: verbose_level = 'debug' - - log_level = { - 'critical': logging.CRITICAL, - 'error': logging.ERROR, - 'warning': logging.WARNING, - 'info': logging.INFO, - 'debug': logging.DEBUG, - }.get(verbose_level, logging.WARNING) - return log_level + return log_level_from_string(verbose_level) def set_warning_filter(log_level): @@ -159,7 +169,7 @@ class LogConfigurator(object): self.dump_trace = cloud_config.config.get('debug', self.dump_trace) self.console_logger.setLevel(log_level) - log_file = cloud_config.config.get('log_file', None) + log_file = cloud_config.config.get('log_file') if log_file: if not self.file_logger: self.file_logger = logging.FileHandler(filename=log_file) @@ -168,3 +178,21 @@ class LogConfigurator(object): self.file_logger.setFormatter(_FileFormatter(config=cloud_config)) self.file_logger.setLevel(log_level) self.root_logger.addHandler(self.file_logger) + + logconfig = cloud_config.config.get('logging') + if logconfig: + highest_level = logging.NOTSET + for k in logconfig.keys(): + level = log_level_from_string(logconfig[k]) + logging.getLogger(k).setLevel(level) + if (highest_level < level): + highest_level = level + self.console_logger.setLevel(highest_level) + if self.file_logger: + self.file_logger.setLevel(highest_level) + # loggers that are not set will use the handler level, so we + # need to set the global level for all the loggers + for logkey in logging.Logger.manager.loggerDict.keys(): + logger = logging.getLogger(logkey) + if logger.level == logging.NOTSET: + logger.setLevel(log_level) diff --git a/openstackclient/common/quota.py b/openstackclient/common/quota.py index f208948e..b3d4c3b6 100644 --- a/openstackclient/common/quota.py +++ b/openstackclient/common/quota.py @@ -169,7 +169,7 @@ class ShowQuota(command.ShowOne): project = utils.find_resource( identity_client.projects, parsed_args.project, - ).id + ).id try: if parsed_args.quota_class: @@ -193,7 +193,7 @@ class ShowQuota(command.ShowOne): project = utils.find_resource( identity_client.projects, parsed_args.project, - ).id + ).id return self.app.client_manager.network.get_quota(project) else: return {} diff --git a/openstackclient/common/timing.py b/openstackclient/common/timing.py index 5f628759..71c2fec7 100644 --- a/openstackclient/common/timing.py +++ b/openstackclient/common/timing.py @@ -30,7 +30,8 @@ class Timing(command.Lister): for url, td in self.app.timing_data: # NOTE(dtroyer): Take the long way here because total_seconds() # was added in py27. - sec = (td.microseconds + (td.seconds + td.days*86400) * 1e6) / 1e6 + sec = (td.microseconds + (td.seconds + td.days * + 86400) * 1e6) / 1e6 total += sec results.append((url, sec)) results.append(('Total', total)) diff --git a/openstackclient/common/utils.py b/openstackclient/common/utils.py index 4142f830..840da402 100644 --- a/openstackclient/common/utils.py +++ b/openstackclient/common/utils.py @@ -163,7 +163,7 @@ def get_field(item, field): raise exceptions.CommandError(msg) -def get_item_properties(item, fields, mixed_case_fields=[], formatters={}): +def get_item_properties(item, fields, mixed_case_fields=None, formatters=None): """Return a tuple containing the item properties. :param item: a single item resource (e.g. Server, Project, etc) @@ -172,6 +172,11 @@ def get_item_properties(item, fields, mixed_case_fields=[], formatters={}): :param formatters: dictionary mapping field names to callables to format the values """ + if mixed_case_fields is None: + mixed_case_fields = [] + if formatters is None: + formatters = {} + row = [] for field in fields: @@ -187,7 +192,7 @@ def get_item_properties(item, fields, mixed_case_fields=[], formatters={}): return tuple(row) -def get_dict_properties(item, fields, mixed_case_fields=[], formatters={}): +def get_dict_properties(item, fields, mixed_case_fields=None, formatters=None): """Return a tuple containing the item properties. :param item: a single dict resource @@ -196,6 +201,11 @@ def get_dict_properties(item, fields, mixed_case_fields=[], formatters={}): :param formatters: dictionary mapping field names to callables to format the values """ + if mixed_case_fields is None: + mixed_case_fields = [] + if formatters is None: + formatters = {} + row = [] for field in fields: |
