diff options
-rw-r--r-- | .coveragerc | 1 | ||||
-rw-r--r-- | openstack-common.conf | 7 | ||||
-rw-r--r-- | releasenotes/notes/remove-old-cli-commands-06b9936ce044dd0f.yaml | 7 | ||||
-rw-r--r-- | requirements.txt | 2 | ||||
-rw-r--r-- | saharaclient/_i18n.py (renamed from saharaclient/openstack/common/_i18n.py) | 10 | ||||
-rw-r--r-- | saharaclient/api/base.py | 2 | ||||
-rw-r--r-- | saharaclient/api/shell.py | 1012 | ||||
-rw-r--r-- | saharaclient/openstack/__init__.py | 0 | ||||
-rw-r--r-- | saharaclient/openstack/common/__init__.py | 0 | ||||
-rw-r--r-- | saharaclient/openstack/common/apiclient/__init__.py | 0 | ||||
-rw-r--r-- | saharaclient/openstack/common/apiclient/auth.py | 234 | ||||
-rw-r--r-- | saharaclient/openstack/common/apiclient/exceptions.py | 479 | ||||
-rw-r--r-- | saharaclient/openstack/common/cliutils.py | 272 | ||||
-rw-r--r-- | saharaclient/shell.py | 732 | ||||
-rw-r--r-- | saharaclient/tests/unit/nova/__init__.py | 0 | ||||
-rw-r--r-- | saharaclient/tests/unit/nova/test_shell.py | 366 | ||||
-rw-r--r-- | saharaclient/tests/unit/nova/utils.py | 57 | ||||
-rw-r--r-- | setup.cfg | 3 | ||||
-rw-r--r-- | tox.ini | 5 |
19 files changed, 9 insertions, 3180 deletions
diff --git a/.coveragerc b/.coveragerc index 2b789e1..1640032 100644 --- a/.coveragerc +++ b/.coveragerc @@ -2,7 +2,6 @@ branch = True source = saharaclient omit = - */openstack/common/* .tox/* saharaclient/tests/* diff --git a/openstack-common.conf b/openstack-common.conf deleted file mode 100644 index 0b4ad3f..0000000 --- a/openstack-common.conf +++ /dev/null @@ -1,7 +0,0 @@ -[DEFAULT] -base=saharaclient - -module=apiclient.auth -module=apiclient.exceptions -module=cliutils -module=_i18n diff --git a/releasenotes/notes/remove-old-cli-commands-06b9936ce044dd0f.yaml b/releasenotes/notes/remove-old-cli-commands-06b9936ce044dd0f.yaml new file mode 100644 index 0000000..3a7ab2e --- /dev/null +++ b/releasenotes/notes/remove-old-cli-commands-06b9936ce044dd0f.yaml @@ -0,0 +1,7 @@ +--- +prelude: > + Old CLI commands are removed. Please use OpenStackClient + instead. +deprecations: + - Old CLI commands are removed. Please use OpenStackClient + instead. diff --git a/requirements.txt b/requirements.txt index 6d5dbee..2ed76f0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,8 +11,6 @@ oslo.log>=3.11.0 # Apache-2.0 oslo.serialization>=1.10.0 # Apache-2.0 oslo.i18n>=2.1.0 # Apache-2.0 oslo.utils>=3.16.0 # Apache-2.0 -python-keystoneclient!=2.1.0,>=2.0.0 # Apache-2.0 python-openstackclient>=2.1.0 # Apache-2.0 requests>=2.10.0 # Apache-2.0 six>=1.9.0 # MIT -PrettyTable<0.8,>=0.7 # BSD diff --git a/saharaclient/openstack/common/_i18n.py b/saharaclient/_i18n.py index 5cc57a1..f0055e9 100644 --- a/saharaclient/openstack/common/_i18n.py +++ b/saharaclient/_i18n.py @@ -11,19 +11,12 @@ # under the License. """oslo.i18n integration module. - See http://docs.openstack.org/developer/oslo.i18n/usage.html - """ try: import oslo_i18n - # NOTE(dhellmann): This reference to o-s-l-o will be replaced by the - # application name when this module is synced into the separate - # repository. It is OK to have more than one translation function - # using the same domain, since there will still only be one message - # catalog. _translators = oslo_i18n.TranslatorFactory(domain='saharaclient') # The primary translation function using the well-known name "_" @@ -39,7 +32,4 @@ try: _LE = _translators.log_error _LC = _translators.log_critical except ImportError: - # NOTE(dims): Support for cases where a project wants to use - # code from oslo-incubator, but is not ready to be internationalized - # (like tempest) _ = _LI = _LW = _LE = _LC = lambda x: x diff --git a/saharaclient/api/base.py b/saharaclient/api/base.py index 36c9e45..de9c16c 100644 --- a/saharaclient/api/base.py +++ b/saharaclient/api/base.py @@ -20,7 +20,7 @@ import logging import six from six.moves.urllib import parse -from saharaclient.openstack.common._i18n import _ +from saharaclient._i18n import _ LOG = logging.getLogger(__name__) diff --git a/saharaclient/api/shell.py b/saharaclient/api/shell.py deleted file mode 100644 index 40c339c..0000000 --- a/saharaclient/api/shell.py +++ /dev/null @@ -1,1012 +0,0 @@ -# Copyright 2013 Red Hat, Inc. -# All Rights Reserved. -# -# 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 argparse -import datetime -import inspect -import json -import os.path -import sys - -from saharaclient.openstack.common.apiclient import exceptions -from saharaclient.openstack.common import cliutils as utils - - -def _print_list_field(field): - return lambda obj: ', '.join(getattr(obj, field)) - - -def _filter_call_args(args, func, remap={}): - """Filter args according to func's parameter list. - - Take three arguments: - * args - a dictionary - * func - a function - * remap - a dictionary - Remove from dct all the keys which are not among the parameters - of func. Before filtering, remap the keys in the args dict - according to remap dict. - """ - - for name, new_name in remap.items(): - if name in args: - args[new_name] = args[name] - del args[name] - - valid_args = inspect.getargspec(func).args - for name in args.keys(): - if name not in valid_args: - print('WARNING: "%s" is not a valid parameter and will be ' - 'discarded from the request' % name) - del args[name] - - -def _print_node_group_field(cluster): - return ', '.join(map(lambda x: ': '.join(x), - [(node_group['name'], - str(node_group['count'])) - for node_group in cluster.node_groups])) - - -def _show_node_group_template(template): - template._info['node_processes'] = ( - ', '.join(template._info['node_processes']) - ) - utils.print_dict(template._info) - - -def _show_cluster_template(template): - template._info['node_groups'] = _print_node_group_field(template) - utils.print_dict(template._info) - - -def _show_cluster(cluster): - # TODO(mattf): Make this pretty, e.g format node_groups and info urls - # Forcing wrap=47 allows for clean display on a terminal of width 80 - utils.print_dict(cluster._info, wrap=47) - - -def _show_job_binary_data(data): - columns = ('id', 'name') - utils.print_list(data, columns) - - -def _show_data_source(source): - # TODO(mattf): why are we passing credentials around like this? - if 'credentials' in source._info: - del source._info['credentials'] - utils.print_dict(source._info) - - -def _show_job_binary(binary): - # TODO(mattf): why are we passing credentials around like this? - if 'extra' in binary._info: - del binary._info['extra'] - utils.print_dict(binary._info) - - -def _show_job_template(template): - # TODO(mattf): Make "mains" property pretty - # TODO(mattf): handle/remove "extra" creds - utils.print_dict(template._info) - - -def _show_job(job): - # TODO(mattf): make display of info pretty, until then - # extract the important status information - job._info['status'] = job._info['info']['status'] - del job._info['info'] - utils.print_dict(job._info) - - -def _get_by_id_or_name(manager, id=None, name=None, **kwargs): - if not (name or id): - raise exceptions.CommandError("either NAME or ID is required") - if id: - return manager.get(id, **kwargs) - ls = manager.find(name=name) - if len(ls) == 0: - raise exceptions.CommandError("%s '%s' not found" % - (manager.resource_class.resource_name, - name)) - elif len(ls) > 1: - raise exceptions.CommandError("%s '%s' not unique, try by ID" % - (manager.resource_class.resource_name, - name)) - return manager.get(ls[0].id, **kwargs) - - -# -# Plugins -# ~~~~~~~ -# plugin-list -# -# plugin-show --name <plugin> [--version <version>] -# - -def do_plugin_list(cs, args): - """Print a list of available plugins.""" - plugins = cs.plugins.list() - columns = ('name', 'versions', 'title') - utils.print_list(plugins, columns, - {'versions': _print_list_field('versions')}) - - -@utils.arg('--name', - metavar='<plugin>', - required=True, - help='Name of the plugin.') -# TODO(mattf) - saharaclient does not support query w/ version -# @utils.arg('--version', -# metavar='<version>', -# help='Optional version') -def do_plugin_show(cs, args): - """Show details of a plugin.""" - plugin = cs.plugins.get(args.name) - plugin._info['versions'] = ', '.join(plugin._info['versions']) - utils.print_dict(plugin._info) - - -# -# Image Registry -# ~~~~~~~~~~~~~~ -# image-list [--tag <tag>]* -# -# image-show --name <image>|--id <image_id> -# -# image-register --name <image>|--id <image_id> -# [--username <name>] [--description <desc>] -# -# image-unregister --name <image>|--id <image_id> -# -# image-add-tag --name <image>|--id <image_id> --tag <tag>+ -# -# image-remove-tag --name <image>|--id <image_id> --tag <tag>+ -# - -# TODO(mattf): [--tag <tag>]* -def do_image_list(cs, args): - """Print a list of available images.""" - images = cs.images.list() - columns = ('name', 'id', 'username', 'tags', 'description') - utils.print_list(images, columns, {'tags': _print_list_field('tags')}) - - -@utils.arg('--name', - help='Name of the image.') -@utils.arg('--id', - metavar='<image_id>', - help='ID of the image.') -def do_image_show(cs, args): - """Show details of an image.""" - image = _get_by_id_or_name(cs.images, args.id, args.name) - del image._info['metadata'] - image._info['tags'] = ', '.join(image._info['tags']) - utils.print_dict(image._info) - - -# TODO(mattf): Add --name, must lookup in glance index -@utils.arg('--id', - metavar='<image_id>', - required=True, - help='ID of image, run "glance image-list" to see all IDs.') -@utils.arg('--username', - default='root', - metavar='<name>', - help='Username of privileged user in the image.') -@utils.arg('--description', - default='', - metavar='<desc>', - help='Description of the image.') -def do_image_register(cs, args): - """Register an image from the Image index.""" - # TODO(mattf): image register should not be through update - cs.images.update_image(args.id, args.username, args.description) - # TODO(mattf): No indication of result, expect image details - - -@utils.arg('--name', - help='Name of the image.') -@utils.arg('--id', - metavar='<image_id>', - help='ID of image to unregister.') -def do_image_unregister(cs, args): - """Unregister an image.""" - cs.images.unregister_image( - args.id or _get_by_id_or_name(cs.images, name=args.name).id - ) - # TODO(mattf): No indication of result, expect result to display - - -@utils.arg('--name', - help='Name of the image.') -@utils.arg('--id', - metavar='<image_id>', - help='ID of image to tag.') -# TODO(mattf): Change --tag to --tag+ -@utils.arg('--tag', - metavar='<tag>', - required=True, - help='Tag to add.') -def do_image_add_tag(cs, args): - """Add a tag to an image.""" - # TODO(mattf): Need proper add_tag API call - id = args.id or _get_by_id_or_name(cs.images, name=args.name).id - cs.images.update_tags(id, cs.images.get(id).tags + [args.tag, ]) - # TODO(mattf): No indication of result, expect image details - - -@utils.arg('--name', - help='Name of the image.') -@utils.arg('--id', - metavar='<image_id>', - help='Image to tag.') -# TODO(mattf): Change --tag to --tag+ -@utils.arg('--tag', - metavar='<tag>', - required=True, - help='Tag to remove.') -def do_image_remove_tag(cs, args): - """Remove a tag from an image.""" - # TODO(mattf): Need proper remove_tag API call - id = args.id or _get_by_id_or_name(cs.images, name=args.name).id - cs.images.update_tags(id, - filter(lambda x: x != args.tag, - cs.images.get(id).tags)) - # TODO(mattf): No indication of result, expect image details - - -# -# Clusters -# ~~~~~~~~ -# cluster-list -# -# cluster-show --name <cluster>|--id <cluster_id> [--json] [--show-progress] -# -# cluster-create [--json <file>] -# -# cluster-scale --name <cluster>|--id <cluster_id> [--json <file>] -# -# cluster-delete --name <cluster>|--id <cluster_id> -# - -def do_cluster_list(cs, args): - """Print a list of available clusters.""" - clusters = cs.clusters.list() - for cluster in clusters: - cluster.node_count = sum(map(lambda g: g['count'], - cluster.node_groups)) - columns = ('name', 'id', 'status', 'node_count') - utils.print_list(clusters, columns) - - -@utils.arg('--name', - help='Name of the cluster.') -@utils.arg('--id', - metavar='<cluster_id>', - help='ID of the cluster to show.') -@utils.arg('--show-progress', - help='Show provision progress events of the cluster.') -@utils.arg('--json', - action='store_true', - default=False, - help='Print JSON representation of the cluster.') -def do_cluster_show(cs, args): - """Show details of a cluster.""" - cluster = _get_by_id_or_name(cs.clusters, args.id, args.name, - show_progress=args.show_progress) - if args.json: - print(json.dumps(cluster._info)) - else: - _show_cluster(cluster) - - -@utils.arg('--json', - default=sys.stdin, - type=argparse.FileType('r'), - help='JSON representation of cluster.') -@utils.arg('--count', - default=1, - type=int, - help='Number of clusters to create.') -def do_cluster_create(cs, args): - """Create a cluster.""" - # TODO(mattf): improve template validation, e.g. template w/o name key - template = json.loads(args.json.read()) - # The neutron_management_network parameter to clusters.create is - # called net_id. Therefore, we must translate before invoking - # create w/ **template. It may be desirable to simple change - # clusters.create in the future. - remap = {'neutron_management_network': 'net_id'} - template['count'] = args.count - _filter_call_args(template, cs.clusters.create, remap) - - _show_cluster(cs.clusters.create(**template)) - - -@utils.arg('--name', - help='Name of the cluster.') -@utils.arg('--id', - metavar='<cluster_id>', - help='ID of the cluster.') -@utils.arg('--json', - default=sys.stdin, - type=argparse.FileType('r'), - help='JSON representation of cluster scale.') -def do_cluster_scale(cs, args): - """Scale a cluster.""" - cluster_id = args.id or _get_by_id_or_name(cs.clusters, name=args.name).id - scale_template = json.loads(args.json.read()) - _show_cluster(cs.clusters.scale(cluster_id, **scale_template)) - - -@utils.arg('--name', - help='Name of the cluster.') -@utils.arg('--id', - metavar='<cluster_id>', - help='ID of the cluster to delete.') -def do_cluster_delete(cs, args): - """Delete a cluster.""" - cs.clusters.delete( - args.id or _get_by_id_or_name(cs.clusters, name=args.name).id - ) - # TODO(mattf): No indication of result - - -# -# Node Group Templates -# ~~~~~~~~~~~~~~~~~~~~ -# node-group-template-list -# -# node-group-template-show --name <template>|--id <template_id> [--json] -# -# node-group-template-create [--json <file>] -# -# node-group-template-delete --name <template>|--id <template_id> -# -# node-group-template-update --name <template>|--id <template_id> --json <file> -# - -def do_node_group_template_list(cs, args): - """Print a list of available node group templates.""" - templates = cs.node_group_templates.list() - columns = ('name', 'id', 'plugin_name', 'node_processes', 'description') - utils.print_list(templates, columns, - {'node_processes': _print_list_field('node_processes')}) - - -@utils.arg('--name', - help='Name of the node group template.') -@utils.arg('--id', - metavar='<template_id>', - help='ID of the node group template to show.') -@utils.arg('--json', - action='store_true', - default=False, - help='Print JSON representation of node group template.') -def do_node_group_template_show(cs, args): - """Show details of a node group template.""" - template = _get_by_id_or_name(cs.node_group_templates, args.id, args.name) - if args.json: - print(json.dumps(template._info)) - else: - _show_node_group_template(template) - - -@utils.arg('--json', - default=sys.stdin, - type=argparse.FileType('r'), - help='JSON representation of node group template.') -def do_node_group_template_create(cs, args): - """Create a node group template.""" - # TODO(mattf): improve template validation, e.g. template w/o name key - template = json.loads(args.json.read()) - _filter_call_args(template, cs.node_group_templates.create) - - _show_node_group_template(cs.node_group_templates.create(**template)) - - -@utils.arg('--name', - help='Name of the node group template.') -@utils.arg('--id', - metavar='<template_id>', - help='ID of the node group template to delete.') -def do_node_group_template_delete(cs, args): - """Delete a node group template.""" - cs.node_group_templates.delete( - args.id or - _get_by_id_or_name(cs.node_group_templates, name=args.name).id - ) - # TODO(mattf): No indication of result - - -@utils.arg('--name', - help='Name of the node group template to update.') -@utils.arg('--id', - metavar='<template_id>', - help='ID of the node group template to update.') -@utils.arg('--json', - default=sys.stdin, - type=argparse.FileType('r'), - help='JSON representation of the node group template update.') -def do_node_group_template_update(cs, args): - """Update a node group template.""" - template = _get_by_id_or_name(cs.node_group_templates, - name=args.name, - id=args.id) - update_template = json.loads(args.json.read()) - _filter_call_args(update_template, cs.node_group_templates.update) - for param in ["plugin_name", "hadoop_version", "name", "flavor_id"]: - if param not in update_template: - update_template[param] = getattr(template, param, None) - - result = cs.node_group_templates.update( - args.id or - template._get_by_id_or_name(cs.node_group_templates, - name=args.name).id, - **update_template - ) - - _show_node_group_template(result) - - -# -# Cluster Templates -# ~~~~~~~~~~~~~~~~~ -# cluster-template-list -# -# cluster-template-show --name <template>|--id <template_id> [--json] -# -# cluster-template-create [--json <file>] -# -# cluster-template-delete --name <template>|--id <template_id> -# - -def do_cluster_template_list(cs, args): - """Print a list of available cluster templates.""" - templates = cs.cluster_templates.list() - columns = ('name', 'id', 'plugin_name', 'node_groups', 'description') - utils.print_list(templates, columns, - {'node_groups': _print_node_group_field}) - - -@utils.arg('--name', - help='Name of the cluster template.') -@utils.arg('--id', - metavar='<template_id>', - help='ID of the cluster template to show.') -@utils.arg('--json', - action='store_true', - default=False, - help='Print JSON representation of cluster template.') -def do_cluster_template_show(cs, args): - """Show details of a cluster template.""" - template = _get_by_id_or_name(cs.cluster_templates, args.id, args.name) - if args.json: - print(json.dumps(template._info)) - else: - _show_cluster_template(template) - - -@utils.arg('--json', - default=sys.stdin, - type=argparse.FileType('r'), - help='JSON representation of cluster template.') -def do_cluster_template_create(cs, args): - """Create a cluster template.""" - # TODO(mattf): improve template validation, e.g. template w/o name key - template = json.loads(args.json.read()) - remap = {'neutron_management_network': 'net_id'} - _filter_call_args(template, cs.cluster_templates.create, remap) - - _show_cluster_template(cs.cluster_templates.create(**template)) - - -@utils.arg('--name', - help='Name of the cluster template.') -@utils.arg('--id', - metavar='<template_id>', - help='ID of the cluster template to delete.') -def do_cluster_template_delete(cs, args): - """Delete a cluster template.""" - cs.cluster_templates.delete( - args.id or _get_by_id_or_name(cs.cluster_templates, name=args.name).id - ) - # TODO(mattf): No indication of result - - -@utils.arg('--name', - help='Name of the cluster template to update.') -@utils.arg('--id', - metavar='<template_id>', - help='ID of the cluster template to update.') -@utils.arg('--json', - default=sys.stdin, - type=argparse.FileType('r'), - help='JSON representation of cluster template update.') -def do_cluster_template_update(cs, args): - """Update a cluster template.""" - template = _get_by_id_or_name(cs.cluster_templates, - name=args.name, - id=args.id) - update_template = json.loads(args.json.read()) - _filter_call_args(update_template, cs.cluster_templates.update) - for param in ["name", "plugin_name", "hadoop_version"]: - if param not in update_template: - update_template[param] = getattr(template, param, None) - - result = cs.cluster_templates.update( - args.id or - _get_by_id_or_name(cs.node_group_templates, name=args.name).id, - **update_template - ) - - _show_cluster_template(result) - - -# -# Data Sources -# ~~~~~~~~~~~~ -# data-source-list -# -# data-source-show --name <name>|--id <id> -# -# data-source-create --name <name> --type <type> -# --url <url> -# [--user <user> --password <password>] -# [--description <desc>] -# NB: user & password if type is swift -# -# data-source-delete --name <name>|--id <id> -# - -def do_data_source_list(cs, args): - """Print a list of available data sources.""" - sources = cs.data_sources.list() - columns = ('name', 'id', 'type', 'description') - utils.print_list(sources, columns) - - -@utils.arg('--name', - help='Name of the data source.') -@utils.arg('--id', - help='ID of the data source.') -def do_data_source_show(cs, args): - """Show details of a data source.""" - _show_data_source(_get_by_id_or_name(cs.data_sources, args.id, args.name)) - - -@utils.arg('--name', - required=True, - help='Name of the data source.') -@utils.arg('--type', - required=True, - help='Type of the data source.') -@utils.arg('--url', - required=True, - help='URL for the data source.') -@utils.arg('--description', - default='', - help='Description of the data source.') -@utils.arg('--user', - default=None, - help='Username for accessing the data source URL.') -@utils.arg('--password', - default=None, - help='Password for accessing the data source URL.') -def do_data_source_create(cs, args): - """Create a data source that provides job input or receives job output.""" - _show_data_source(cs.data_sources.create(args.name, args.description, - args.type, args.url, - args.user, args.password)) - - -@utils.arg('--name', - help='Name of the data source.') -@utils.arg('--id', - help='ID of data source to delete.') -def do_data_source_delete(cs, args): - """Delete a data source.""" - cs.data_sources.delete( - args.id or _get_by_id_or_name(cs.data_sources, name=args.name).id - ) - # TODO(mattf): No indication of result - - -@utils.arg('--name', - help="Name of the data source to update.") -@utils.arg('--id', - help="ID of the data source to update.") -@utils.arg('--json', - default=sys.stdin, - type=argparse.FileType('r'), - help='JSON containing the data source fields to update.') -def do_data_source_update(cs, args): - """Update a data source.""" - update_data = json.loads(args.json.read()) - result = cs.data_sources.update( - args.id or _get_by_id_or_name(cs.data_sources, name=args.name).id, - update_data - ) - _show_data_source(result) - - -# -# Job Binary Internals -# ~~~~~~~~~~~~~~~~~~~~ -# job-binary-data-list -# -# job-binary-data-create [--file <file>] [--name <name>] -# -# job-binary-data-delete --id <id> -# - -def do_job_binary_data_list(cs, args): - """Print a list of internally stored job binary data.""" - _show_job_binary_data(cs.job_binary_internals.list()) - - -@utils.arg('--file', - default=sys.stdin, - type=argparse.FileType('r'), - help='Data to store.') -@utils.arg('--name', - help="Name of the job binary internal.") -def do_job_binary_data_create(cs, args): - """Store data in the internal DB. - - Use 'swift upload' instead of this command. - Use this command only if Swift is not available. - """ - if args.name: - name = args.name - elif args.file is not sys.stdin: - name = os.path.basename(args.file.name) - else: - name = datetime.datetime.now().strftime('d%Y%m%d%H%M%S') - # Should be %F-%T except for type validation errors - _show_job_binary_data((cs.job_binary_internals.create( - name, - args.file.read()),) - ) - - -@utils.arg('--id', - required=True, - help='ID of internally stored job binary data.') -def do_job_binary_data_delete(cs, args): - """Delete an internally stored job binary data.""" - cs.job_binary_internals.delete(args.id) - # TODO(mattf): No indication of result - # TODO(mattf): Appears no DB constraints for removing data used by job - - -# -# Job Binaries -# ~~~~~~~~~~~~ -# job-binary-list -# -# job-binary-show --name <name>|--id <id> -# -# job-binary-create --name <name> --url <url> -# [--user <user> --password <password>] -# [--description <desc>] -# -# job-binary-delete --name <name>|--id <id> -# - -def do_job_binary_list(cs, args): - """Print a list of job binaries.""" - binaries = cs.job_binaries.list() - columns = ('id', 'name', 'description') - utils.print_list(binaries, columns) - - -@utils.arg('--name', - help='Name of the job binary.') -@utils.arg('--id', - help='ID of the job binary.') -def do_job_binary_show(cs, args): - """Show details of a job binary.""" - _show_job_binary(_get_by_id_or_name(cs.job_binaries, args.id, args.name)) - - -@utils.arg('--name', - required=True, - help='Name of the job binary.') -@utils.arg('--url', - required=True, - help='URL for the job binary.') -@utils.arg('--description', - default='', - help='Description of the job binary.') -@utils.arg('--user', - default=None, - help='Username for accessing the job binary URL.') -@utils.arg('--password', - default=None, - help='Password for accessing the job binary URL.') -def do_job_binary_create(cs, args): - """Record a job binary.""" - # TODO(mattf): make credentials consistent w/ data source - extra = {} - if args.user: - extra['user'] = args.user - if args.password: - extra['password'] = args.password - _show_job_binary(cs.job_binaries.create(args.name, args.url, - args.description, extra)) - - -@utils.arg('--name', - help='Name of the job binary.') -@utils.arg('--id', - help='ID of the job binary to delete.') -def do_job_binary_delete(cs, args): - """Delete a job binary.""" - cs.job_binaries.delete( - args.id or _get_by_id_or_name(cs.job_binaries, name=args.name).id - ) - # TODO(mattf): No indication of result - - -@utils.arg('--name', - help='Name of the job binary to update.') -@utils.arg('--id', - metavar='<job_binary_id>', - help='ID of the job binary to update.') -@utils.arg('--json', - default=sys.stdin, - type=argparse.FileType('r'), - help='JSON representation of job binary update.') -def do_job_binary_update(cs, args): - """Update a job binary.""" - update_data = json.loads(args.json.read()) - result = cs.job_binaries.update( - args.id or - _get_by_id_or_name(cs.job_binaries, name=args.name).id, - update_data - ) - - _show_job_binary(result) - - -# -# Jobs -# ~~~~ -# job-template-list -# -# job-template-show --name <name>|--id <id> -# -# job-template-create --name <name> -# --type <Pig|Hive|MapReduce|Java|...> -# [--mains <array of string>] -# [--libs <array of string>] -# [--description <desc>] -# -# job-template-delete --name <name>|--id <id> -# - -def do_job_template_list(cs, args): - """Print a list of job templates.""" - templates = cs.jobs.list() - columns = ('id', 'name', 'description') - utils.print_list(templates, columns) - - -@utils.arg('--name', - help='Name of the job template.') -@utils.arg('--id', - help='ID of the job template.') -def do_job_template_show(cs, args): - """Show details of a job template.""" - _show_job_template(_get_by_id_or_name(cs.jobs, args.id, args.name)) - - -@utils.arg('--name', - help='Name of the job template.') -@utils.arg('--type', - help='Type of the job template.') -@utils.arg('--main', - action='append', - default=[], - help='ID for job\'s main job-binary.') -@utils.arg('--lib', - action='append', - default=[], - help='ID of job\'s lib job-binary, repeatable.') -@utils.arg('--description', - default='', - help='Description of the job template.') -@utils.arg('--json', - default=None, - type=argparse.FileType('r'), - help='JSON representation of job template.') -def do_job_template_create(cs, args): - """Create a job template.""" - template = json.loads(args.json.read()) if args.json else {} - _filter_call_args(template, cs.jobs.create) - template = { - "name": args.name or template.get("name") or None, - "type": args.type or template.get("type") or None, - "mains": args.main or template.get("mains") or [], - "libs": args.lib or template.get("libs") or [], - "description": args.description or template.get("description") or '', - "interface": template.get("interface") or [] - } - if not template["name"]: - raise Exception("name is required") - if not template["type"]: - raise Exception("type is required") - - _show_job_template(cs.jobs.create(**template)) - - -@utils.arg('--name', - help='Name of the job template.') -@utils.arg('--id', - help='ID of the job template.') -def do_job_template_delete(cs, args): - """Delete a job template.""" - cs.jobs.delete( - args.id or _get_by_id_or_name(cs.jobs, name=args.name).id - ) - # TODO(mattf): No indication of result - - -# -# Job Executions -# ~~~~~~~~~~~~~~ -# job-list -# -# job-show --id <id> -# -# job-create --job-template <id> --cluster <id> -# [--input-data <id>] [--output-data <id>] -# [--param <name=value>] -# [--arg <arg>] -# [--config <name=value>] -# -# job-delete --id <id> -# - -def do_job_list(cs, args): - """Print a list of jobs.""" - jobs = cs.job_executions.list() - for job in jobs: - # why is status in info.status? - job.status = job.info['status'] - # TODO(mattf): why can cluster_id be None? - columns = ('id', 'cluster_id', 'start_time', 'status') - utils.print_list(jobs, columns, sortby_index=2) - - -@utils.arg('--id', - required=True, - help='ID of the job.') -def do_job_show(cs, args): - """Show details of a job.""" - _show_job(cs.job_executions.get(args.id)) - - -@utils.arg('--job-template', - required=True, - help='ID of the job template to run.') -@utils.arg('--cluster', - required=False, - help='ID of the cluster to run the job in.') -@utils.arg('--input-data', - default=None, - help='ID of the input data source.') -@utils.arg('--output-data', - default=None, - help='ID of the output data source.') -@utils.arg('--param', - metavar='name=value', - action='append', - default=[], - help='Parameters to add to the job, repeatable.') -@utils.arg('--arg', - action='append', - default=[], - help='Arguments to add to the job, repeatable.') -@utils.arg('--config', - metavar='name=value', - action='append', - default=[], - help='Config parameters to add to the job, repeatable.') -@utils.arg('--json', - default=None, - type=argparse.FileType('r'), - help='JSON representation of the job.') -def do_job_create(cs, args): - """Create a job.""" - job = json.loads(args.json.read()) if args.json else {} - remap = {"job_configs": "configs"} - _filter_call_args(job, cs.job_executions.create, remap) - _convert = lambda ls: dict(map(lambda i: i.split('=', 1), ls)) - job = { - "cluster_id": args.cluster or job.get("cluster_id") or None, - "input_id": args.input_data or job.get("input_id") or None, - "output_id": args.output_data or job.get("output_id") or None, - "interface": job.get("interface") or [], - "configs": job.get("configs") or {} - } - if any((args.config, args.param, args.arg)): - job["configs"] = {"configs": _convert(args.config), - "args": args.arg, - "params": _convert(args.param)} - if not job["cluster_id"]: - raise Exception("cluster is required") - _show_job(cs.job_executions.create(args.job_template, **job)) - - -@utils.arg('--id', - required=True, - help='ID of a job.') -def do_job_delete(cs, args): - """Delete a job.""" - cs.job_executions.delete(args.id) - # TODO(mattf): No indication of result - - -# -# Job Types -# ~~~~~~~~~ -# job-type-list [--type] [--plugin [--plugin-version]] -# - -def _print_plugin_field(job_type): - - def plugin_version_string(plugin): - versions = ", ".join(plugin["versions"].keys()) - if versions: - versions = "(" + versions + ")" - return plugin["name"] + versions - - return ", ".join(map(lambda x: plugin_version_string(x), job_type.plugins)) - - -@utils.arg('--type', - metavar='<job_type>', - default=None, - help='Report only on this job type.') -@utils.arg('--plugin', - metavar='<plugin>', - default=None, - help='Report only job types supported by this plugin.') -@utils.arg('--plugin-version', - metavar='<plugin_version>', - default=None, - help='Report only on job types supported by this version ' - 'of a specified plugin. Only valid with --plugin.') -def do_job_type_list(cs, args): - """Show supported job types.""" - search_opts = {} - if args.type: - search_opts["type"] = args.type - if args.plugin: - search_opts["plugin"] = args.plugin - if args.plugin_version: - search_opts["version"] = args.plugin_version - elif args.plugin_version: - raise exceptions.CommandError( - 'The --plugin-version option is only valid when ' - '--plugin is specified') - - job_types = cs.job_types.list(search_opts) - columns = ('name', 'plugin(versions)') - utils.print_list(job_types, columns, - {'plugin(versions)': _print_plugin_field}) diff --git a/saharaclient/openstack/__init__.py b/saharaclient/openstack/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/saharaclient/openstack/__init__.py +++ /dev/null diff --git a/saharaclient/openstack/common/__init__.py b/saharaclient/openstack/common/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/saharaclient/openstack/common/__init__.py +++ /dev/null diff --git a/saharaclient/openstack/common/apiclient/__init__.py b/saharaclient/openstack/common/apiclient/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/saharaclient/openstack/common/apiclient/__init__.py +++ /dev/null diff --git a/saharaclient/openstack/common/apiclient/auth.py b/saharaclient/openstack/common/apiclient/auth.py deleted file mode 100644 index d27450c..0000000 --- a/saharaclient/openstack/common/apiclient/auth.py +++ /dev/null @@ -1,234 +0,0 @@ -# Copyright 2013 OpenStack Foundation -# Copyright 2013 Spanish National Research Council. -# All Rights Reserved. -# -# 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. - -# E0202: An attribute inherited from %s hide this method -# pylint: disable=E0202 - -######################################################################## -# -# THIS MODULE IS DEPRECATED -# -# Please refer to -# https://etherpad.openstack.org/p/kilo-saharaclient-library-proposals for -# the discussion leading to this deprecation. -# -# We recommend checking out the python-openstacksdk project -# (https://launchpad.net/python-openstacksdk) instead. -# -######################################################################## - -import abc -import argparse -import os - -import six -from stevedore import extension - -from saharaclient.openstack.common.apiclient import exceptions - - -_discovered_plugins = {} - - -def discover_auth_systems(): - """Discover the available auth-systems. - - This won't take into account the old style auth-systems. - """ - global _discovered_plugins - _discovered_plugins = {} - - def add_plugin(ext): - _discovered_plugins[ext.name] = ext.plugin - - ep_namespace = "saharaclient.openstack.common.apiclient.auth" - mgr = extension.ExtensionManager(ep_namespace) - mgr.map(add_plugin) - - -def load_auth_system_opts(parser): - """Load options needed by the available auth-systems into a parser. - - This function will try to populate the parser with options from the - available plugins. - """ - group = parser.add_argument_group("Common auth options") - BaseAuthPlugin.add_common_opts(group) - for name, auth_plugin in six.iteritems(_discovered_plugins): - group = parser.add_argument_group( - "Auth-system '%s' options" % name, - conflict_handler="resolve") - auth_plugin.add_opts(group) - - -def load_plugin(auth_system): - try: - plugin_class = _discovered_plugins[auth_system] - except KeyError: - raise exceptions.AuthSystemNotFound(auth_system) - return plugin_class(auth_system=auth_system) - - -def load_plugin_from_args(args): - """Load required plugin and populate it with options. - - Try to guess auth system if it is not specified. Systems are tried in - alphabetical order. - - :type args: argparse.Namespace - :raises: AuthPluginOptionsMissing - """ - auth_system = args.os_auth_system - if auth_system: - plugin = load_plugin(auth_system) - plugin.parse_opts(args) - plugin.sufficient_options() - return plugin - - for plugin_auth_system in sorted(six.iterkeys(_discovered_plugins)): - plugin_class = _discovered_plugins[plugin_auth_system] - plugin = plugin_class() - plugin.parse_opts(args) - try: - plugin.sufficient_options() - except exceptions.AuthPluginOptionsMissing: - continue - return plugin - raise exceptions.AuthPluginOptionsMissing(["auth_system"]) - - -@six.add_metaclass(abc.ABCMeta) -class BaseAuthPlugin(object): - """Base class for authentication plugins. - - An authentication plugin needs to override at least the authenticate - method to be a valid plugin. - """ - - auth_system = None - opt_names = [] - common_opt_names = [ - "auth_system", - "username", - "password", - "tenant_name", - "token", - "auth_url", - ] - - def __init__(self, auth_system=None, **kwargs): - self.auth_system = auth_system or self.auth_system - self.opts = dict((name, kwargs.get(name)) - for name in self.opt_names) - - @staticmethod - def _parser_add_opt(parser, opt): - """Add an option to parser in two variants. - - :param opt: option name (with underscores) - """ - dashed_opt = opt.replace("_", "-") - env_var = "OS_%s" % opt.upper() - arg_default = os.environ.get(env_var, "") - arg_help = "Defaults to env[%s]." % env_var - parser.add_argument( - "--os-%s" % dashed_opt, - metavar="<%s>" % dashed_opt, - default=arg_default, - help=arg_help) - parser.add_argument( - "--os_%s" % opt, - metavar="<%s>" % dashed_opt, - help=argparse.SUPPRESS) - - @classmethod - def add_opts(cls, parser): - """Populate the parser with the options for this plugin. - """ - for opt in cls.opt_names: - # use `BaseAuthPlugin.common_opt_names` since it is never - # changed in child classes - if opt not in BaseAuthPlugin.common_opt_names: - cls._parser_add_opt(parser, opt) - - @classmethod - def add_common_opts(cls, parser): - """Add options that are common for several plugins. - """ - for opt in cls.common_opt_names: - cls._parser_add_opt(parser, opt) - - @staticmethod - def get_opt(opt_name, args): - """Return option name and value. - - :param opt_name: name of the option, e.g., "username" - :param args: parsed arguments - """ - return (opt_name, getattr(args, "os_%s" % opt_name, None)) - - def parse_opts(self, args): - """Parse the actual auth-system options if any. - - This method is expected to populate the attribute `self.opts` with a - dict containing the options and values needed to make authentication. - """ - self.opts.update(dict(self.get_opt(opt_name, args) - for opt_name in self.opt_names)) - - def authenticate(self, http_client): - """Authenticate using plugin defined method. - - The method usually analyses `self.opts` and performs - a request to authentication server. - - :param http_client: client object that needs authentication - :type http_client: HTTPClient - :raises: AuthorizationFailure - """ - self.sufficient_options() - self._do_authenticate(http_client) - - @abc.abstractmethod - def _do_authenticate(self, http_client): - """Protected method for authentication. - """ - - def sufficient_options(self): - """Check if all required options are present. - - :raises: AuthPluginOptionsMissing - """ - missing = [opt - for opt in self.opt_names - if not self.opts.get(opt)] - if missing: - raise exceptions.AuthPluginOptionsMissing(missing) - - @abc.abstractmethod - def token_and_endpoint(self, endpoint_type, service_type): - """Return token and endpoint. - - :param service_type: Service type of the endpoint - :type service_type: string - :param endpoint_type: Type of endpoint. - Possible values: public or publicURL, - internal or internalURL, - admin or adminURL - :type endpoint_type: string - :returns: tuple of token and endpoint strings - :raises: EndpointException - """ diff --git a/saharaclient/openstack/common/apiclient/exceptions.py b/saharaclient/openstack/common/apiclient/exceptions.py deleted file mode 100644 index ce1c53b..0000000 --- a/saharaclient/openstack/common/apiclient/exceptions.py +++ /dev/null @@ -1,479 +0,0 @@ -# Copyright 2010 Jacob Kaplan-Moss -# Copyright 2011 Nebula, Inc. -# Copyright 2013 Alessio Ababilov -# Copyright 2013 OpenStack Foundation -# All Rights Reserved. -# -# 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. - -""" -Exception definitions. -""" - -######################################################################## -# -# THIS MODULE IS DEPRECATED -# -# Please refer to -# https://etherpad.openstack.org/p/kilo-saharaclient-library-proposals for -# the discussion leading to this deprecation. -# -# We recommend checking out the python-openstacksdk project -# (https://launchpad.net/python-openstacksdk) instead. -# -######################################################################## - -import inspect -import sys - -import six - -from saharaclient.openstack.common._i18n import _ - - -class ClientException(Exception): - """The base exception class for all exceptions this library raises. - """ - pass - - -class ValidationError(ClientException): - """Error in validation on API client side.""" - pass - - -class UnsupportedVersion(ClientException): - """User is trying to use an unsupported version of the API.""" - pass - - -class CommandError(ClientException): - """Error in CLI tool.""" - pass - - -class AuthorizationFailure(ClientException): - """Cannot authorize API client.""" - pass - - -class ConnectionError(ClientException): - """Cannot connect to API service.""" - pass - - -class ConnectionRefused(ConnectionError): - """Connection refused while trying to connect to API service.""" - pass - - -class AuthPluginOptionsMissing(AuthorizationFailure): - """Auth plugin misses some options.""" - def __init__(self, opt_names): - super(AuthPluginOptionsMissing, self).__init__( - _("Authentication failed. Missing options: %s") % - ", ".join(opt_names)) - self.opt_names = opt_names - - -class AuthSystemNotFound(AuthorizationFailure): - """User has specified an AuthSystem that is not installed.""" - def __init__(self, auth_system): - super(AuthSystemNotFound, self).__init__( - _("AuthSystemNotFound: %r") % auth_system) - self.auth_system = auth_system - - -class NoUniqueMatch(ClientException): - """Multiple entities found instead of one.""" - pass - - -class EndpointException(ClientException): - """Something is rotten in Service Catalog.""" - pass - - -class EndpointNotFound(EndpointException): - """Could not find requested endpoint in Service Catalog.""" - pass - - -class AmbiguousEndpoints(EndpointException): - """Found more than one matching endpoint in Service Catalog.""" - def __init__(self, endpoints=None): - super(AmbiguousEndpoints, self).__init__( - _("AmbiguousEndpoints: %r") % endpoints) - self.endpoints = endpoints - - -class HttpError(ClientException): - """The base exception class for all HTTP exceptions. - """ - http_status = 0 - message = _("HTTP Error") - - def __init__(self, message=None, details=None, - response=None, request_id=None, - url=None, method=None, http_status=None): - self.http_status = http_status or self.http_status - self.message = message or self.message - self.details = details - self.request_id = request_id - self.response = response - self.url = url - self.method = method - formatted_string = "%s (HTTP %s)" % (self.message, self.http_status) - if request_id: - formatted_string += " (Request-ID: %s)" % request_id - super(HttpError, self).__init__(formatted_string) - - -class HTTPRedirection(HttpError): - """HTTP Redirection.""" - message = _("HTTP Redirection") - - -class HTTPClientError(HttpError): - """Client-side HTTP error. - - Exception for cases in which the client seems to have erred. - """ - message = _("HTTP Client Error") - - -class HttpServerError(HttpError): - """Server-side HTTP error. - - Exception for cases in which the server is aware that it has - erred or is incapable of performing the request. - """ - message = _("HTTP Server Error") - - -class MultipleChoices(HTTPRedirection): - """HTTP 300 - Multiple Choices. - - Indicates multiple options for the resource that the client may follow. - """ - - http_status = 300 - message = _("Multiple Choices") - - -class BadRequest(HTTPClientError): - """HTTP 400 - Bad Request. - - The request cannot be fulfilled due to bad syntax. - """ - http_status = 400 - message = _("Bad Request") - - -class Unauthorized(HTTPClientError): - """HTTP 401 - Unauthorized. - - Similar to 403 Forbidden, but specifically for use when authentication - is required and has failed or has not yet been provided. - """ - http_status = 401 - message = _("Unauthorized") - - -class PaymentRequired(HTTPClientError): - """HTTP 402 - Payment Required. - - Reserved for future use. - """ - http_status = 402 - message = _("Payment Required") - - -class Forbidden(HTTPClientError): - """HTTP 403 - Forbidden. - - The request was a valid request, but the server is refusing to respond - to it. - """ - http_status = 403 - message = _("Forbidden") - - -class NotFound(HTTPClientError): - """HTTP 404 - Not Found. - - The requested resource could not be found but may be available again - in the future. - """ - http_status = 404 - message = _("Not Found") - - -class MethodNotAllowed(HTTPClientError): - """HTTP 405 - Method Not Allowed. - - A request was made of a resource using a request method not supported - by that resource. - """ - http_status = 405 - message = _("Method Not Allowed") - - -class NotAcceptable(HTTPClientError): - """HTTP 406 - Not Acceptable. - - The requested resource is only capable of generating content not - acceptable according to the Accept headers sent in the request. - """ - http_status = 406 - message = _("Not Acceptable") - - -class ProxyAuthenticationRequired(HTTPClientError): - """HTTP 407 - Proxy Authentication Required. - - The client must first authenticate itself with the proxy. - """ - http_status = 407 - message = _("Proxy Authentication Required") - - -class RequestTimeout(HTTPClientError): - """HTTP 408 - Request Timeout. - - The server timed out waiting for the request. - """ - http_status = 408 - message = _("Request Timeout") - - -class Conflict(HTTPClientError): - """HTTP 409 - Conflict. - - Indicates that the request could not be processed because of conflict - in the request, such as an edit conflict. - """ - http_status = 409 - message = _("Conflict") - - -class Gone(HTTPClientError): - """HTTP 410 - Gone. - - Indicates that the resource requested is no longer available and will - not be available again. - """ - http_status = 410 - message = _("Gone") - - -class LengthRequired(HTTPClientError): - """HTTP 411 - Length Required. - - The request did not specify the length of its content, which is - required by the requested resource. - """ - http_status = 411 - message = _("Length Required") - - -class PreconditionFailed(HTTPClientError): - """HTTP 412 - Precondition Failed. - - The server does not meet one of the preconditions that the requester - put on the request. - """ - http_status = 412 - message = _("Precondition Failed") - - -class RequestEntityTooLarge(HTTPClientError): - """HTTP 413 - Request Entity Too Large. - - The request is larger than the server is willing or able to process. - """ - http_status = 413 - message = _("Request Entity Too Large") - - def __init__(self, *args, **kwargs): - try: - self.retry_after = int(kwargs.pop('retry_after')) - except (KeyError, ValueError): - self.retry_after = 0 - - super(RequestEntityTooLarge, self).__init__(*args, **kwargs) - - -class RequestUriTooLong(HTTPClientError): - """HTTP 414 - Request-URI Too Long. - - The URI provided was too long for the server to process. - """ - http_status = 414 - message = _("Request-URI Too Long") - - -class UnsupportedMediaType(HTTPClientError): - """HTTP 415 - Unsupported Media Type. - - The request entity has a media type which the server or resource does - not support. - """ - http_status = 415 - message = _("Unsupported Media Type") - - -class RequestedRangeNotSatisfiable(HTTPClientError): - """HTTP 416 - Requested Range Not Satisfiable. - - The client has asked for a portion of the file, but the server cannot - supply that portion. - """ - http_status = 416 - message = _("Requested Range Not Satisfiable") - - -class ExpectationFailed(HTTPClientError): - """HTTP 417 - Expectation Failed. - - The server cannot meet the requirements of the Expect request-header field. - """ - http_status = 417 - message = _("Expectation Failed") - - -class UnprocessableEntity(HTTPClientError): - """HTTP 422 - Unprocessable Entity. - - The request was well-formed but was unable to be followed due to semantic - errors. - """ - http_status = 422 - message = _("Unprocessable Entity") - - -class InternalServerError(HttpServerError): - """HTTP 500 - Internal Server Error. - - A generic error message, given when no more specific message is suitable. - """ - http_status = 500 - message = _("Internal Server Error") - - -# NotImplemented is a python keyword. -class HttpNotImplemented(HttpServerError): - """HTTP 501 - Not Implemented. - - The server either does not recognize the request method, or it lacks - the ability to fulfill the request. - """ - http_status = 501 - message = _("Not Implemented") - - -class BadGateway(HttpServerError): - """HTTP 502 - Bad Gateway. - - The server was acting as a gateway or proxy and received an invalid - response from the upstream server. - """ - http_status = 502 - message = _("Bad Gateway") - - -class ServiceUnavailable(HttpServerError): - """HTTP 503 - Service Unavailable. - - The server is currently unavailable. - """ - http_status = 503 - message = _("Service Unavailable") - - -class GatewayTimeout(HttpServerError): - """HTTP 504 - Gateway Timeout. - - The server was acting as a gateway or proxy and did not receive a timely - response from the upstream server. - """ - http_status = 504 - message = _("Gateway Timeout") - - -class HttpVersionNotSupported(HttpServerError): - """HTTP 505 - HttpVersion Not Supported. - - The server does not support the HTTP protocol version used in the request. - """ - http_status = 505 - message = _("HTTP Version Not Supported") - - -# _code_map contains all the classes that have http_status attribute. -_code_map = dict( - (getattr(obj, 'http_status', None), obj) - for name, obj in six.iteritems(vars(sys.modules[__name__])) - if inspect.isclass(obj) and getattr(obj, 'http_status', False) -) - - -def from_response(response, method, url): - """Returns an instance of :class:`HttpError` or subclass based on response. - - :param response: instance of `requests.Response` class - :param method: HTTP method used for request - :param url: URL used for request - """ - - req_id = response.headers.get("x-openstack-request-id") - # NOTE(hdd) true for older versions of nova and cinder - if not req_id: - req_id = response.headers.get("x-compute-request-id") - kwargs = { - "http_status": response.status_code, - "response": response, - "method": method, - "url": url, - "request_id": req_id, - } - if "retry-after" in response.headers: - kwargs["retry_after"] = response.headers["retry-after"] - - content_type = response.headers.get("Content-Type", "") - if content_type.startswith("application/json"): - try: - body = response.json() - except ValueError: - pass - else: - if isinstance(body, dict): - error = body.get(list(body)[0]) - if isinstance(error, dict): - kwargs["message"] = (error.get("message") or - error.get("faultstring")) - kwargs["details"] = (error.get("details") or - six.text_type(body)) - elif content_type.startswith("text/"): - kwargs["details"] = response.text - - try: - cls = _code_map[response.status_code] - except KeyError: - if 500 <= response.status_code < 600: - cls = HttpServerError - elif 400 <= response.status_code < 500: - cls = HTTPClientError - else: - cls = HttpError - return cls(**kwargs) diff --git a/saharaclient/openstack/common/cliutils.py b/saharaclient/openstack/common/cliutils.py deleted file mode 100644 index 4b30d89..0000000 --- a/saharaclient/openstack/common/cliutils.py +++ /dev/null @@ -1,272 +0,0 @@ -# Copyright 2012 Red Hat, 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. - -# W0603: Using the global statement -# W0621: Redefining name %s from outer scope -# pylint: disable=W0603,W0621 - -from __future__ import print_function - -import getpass -import inspect -import os -import sys -import textwrap - -from oslo_utils import encodeutils -from oslo_utils import strutils -import prettytable -import six -from six import moves - -from saharaclient.openstack.common._i18n import _ - - -class MissingArgs(Exception): - """Supplied arguments are not sufficient for calling a function.""" - def __init__(self, missing): - self.missing = missing - msg = _("Missing arguments: %s") % ", ".join(missing) - super(MissingArgs, self).__init__(msg) - - -def validate_args(fn, *args, **kwargs): - """Check that the supplied args are sufficient for calling a function. - - >>> validate_args(lambda a: None) - Traceback (most recent call last): - ... - MissingArgs: Missing argument(s): a - >>> validate_args(lambda a, b, c, d: None, 0, c=1) - Traceback (most recent call last): - ... - MissingArgs: Missing argument(s): b, d - - :param fn: the function to check - :param arg: the positional arguments supplied - :param kwargs: the keyword arguments supplied - """ - argspec = inspect.getargspec(fn) - - num_defaults = len(argspec.defaults or []) - required_args = argspec.args[:len(argspec.args) - num_defaults] - - def isbound(method): - return getattr(method, '__self__', None) is not None - - if isbound(fn): - required_args.pop(0) - - missing = [arg for arg in required_args if arg not in kwargs] - missing = missing[len(args):] - if missing: - raise MissingArgs(missing) - - -def arg(*args, **kwargs): - """Decorator for CLI args. - - Example: - - >>> @arg("name", help="Name of the new entity") - ... def entity_create(args): - ... pass - """ - def _decorator(func): - add_arg(func, *args, **kwargs) - return func - return _decorator - - -def env(*args, **kwargs): - """Returns the first environment variable set. - - If all are empty, defaults to '' or keyword arg `default`. - """ - for arg in args: - value = os.environ.get(arg) - if value: - return value - return kwargs.get('default', '') - - -def add_arg(func, *args, **kwargs): - """Bind CLI arguments to a shell.py `do_foo` function.""" - - if not hasattr(func, 'arguments'): - func.arguments = [] - - # NOTE(sirp): avoid dups that can occur when the module is shared across - # tests. - if (args, kwargs) not in func.arguments: - # Because of the semantics of decorator composition if we just append - # to the options list positional options will appear to be backwards. - func.arguments.insert(0, (args, kwargs)) - - -def unauthenticated(func): - """Adds 'unauthenticated' attribute to decorated function. - - Usage: - - >>> @unauthenticated - ... def mymethod(f): - ... pass - """ - func.unauthenticated = True - return func - - -def isunauthenticated(func): - """Checks if the function does not require authentication. - - Mark such functions with the `@unauthenticated` decorator. - - :returns: bool - """ - return getattr(func, 'unauthenticated', False) - - -def print_list(objs, fields, formatters=None, sortby_index=0, - mixed_case_fields=None, field_labels=None): - """Print a list of objects as a table, one row per object. - - :param objs: iterable of :class:`Resource` - :param fields: attributes that correspond to columns, in order - :param formatters: `dict` of callables for field formatting - :param sortby_index: index of the field for sorting table rows - :param mixed_case_fields: fields corresponding to object attributes that - have mixed case names (e.g., 'serverId') - :param field_labels: Labels to use in the heading of the table, default to - fields. - """ - formatters = formatters or {} - mixed_case_fields = mixed_case_fields or [] - field_labels = field_labels or fields - if len(field_labels) != len(fields): - raise ValueError(_("Field labels list %(labels)s has different number " - "of elements than fields list %(fields)s"), - {'labels': field_labels, 'fields': fields}) - - if sortby_index is None: - kwargs = {} - else: - kwargs = {'sortby': field_labels[sortby_index]} - pt = prettytable.PrettyTable(field_labels) - pt.align = 'l' - - for o in objs: - row = [] - for field in fields: - if field in formatters: - row.append(formatters[field](o)) - else: - if field in mixed_case_fields: - field_name = field.replace(' ', '_') - else: - field_name = field.lower().replace(' ', '_') - data = getattr(o, field_name, '') - row.append(data) - pt.add_row(row) - - if six.PY3: - print(encodeutils.safe_encode(pt.get_string(**kwargs)).decode()) - else: - print(encodeutils.safe_encode(pt.get_string(**kwargs))) - - -def print_dict(dct, dict_property="Property", wrap=0, dict_value='Value'): - """Print a `dict` as a table of two columns. - - :param dct: `dict` to print - :param dict_property: name of the first column - :param wrap: wrapping for the second column - :param dict_value: header label for the value (second) column - """ - pt = prettytable.PrettyTable([dict_property, dict_value]) - pt.align = 'l' - for k, v in sorted(dct.items()): - # convert dict to str to check length - if isinstance(v, dict): - v = six.text_type(v) - if wrap > 0: - v = textwrap.fill(six.text_type(v), wrap) - # if value has a newline, add in multiple rows - # e.g. fault with stacktrace - if v and isinstance(v, six.string_types) and r'\n' in v: - lines = v.strip().split(r'\n') - col1 = k - for line in lines: - pt.add_row([col1, line]) - col1 = '' - else: - pt.add_row([k, v]) - - if six.PY3: - print(encodeutils.safe_encode(pt.get_string()).decode()) - else: - print(encodeutils.safe_encode(pt.get_string())) - - -def get_password(max_password_prompts=3): - """Read password from TTY.""" - verify = strutils.bool_from_string(env("OS_VERIFY_PASSWORD")) - pw = None - if hasattr(sys.stdin, "isatty") and sys.stdin.isatty(): - # Check for Ctrl-D - try: - for __ in moves.range(max_password_prompts): - pw1 = getpass.getpass("OS Password: ") - if verify: - pw2 = getpass.getpass("Please verify: ") - else: - pw2 = pw1 - if pw1 == pw2 and pw1: - pw = pw1 - break - except EOFError: - pass - return pw - - -def service_type(stype): - """Adds 'service_type' attribute to decorated function. - - Usage: - - .. code-block:: python - - @service_type('volume') - def mymethod(f): - ... - """ - def inner(f): - f.service_type = stype - return f - return inner - - -def get_service_type(f): - """Retrieves service type from function.""" - return getattr(f, 'service_type', None) - - -def pretty_choice_list(l): - return ', '.join("'%s'" % i for i in l) - - -def exit(msg=''): - if msg: - print (msg, file=sys.stderr) - sys.exit(1) diff --git a/saharaclient/shell.py b/saharaclient/shell.py deleted file mode 100644 index b0236eb..0000000 --- a/saharaclient/shell.py +++ /dev/null @@ -1,732 +0,0 @@ -# Copyright 2010 Jacob Kaplan-Moss -# Copyright 2011 OpenStack Foundation -# All Rights Reserved. -# -# 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. - -### -# This code is taken from python-novaclient. Goal is minimal modification. -### - -""" -Command-line interface to the OpenStack Sahara API. -""" - -from __future__ import print_function -import argparse -import getpass -import logging -import sys -import warnings - -import six - - -HAS_KEYRING = False -all_errors = ValueError -try: - import keyring - HAS_KEYRING = True - try: - if isinstance(keyring.get_keyring(), keyring.backend.GnomeKeyring): - import gnomekeyring - all_errors = (ValueError, - gnomekeyring.IOError, - gnomekeyring.NoKeyringDaemonError) - except Exception: - pass -except ImportError: - pass - -from keystoneauth1.identity.generic import password -from keystoneauth1.identity.generic import token -from keystoneauth1.loading import session -from keystoneclient.auth.identity import v3 as identity -from oslo_utils import encodeutils -from oslo_utils import strutils - -from saharaclient.api import client -from saharaclient.api import shell as shell_api -from saharaclient.openstack.common.apiclient import auth -from saharaclient.openstack.common.apiclient import exceptions as exc -from saharaclient.openstack.common import cliutils -from saharaclient import version - -DEFAULT_API_VERSION = 'api' -DEFAULT_ENDPOINT_TYPE = 'publicURL' -DEFAULT_SERVICE_TYPE = 'data-processing' - -logger = logging.getLogger(__name__) - - -def positive_non_zero_float(text): - if text is None: - return None - try: - value = float(text) - except ValueError: - msg = "%s must be a float" % text - raise argparse.ArgumentTypeError(msg) - if value <= 0: - msg = "%s must be greater than 0" % text - raise argparse.ArgumentTypeError(msg) - return value - - -class SecretsHelper(object): - def __init__(self, args, client): - self.args = args - self.client = client - self.key = None - - def _validate_string(self, text): - if text is None or len(text) == 0: - return False - return True - - def _make_key(self): - if self.key is not None: - return self.key - keys = [ - self.client.auth_url, - self.client.projectid, - self.client.user, - self.client.region_name, - self.client.endpoint_type, - self.client.service_type, - self.client.service_name, - self.client.volume_service_name, - ] - for (index, key) in enumerate(keys): - if key is None: - keys[index] = '?' - else: - keys[index] = str(keys[index]) - self.key = "/".join(keys) - return self.key - - def _prompt_password(self, verify=True): - pw = None - if hasattr(sys.stdin, 'isatty') and sys.stdin.isatty(): - # Check for Ctl-D - try: - while True: - pw1 = getpass.getpass('OS Password: ') - if verify: - pw2 = getpass.getpass('Please verify: ') - else: - pw2 = pw1 - if pw1 == pw2 and self._validate_string(pw1): - pw = pw1 - break - except EOFError: - pass - return pw - - def save(self, auth_token, management_url, tenant_id): - if not HAS_KEYRING or not self.args.os_cache: - return - if (auth_token == self.auth_token and - management_url == self.management_url): - # Nothing changed.... - return - if not all([management_url, auth_token, tenant_id]): - raise ValueError("Unable to save empty management url/auth token") - value = "|".join([str(auth_token), - str(management_url), - str(tenant_id)]) - keyring.set_password("saharaclient_auth", self._make_key(), value) - - @property - def password(self): - if self._validate_string(self.args.os_password): - return self.args.os_password - verify_pass = ( - strutils.bool_from_string(cliutils.env("OS_VERIFY_PASSWORD")) - ) - return self._prompt_password(verify_pass) - - @property - def management_url(self): - if not HAS_KEYRING or not self.args.os_cache: - return None - management_url = None - try: - block = keyring.get_password('saharaclient_auth', - self._make_key()) - if block: - _token, management_url, _tenant_id = block.split('|', 2) - except all_errors: - pass - return management_url - - @property - def auth_token(self): - # Now is where it gets complicated since we - # want to look into the keyring module, if it - # exists and see if anything was provided in that - # file that we can use. - if not HAS_KEYRING or not self.args.os_cache: - return None - token = None - try: - block = keyring.get_password('saharaclient_auth', - self._make_key()) - if block: - token, _management_url, _tenant_id = block.split('|', 2) - except all_errors: - pass - return token - - @property - def tenant_id(self): - if not HAS_KEYRING or not self.args.os_cache: - return None - tenant_id = None - try: - block = keyring.get_password('saharaclient_auth', - self._make_key()) - if block: - _token, _management_url, tenant_id = block.split('|', 2) - except all_errors: - pass - return tenant_id - - -class SaharaClientArgumentParser(argparse.ArgumentParser): - - def __init__(self, *args, **kwargs): - super(SaharaClientArgumentParser, self).__init__(*args, **kwargs) - - def error(self, message): - """error(message: string) - - Prints a usage message incorporating the message to stderr and - exits. - """ - self.print_usage(sys.stderr) - # FIXME(lzyeval): if changes occur in argparse.ArgParser._check_value - choose_from = ' (choose from' - progparts = self.prog.partition(' ') - self.exit(2, "error: %(errmsg)s\nTry '%(mainp)s help %(subp)s'" - " for more information.\n" % - {'errmsg': message.split(choose_from)[0], - 'mainp': progparts[0], - 'subp': progparts[2]}) - - -class OpenStackSaharaShell(object): - - def get_base_parser(self): - parser = SaharaClientArgumentParser( - prog='sahara', - description=__doc__.strip(), - epilog='See "sahara help COMMAND" ' - 'for help on a specific command.', - add_help=False, - formatter_class=OpenStackHelpFormatter, - ) - - # Global arguments - parser.add_argument('-h', '--help', - action='store_true', - help=argparse.SUPPRESS) - - parser.add_argument('--version', - action='version', - version=version.version_info.version_string()) - - parser.add_argument('--debug', - default=False, - action='store_true', - help="Print debugging output.") - - parser.add_argument('--os-cache', - default=strutils.bool_from_string( - cliutils.env('OS_CACHE', default=False)), - action='store_true', - help="Use the auth token cache. Defaults to False " - "if env[OS_CACHE] is not set.") - - -# TODO(mattf) - add get_timings support to Client -# parser.add_argument('--timings', -# default=False, -# action='store_true', -# help="Print call timing info") - -# TODO(mattf) - use timeout -# parser.add_argument('--timeout', -# default=600, -# metavar='<seconds>', -# type=positive_non_zero_float, -# help="Set HTTP call timeout (in seconds)") - - parser.add_argument('--region-name', - metavar='<region-name>', - default=cliutils.env('SAHARA_REGION_NAME', - 'OS_REGION_NAME'), - help='Defaults to env[OS_REGION_NAME].') - parser.add_argument('--region_name', - help=argparse.SUPPRESS) - - parser.add_argument('--service-type', - metavar='<service-type>', - help='Defaults to data-processing for all ' - 'actions.') - parser.add_argument('--service_type', - help=argparse.SUPPRESS) - -# NA -# parser.add_argument('--service-name', -# metavar='<service-name>', -# default=utils.env('SAHARA_SERVICE_NAME'), -# help='Defaults to env[SAHARA_SERVICE_NAME]') -# parser.add_argument('--service_name', -# help=argparse.SUPPRESS) - -# NA -# parser.add_argument('--volume-service-name', -# metavar='<volume-service-name>', -# default=utils.env('NOVA_VOLUME_SERVICE_NAME'), -# help='Defaults to env[NOVA_VOLUME_SERVICE_NAME]') -# parser.add_argument('--volume_service_name', -# help=argparse.SUPPRESS) - - parser.add_argument('--endpoint-type', - metavar='<endpoint-type>', - default=cliutils.env( - 'SAHARA_ENDPOINT_TYPE', - 'OS_ENDPOINT_TYPE', - default=DEFAULT_ENDPOINT_TYPE), - help=('Defaults to env[SAHARA_ENDPOINT_TYPE] or' - ' env[OS_ENDPOINT_TYPE] or ') - + DEFAULT_ENDPOINT_TYPE + '.') - # NOTE(dtroyer): We can't add --endpoint_type here due to argparse - # thinking usage-list --end is ambiguous; but it - # works fine with only --endpoint-type present - # Go figure. I'm leaving this here for doc purposes. - # parser.add_argument('--endpoint_type', - # help=argparse.SUPPRESS) - - parser.add_argument('--sahara-api-version', - metavar='<sahara-api-ver>', - default=cliutils.env( - 'SAHARA_API_VERSION', - default=DEFAULT_API_VERSION), - help='Accepts "api", ' - 'defaults to env[SAHARA_API_VERSION].') - parser.add_argument('--sahara_api_version', - help=argparse.SUPPRESS) - - parser.add_argument('--bypass-url', - metavar='<bypass-url>', - default=cliutils.env('BYPASS_URL', default=None), - dest='bypass_url', - help="Use this API endpoint instead of the " - "Service Catalog.") - parser.add_argument('--bypass_url', - help=argparse.SUPPRESS) - - parser.add_argument('--os-tenant-name', - default=cliutils.env('OS_TENANT_NAME'), - help='Defaults to env[OS_TENANT_NAME].') - - parser.add_argument('--os-tenant-id', - default=cliutils.env('OS_TENANT_ID'), - help='Defaults to env[OS_TENANT_ID].') - - parser.add_argument('--os-auth-system', - default=cliutils.env('OS_AUTH_SYSTEM'), - help='Defaults to env[OS_AUTH_SYSTEM].') - - parser.add_argument('--os-auth-token', - default=cliutils.env('OS_AUTH_TOKEN'), - help='Defaults to env[OS_AUTH_TOKEN].') - - # Use Keystoneclient/Keystoneauth API to parse authentication arguments - session.Session().register_argparse_arguments(parser) - identity.Password.register_argparse_arguments(parser) - - return parser - - def get_subcommand_parser(self, version): - parser = self.get_base_parser() - - self.subcommands = {} - subparsers = parser.add_subparsers(metavar='<subcommand>') - - try: - actions_module = { - 'api': shell_api, - }[version] - except KeyError: - actions_module = shell_api - actions_module = shell_api - - self._find_actions(subparsers, actions_module) - self._find_actions(subparsers, self) - - self._add_bash_completion_subparser(subparsers) - - return parser - - def _add_bash_completion_subparser(self, subparsers): - subparser = ( - subparsers.add_parser('bash_completion', - add_help=False, - formatter_class=OpenStackHelpFormatter) - ) - self.subcommands['bash_completion'] = subparser - subparser.set_defaults(func=self.do_bash_completion) - - def _find_actions(self, subparsers, actions_module): - for attr in (a for a in dir(actions_module) if a.startswith('do_')): - # I prefer to be hyphen-separated instead of underscores. - command = attr[3:].replace('_', '-') - callback = getattr(actions_module, attr) - desc = callback.__doc__ or '' - action_help = desc.strip() - arguments = getattr(callback, 'arguments', []) - - subparser = ( - subparsers.add_parser(command, - help=action_help, - description=desc, - add_help=False, - formatter_class=OpenStackHelpFormatter) - ) - subparser.add_argument('-h', '--help', - action='help', - help=argparse.SUPPRESS,) - self.subcommands[command] = subparser - for (args, kwargs) in arguments: - subparser.add_argument(*args, **kwargs) - subparser.set_defaults(func=callback) - - def setup_debugging(self, debug): - if not debug: - return - - streamformat = "%(levelname)s (%(module)s:%(lineno)d) %(message)s" - # Set up the root logger to debug so that the submodules can - # print debug messages - logging.basicConfig(level=logging.DEBUG, - format=streamformat) - - def _get_keystone_auth(self, session, auth_url, **kwargs): - auth_token = kwargs.pop('auth_token', None) - if auth_token: - return token.Token(auth_url, auth_token, **kwargs) - else: - return password.Password( - auth_url, - username=kwargs.pop('username'), - user_id=kwargs.pop('user_id'), - password=kwargs.pop('password'), - user_domain_id=kwargs.pop('user_domain_id'), - user_domain_name=kwargs.pop('user_domain_name'), - **kwargs) - - def main(self, argv): - - # Parse args once to find version and debug settings - parser = self.get_base_parser() - (options, args) = parser.parse_known_args(argv) - self.setup_debugging(options.debug) - self.options = options - - # NOTE(dtroyer): Hackery to handle --endpoint_type due to argparse - # thinking usage-list --end is ambiguous; but it - # works fine with only --endpoint-type present - # Go figure. - if '--endpoint_type' in argv: - spot = argv.index('--endpoint_type') - argv[spot] = '--endpoint-type' - - subcommand_parser = ( - self.get_subcommand_parser(options.sahara_api_version) - ) - self.parser = subcommand_parser - - if options.help or not argv: - subcommand_parser.print_help() - return 0 - - args = subcommand_parser.parse_args(argv) - - # Short-circuit and deal with help right away. - if args.func == self.do_help: - self.do_help(args) - return 0 - elif args.func == self.do_bash_completion: - self.do_bash_completion(args) - return 0 - -# (os_username, os_tenant_name, os_tenant_id, os_auth_url, -# os_region_name, os_auth_system, endpoint_type, insecure, -# service_type, service_name, volume_service_name, -# bypass_url, os_cache, cacert) = ( #, timeout) = ( -# args.os_username, -# args.os_tenant_name, args.os_tenant_id, -# args.os_auth_url, -# args.os_region_name, -# args.os_auth_system, -# args.endpoint_type, args.insecure, -# args.service_type, -# args.service_name, args.volume_service_name, -# args.bypass_url, args.os_cache, -# args.os_cacert, args.timeout) - (os_username, os_tenant_name, os_tenant_id, - os_auth_url, os_auth_system, endpoint_type, - service_type, bypass_url, os_cacert, insecure, region_name) = ( - (args.os_username, args.os_tenant_name, args.os_tenant_id, - args.os_auth_url, args.os_auth_system, args.endpoint_type, - args.service_type, args.bypass_url, args.os_cacert, args.insecure, - args.region_name) - ) - - if os_auth_system and os_auth_system != "keystone": - auth_plugin = auth.load_plugin(os_auth_system) - else: - auth_plugin = None - - # Fetched and set later as needed - os_password = None - - if not endpoint_type: - endpoint_type = DEFAULT_ENDPOINT_TYPE - - if not service_type: - service_type = DEFAULT_SERVICE_TYPE -# NA - there is only one service this CLI accesses -# service_type = utils.get_service_type(args.func) or service_type - - # FIXME(usrleon): Here should be restrict for project id same as - # for os_username or os_password but for compatibility it is not. - if not cliutils.isunauthenticated(args.func): - if auth_plugin: - auth_plugin.parse_opts(args) - - if not auth_plugin or not auth_plugin.opts: - if not os_username: - raise exc.CommandError("You must provide a username " - "via either --os-username or " - "env[OS_USERNAME]") - - if not os_auth_url: - if os_auth_system and os_auth_system != 'keystone': - os_auth_url = auth_plugin.get_auth_url() - - if not os_auth_url: - raise exc.CommandError("You must provide an auth url " - "via either --os-auth-url or " - "env[OS_AUTH_URL] or specify an " - "auth_system which defines a " - "default url with --os-auth-system " - "or env[OS_AUTH_SYSTEM]") - -# NA -# if (options.os_compute_api_version and -# options.os_compute_api_version != '1.0'): -# if not os_tenant_name and not os_tenant_id: -# raise exc.CommandError("You must provide a tenant name " -# "or tenant id via --os-tenant-name, " -# "--os-tenant-id, env[OS_TENANT_NAME] " -# "or env[OS_TENANT_ID]") -# -# if not os_auth_url: -# raise exc.CommandError("You must provide an auth url " -# "via either --os-auth-url or env[OS_AUTH_URL]") - -# NOTE: The Sahara client authenticates when you create it. So instead of -# creating here and authenticating later, which is what the novaclient -# does, we just create the client later. - - # Now check for the password/token of which pieces of the - # identifying keyring key can come from the underlying client - if not cliutils.isunauthenticated(args.func): - # NA - Client can't be used with SecretsHelper - # helper = SecretsHelper(args, self.cs.client) - if (auth_plugin and auth_plugin.opts and - "os_password" not in auth_plugin.opts): - use_pw = False - else: - use_pw = True - -# tenant_id, auth_token, management_url = (helper.tenant_id, -# helper.auth_token, -# helper.management_url) -# -# if tenant_id and auth_token and management_url: -# self.cs.client.tenant_id = tenant_id -# self.cs.client.auth_token = auth_token -# self.cs.client.management_url = management_url -# # authenticate just sets up some values in this case, no REST -# # calls -# self.cs.authenticate() - if use_pw: - # Auth using token must have failed or not happened - # at all, so now switch to password mode and save - # the token when its gotten... using our keyring - # saver - # os_password = helper.password - os_password = args.os_password - if not os_password: - raise exc.CommandError( - 'Expecting a password provided via either ' - '--os-password, env[OS_PASSWORD], or ' - 'prompted response') -# self.cs.client.password = os_password -# self.cs.client.keyring_saver = helper - - # V3 stuff - project_info_provided = (self.options.os_tenant_name or - self.options.os_tenant_id or - (self.options.os_project_name and - (self.options.os_project_domain_name or - self.options.os_project_domain_id)) or - self.options.os_project_id) - - if (not project_info_provided): - raise exc.CommandError( - ("You must provide a tenant_name, tenant_id, " - "project_id or project_name (with " - "project_domain_name or project_domain_id) via " - " --os-tenant-name (env[OS_TENANT_NAME])," - " --os-tenant-id (env[OS_TENANT_ID])," - " --os-project-id (env[OS_PROJECT_ID])" - " --os-project-name (env[OS_PROJECT_NAME])," - " --os-project-domain-id " - "(env[OS_PROJECT_DOMAIN_ID])" - " --os-project-domain-name " - "(env[OS_PROJECT_DOMAIN_NAME])")) - - if not os_auth_url: - raise exc.CommandError( - "You must provide an auth url " - "via either --os-auth-url or env[OS_AUTH_URL]") - - keystone_session = None - keystone_auth = None - if not auth_plugin: - project_id = args.os_project_id or args.os_tenant_id - project_name = args.os_project_name or args.os_tenant_name - - keystone_session = (session.Session(). - load_from_argparse_arguments(args)) - keystone_auth = self._get_keystone_auth( - keystone_session, - args.os_auth_url, - username=args.os_username, - user_id=args.os_user_id, - user_domain_id=args.os_user_domain_id, - user_domain_name=args.os_user_domain_name, - password=args.os_password, - auth_token=args.os_auth_token, - project_id=project_id, - project_name=project_name, - project_domain_id=args.os_project_domain_id, - project_domain_name=args.os_project_domain_name) - - self.cs = client.Client(username=os_username, - api_key=os_password, - project_id=os_tenant_id, - project_name=os_tenant_name, - auth_url=os_auth_url, - sahara_url=bypass_url, - endpoint_type=endpoint_type, - session=keystone_session, - auth=keystone_auth, - cacert=os_cacert, - insecure=insecure, - service_type=service_type, - region_name=region_name) - - args.func(self.cs, args) - -# TODO(mattf) - add get_timings support to Client -# if args.timings: -# self._dump_timings(self.cs.get_timings()) - - def _dump_timings(self, timings): - class Tyme(object): - def __init__(self, url, seconds): - self.url = url - self.seconds = seconds - results = [Tyme(url, end - start) for url, start, end in timings] - total = 0.0 - for tyme in results: - total += tyme.seconds - results.append(Tyme("Total", total)) - cliutils.print_list(results, ["url", "seconds"], sortby_index=None) - - def do_bash_completion(self, _args): - """Prints arguments for bash-completion. - - Prints all of the commands and options to stdout so that the - sahara.bash_completion script doesn't have to hard code them. - """ - commands = set() - options = set() - for sc_str, sc in self.subcommands.items(): - commands.add(sc_str) - for option in sc._optionals._option_string_actions.keys(): - options.add(option) - - commands.remove('bash-completion') - commands.remove('bash_completion') - print(' '.join(commands | options)) - - @cliutils.arg('command', metavar='<subcommand>', nargs='?', - help='Display help for <subcommand>.') - def do_help(self, args): - """Display help about this program or one of its subcommands.""" - if args.command: - if args.command in self.subcommands: - self.subcommands[args.command].print_help() - else: - raise exc.CommandError("'%s' is not a valid subcommand" % - args.command) - else: - self.parser.print_help() - - -# I'm picky about my shell help. -class OpenStackHelpFormatter(argparse.HelpFormatter): - def start_section(self, heading): - # Title-case the headings - heading = '%s%s' % (heading[0].upper(), heading[1:]) - super(OpenStackHelpFormatter, self).start_section(heading) - - -def main(): - warnings.simplefilter('once', category=DeprecationWarning) - warnings.warn('The sahara CLI is deprecated in favor of OpenStackClient ' - 'plugin and will not be maintained anymore. ' - 'For a Python library, continue using python-saharaclient.', - DeprecationWarning) - warnings.resetwarnings() - try: - argv = [encodeutils.safe_decode(a) for a in sys.argv[1:]] - OpenStackSaharaShell().main(argv) - - except Exception as e: - logger.debug(e, exc_info=1) - print("ERROR: %s" % encodeutils.safe_encode(six.text_type(e)), - file=sys.stderr) - sys.exit(1) - - -if __name__ == "__main__": - main() diff --git a/saharaclient/tests/unit/nova/__init__.py b/saharaclient/tests/unit/nova/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/saharaclient/tests/unit/nova/__init__.py +++ /dev/null diff --git a/saharaclient/tests/unit/nova/test_shell.py b/saharaclient/tests/unit/nova/test_shell.py deleted file mode 100644 index a99a745..0000000 --- a/saharaclient/tests/unit/nova/test_shell.py +++ /dev/null @@ -1,366 +0,0 @@ -# -# 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 re -import sys - -import fixtures -import mock -import six -from testtools import matchers - -import saharaclient.api.client -from saharaclient.openstack.common.apiclient import exceptions -import saharaclient.shell -from saharaclient.tests.unit.nova import utils - -FAKE_ENV = {'OS_USERNAME': 'username', - 'OS_PASSWORD': 'password', - 'OS_TENANT_NAME': 'tenant_name', - 'OS_AUTH_URL': 'http://no.where'} - -FAKE_ENV2 = {'OS_USERNAME': 'username', - 'OS_PASSWORD': 'password', - 'OS_TENANT_ID': 'tenant_id', - 'OS_AUTH_URL': 'http://no.where'} - - -class FakePlugin(object): - name = 'fake' - versions = ['1.0', ] - title = 'a fake plugin' - - -class FakePluginManager(object): - def list(self): - return (FakePlugin(),) - - -class FakeImage(object): - name = 'fake' - id = 'aaa-bb-ccc' - username = 'you' - description = None - tags = [] - - -class FakeImageManager(object): - def list(self): - return (FakeImage(),) - - -class FakePluginClient(object): - def __init__(self, *args, **kwargs): - self.plugins = FakePluginManager() - self.images = FakeImageManager() - - -class ShellTest(utils.TestCase): - - def make_env(self, exclude=None, fake_env=FAKE_ENV): - env = dict((k, v) for k, v in fake_env.items() if k != exclude) - self.useFixture(fixtures.MonkeyPatch('os.environ', env)) - - def setUp(self): - super(ShellTest, self).setUp() -# NA atm -# self.useFixture(fixtures.MonkeyPatch( -# 'novaclient.client.get_client_class', -# mock.MagicMock)) -# self.nc_util = mock.patch('novaclient.utils.isunauthenticated').start() -# self.nc_util.return_value = False - - def shell(self, argstr, exitcodes=(0,)): - orig = sys.stdout - orig_stderr = sys.stderr - try: - sys.stdout = six.StringIO() - sys.stderr = six.StringIO() - _shell = saharaclient.shell.OpenStackSaharaShell() - _shell.main(argstr.split()) - except SystemExit: - exc_type, exc_value, exc_traceback = sys.exc_info() - self.assertIn(exc_value.code, exitcodes) - finally: - stdout = sys.stdout.getvalue() - sys.stdout.close() - sys.stdout = orig - stderr = sys.stderr.getvalue() - sys.stderr.close() - sys.stderr = orig_stderr - return (stdout, stderr) - - def test_help_unknown_command(self): - self.assertRaises(exceptions.CommandError, self.shell, 'help foofoo') - -# NA -# def test_invalid_timeout(self): -# for f in [0, -1, -10]: -# cmd_text = '--timeout %s' % (f) -# stdout, stderr = self.shell(cmd_text, exitcodes=[0, 2]) -# required = [ -# 'argument --timeout: %s must be greater than 0' % (f), -# ] -# for r in required: -# self.assertIn(r, stderr) - - def test_help(self): - required = [ - '.*?^usage: sahara', - '.*?^\s+plugin-list\s+Print a list of available plugins.', - '.*?^See "sahara help COMMAND" for help on a specific command', - ] - stdout, stderr = self.shell('help') - for r in required: - self.assertThat((stdout + stderr), - matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE)) - - def test_help_on_subcommand(self): - required = [ - '.*?^usage: sahara plugin-list', - '.*?^Print a list of available plugins.', - ] - stdout, stderr = self.shell('help plugin-list') - for r in required: - self.assertThat((stdout + stderr), - matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE)) - - def test_help_no_options(self): - required = [ - '.*?^usage: sahara', - '.*?^\s+plugin-list\s+Print a list of available plugins.', - '.*?^See "sahara help COMMAND" for help on a specific command', - ] - stdout, stderr = self.shell('') - for r in required: - self.assertThat((stdout + stderr), - matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE)) - - def test_bash_completion(self): - stdout, stderr = self.shell('bash-completion') - # just check we have some output - required = [ - '.*help', - '.*plugin-list', - '.*plugin-show', - '.*--name'] - for r in required: - self.assertThat((stdout + stderr), - matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE)) - - def test_no_username(self): - required = ('You must provide a username' - ' via either --os-username or env[OS_USERNAME]',) - self.make_env(exclude='OS_USERNAME') - try: - self.shell('plugin-list') - except exceptions.CommandError as message: - self.assertEqual(required, message.args) - else: - self.fail('CommandError not raised') - - def test_no_tenant_name(self): - required = ( - 'You must provide a tenant_name, tenant_id, ' - 'project_id or project_name (with ' - 'project_domain_name or project_domain_id) via ' - ' --os-tenant-name (env[OS_TENANT_NAME]),' - ' --os-tenant-id (env[OS_TENANT_ID]),' - ' --os-project-id (env[OS_PROJECT_ID])' - ' --os-project-name (env[OS_PROJECT_NAME]),' - ' --os-project-domain-id ' - '(env[OS_PROJECT_DOMAIN_ID])' - ' --os-project-domain-name ' - '(env[OS_PROJECT_DOMAIN_NAME])', - ) - self.make_env(exclude='OS_TENANT_NAME') - try: - self.shell('plugin-list') - except exceptions.CommandError as message: - self.assertEqual(required, message.args) - else: - self.fail('CommandError not raised') - - def test_no_tenant_id(self): - required = ( - 'You must provide a tenant_name, tenant_id, ' - 'project_id or project_name (with ' - 'project_domain_name or project_domain_id) via ' - ' --os-tenant-name (env[OS_TENANT_NAME]),' - ' --os-tenant-id (env[OS_TENANT_ID]),' - ' --os-project-id (env[OS_PROJECT_ID])' - ' --os-project-name (env[OS_PROJECT_NAME]),' - ' --os-project-domain-id ' - '(env[OS_PROJECT_DOMAIN_ID])' - ' --os-project-domain-name ' - '(env[OS_PROJECT_DOMAIN_NAME])', - ) - self.make_env(exclude='OS_TENANT_ID', fake_env=FAKE_ENV2) - try: - self.shell('plugin-list') - except exceptions.CommandError as message: - self.assertEqual(required, message.args) - else: - self.fail('CommandError not raised') - - def test_no_auth_url(self): - required = ('You must provide an auth url' - ' via either --os-auth-url or env[OS_AUTH_URL] or' - ' specify an auth_system which defines a default url' - ' with --os-auth-system or env[OS_AUTH_SYSTEM]',) - self.make_env(exclude='OS_AUTH_URL') - try: - self.shell('plugin-list') - except exceptions.CommandError as message: - self.assertEqual(required, message.args) - else: - self.fail('CommandError not raised') - -# @mock.patch('sys.stdin', side_effect=mock.MagicMock) -# @mock.patch('getpass.getpass', return_value='password') -# def test_password(self, mock_getpass, mock_stdin): - @mock.patch('saharaclient.api.client.Client', FakePluginClient) - def test_password(self): - ex = ( - '+------+----------+---------------+\n' - '| name | versions | title |\n' - '+------+----------+---------------+\n' - '| fake | 1.0 | a fake plugin |\n' - '+------+----------+---------------+\n' - ) -# self.make_env(exclude='OS_PASSWORD') - self.make_env() - stdout, stderr = self.shell('plugin-list') - self.assertEqual(ex, (stdout + stderr)) - -# @mock.patch('sys.stdin', side_effect=mock.MagicMock) -# @mock.patch('getpass.getpass', side_effect=EOFError) -# def test_no_password(self, mock_getpass, mock_stdin): - def test_no_password(self): - required = ('Expecting a password provided' - ' via either --os-password, env[OS_PASSWORD],' - ' or prompted response',) - self.make_env(exclude='OS_PASSWORD') - try: - self.shell('plugin-list') - except exceptions.CommandError as message: - self.assertEqual(required, message.args) - else: - self.fail('CommandError not raised') - -# TODO(mattf) Only one version of API right now -# def _test_service_type(self, version, service_type, mock_client): -# if version is None: -# cmd = 'list' -# else: -# cmd = '--os-compute-api-version %s list' % version -# self.make_env() -# self.shell(cmd) -# _, client_kwargs = mock_client.call_args -# self.assertEqual(service_type, client_kwargs['service_type']) -# -# @mock.patch('novaclient.client.Client') -# def test_default_service_type(self, mock_client): -# self._test_service_type(None, 'compute', mock_client) -# -# @mock.patch('novaclient.client.Client') -# def test_v1_1_service_type(self, mock_client): -# self._test_service_type('1.1', 'compute', mock_client) -# -# @mock.patch('novaclient.client.Client') -# def test_v2_service_type(self, mock_client): -# self._test_service_type('2', 'compute', mock_client) -# -# @mock.patch('novaclient.client.Client') -# def test_v3_service_type(self, mock_client): -# self._test_service_type('3', 'computev3', mock_client) -# -# @mock.patch('novaclient.client.Client') -# def test_v_unknown_service_type(self, mock_client): -# self._test_service_type('unknown', 'compute', mock_client) - - @mock.patch('saharaclient.api.client.Client', FakePluginClient) - def test_image_list(self): - ex = ( - '+------+------------+----------+------+-------------+\n' - '| name | id | username | tags | description |\n' - '+------+------------+----------+------+-------------+\n' - '| fake | aaa-bb-ccc | you | | None |\n' - '+------+------------+----------+------+-------------+\n' - ) - self.make_env() - stdout, stderr = self.shell('image-list') - self.assertEqual(ex, (stdout + stderr)) - - -class ShellTestKeystoneV3(ShellTest): - - FAKE_V3_ENV = {'OS_USERNAME': 'username', - 'OS_PASSWORD': 'password', - 'OS_PROJECT_NAME': 'project_name', - 'OS_PROJECT_DOMAIN_NAME': 'project_domain_name', - 'OS_USER_DOMAIN_NAME': 'user_domain_name', - 'OS_AUTH_URL': 'http://no.where/v3'} - - version_id = u'v3' - links = [{u'href': u'http://no.where/v3', u'rel': u'self'}] - - def make_env(self, exclude=None, fake_env=FAKE_V3_ENV): - if 'OS_AUTH_URL' in fake_env: - fake_env.update({'OS_AUTH_URL': 'http://no.where/v3'}) - env = dict((k, v) for k, v in fake_env.items() if k != exclude) - self.useFixture(fixtures.MonkeyPatch('os.environ', env)) - - def test_no_tenant_name(self): - # In V3, tenant_name = project_name - required = ( - 'You must provide a tenant_name, tenant_id, ' - 'project_id or project_name (with ' - 'project_domain_name or project_domain_id) via ' - ' --os-tenant-name (env[OS_TENANT_NAME]),' - ' --os-tenant-id (env[OS_TENANT_ID]),' - ' --os-project-id (env[OS_PROJECT_ID])' - ' --os-project-name (env[OS_PROJECT_NAME]),' - ' --os-project-domain-id ' - '(env[OS_PROJECT_DOMAIN_ID])' - ' --os-project-domain-name ' - '(env[OS_PROJECT_DOMAIN_NAME])', - ) - self.make_env(exclude='OS_PROJECT_NAME') - try: - self.shell('plugin-list') - except exceptions.CommandError as message: - self.assertEqual(required, message.args) - else: - self.fail('CommandError not raised') - - def test_job_list(self): - expected = '\n'.join([ - '+----+------------+------------+--------+', - '| id | cluster_id | start_time | status |', - '+----+------------+------------+--------+', - '+----+------------+------------+--------+', - '' - ]) - - mock_get_service_type_method_name = ( - 'saharaclient.api.client.Client._determine_service_type') - mock_job_executions_class_name = ( - 'saharaclient.api.job_executions.JobExecutionsManager') - - with mock.patch(mock_get_service_type_method_name) as mock_st: - with mock.patch(mock_job_executions_class_name): - mock_st.return_value = 'data-processing' - self.make_env() - stdout, stderr = self.shell('job-list') - self.assertEqual((stdout + stderr), expected) diff --git a/saharaclient/tests/unit/nova/utils.py b/saharaclient/tests/unit/nova/utils.py deleted file mode 100644 index 5c7acba..0000000 --- a/saharaclient/tests/unit/nova/utils.py +++ /dev/null @@ -1,57 +0,0 @@ -# -# 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 os - -import fixtures -import requests -import testtools - - -class TestCase(testtools.TestCase): - TEST_REQUEST_BASE = { - 'verify': True, - } - - def setUp(self): - super(TestCase, self).setUp() - if (os.environ.get('OS_STDOUT_CAPTURE') == 'True' or - os.environ.get('OS_STDOUT_CAPTURE') == '1'): - stdout = self.useFixture(fixtures.StringStream('stdout')).stream - self.useFixture(fixtures.MonkeyPatch('sys.stdout', stdout)) - if (os.environ.get('OS_STDERR_CAPTURE') == 'True' or - os.environ.get('OS_STDERR_CAPTURE') == '1'): - stderr = self.useFixture(fixtures.StringStream('stderr')).stream - self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr)) - - -class TestResponse(requests.Response): - """Wrap requests.Response and provide a way to initialize with a dict.""" - - def __init__(self, data): - self._text = None - super(TestResponse, self) - if isinstance(data, dict): - self.status_code = data.get('status_code', None) - self.headers = data.get('headers', None) - # Fake the text attribute to streamline Response creation - self._text = data.get('text', None) - else: - self.status_code = data - - def __eq__(self, other): - return self.__dict__ == other.__dict__ - - @property - def text(self): - return self._text @@ -27,9 +27,6 @@ packages = saharaclient [entry_points] -console_scripts = - sahara = saharaclient.shell:main - openstack.cli.extension = data_processing = saharaclient.osc.plugin @@ -62,7 +62,4 @@ commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasen [flake8] show-source = true builtins = _ -exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,tools - -[hacking] -import_exceptions = saharaclient.openstack.common._i18n._ +exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,tools |