summaryrefslogtreecommitdiff
path: root/openstackclient/common
diff options
context:
space:
mode:
Diffstat (limited to 'openstackclient/common')
-rw-r--r--openstackclient/common/availability_zone.py2
-rw-r--r--openstackclient/common/client_config.py196
-rw-r--r--openstackclient/common/clientmanager.py27
-rw-r--r--openstackclient/common/configuration.py2
-rw-r--r--openstackclient/common/extension.py2
-rw-r--r--openstackclient/common/limits.py2
-rw-r--r--openstackclient/common/module.py52
-rw-r--r--openstackclient/common/quota.py183
8 files changed, 254 insertions, 212 deletions
diff --git a/openstackclient/common/availability_zone.py b/openstackclient/common/availability_zone.py
index 63c55370..b2385ef7 100644
--- a/openstackclient/common/availability_zone.py
+++ b/openstackclient/common/availability_zone.py
@@ -88,7 +88,7 @@ def _xform_network_availability_zone(az):
class ListAvailabilityZone(command.Lister):
- """List availability zones and their status"""
+ _description = _("List availability zones and their status")
def get_parser(self, prog_name):
parser = super(ListAvailabilityZone, self).get_parser(prog_name)
diff --git a/openstackclient/common/client_config.py b/openstackclient/common/client_config.py
index 30286df8..a22dd0cb 100644
--- a/openstackclient/common/client_config.py
+++ b/openstackclient/common/client_config.py
@@ -13,122 +13,15 @@
"""OpenStackConfig subclass for argument compatibility"""
-import logging
-
-from os_client_config import config
-from os_client_config import exceptions as occ_exceptions
-
-
-LOG = logging.getLogger(__name__)
+from osc_lib.cli import client_config
# Sublcass OpenStackConfig in order to munge config values
# before auth plugins are loaded
-class OSC_Config(config.OpenStackConfig):
-
- # TODO(dtroyer): Once os-client-config with pw_func argument is in
- # global-requirements we can remove __init()__
- def __init__(
- self,
- config_files=None,
- vendor_files=None,
- override_defaults=None,
- force_ipv4=None,
- envvar_prefix=None,
- secure_files=None,
- pw_func=None,
- ):
- ret = super(OSC_Config, self).__init__(
- config_files=config_files,
- vendor_files=vendor_files,
- override_defaults=override_defaults,
- force_ipv4=force_ipv4,
- envvar_prefix=envvar_prefix,
- secure_files=secure_files,
- )
-
- # NOTE(dtroyer): This will be pushed down into os-client-config
- # The default is there is no callback, the calling
- # application must specify what to use, typically
- # it will be osc_lib.shell.prompt_for_password()
- if '_pw_callback' not in vars(self):
- # Set the default if it doesn't already exist
- self._pw_callback = None
- if pw_func is not None:
- # Set the passed in value
- self._pw_callback = pw_func
-
- return ret
-
- def _auth_select_default_plugin(self, config):
- """Select a default plugin based on supplied arguments
-
- Migrated from auth.select_auth_plugin()
- """
-
- identity_version = config.get('identity_api_version', '')
-
- if config.get('username', None) and not config.get('auth_type', None):
- if identity_version == '3':
- config['auth_type'] = 'v3password'
- elif identity_version.startswith('2'):
- config['auth_type'] = 'v2password'
- else:
- # let keystoneauth figure it out itself
- config['auth_type'] = 'password'
- elif config.get('token', None) and not config.get('auth_type', None):
- if identity_version == '3':
- config['auth_type'] = 'v3token'
- elif identity_version.startswith('2'):
- config['auth_type'] = 'v2token'
- else:
- # let keystoneauth figure it out itself
- config['auth_type'] = 'token'
- else:
- # The ultimate default is similar to the original behaviour,
- # but this time with version discovery
- if not config.get('auth_type', None):
- config['auth_type'] = 'password'
-
- LOG.debug("Auth plugin %s selected" % config['auth_type'])
- return config
-
- def _auth_v2_arguments(self, config):
- """Set up v2-required arguments from v3 info
-
- Migrated from auth.build_auth_params()
- """
-
- if ('auth_type' in config and config['auth_type'].startswith("v2")):
- if 'project_id' in config['auth']:
- config['auth']['tenant_id'] = config['auth']['project_id']
- if 'project_name' in config['auth']:
- config['auth']['tenant_name'] = config['auth']['project_name']
- return config
-
- def _auth_v2_ignore_v3(self, config):
- """Remove v3 arguemnts if present for v2 plugin
-
- Migrated from clientmanager.setup_auth()
- """
-
- # NOTE(hieulq): If USER_DOMAIN_NAME, USER_DOMAIN_ID, PROJECT_DOMAIN_ID
- # or PROJECT_DOMAIN_NAME is present and API_VERSION is 2.0, then
- # ignore all domain related configs.
- if (config.get('identity_api_version', '').startswith('2') and
- config.get('auth_type', None).endswith('password')):
- domain_props = [
- 'project_domain_id',
- 'project_domain_name',
- 'user_domain_id',
- 'user_domain_name',
- ]
- for prop in domain_props:
- if config['auth'].pop(prop, None) is not None:
- LOG.warning("Ignoring domain related config " +
- prop + " because identity API version is 2.0")
- return config
+class OSC_Config(client_config.OSC_Config):
+ # TODO(dtroyer): Remove _auth_default_domain when the v3otp fix is
+ # backported to osc-lib, should be in release 1.3.0
def _auth_default_domain(self, config):
"""Set a default domain from available arguments
@@ -149,6 +42,7 @@ class OSC_Config(config.OpenStackConfig):
# present, then do not change the behaviour. Otherwise, set the
# PROJECT_DOMAIN_ID to 'OS_DEFAULT_DOMAIN' for better usability.
if (
+ auth_type in ("password", "v3password", "v3totp") and
not config['auth'].get('project_domain_id') and
not config['auth'].get('project_domain_name')
):
@@ -168,21 +62,6 @@ class OSC_Config(config.OpenStackConfig):
config['auth']['user_domain_id'] = default_domain
return config
- def auth_config_hook(self, config):
- """Allow examination of config values before loading auth plugin
-
- OpenStackClient will override this to perform additional chacks
- on auth_type.
- """
-
- config = self._auth_select_default_plugin(config)
- config = self._auth_v2_arguments(config)
- config = self._auth_v2_ignore_v3(config)
- config = self._auth_default_domain(config)
-
- LOG.debug("auth_config_hook(): %s" % config)
- return config
-
def load_auth_plugin(self, config):
"""Get auth plugin and validate args"""
@@ -190,68 +69,3 @@ class OSC_Config(config.OpenStackConfig):
config = self._validate_auth(config, loader)
auth_plugin = loader.load_from_options(**config['auth'])
return auth_plugin
-
- def _validate_auth_ksc(self, config, cloud, fixed_argparse=None):
- """Old compatibility hack for OSC, no longer needed/wanted"""
- return config
-
- def _validate_auth(self, config, loader, fixed_argparse=None):
- """Validate auth plugin arguments"""
- # May throw a keystoneauth1.exceptions.NoMatchingPlugin
-
- plugin_options = loader.get_options()
-
- msgs = []
- prompt_options = []
- for p_opt in plugin_options:
- # if it's in config, win, move it and kill it from config dict
- # if it's in config.auth but not in config we're good
- # deprecated loses to current
- # provided beats default, deprecated or not
- winning_value = self._find_winning_auth_value(p_opt, config)
- if not winning_value:
- winning_value = self._find_winning_auth_value(
- p_opt, config['auth'])
-
- # if the plugin tells us that this value is required
- # then error if it's doesn't exist now
- if not winning_value and p_opt.required:
- msgs.append(
- 'Missing value {auth_key}'
- ' required for auth plugin {plugin}'.format(
- auth_key=p_opt.name, plugin=config.get('auth_type'),
- )
- )
-
- # Clean up after ourselves
- for opt in [p_opt.name] + [o.name for o in p_opt.deprecated]:
- opt = opt.replace('-', '_')
- config.pop(opt, None)
- config['auth'].pop(opt, None)
-
- if winning_value:
- # Prefer the plugin configuration dest value if the value's key
- # is marked as depreciated.
- if p_opt.dest is None:
- config['auth'][p_opt.name.replace('-', '_')] = (
- winning_value)
- else:
- config['auth'][p_opt.dest] = winning_value
-
- # See if this needs a prompting
- if (
- 'prompt' in vars(p_opt) and
- p_opt.prompt is not None and
- p_opt.dest not in config['auth'] and
- self._pw_callback is not None
- ):
- # Defer these until we know all required opts are present
- prompt_options.append(p_opt)
-
- if msgs:
- raise occ_exceptions.OpenStackConfigException('\n'.join(msgs))
- else:
- for p_opt in prompt_options:
- config['auth'][p_opt.dest] = self._pw_callback(p_opt.prompt)
-
- return config
diff --git a/openstackclient/common/clientmanager.py b/openstackclient/common/clientmanager.py
index 23c35a3b..3e1a50e3 100644
--- a/openstackclient/common/clientmanager.py
+++ b/openstackclient/common/clientmanager.py
@@ -59,6 +59,8 @@ class ClientManager(clientmanager.ClientManager):
self._interface = self.interface
self._cacert = self.cacert
self._insecure = not self.verify
+ # store original auth_type
+ self._original_auth_type = cli_options.auth_type
def setup_auth(self):
"""Set up authentication"""
@@ -73,12 +75,33 @@ class ClientManager(clientmanager.ClientManager):
if self._cli_options._openstack_config is not None:
self._cli_options._openstack_config._pw_callback = \
shell.prompt_for_password
+ try:
+ self._cli_options._auth = \
+ self._cli_options._openstack_config.load_auth_plugin(
+ self._cli_options.config,
+ )
+ except TypeError as e:
+ self._fallback_load_auth_plugin(e)
+
+ return super(ClientManager, self).setup_auth()
+
+ def _fallback_load_auth_plugin(self, e):
+ # NOTES(RuiChen): Hack to avoid auth plugins choking on data they don't
+ # expect, delete fake token and endpoint, then try to
+ # load auth plugin again with user specified options.
+ # We know it looks ugly, but it's necessary.
+ if self._cli_options.config['auth']['token'] == 'x':
+ # restore original auth_type
+ self._cli_options.config['auth_type'] = \
+ self._original_auth_type
+ del self._cli_options.config['auth']['token']
+ del self._cli_options.config['auth']['endpoint']
self._cli_options._auth = \
self._cli_options._openstack_config.load_auth_plugin(
self._cli_options.config,
)
-
- return super(ClientManager, self).setup_auth()
+ else:
+ raise e
def is_network_endpoint_enabled(self):
"""Check if the network endpoint is enabled"""
diff --git a/openstackclient/common/configuration.py b/openstackclient/common/configuration.py
index 016e9191..57825bb0 100644
--- a/openstackclient/common/configuration.py
+++ b/openstackclient/common/configuration.py
@@ -23,7 +23,7 @@ REDACTED = "<redacted>"
class ShowConfiguration(command.ShowOne):
- """Display configuration details"""
+ _description = _("Display configuration details")
def get_parser(self, prog_name):
parser = super(ShowConfiguration, self).get_parser(prog_name)
diff --git a/openstackclient/common/extension.py b/openstackclient/common/extension.py
index 07c407f6..d5b72238 100644
--- a/openstackclient/common/extension.py
+++ b/openstackclient/common/extension.py
@@ -28,7 +28,7 @@ LOG = logging.getLogger(__name__)
class ListExtension(command.Lister):
- """List API extensions"""
+ _description = _("List API extensions")
def get_parser(self, prog_name):
parser = super(ListExtension, self).get_parser(prog_name)
diff --git a/openstackclient/common/limits.py b/openstackclient/common/limits.py
index f7aa82f6..957f1d02 100644
--- a/openstackclient/common/limits.py
+++ b/openstackclient/common/limits.py
@@ -25,7 +25,7 @@ from openstackclient.identity import common as identity_common
class ShowLimits(command.Lister):
- """Show compute and block storage limits"""
+ _description = _("Show compute and block storage limits")
def get_parser(self, prog_name):
parser = super(ShowLimits, self).get_parser(prog_name)
diff --git a/openstackclient/common/module.py b/openstackclient/common/module.py
index 7c5fcd55..20497f21 100644
--- a/openstackclient/common/module.py
+++ b/openstackclient/common/module.py
@@ -25,16 +25,30 @@ from openstackclient.i18n import _
class ListCommand(command.Lister):
- """List recognized commands by group"""
+ _description = _("List recognized commands by group")
auth_required = False
+ def get_parser(self, prog_name):
+ parser = super(ListCommand, self).get_parser(prog_name)
+ parser.add_argument(
+ '--group',
+ metavar='<group-keyword>',
+ help=_('Show commands filtered by a command group, for example: '
+ 'identity, volume, compute, image, network and '
+ 'other keywords'),
+ )
+ return parser
+
def take_action(self, parsed_args):
cm = self.app.command_manager
groups = cm.get_command_groups()
groups = sorted(groups)
columns = ('Command Group', 'Commands')
+ if parsed_args.group:
+ groups = (group for group in groups if parsed_args.group in group)
+
commands = []
for group in groups:
command_names = cm.get_command_names(group)
@@ -53,7 +67,7 @@ class ListCommand(command.Lister):
class ListModule(command.ShowOne):
- """List module versions"""
+ _description = _("List module versions")
auth_required = False
@@ -74,15 +88,29 @@ class ListModule(command.ShowOne):
mods = sys.modules
for k in mods.keys():
k = k.split('.')[0]
- # TODO(dtroyer): Need a better way to decide which modules to
- # show for the default (not --all) invocation.
- # It should be just the things we actually care
- # about like client and plugin modules...
- if (parsed_args.all or 'client' in k):
- try:
- data[k] = mods[k].__version__
- except AttributeError:
- # aw, just skip it
- pass
+ # Skip private modules and the modules that had been added,
+ # like: keystoneclient, keystoneclient.exceptions and
+ # keystoneclient.auth
+ if not k.startswith('_') and k not in data:
+ # TODO(dtroyer): Need a better way to decide which modules to
+ # show for the default (not --all) invocation.
+ # It should be just the things we actually care
+ # about like client and plugin modules...
+ if (parsed_args.all or
+ # Handle xxxclient and openstacksdk
+ (k.endswith('client') or k == 'openstack')):
+ try:
+ # NOTE(RuiChen): openstacksdk bug/1588823 exist,
+ # no good way to add __version__ for
+ # openstack module properly, hard code
+ # looks bad, but openstacksdk module
+ # information is important.
+ if k == 'openstack':
+ data[k] = mods[k].version.__version__
+ else:
+ data[k] = mods[k].__version__
+ except Exception:
+ # Catch all exceptions, just skip it
+ pass
return zip(*sorted(six.iteritems(data)))
diff --git a/openstackclient/common/quota.py b/openstackclient/common/quota.py
index 8f099cc9..ec4c8b51 100644
--- a/openstackclient/common/quota.py
+++ b/openstackclient/common/quota.py
@@ -16,6 +16,7 @@
"""Quota action implementations"""
import itertools
+import logging
import sys
from osc_lib.command import command
@@ -25,6 +26,8 @@ import six
from openstackclient.i18n import _
+LOG = logging.getLogger(__name__)
+
# List the quota items, map the internal argument name to the option
# name that the user sees.
@@ -78,9 +81,179 @@ NETWORK_QUOTAS = {
'l7policy': 'l7policies',
}
+NETWORK_KEYS = ['floating_ips', 'networks', 'rbac_policies', 'routers',
+ 'ports', 'security_group_rules', 'security_groups',
+ 'subnet_pools', 'subnets']
+
+
+def _xform_get_quota(data, value, keys):
+ res = []
+ res_info = {}
+ for key in keys:
+ res_info[key] = getattr(data, key, '')
+
+ res_info['id'] = value
+ res.append(res_info)
+ return res
+
+
+class ListQuota(command.Lister):
+ _description = _("List quotas for all projects "
+ "with non-default quota values")
+
+ def get_parser(self, prog_name):
+ parser = super(ListQuota, self).get_parser(prog_name)
+ option = parser.add_mutually_exclusive_group(required=True)
+ option.add_argument(
+ '--compute',
+ action='store_true',
+ default=False,
+ help=_('List compute quota'),
+ )
+ option.add_argument(
+ '--volume',
+ action='store_true',
+ default=False,
+ help=_('List volume quota'),
+ )
+ option.add_argument(
+ '--network',
+ action='store_true',
+ default=False,
+ help=_('List network quota'),
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ projects = self.app.client_manager.identity.projects.list()
+ result = []
+ project_ids = [getattr(p, 'id', '') for p in projects]
+
+ if parsed_args.compute:
+ compute_client = self.app.client_manager.compute
+ for p in project_ids:
+ data = compute_client.quotas.get(p)
+ result_data = _xform_get_quota(data, p,
+ COMPUTE_QUOTAS.keys())
+ default_data = compute_client.quotas.defaults(p)
+ result_default = _xform_get_quota(default_data,
+ p,
+ COMPUTE_QUOTAS.keys())
+ if result_default != result_data:
+ result += result_data
+
+ columns = (
+ 'id',
+ 'cores',
+ 'fixed_ips',
+ 'injected_files',
+ 'injected_file_content_bytes',
+ 'injected_file_path_bytes',
+ 'instances',
+ 'key_pairs',
+ 'metadata_items',
+ 'ram',
+ 'server_groups',
+ 'server_group_members',
+ )
+ column_headers = (
+ 'Project ID',
+ 'Cores',
+ 'Fixed IPs',
+ 'Injected Files',
+ 'Injected File Content Bytes',
+ 'Injected File Path Bytes',
+ 'Instances',
+ 'Key Pairs',
+ 'Metadata Items',
+ 'Ram',
+ 'Server Groups',
+ 'Server Group Members',
+ )
+ return (column_headers,
+ (utils.get_dict_properties(
+ s, columns,
+ ) for s in result))
+ if parsed_args.volume:
+ volume_client = self.app.client_manager.volume
+ for p in project_ids:
+ data = volume_client.quotas.get(p)
+ result_data = _xform_get_quota(data, p,
+ VOLUME_QUOTAS.keys())
+ default_data = volume_client.quotas.defaults(p)
+ result_default = _xform_get_quota(default_data,
+ p,
+ VOLUME_QUOTAS.keys())
+ if result_default != result_data:
+ result += result_data
+
+ columns = (
+ 'id',
+ 'backups',
+ 'backup_gigabytes',
+ 'gigabytes',
+ 'per_volume_gigabytes',
+ 'snapshots',
+ 'volumes',
+ )
+ column_headers = (
+ 'Project ID',
+ 'Backups',
+ 'Backup Gigabytes',
+ 'Gigabytes',
+ 'Per Volume Gigabytes',
+ 'Snapshots',
+ 'Volumes',
+ )
+ return (column_headers,
+ (utils.get_dict_properties(
+ s, columns,
+ ) for s in result))
+ if parsed_args.network:
+ client = self.app.client_manager.network
+ for p in project_ids:
+ data = client.get_quota(p)
+ result_data = _xform_get_quota(data, p, NETWORK_KEYS)
+ default_data = client.get_quota_default(p)
+ result_default = _xform_get_quota(default_data,
+ p, NETWORK_KEYS)
+ if result_default != result_data:
+ result += result_data
+
+ columns = (
+ 'id',
+ 'floating_ips',
+ 'networks',
+ 'ports',
+ 'rbac_policies',
+ 'routers',
+ 'security_groups',
+ 'security_group_rules',
+ 'subnets',
+ 'subnet_pools',
+ )
+ column_headers = (
+ 'Project ID',
+ 'Floating IPs',
+ 'Networks',
+ 'Ports',
+ 'RBAC Policies',
+ 'Routers',
+ 'Security Groups',
+ 'Security Group Rules',
+ 'Subnets',
+ 'Subnet Pools'
+ )
+ return (column_headers,
+ (utils.get_dict_properties(
+ s, columns,
+ ) for s in result))
+
+ return ((), ())
+
class SetQuota(command.Command):
- """Set quotas for project or class"""
+ _description = _("Set quotas for project or class")
def _build_options_list(self):
if self.app.client_manager.is_network_endpoint_enabled():
@@ -165,7 +338,7 @@ class SetQuota(command.Command):
**volume_kwargs)
if network_kwargs:
sys.stderr.write("Network quotas are ignored since quota class"
- "is not supported.")
+ " is not supported.")
else:
project = utils.find_resource(
identity_client.projects,
@@ -186,7 +359,7 @@ class SetQuota(command.Command):
class ShowQuota(command.ShowOne):
- """Show quotas for project or class"""
+ _description = _("Show quotas for project or class")
def get_parser(self, prog_name):
parser = super(ShowQuota, self).get_parser(prog_name)
@@ -273,6 +446,10 @@ class ShowQuota(command.ShowOne):
volume_quota_info = self.get_compute_volume_quota(volume_client,
parsed_args)
network_quota_info = self.get_network_quota(parsed_args)
+ # NOTE(reedip): Remove the below check once requirement for
+ # Openstack SDK is fixed to version 0.9.12 and above
+ if type(network_quota_info) is not dict:
+ network_quota_info = network_quota_info.to_dict()
info = {}
info.update(compute_quota_info)