summaryrefslogtreecommitdiff
path: root/openstackclient
diff options
context:
space:
mode:
Diffstat (limited to 'openstackclient')
-rw-r--r--openstackclient/common/clientmanager.py73
-rw-r--r--openstackclient/common/restapi.py320
-rw-r--r--openstackclient/common/timing.py44
-rw-r--r--openstackclient/common/utils.py28
-rw-r--r--openstackclient/compute/client.py4
-rw-r--r--openstackclient/compute/v2/console.py8
-rw-r--r--openstackclient/compute/v2/flavor.py23
-rw-r--r--openstackclient/compute/v2/floatingip.py14
-rw-r--r--openstackclient/compute/v2/floatingippool.py2
-rw-r--r--openstackclient/compute/v2/hypervisor.py10
-rw-r--r--openstackclient/compute/v2/keypair.py13
-rw-r--r--openstackclient/compute/v2/security_group.py12
-rw-r--r--openstackclient/compute/v2/server.py38
-rw-r--r--openstackclient/i18n.py31
-rw-r--r--openstackclient/identity/client.py53
-rw-r--r--openstackclient/identity/v2_0/token.py5
-rw-r--r--openstackclient/identity/v2_0/user.py7
-rw-r--r--openstackclient/identity/v3/domain.py11
-rw-r--r--openstackclient/identity/v3/endpoint.py60
-rw-r--r--openstackclient/identity/v3/identity_provider.py37
-rw-r--r--openstackclient/identity/v3/user.py29
-rw-r--r--openstackclient/image/v1/image.py12
-rw-r--r--openstackclient/image/v2/image.py14
-rw-r--r--openstackclient/network/v2/network.py35
-rw-r--r--openstackclient/object/client.py7
-rw-r--r--openstackclient/object/v1/container.py90
-rw-r--r--openstackclient/object/v1/lib/container.py97
-rw-r--r--openstackclient/object/v1/lib/object.py110
-rw-r--r--openstackclient/object/v1/object.py115
-rw-r--r--openstackclient/openstack/common/gettextutils.py67
-rw-r--r--openstackclient/openstack/common/importutils.py73
-rw-r--r--openstackclient/openstack/common/strutils.py72
-rw-r--r--openstackclient/shell.py94
-rw-r--r--openstackclient/tests/common/test_clientmanager.py102
-rw-r--r--openstackclient/tests/common/test_restapi.py341
-rw-r--r--openstackclient/tests/common/test_timing.py87
-rw-r--r--openstackclient/tests/common/test_utils.py12
-rw-r--r--openstackclient/tests/compute/v2/fakes.py14
-rw-r--r--openstackclient/tests/compute/v2/test_server.py135
-rw-r--r--openstackclient/tests/fakes.py17
-rw-r--r--openstackclient/tests/identity/v2_0/fakes.py1
-rw-r--r--openstackclient/tests/identity/v2_0/test_token.py7
-rw-r--r--openstackclient/tests/identity/v3/fakes.py29
-rw-r--r--openstackclient/tests/identity/v3/test_domain.py414
-rw-r--r--openstackclient/tests/identity/v3/test_endpoint.py671
-rw-r--r--openstackclient/tests/identity/v3/test_identity_provider.py14
-rw-r--r--openstackclient/tests/identity/v3/test_role.py2
-rw-r--r--openstackclient/tests/identity/v3/test_user.py48
-rw-r--r--openstackclient/tests/image/v1/test_image.py45
-rw-r--r--openstackclient/tests/image/v2/test_image.py47
-rw-r--r--openstackclient/tests/network/v2/test_network.py8
-rw-r--r--openstackclient/tests/object/v1/lib/test_container.py72
-rw-r--r--openstackclient/tests/object/v1/lib/test_object.py103
-rw-r--r--openstackclient/tests/object/v1/test_container.py16
-rw-r--r--openstackclient/tests/object/v1/test_object.py18
-rw-r--r--openstackclient/tests/utils.py8
-rw-r--r--openstackclient/tests/volume/test_find_resource.py1
-rw-r--r--openstackclient/tests/volume/v1/test_volume.py20
-rw-r--r--openstackclient/volume/v1/volume.py22
59 files changed, 2766 insertions, 1096 deletions
diff --git a/openstackclient/common/clientmanager.py b/openstackclient/common/clientmanager.py
index b310f3ac..4206ad00 100644
--- a/openstackclient/common/clientmanager.py
+++ b/openstackclient/common/clientmanager.py
@@ -19,6 +19,9 @@ import logging
import pkg_resources
import sys
+from keystoneclient.auth.identity import v2 as v2_auth
+from keystoneclient.auth.identity import v3 as v3_auth
+from keystoneclient import session
from openstackclient.identity import client as identity_client
@@ -49,7 +52,7 @@ class ClientManager(object):
user_domain_id=None, user_domain_name=None,
project_domain_id=None, project_domain_name=None,
region_name=None, api_version=None, verify=True,
- trust_id=None):
+ trust_id=None, timing=None):
self._token = token
self._url = url
self._auth_url = auth_url
@@ -67,6 +70,7 @@ class ClientManager(object):
self._api_version = api_version
self._trust_id = trust_id
self._service_catalog = None
+ self.timing = timing
# verify is the Requests-compatible form
self._verify = verify
@@ -76,15 +80,70 @@ class ClientManager(object):
self._insecure = not verify
else:
self._cacert = verify
- self._insecure = True
+ self._insecure = False
+
+ ver_prefix = identity_client.AUTH_VERSIONS[
+ self._api_version[identity_client.API_NAME]
+ ]
+
+ # Get logging from root logger
+ root_logger = logging.getLogger('')
+ LOG.setLevel(root_logger.getEffectiveLevel())
+
+ # NOTE(dtroyer): These plugins are hard-coded for the first step
+ # in using the new Keystone auth plugins.
+
+ if self._url:
+ LOG.debug('Using token auth %s', ver_prefix)
+ if ver_prefix == 'v2':
+ self.auth = v2_auth.Token(
+ auth_url=url,
+ token=token,
+ )
+ else:
+ self.auth = v3_auth.Token(
+ auth_url=url,
+ token=token,
+ )
+ else:
+ LOG.debug('Using password auth %s', ver_prefix)
+ if ver_prefix == 'v2':
+ self.auth = v2_auth.Password(
+ auth_url=auth_url,
+ username=username,
+ password=password,
+ trust_id=trust_id,
+ tenant_id=project_id,
+ tenant_name=project_name,
+ )
+ else:
+ self.auth = v3_auth.Password(
+ auth_url=auth_url,
+ username=username,
+ password=password,
+ trust_id=trust_id,
+ user_domain_id=user_domain_id,
+ user_domain_name=user_domain_name,
+ domain_id=domain_id,
+ domain_name=domain_name,
+ project_id=project_id,
+ project_name=project_name,
+ project_domain_id=project_domain_id,
+ project_domain_name=project_domain_name,
+ )
+
+ self.session = session.Session(
+ auth=self.auth,
+ verify=verify,
+ )
self.auth_ref = None
-
if not self._url:
+ # Trigger the auth call
+ self.auth_ref = self.session.auth.get_auth_ref(self.session)
# Populate other password flow attributes
- self.auth_ref = self.identity.auth_ref
- self._token = self.identity.auth_token
- self._service_catalog = self.identity.service_catalog
+ self._token = self.session.auth.get_token(self.session)
+ self._service_catalog = self.auth_ref.service_catalog
return
@@ -116,7 +175,7 @@ def get_extension_modules(group):
setattr(
ClientManager,
- ep.name,
+ module.API_NAME,
ClientCache(
getattr(sys.modules[ep.module_name], 'make_client', None)
),
diff --git a/openstackclient/common/restapi.py b/openstackclient/common/restapi.py
deleted file mode 100644
index f20ad23d..00000000
--- a/openstackclient/common/restapi.py
+++ /dev/null
@@ -1,320 +0,0 @@
-# Copyright 2013 Nebula 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.
-#
-
-"""REST API bits"""
-
-import json
-import logging
-import requests
-
-try:
- from urllib.parse import urlencode
-except ImportError:
- from urllib import urlencode
-
-
-USER_AGENT = 'RAPI'
-
-_logger = logging.getLogger(__name__)
-
-
-class RESTApi(object):
- """A REST API client that handles the interface from us to the server
-
- RESTApi is requests.Session wrapper that knows how to do:
- * JSON serialization/deserialization
- * log requests in 'curl' format
- * basic API boilerplate for create/delete/list/set/show verbs
-
- * authentication is handled elsewhere and a token is passed in
-
- The expectation that there will be a RESTApi object per authentication
- token in use, i.e. project/username/auth_endpoint
-
- On the other hand, a Client knows details about the specific REST Api that
- it communicates with, such as the available endpoints, API versions, etc.
- """
-
- def __init__(
- self,
- session=None,
- auth_header=None,
- user_agent=USER_AGENT,
- verify=True,
- logger=None,
- debug=None,
- ):
- """Construct a new REST client
-
- :param object session: A Session object to be used for
- communicating with the identity service.
- :param string auth_header: A token from an initialized auth_reference
- to be used in the X-Auth-Token header
- :param string user_agent: Set the User-Agent header in the requests
- :param boolean/string verify: If ``True``, the SSL cert will be
- verified. A CA_BUNDLE path can also be
- provided.
- :param logging.Logger logger: A logger to output to. (optional)
- :param boolean debug: Enables debug logging of all request and
- responses to identity service.
- default False (optional)
- """
-
- self.set_auth(auth_header)
- self.debug = debug
-
- if not session:
- # We create a default session object
- session = requests.Session()
- self.session = session
- self.session.verify = verify
- self.session.user_agent = user_agent
-
- if logger:
- self.logger = logger
- else:
- self.logger = _logger
-
- def set_auth(self, auth_header):
- """Sets the current auth blob"""
- self.auth_header = auth_header
-
- def set_header(self, header, content):
- """Sets passed in headers into the session headers
-
- Replaces existing headers!!
- """
- if content is None:
- del self.session.headers[header]
- else:
- self.session.headers[header] = content
-
- def request(self, method, url, **kwargs):
- """Make an authenticated (if token available) request
-
- :param method: Request HTTP method
- :param url: Request URL
- :param data: Request body
- :param json: Request body to be encoded as JSON
- Overwrites ``data`` argument if present
- """
-
- kwargs.setdefault('headers', {})
- if self.auth_header:
- kwargs['headers']['X-Auth-Token'] = self.auth_header
-
- if 'json' in kwargs and isinstance(kwargs['json'], type({})):
- kwargs['data'] = json.dumps(kwargs.pop('json'))
- kwargs['headers']['Content-Type'] = 'application/json'
-
- kwargs.setdefault('allow_redirects', True)
-
- if self.debug:
- self._log_request(method, url, **kwargs)
-
- response = self.session.request(method, url, **kwargs)
-
- if self.debug:
- self._log_response(response)
-
- return self._error_handler(response)
-
- def _error_handler(self, response):
- if response.status_code < 200 or response.status_code >= 300:
- self.logger.debug(
- "ERROR: %s",
- response.text,
- )
- response.raise_for_status()
- return response
-
- # Convenience methods to mimic the ones provided by requests.Session
-
- def delete(self, url, **kwargs):
- """Send a DELETE request. Returns :class:`requests.Response` object.
-
- :param url: Request URL
- :param \*\*kwargs: Optional arguments passed to ``request``
- """
-
- return self.request('DELETE', url, **kwargs)
-
- def get(self, url, **kwargs):
- """Send a GET request. Returns :class:`requests.Response` object.
-
- :param url: Request URL
- :param \*\*kwargs: Optional arguments passed to ``request``
- """
-
- return self.request('GET', url, **kwargs)
-
- def head(self, url, **kwargs):
- """Send a HEAD request. Returns :class:`requests.Response` object.
-
- :param url: Request URL
- :param \*\*kwargs: Optional arguments passed to ``request``
- """
-
- kwargs.setdefault('allow_redirects', False)
- return self.request('HEAD', url, **kwargs)
-
- def options(self, url, **kwargs):
- """Send an OPTIONS request. Returns :class:`requests.Response` object.
-
- :param url: Request URL
- :param \*\*kwargs: Optional arguments passed to ``request``
- """
-
- return self.request('OPTIONS', url, **kwargs)
-
- def patch(self, url, data=None, json=None, **kwargs):
- """Send a PUT request. Returns :class:`requests.Response` object.
-
- :param url: Request URL
- :param data: Request body
- :param json: Request body to be encoded as JSON
- Overwrites ``data`` argument if present
- :param \*\*kwargs: Optional arguments passed to ``request``
- """
-
- return self.request('PATCH', url, data=data, json=json, **kwargs)
-
- def post(self, url, data=None, json=None, **kwargs):
- """Send a POST request. Returns :class:`requests.Response` object.
-
- :param url: Request URL
- :param data: Request body
- :param json: Request body to be encoded as JSON
- Overwrites ``data`` argument if present
- :param \*\*kwargs: Optional arguments passed to ``request``
- """
-
- return self.request('POST', url, data=data, json=json, **kwargs)
-
- def put(self, url, data=None, json=None, **kwargs):
- """Send a PUT request. Returns :class:`requests.Response` object.
-
- :param url: Request URL
- :param data: Request body
- :param json: Request body to be encoded as JSON
- Overwrites ``data`` argument if present
- :param \*\*kwargs: Optional arguments passed to ``request``
- """
-
- return self.request('PUT', url, data=data, json=json, **kwargs)
-
- # Command verb methods
-
- def create(self, url, data=None, response_key=None, **kwargs):
- """Create a new object via a POST request
-
- :param url: Request URL
- :param data: Request body, wil be JSON encoded
- :param response_key: Dict key in response body to extract
- :param \*\*kwargs: Optional arguments passed to ``request``
- """
-
- response = self.request('POST', url, json=data, **kwargs)
- if response_key:
- return response.json()[response_key]
- else:
- return response.json()
-
- def list(self, url, data=None, response_key=None, **kwargs):
- """Retrieve a list of objects via a GET or POST request
-
- :param url: Request URL
- :param data: Request body, will be JSON encoded
- :param response_key: Dict key in response body to extract
- :param \*\*kwargs: Optional arguments passed to ``request``
- """
-
- if data:
- response = self.request('POST', url, json=data, **kwargs)
- else:
- response = self.request('GET', url, **kwargs)
-
- if response_key:
- return response.json()[response_key]
- else:
- return response.json()
-
- def set(self, url, data=None, response_key=None, **kwargs):
- """Update an object via a PUT request
-
- :param url: Request URL
- :param data: Request body
- :param json: Request body to be encoded as JSON
- Overwrites ``data`` argument if present
- :param \*\*kwargs: Optional arguments passed to ``request``
- """
-
- response = self.request('PUT', url, json=data)
- if data:
- if response_key:
- return response.json()[response_key]
- else:
- return response.json()
- else:
- # Nothing to do here
- return None
-
- def show(self, url, response_key=None, **kwargs):
- """Retrieve a single object via a GET request
-
- :param url: Request URL
- :param response_key: Dict key in response body to extract
- :param \*\*kwargs: Optional arguments passed to ``request``
- """
-
- response = self.request('GET', url, **kwargs)
- if response_key:
- return response.json()[response_key]
- else:
- return response.json()
-
- def _log_request(self, method, url, **kwargs):
- if 'params' in kwargs and kwargs['params'] != {}:
- url += '?' + urlencode(kwargs['params'])
-
- string_parts = [
- "curl -i",
- "-X '%s'" % method,
- "'%s'" % url,
- ]
-
- for element in kwargs['headers']:
- header = " -H '%s: %s'" % (element, kwargs['headers'][element])
- string_parts.append(header)
-
- self.logger.debug("REQ: %s" % " ".join(string_parts))
- if 'data' in kwargs:
- self.logger.debug(" REQ BODY: %r\n" % (kwargs['data']))
-
- def _log_response(self, response):
- self.logger.debug(
- "RESP: [%s] %r\n",
- response.status_code,
- response.headers,
- )
- if response._content_consumed:
- self.logger.debug(
- " RESP BODY: %s\n",
- response.text,
- )
- self.logger.debug(
- " encoding: %s",
- response.encoding,
- )
diff --git a/openstackclient/common/timing.py b/openstackclient/common/timing.py
new file mode 100644
index 00000000..1c94682c
--- /dev/null
+++ b/openstackclient/common/timing.py
@@ -0,0 +1,44 @@
+# 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.
+#
+
+"""Timing Implementation"""
+
+import logging
+
+from cliff import lister
+
+
+class Timing(lister.Lister):
+ """Show timing data"""
+
+ log = logging.getLogger(__name__ + '.Timing')
+
+ def take_action(self, parsed_args):
+ self.log.debug('take_action(%s)' % parsed_args)
+
+ column_headers = (
+ 'URL',
+ 'Seconds',
+ )
+
+ results = []
+ total = 0.0
+ for url, start, end in self.app.timing_data:
+ seconds = end - start
+ total += seconds
+ results.append((url, seconds))
+ results.append(('Total', total))
+ return (
+ column_headers,
+ results,
+ )
diff --git a/openstackclient/common/utils.py b/openstackclient/common/utils.py
index 51c3ed4b..c013deee 100644
--- a/openstackclient/common/utils.py
+++ b/openstackclient/common/utils.py
@@ -19,10 +19,10 @@ import getpass
import logging
import os
import six
-import sys
import time
from openstackclient.common import exceptions
+from openstackclient.openstack.common import importutils
def find_resource(manager, name_or_id):
@@ -77,7 +77,7 @@ def format_dict(data):
"""
output = ""
- for s in data:
+ for s in sorted(data):
output = output + s + "='" + six.text_type(data[s]) + "', "
return output[:-2]
@@ -89,7 +89,7 @@ def format_list(data):
:rtype: a string formatted to a,b,c
"""
- return ', '.join(data)
+ return ', '.join(sorted(data))
def get_item_properties(item, fields, mixed_case_fields=[], formatters={}):
@@ -157,17 +157,6 @@ def env(*vars, **kwargs):
return kwargs.get('default', '')
-def import_class(import_str):
- """Returns a class from a string including module and class
-
- :param import_str: a string representation of the class name
- :rtype: the requested class
- """
- mod_str, _sep, class_str = import_str.rpartition('.')
- __import__(mod_str)
- return getattr(sys.modules[mod_str], class_str)
-
-
def get_client_class(api_name, version, version_map):
"""Returns the client class for the requested API version
@@ -183,7 +172,7 @@ def get_client_class(api_name, version, version_map):
(api_name, version, ', '.join(version_map.keys())))
raise exceptions.UnsupportedVersion(msg)
- return import_class(client_path)
+ return importutils.import_class(client_path)
def wait_for_status(status_f,
@@ -231,12 +220,15 @@ def get_effective_log_level():
return min_log_lvl
-def get_password(stdin):
+def get_password(stdin, prompt=None, confirm=True):
+ message = prompt or "User Password:"
if hasattr(stdin, 'isatty') and stdin.isatty():
try:
while True:
- first_pass = getpass.getpass("User password: ")
- second_pass = getpass.getpass("Repeat user password: ")
+ first_pass = getpass.getpass(message)
+ if not confirm:
+ return first_pass
+ second_pass = getpass.getpass("Repeat " + message)
if first_pass == second_pass:
return first_pass
print("The passwords entered were not the same")
diff --git a/openstackclient/compute/client.py b/openstackclient/compute/client.py
index 36391c6d..dc50507e 100644
--- a/openstackclient/compute/client.py
+++ b/openstackclient/compute/client.py
@@ -57,7 +57,9 @@ def make_client(instance):
service_type=API_NAME,
# FIXME(dhellmann): what is service_name?
service_name='',
- http_log_debug=http_log_debug)
+ http_log_debug=http_log_debug,
+ timings=instance.timing,
+ )
# Populate the Nova client to skip another auth query to Identity
if instance._url:
diff --git a/openstackclient/compute/v2/console.py b/openstackclient/compute/v2/console.py
index e1f84e23..8206f302 100644
--- a/openstackclient/compute/v2/console.py
+++ b/openstackclient/compute/v2/console.py
@@ -26,7 +26,7 @@ from openstackclient.common import utils
class ShowConsoleLog(command.Command):
- """Show console-log command"""
+ """Show server's console output"""
log = logging.getLogger(__name__ + '.ShowConsoleLog')
@@ -35,7 +35,7 @@ class ShowConsoleLog(command.Command):
parser.add_argument(
'server',
metavar='<server>',
- help='Name or ID of server to display console log',
+ help='Server (name or ID)',
)
parser.add_argument(
'--lines',
@@ -67,7 +67,7 @@ class ShowConsoleLog(command.Command):
class ShowConsoleURL(show.ShowOne):
- """Show console-url command"""
+ """Show server's remote console URL"""
log = logging.getLogger(__name__ + '.ShowConsoleURL')
@@ -76,7 +76,7 @@ class ShowConsoleURL(show.ShowOne):
parser.add_argument(
'server',
metavar='<server>',
- help='Name or ID of server to display console log',
+ help='Server (name or ID)',
)
type_group = parser.add_mutually_exclusive_group()
type_group.add_argument(
diff --git a/openstackclient/compute/v2/flavor.py b/openstackclient/compute/v2/flavor.py
index 73429796..6dd00b1b 100644
--- a/openstackclient/compute/v2/flavor.py
+++ b/openstackclient/compute/v2/flavor.py
@@ -26,7 +26,7 @@ from openstackclient.common import utils
class CreateFlavor(show.ShowOne):
- """Create flavor command"""
+ """Create new flavor"""
log = logging.getLogger(__name__ + ".CreateFlavor")
@@ -35,7 +35,8 @@ class CreateFlavor(show.ShowOne):
parser.add_argument(
"name",
metavar="<name>",
- help="Name of the new flavor")
+ help="New flavor name",
+ )
parser.add_argument(
"--id",
metavar="<id>",
@@ -84,12 +85,14 @@ class CreateFlavor(show.ShowOne):
dest="public",
action="store_true",
default=True,
- help="Flavor is accessible to the public (default)")
+ help="Flavor is accessible to other projects (default)",
+ )
public_group.add_argument(
"--private",
dest="public",
action="store_false",
- help="Flavor is inaccessible to the public")
+ help="Flavor is inaccessible to other projects",
+ )
return parser
def take_action(self, parsed_args):
@@ -115,7 +118,7 @@ class CreateFlavor(show.ShowOne):
class DeleteFlavor(command.Command):
- """Delete flavor command"""
+ """Delete a flavor"""
log = logging.getLogger(__name__ + ".DeleteFlavor")
@@ -124,7 +127,8 @@ class DeleteFlavor(command.Command):
parser.add_argument(
"flavor",
metavar="<flavor>",
- help="Name or ID of flavor to delete")
+ help="Flavor to delete (name or ID)",
+ )
return parser
def take_action(self, parsed_args):
@@ -137,7 +141,7 @@ class DeleteFlavor(command.Command):
class ListFlavor(lister.Lister):
- """List flavor command"""
+ """List flavors"""
log = logging.getLogger(__name__ + ".ListFlavor")
@@ -164,7 +168,7 @@ class ListFlavor(lister.Lister):
class ShowFlavor(show.ShowOne):
- """Show flavor command"""
+ """Show flavor details"""
log = logging.getLogger(__name__ + ".ShowFlavor")
@@ -173,7 +177,8 @@ class ShowFlavor(show.ShowOne):
parser.add_argument(
"flavor",
metavar="<flavor>",
- help="Name or ID of flavor to display")
+ help="Flavor to display (name or ID)",
+ )
return parser
def take_action(self, parsed_args):
diff --git a/openstackclient/compute/v2/floatingip.py b/openstackclient/compute/v2/floatingip.py
index 72b19c6c..658f0d5a 100644
--- a/openstackclient/compute/v2/floatingip.py
+++ b/openstackclient/compute/v2/floatingip.py
@@ -26,7 +26,7 @@ from openstackclient.common import utils
class AddFloatingIP(command.Command):
- """Add floating-ip command"""
+ """Add floating-ip to server"""
log = logging.getLogger(__name__ + ".AddFloatingIP")
@@ -40,7 +40,7 @@ class AddFloatingIP(command.Command):
parser.add_argument(
"server",
metavar="<server>",
- help="Name of the server to receive the IP address",
+ help="Server to receive the IP address (name or ID)",
)
return parser
@@ -56,7 +56,7 @@ class AddFloatingIP(command.Command):
class CreateFloatingIP(show.ShowOne):
- """Create floating-ip command"""
+ """Create new floating-ip"""
log = logging.getLogger(__name__ + '.CreateFloatingIP')
@@ -80,7 +80,7 @@ class CreateFloatingIP(show.ShowOne):
class DeleteFloatingIP(command.Command):
- """Delete floating-ip command"""
+ """Delete a floating-ip"""
log = logging.getLogger(__name__ + '.DeleteFloatingIP')
@@ -107,7 +107,7 @@ class DeleteFloatingIP(command.Command):
class ListFloatingIP(lister.Lister):
- """List floating-ip command"""
+ """List floating-ips"""
log = logging.getLogger(__name__ + '.ListFloatingIP')
@@ -127,7 +127,7 @@ class ListFloatingIP(lister.Lister):
class RemoveFloatingIP(command.Command):
- """Remove floating-ip command"""
+ """Remove floating-ip from server"""
log = logging.getLogger(__name__ + ".RemoveFloatingIP")
@@ -141,7 +141,7 @@ class RemoveFloatingIP(command.Command):
parser.add_argument(
"server",
metavar="<server>",
- help="Name of the server to remove the IP address from",
+ help="Server to remove the IP address from (name or ID)",
)
return parser
diff --git a/openstackclient/compute/v2/floatingippool.py b/openstackclient/compute/v2/floatingippool.py
index d5e8d0dd..db1c9f0f 100644
--- a/openstackclient/compute/v2/floatingippool.py
+++ b/openstackclient/compute/v2/floatingippool.py
@@ -23,7 +23,7 @@ from openstackclient.common import utils
class ListFloatingIPPool(lister.Lister):
- """List floating-ip-pool command"""
+ """List floating-ip-pools"""
log = logging.getLogger(__name__ + '.ListFloatingIPPool')
diff --git a/openstackclient/compute/v2/hypervisor.py b/openstackclient/compute/v2/hypervisor.py
index 334987e2..e01258d1 100644
--- a/openstackclient/compute/v2/hypervisor.py
+++ b/openstackclient/compute/v2/hypervisor.py
@@ -25,7 +25,7 @@ from openstackclient.common import utils
class ListHypervisor(lister.Lister):
- """List hypervisor command"""
+ """List hypervisors"""
log = logging.getLogger(__name__ + ".ListHypervisor")
@@ -33,9 +33,9 @@ class ListHypervisor(lister.Lister):
parser = super(ListHypervisor, self).get_parser(prog_name)
parser.add_argument(
"--matching",
- metavar="<hostname>",
- help="List hypervisors with hostnames matching the given"
- " substring")
+ metavar="<hostname-str>",
+ help="Filter hypervisors using <hostname-str> substring",
+ )
return parser
def take_action(self, parsed_args):
@@ -58,7 +58,7 @@ class ListHypervisor(lister.Lister):
class ShowHypervisor(show.ShowOne):
- """Show hypervisor command"""
+ """Show hypervisor details"""
log = logging.getLogger(__name__ + ".ShowHypervisor")
diff --git a/openstackclient/compute/v2/keypair.py b/openstackclient/compute/v2/keypair.py
index 972443a4..22c07ef7 100644
--- a/openstackclient/compute/v2/keypair.py
+++ b/openstackclient/compute/v2/keypair.py
@@ -29,7 +29,7 @@ from openstackclient.common import utils
class CreateKeypair(show.ShowOne):
- """Create keypair command"""
+ """Create new keypair"""
log = logging.getLogger(__name__ + '.CreateKeypair')
@@ -57,8 +57,9 @@ class CreateKeypair(show.ShowOne):
with open(os.path.expanduser(parsed_args.public_key)) as p:
public_key = p.read()
except IOError as e:
- raise exceptions.CommandError(
- "Key file %s not found: %s" % (parsed_args.public_key, e))
+ msg = "Key file %s not found: %s"
+ raise exceptions.CommandError(msg
+ % (parsed_args.public_key, e))
keypair = compute_client.keypairs.create(
parsed_args.name,
@@ -79,7 +80,7 @@ class CreateKeypair(show.ShowOne):
class DeleteKeypair(command.Command):
- """Delete keypair command"""
+ """Delete a keypair"""
log = logging.getLogger(__name__ + '.DeleteKeypair')
@@ -100,7 +101,7 @@ class DeleteKeypair(command.Command):
class ListKeypair(lister.Lister):
- """List keypair command"""
+ """List keypairs"""
log = logging.getLogger(__name__ + ".ListKeypair")
@@ -120,7 +121,7 @@ class ListKeypair(lister.Lister):
class ShowKeypair(show.ShowOne):
- """Show keypair command"""
+ """Show keypair details"""
log = logging.getLogger(__name__ + '.ShowKeypair')
diff --git a/openstackclient/compute/v2/security_group.py b/openstackclient/compute/v2/security_group.py
index 0ba55c98..cd330857 100644
--- a/openstackclient/compute/v2/security_group.py
+++ b/openstackclient/compute/v2/security_group.py
@@ -23,6 +23,7 @@ from cliff import command
from cliff import lister
from cliff import show
+from keystoneclient.openstack.common.apiclient import exceptions as ksc_exc
from novaclient.v1_1 import security_group_rules
from openstackclient.common import parseractions
from openstackclient.common import utils
@@ -150,10 +151,15 @@ class ListSecurityGroup(lister.Lister):
search = {'all_tenants': parsed_args.all_projects}
data = compute_client.security_groups.list(search_opts=search)
- projects = self.app.client_manager.identity.projects.list()
project_hash = {}
- for project in projects:
- project_hash[project.id] = project
+ try:
+ projects = self.app.client_manager.identity.projects.list()
+ except ksc_exc.Forbidden:
+ # This fails when the user is not an admin, just move along
+ pass
+ else:
+ for project in projects:
+ project_hash[project.id] = project
return (column_headers,
(utils.get_item_properties(
diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py
index 2dcc7ae9..ec7f212d 100644
--- a/openstackclient/compute/v2/server.py
+++ b/openstackclient/compute/v2/server.py
@@ -175,7 +175,7 @@ class AddServerSecurityGroup(command.Command):
parsed_args.group,
)
- server.add_security_group(security_group)
+ server.add_security_group(security_group.name)
return
@@ -300,19 +300,22 @@ class CreateServer(show.ShowOne):
raise exceptions.CommandError("Can't open '%s': %s" % (src, e))
if parsed_args.min > parsed_args.max:
- raise exceptions.CommandError("min instances should be <= "
- "max instances")
+ msg = "min instances should be <= max instances"
+ raise exceptions.CommandError(msg)
if parsed_args.min < 1:
- raise exceptions.CommandError("min instances should be > 0")
+ msg = "min instances should be > 0"
+ raise exceptions.CommandError(msg)
if parsed_args.max < 1:
- raise exceptions.CommandError("max instances should be > 0")
+ msg = "max instances should be > 0"
+ raise exceptions.CommandError(msg)
userdata = None
if parsed_args.user_data:
try:
userdata = open(parsed_args.user_data)
except IOError as e:
- raise exceptions.CommandError("Can't open '%s': %s" %
+ msg = "Can't open '%s': %s"
+ raise exceptions.CommandError(msg %
(parsed_args.user_data, e))
block_device_mapping = dict(v.split('=', 1)
@@ -938,27 +941,32 @@ class RescueServer(show.ShowOne):
class ResizeServer(command.Command):
- """Convert server to a new flavor"""
+ """Scale server to a new flavor"""
log = logging.getLogger(__name__ + '.ResizeServer')
def get_parser(self, prog_name):
parser = super(ResizeServer, self).get_parser(prog_name)
phase_group = parser.add_mutually_exclusive_group()
+ parser.add_argument(
+ 'server',
+ metavar='<server>',
+ help='Server (name or ID)',
+ )
phase_group.add_argument(
'--flavor',
metavar='<flavor>',
- help='Resize server to this flavor',
+ help='Resize server to specified flavor',
)
phase_group.add_argument(
'--verify',
action="store_true",
- help='Verify previous server resize',
+ help='Verify server resize is complete',
)
phase_group.add_argument(
'--revert',
action="store_true",
- help='Restore server before resize',
+ help='Restore server state before resize',
)
parser.add_argument(
'--wait',
@@ -980,7 +988,7 @@ class ResizeServer(command.Command):
compute_client.flavors,
parsed_args.flavor,
)
- server.resize(flavor)
+ compute_client.servers.resize(server, flavor)
if parsed_args.wait:
if utils.wait_for_status(
compute_client.servers.get,
@@ -993,9 +1001,9 @@ class ResizeServer(command.Command):
sys.stdout.write('\nError resizing server')
raise SystemExit
elif parsed_args.verify:
- server.confirm_resize()
+ compute_client.servers.confirm_resize(server)
elif parsed_args.revert:
- server.revert_resize()
+ compute_client.servers.revert_resize(server)
class ResumeServer(command.Command):
@@ -1077,8 +1085,8 @@ class SetServer(command.Command):
if p1 == p2:
server.change_password(p1)
else:
- raise exceptions.CommandError(
- "Passwords do not match, password unchanged")
+ msg = "Passwords do not match, password unchanged"
+ raise exceptions.CommandError(msg)
class ShowServer(show.ShowOne):
diff --git a/openstackclient/i18n.py b/openstackclient/i18n.py
new file mode 100644
index 00000000..bd52d648
--- /dev/null
+++ b/openstackclient/i18n.py
@@ -0,0 +1,31 @@
+# Copyright 2012-2013 OpenStack Foundation
+#
+# 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.
+#
+
+from oslo import i18n
+
+_translators = i18n.TranslatorFactory(domain='openstackclient')
+
+# The primary translation function using the well-known name "_"
+_ = _translators.primary
+
+# Translators for log levels.
+#
+# The abbreviated names are meant to reflect the usual use of a short
+# name like '_'. The "L" is for "log" and the other letter comes from
+# the level.
+_LI = _translators.log_info
+_LW = _translators.log_warning
+_LE = _translators.log_error
+_LC = _translators.log_critical
diff --git a/openstackclient/identity/client.py b/openstackclient/identity/client.py
index 72e8bfae..a43b50e3 100644
--- a/openstackclient/identity/client.py
+++ b/openstackclient/identity/client.py
@@ -29,6 +29,12 @@ API_VERSIONS = {
'3': 'keystoneclient.v3.client.Client',
}
+# Translate our API version to auth plugin version prefix
+AUTH_VERSIONS = {
+ '2.0': 'v2',
+ '3': 'v3',
+}
+
def make_client(instance):
"""Returns an identity service client."""
@@ -36,8 +42,10 @@ def make_client(instance):
API_NAME,
instance._api_version[API_NAME],
API_VERSIONS)
- LOG.debug('Instantiating identity client: %s' % identity_client)
+ LOG.debug('Instantiating identity client: %s', identity_client)
+ # TODO(dtroyer): Something doesn't like the session.auth when using
+ # token auth, chase that down.
if instance._url:
LOG.debug('Using token auth')
client = identity_client(
@@ -50,26 +58,39 @@ def make_client(instance):
else:
LOG.debug('Using password auth')
client = identity_client(
- username=instance._username,
- password=instance._password,
- user_domain_id=instance._user_domain_id,
- user_domain_name=instance._user_domain_name,
- project_domain_id=instance._project_domain_id,
- project_domain_name=instance._project_domain_name,
- domain_id=instance._domain_id,
- domain_name=instance._domain_name,
- tenant_name=instance._project_name,
- tenant_id=instance._project_id,
- auth_url=instance._auth_url,
- region_name=instance._region_name,
+ session=instance.session,
cacert=instance._cacert,
- insecure=instance._insecure,
- trust_id=instance._trust_id,
)
- instance.auth_ref = client.auth_ref
+
+ # TODO(dtroyer): the identity v2 role commands use this yet, fix that
+ # so we can remove it
+ if not instance._url:
+ instance.auth_ref = instance.auth.get_auth_ref(instance.session)
+
return client
+def build_option_parser(parser):
+ """Hook to add global options"""
+ parser.add_argument(
+ '--os-identity-api-version',
+ metavar='<identity-api-version>',
+ default=utils.env(
+ 'OS_IDENTITY_API_VERSION',
+ default=DEFAULT_IDENTITY_API_VERSION),
+ help='Identity API version, default=' +
+ DEFAULT_IDENTITY_API_VERSION +
+ ' (Env: OS_IDENTITY_API_VERSION)')
+ parser.add_argument(
+ '--os-trust-id',
+ metavar='<trust-id>',
+ default=utils.env('OS_TRUST_ID'),
+ help='Trust ID to use when authenticating. '
+ 'This can only be used with Keystone v3 API '
+ '(Env: OS_TRUST_ID)')
+ return parser
+
+
class IdentityClientv2_0(identity_client_v2_0.Client):
"""Tweak the earlier client class to deal with some changes"""
def __getattr__(self, name):
diff --git a/openstackclient/identity/v2_0/token.py b/openstackclient/identity/v2_0/token.py
index 01e1b3b2..f3fedc01 100644
--- a/openstackclient/identity/v2_0/token.py
+++ b/openstackclient/identity/v2_0/token.py
@@ -33,15 +33,14 @@ class IssueToken(show.ShowOne):
def take_action(self, parsed_args):
self.log.debug('take_action(%s)', parsed_args)
- identity_client = self.app.client_manager.identity
- token = identity_client.service_catalog.get_token()
+ token = self.app.client_manager.auth_ref.service_catalog.get_token()
token['project_id'] = token.pop('tenant_id')
return zip(*sorted(six.iteritems(token)))
class RevokeToken(command.Command):
- """Revoke token command"""
+ """Revoke existing token"""
log = logging.getLogger(__name__ + '.RevokeToken')
diff --git a/openstackclient/identity/v2_0/user.py b/openstackclient/identity/v2_0/user.py
index 60af6ddb..b291c882 100644
--- a/openstackclient/identity/v2_0/user.py
+++ b/openstackclient/identity/v2_0/user.py
@@ -99,9 +99,10 @@ class CreateUser(show.ShowOne):
# NOTE(dtroyer): The users.create() method wants 'tenant_id' but
# the returned resource has 'tenantId'. Sigh.
# We're using project_id now inside OSC so there.
- user._info.update(
- {'project_id': user._info.pop('tenantId')}
- )
+ if 'tenantId' in user._info:
+ user._info.update(
+ {'project_id': user._info.pop('tenantId')}
+ )
info = {}
info.update(user._info)
diff --git a/openstackclient/identity/v3/domain.py b/openstackclient/identity/v3/domain.py
index f9763847..49397afc 100644
--- a/openstackclient/identity/v3/domain.py
+++ b/openstackclient/identity/v3/domain.py
@@ -135,13 +135,12 @@ class SetDomain(command.Command):
'--enable',
dest='enabled',
action='store_true',
- default=True,
help='Enable domain (default)',
)
enable_group.add_argument(
'--disable',
- dest='enabled',
- action='store_false',
+ dest='disabled',
+ action='store_true',
help='Disable domain',
)
return parser
@@ -156,8 +155,10 @@ class SetDomain(command.Command):
kwargs['name'] = parsed_args.name
if parsed_args.description:
kwargs['description'] = parsed_args.description
- if 'enabled' in parsed_args:
- kwargs['enabled'] = parsed_args.enabled
+ if parsed_args.enabled:
+ kwargs['enabled'] = True
+ if parsed_args.disabled:
+ kwargs['enabled'] = False
if not kwargs:
sys.stdout.write("Domain not updated, no arguments present")
diff --git a/openstackclient/identity/v3/endpoint.py b/openstackclient/identity/v3/endpoint.py
index 93d77be3..39798b2d 100644
--- a/openstackclient/identity/v3/endpoint.py
+++ b/openstackclient/identity/v3/endpoint.py
@@ -57,13 +57,13 @@ class CreateEndpoint(show.ShowOne):
dest='enabled',
action='store_true',
default=True,
- help='Enable user',
+ help='Enable endpoint',
)
enable_group.add_argument(
'--disable',
dest='enabled',
action='store_false',
- help='Disable user',
+ help='Disable endpoint',
)
return parser
@@ -114,12 +114,38 @@ class ListEndpoint(lister.Lister):
log = logging.getLogger(__name__ + '.ListEndpoint')
+ def get_parser(self, prog_name):
+ parser = super(ListEndpoint, self).get_parser(prog_name)
+ parser.add_argument(
+ '--service',
+ metavar='<service>',
+ help='Filter by a specific service')
+ parser.add_argument(
+ '--interface',
+ metavar='<interface>',
+ choices=['admin', 'public', 'internal'],
+ help='Filter by a specific interface, must be admin, public or'
+ ' internal')
+ parser.add_argument(
+ '--region',
+ metavar='<region>',
+ help='Filter by a specific region')
+ return parser
+
def take_action(self, parsed_args):
self.log.debug('take_action(%s)', parsed_args)
identity_client = self.app.client_manager.identity
columns = ('ID', 'Region', 'Service Name', 'Service Type',
'Enabled', 'Interface', 'URL')
- data = identity_client.endpoints.list()
+ kwargs = {}
+ if parsed_args.service:
+ service = common.find_service(identity_client, parsed_args.service)
+ kwargs['service'] = service.id
+ if parsed_args.interface:
+ kwargs['interface'] = parsed_args.interface
+ if parsed_args.region:
+ kwargs['region'] = parsed_args.region
+ data = identity_client.endpoints.list(**kwargs)
for ep in data:
service = common.find_service(identity_client, ep.service_id)
@@ -165,14 +191,13 @@ class SetEndpoint(command.Command):
'--enable',
dest='enabled',
action='store_true',
- default=True,
- help='Enable user',
+ help='Enable endpoint',
)
enable_group.add_argument(
'--disable',
- dest='enabled',
- action='store_false',
- help='Disable user',
+ dest='disabled',
+ action='store_true',
+ help='Disable endpoint',
)
return parser
@@ -181,20 +206,31 @@ class SetEndpoint(command.Command):
identity_client = self.app.client_manager.identity
endpoint = utils.find_resource(identity_client.endpoints,
parsed_args.endpoint)
- service = common.find_service(identity_client, parsed_args.service)
if (not parsed_args.interface and not parsed_args.url
- and not parsed_args.service and not parsed_args.region):
+ and not parsed_args.service and not parsed_args.region
+ and not parsed_args.enabled and not parsed_args.disabled):
sys.stdout.write("Endpoint not updated, no arguments present")
return
+ service_id = None
+ if parsed_args.service:
+ service = common.find_service(identity_client, parsed_args.service)
+ service_id = service.id
+
+ enabled = None
+ if parsed_args.enabled:
+ enabled = True
+ if parsed_args.disabled:
+ enabled = False
+
identity_client.endpoints.update(
endpoint.id,
- service=service.id,
+ service=service_id,
url=parsed_args.url,
interface=parsed_args.interface,
region=parsed_args.region,
- enabled=parsed_args.enabled
+ enabled=enabled
)
return
diff --git a/openstackclient/identity/v3/identity_provider.py b/openstackclient/identity/v3/identity_provider.py
index b60678b5..5e8ee566 100644
--- a/openstackclient/identity/v3/identity_provider.py
+++ b/openstackclient/identity/v3/identity_provider.py
@@ -25,7 +25,7 @@ from openstackclient.common import utils
class CreateIdentityProvider(show.ShowOne):
- """Create identity_provider command"""
+ """Create new identity provider"""
log = logging.getLogger(__name__ + '.CreateIdentityProvider')
@@ -33,7 +33,7 @@ class CreateIdentityProvider(show.ShowOne):
parser = super(CreateIdentityProvider, self).get_parser(prog_name)
parser.add_argument(
'identity_provider_id',
- metavar='<identity_provider_id>',
+ metavar='<identity-provider-id>',
help='New identity provider ID (must be unique)'
)
parser.add_argument(
@@ -61,8 +61,8 @@ class CreateIdentityProvider(show.ShowOne):
def take_action(self, parsed_args):
self.log.debug('take_action(%s)', parsed_args)
identity_client = self.app.client_manager.identity
- idp = identity_client.identity_providers.create(
- parsed_args.identity_provider_id,
+ idp = identity_client.federation.identity_providers.create(
+ id=parsed_args.identity_provider_id,
description=parsed_args.description,
enabled=parsed_args.enabled)
info = {}
@@ -71,7 +71,7 @@ class CreateIdentityProvider(show.ShowOne):
class DeleteIdentityProvider(command.Command):
- """Delete identity provider"""
+ """Delete an identity provider"""
log = logging.getLogger(__name__ + '.DeleteIdentityProvider')
@@ -79,15 +79,15 @@ class DeleteIdentityProvider(command.Command):
parser = super(DeleteIdentityProvider, self).get_parser(prog_name)
parser.add_argument(
'identity_provider',
- metavar='<identity_provider>',
- help='ID of the identity provider to be deleted',
+ metavar='<identity-provider-id>',
+ help='Identity provider ID to delete',
)
return parser
def take_action(self, parsed_args):
self.log.debug('take_action(%s)', parsed_args)
identity_client = self.app.client_manager.identity
- identity_client.identity_providers.delete(
+ identity_client.federation.identity_providers.delete(
parsed_args.identity_provider)
return
@@ -100,7 +100,8 @@ class ListIdentityProvider(lister.Lister):
def take_action(self, parsed_args):
self.log.debug('take_action(%s)', parsed_args)
columns = ('ID', 'Enabled', 'Description')
- data = self.app.client_manager.identity.identity_providers.list()
+ identity_client = self.app.client_manager.identity
+ data = identity_client.federation.identity_providers.list()
return (columns,
(utils.get_item_properties(
s, columns,
@@ -109,7 +110,7 @@ class ListIdentityProvider(lister.Lister):
class SetIdentityProvider(command.Command):
- """Set identity provider"""
+ """Set identity provider properties"""
log = logging.getLogger(__name__ + '.SetIdentityProvider')
@@ -117,8 +118,8 @@ class SetIdentityProvider(command.Command):
parser = super(SetIdentityProvider, self).get_parser(prog_name)
parser.add_argument(
'identity_provider',
- metavar='<identity_provider>',
- help='ID of the identity provider to be changed',
+ metavar='<identity-provider-id>',
+ help='Identity provider ID to change',
)
enable_identity_provider = parser.add_mutually_exclusive_group()
@@ -136,7 +137,7 @@ class SetIdentityProvider(command.Command):
def take_action(self, parsed_args):
self.log.debug('take_action(%s)', parsed_args)
- identity_client = self.app.client_manager.identity
+ federation_client = self.app.client_manager.identity.federation
if parsed_args.enable is True:
enabled = True
@@ -147,7 +148,7 @@ class SetIdentityProvider(command.Command):
"no arguments present")
return (None, None)
- identity_provider = identity_client.identity_providers.update(
+ identity_provider = federation_client.identity_providers.update(
parsed_args.identity_provider, enabled=enabled)
info = {}
info.update(identity_provider._info)
@@ -155,7 +156,7 @@ class SetIdentityProvider(command.Command):
class ShowIdentityProvider(show.ShowOne):
- """Show identity provider"""
+ """Show identity provider details"""
log = logging.getLogger(__name__ + '.ShowIdentityProvider')
@@ -163,8 +164,8 @@ class ShowIdentityProvider(show.ShowOne):
parser = super(ShowIdentityProvider, self).get_parser(prog_name)
parser.add_argument(
'identity_provider',
- metavar='<identity_provider>',
- help='ID of the identity provider to be displayed',
+ metavar='<identity-provider-id>',
+ help='Identity provider ID to show',
)
return parser
@@ -172,7 +173,7 @@ class ShowIdentityProvider(show.ShowOne):
self.log.debug('take_action(%s)', parsed_args)
identity_client = self.app.client_manager.identity
identity_provider = utils.find_resource(
- identity_client.identity_providers,
+ identity_client.federation.identity_providers,
parsed_args.identity_provider)
info = {}
diff --git a/openstackclient/identity/v3/user.py b/openstackclient/identity/v3/user.py
index 38c34973..6ba54368 100644
--- a/openstackclient/identity/v3/user.py
+++ b/openstackclient/identity/v3/user.py
@@ -323,6 +323,35 @@ class SetUser(command.Command):
return
+class SetPasswordUser(command.Command):
+ """Change current user password"""
+
+ log = logging.getLogger(__name__ + '.SetPasswordUser')
+
+ def get_parser(self, prog_name):
+ parser = super(SetPasswordUser, self).get_parser(prog_name)
+ parser.add_argument(
+ '--password',
+ metavar='<new-password>',
+ help='New user password'
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ self.log.debug('take_action(%s)', parsed_args)
+ identity_client = self.app.client_manager.identity
+
+ current_password = utils.get_password(
+ self.app.stdin, prompt="Current Password:", confirm=False)
+
+ password = parsed_args.password
+ if password is None:
+ password = utils.get_password(
+ self.app.stdin, prompt="New Password:")
+
+ identity_client.users.update_password(current_password, password)
+
+
class ShowUser(show.ShowOne):
"""Show user details"""
diff --git a/openstackclient/image/v1/image.py b/openstackclient/image/v1/image.py
index 92d09953..cd746cf5 100644
--- a/openstackclient/image/v1/image.py
+++ b/openstackclient/image/v1/image.py
@@ -291,6 +291,12 @@ class ListImage(lister.Lister):
metavar="<size>",
help="Number of images to request in each paginated request",
)
+ parser.add_argument(
+ '--long',
+ action='store_true',
+ default=False,
+ help='List additional fields in output',
+ )
return parser
def take_action(self, parsed_args):
@@ -303,7 +309,11 @@ class ListImage(lister.Lister):
kwargs["page_size"] = parsed_args.page_size
data = image_client.images.list(**kwargs)
- columns = ["ID", "Name"]
+ if parsed_args.long:
+ columns = ('ID', 'Name', 'Disk Format', 'Container Format',
+ 'Size', 'Status')
+ else:
+ columns = ("ID", "Name")
return (columns, (utils.get_item_properties(s, columns) for s in data))
diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py
index 08897b2b..ec023b64 100644
--- a/openstackclient/image/v2/image.py
+++ b/openstackclient/image/v2/image.py
@@ -48,7 +48,7 @@ class DeleteImage(command.Command):
image_client.images,
parsed_args.image,
)
- image_client.images.delete(image)
+ image_client.images.delete(image.id)
class ListImage(lister.Lister):
@@ -63,6 +63,12 @@ class ListImage(lister.Lister):
metavar="<size>",
help="Number of images to request in each paginated request",
)
+ parser.add_argument(
+ '--long',
+ action='store_true',
+ default=False,
+ help='List additional fields in output',
+ )
return parser
def take_action(self, parsed_args):
@@ -75,7 +81,11 @@ class ListImage(lister.Lister):
kwargs["page_size"] = parsed_args.page_size
data = image_client.images.list(**kwargs)
- columns = ["ID", "Name"]
+ if parsed_args.long:
+ columns = ('ID', 'Name', 'Disk Format', 'Container Format',
+ 'Size', 'Status')
+ else:
+ columns = ("ID", "Name")
return (columns, (utils.get_item_properties(s, columns) for s in data))
diff --git a/openstackclient/network/v2/network.py b/openstackclient/network/v2/network.py
index c0c25e71..f34666ba 100644
--- a/openstackclient/network/v2/network.py
+++ b/openstackclient/network/v2/network.py
@@ -43,13 +43,16 @@ class CreateNetwork(show.ShowOne):
help='Name of network to create')
admin_group = parser.add_mutually_exclusive_group()
admin_group.add_argument(
- '--admin-state-up',
- dest='admin_state', action='store_true',
- default=True, help='Set Admin State Up')
+ '--enable',
+ dest='admin_state',
+ default=True,
+ action='store_true',
+ help='Set administrative state up')
admin_group.add_argument(
- '--admin-state-down',
- dest='admin_state', action='store_false',
- help='Set Admin State Down')
+ '--disable',
+ dest='admin_state',
+ action='store_false',
+ help='Set administrative state down')
share_group = parser.add_mutually_exclusive_group()
share_group.add_argument(
'--share',
@@ -83,6 +86,7 @@ class CreateNetwork(show.ShowOne):
class DeleteNetwork(command.Command):
+ """Delete a network"""
log = logging.getLogger(__name__ + '.DeleteNetwork')
@@ -157,6 +161,7 @@ class ListNetwork(lister.Lister):
class SetNetwork(command.Command):
+ """Set network properties"""
log = logging.getLogger(__name__ + '.SetNetwork')
@@ -169,14 +174,16 @@ class SetNetwork(command.Command):
)
admin_group = parser.add_mutually_exclusive_group()
admin_group.add_argument(
- '--admin-state-up',
- dest='admin_state', action='store_true',
+ '--enable',
+ dest='admin_state',
default=None,
- help='Set Admin State Up')
+ action='store_true',
+ help='Set administrative state up')
admin_group.add_argument(
- '--admin-state-down',
- dest='admin_state', action='store_false',
- help='Set Admin State Down')
+ '--disable',
+ dest='admin_state',
+ action='store_false',
+ help='Set administrative state down')
parser.add_argument(
'--name',
metavar='<network_name>',
@@ -206,13 +213,15 @@ class SetNetwork(command.Command):
if parsed_args.shared is not None:
body['shared'] = parsed_args.shared
if body == {}:
- raise exceptions.CommandError("Nothing specified to be set")
+ msg = "Nothing specified to be set"
+ raise exceptions.CommandError(msg)
update_method = getattr(client, "update_network")
update_method(_id, {'network': body})
return
class ShowNetwork(show.ShowOne):
+ """Show network details"""
log = logging.getLogger(__name__ + '.ShowNetwork')
diff --git a/openstackclient/object/client.py b/openstackclient/object/client.py
index 4fe59794..b81ffaaf 100644
--- a/openstackclient/object/client.py
+++ b/openstackclient/object/client.py
@@ -23,7 +23,7 @@ LOG = logging.getLogger(__name__)
DEFAULT_OBJECT_API_VERSION = '1'
API_VERSION_OPTION = 'os_object_api_version'
-API_NAME = 'object-store'
+API_NAME = 'object_store'
API_VERSIONS = {
'1': 'openstackclient.object.client.ObjectClientv1',
}
@@ -31,16 +31,17 @@ API_VERSIONS = {
def make_client(instance):
"""Returns an object service client."""
+
object_client = utils.get_client_class(
API_NAME,
instance._api_version[API_NAME],
API_VERSIONS)
- LOG.debug('Instantiating object client: %s' % object_client)
+ LOG.debug('Instantiating object client: %s', object_client)
if instance._url:
endpoint = instance._url
else:
- endpoint = instance.get_endpoint_for_service_type(API_NAME)
+ endpoint = instance.get_endpoint_for_service_type("object-store")
client = object_client(
endpoint=endpoint,
token=instance._token,
diff --git a/openstackclient/object/v1/container.py b/openstackclient/object/v1/container.py
index 1e252aaf..9d55381c 100644
--- a/openstackclient/object/v1/container.py
+++ b/openstackclient/object/v1/container.py
@@ -19,6 +19,7 @@
import logging
import six
+from cliff import command
from cliff import lister
from cliff import show
@@ -26,6 +27,67 @@ from openstackclient.common import utils
from openstackclient.object.v1.lib import container as lib_container
+class CreateContainer(lister.Lister):
+ """Create a container"""
+
+ log = logging.getLogger(__name__ + '.CreateContainer')
+
+ def get_parser(self, prog_name):
+ parser = super(CreateContainer, self).get_parser(prog_name)
+ parser.add_argument(
+ 'containers',
+ metavar='<container>',
+ nargs="+",
+ help='Container name(s) to create',
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ self.log.debug('take_action(%s)', parsed_args)
+
+ results = []
+ for container in parsed_args.containers:
+ data = lib_container.create_container(
+ self.app.client_manager.session,
+ self.app.client_manager.object_store.endpoint,
+ container,
+ )
+ results.append(data)
+
+ columns = ("account", "container", "x-trans-id")
+ return (columns,
+ (utils.get_dict_properties(
+ s, columns,
+ formatters={},
+ ) for s in results))
+
+
+class DeleteContainer(command.Command):
+ """Delete a container"""
+
+ log = logging.getLogger(__name__ + '.DeleteContainer')
+
+ def get_parser(self, prog_name):
+ parser = super(DeleteContainer, self).get_parser(prog_name)
+ parser.add_argument(
+ 'containers',
+ metavar='<container>',
+ nargs="+",
+ help='Container name(s) to delete',
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ self.log.debug('take_action(%s)', parsed_args)
+
+ for container in parsed_args.containers:
+ lib_container.delete_container(
+ self.app.client_manager.session,
+ self.app.client_manager.object_store.endpoint,
+ container,
+ )
+
+
class ListContainer(lister.Lister):
"""List containers"""
@@ -89,7 +151,7 @@ class ListContainer(lister.Lister):
kwargs['full_listing'] = True
data = lib_container.list_containers(
- self.app.restapi,
+ self.app.client_manager.session,
self.app.client_manager.object_store.endpoint,
**kwargs
)
@@ -101,6 +163,30 @@ class ListContainer(lister.Lister):
) for s in data))
+class SaveContainer(command.Command):
+ """Save the contents of a container locally"""
+
+ log = logging.getLogger(__name__ + ".SaveContainer")
+
+ def get_parser(self, prog_name):
+ parser = super(SaveContainer, self).get_parser(prog_name)
+ parser.add_argument(
+ 'container',
+ metavar='<container>',
+ help='Container name to save',
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ self.log.debug("take_action(%s)", parsed_args)
+
+ lib_container.save_container(
+ self.app.client_manager.session,
+ self.app.client_manager.object_store.endpoint,
+ parsed_args.container
+ )
+
+
class ShowContainer(show.ShowOne):
"""Show container information"""
@@ -119,7 +205,7 @@ class ShowContainer(show.ShowOne):
self.log.debug('take_action(%s)', parsed_args)
data = lib_container.show_container(
- self.app.restapi,
+ self.app.client_manager.session,
self.app.client_manager.object_store.endpoint,
parsed_args.container,
)
diff --git a/openstackclient/object/v1/lib/container.py b/openstackclient/object/v1/lib/container.py
index 0bae2349..4293ff4a 100644
--- a/openstackclient/object/v1/lib/container.py
+++ b/openstackclient/object/v1/lib/container.py
@@ -17,13 +17,54 @@
"""Object v1 API library"""
try:
- from urllib.parse import urlparse
+ from urllib.parse import urlparse # noqa
except ImportError:
- from urlparse import urlparse
+ from urlparse import urlparse # noqa
+
+from openstackclient.object.v1.lib import object as object_lib
+
+
+def create_container(
+ session,
+ url,
+ container,
+):
+ """Create a container
+
+ :param session: an authenticated keystoneclient.session.Session object
+ :param url: endpoint
+ :param container: name of container to create
+ :returns: dict of returned headers
+ """
+
+ response = session.put("%s/%s" % (url, container))
+ url_parts = urlparse(url)
+ data = {
+ 'account': url_parts.path.split('/')[-1],
+ 'container': container,
+ 'x-trans-id': response.headers.get('x-trans-id', None),
+ }
+
+ return data
+
+
+def delete_container(
+ session,
+ url,
+ container,
+):
+ """Delete a container
+
+ :param session: an authenticated keystoneclient.session.Session object
+ :param url: endpoint
+ :param container: name of container to delete
+ """
+
+ session.delete("%s/%s" % (url, container))
def list_containers(
- api,
+ session,
url,
marker=None,
limit=None,
@@ -33,7 +74,7 @@ def list_containers(
):
"""Get containers in an account
- :param api: a restapi object
+ :param session: an authenticated keystoneclient.session.Session object
:param url: endpoint
:param marker: marker query
:param limit: limit query
@@ -46,7 +87,7 @@ def list_containers(
if full_listing:
data = listing = list_containers(
- api,
+ session,
url,
marker,
limit,
@@ -56,7 +97,7 @@ def list_containers(
while listing:
marker = listing[-1]['name']
listing = list_containers(
- api,
+ session,
url,
marker,
limit,
@@ -78,34 +119,52 @@ def list_containers(
params['end_marker'] = end_marker
if prefix:
params['prefix'] = prefix
- return api.list(url, params=params)
+ return session.get(url, params=params).json()
+
+
+def save_container(
+ session,
+ url,
+ container
+):
+ """Save all the content from a container
+
+ :param session: an authenticated keystoneclient.session.Session object
+ :param url: endpoint
+ :param container: name of container to save
+ """
+
+ objects = object_lib.list_objects(session, url, container)
+ for object in objects:
+ object_lib.save_object(session, url, container, object['name'])
def show_container(
- api,
+ session,
url,
container,
):
"""Get container details
- :param api: a restapi object
+ :param session: an authenticated keystoneclient.session.Session object
:param url: endpoint
:param container: name of container to show
:returns: dict of returned headers
"""
- response = api.head("%s/%s" % (url, container))
- url_parts = urlparse(url)
+ response = session.head("%s/%s" % (url, container))
data = {
- 'account': url_parts.path.split('/')[-1],
+ 'account': response.headers.get('x-container-meta-owner', None),
'container': container,
+ 'object_count': response.headers.get(
+ 'x-container-object-count',
+ None,
+ ),
+ 'bytes_used': response.headers.get('x-container-bytes-used', None),
+ 'read_acl': response.headers.get('x-container-read', None),
+ 'write_acl': response.headers.get('x-container-write', None),
+ 'sync_to': response.headers.get('x-container-sync-to', None),
+ 'sync_key': response.headers.get('x-container-sync-key', None),
}
- data['object_count'] = response.headers.get(
- 'x-container-object-count', None)
- data['bytes_used'] = response.headers.get('x-container-bytes-used', None)
- data['read_acl'] = response.headers.get('x-container-read', None)
- data['write_acl'] = response.headers.get('x-container-write', None)
- data['sync_to'] = response.headers.get('x-container-sync-to', None)
- data['sync_key'] = response.headers.get('x-container-sync-key', None)
return data
diff --git a/openstackclient/object/v1/lib/object.py b/openstackclient/object/v1/lib/object.py
index 646737bd..7a23fc76 100644
--- a/openstackclient/object/v1/lib/object.py
+++ b/openstackclient/object/v1/lib/object.py
@@ -16,16 +16,64 @@
"""Object v1 API library"""
+import os
+
import six
try:
- from urllib.parse import urlparse
+ from urllib.parse import urlparse # noqa
except ImportError:
- from urlparse import urlparse
+ from urlparse import urlparse # noqa
+
+
+def create_object(
+ session,
+ url,
+ container,
+ object,
+):
+ """Create an object, upload it to a container
+
+ :param session: an authenticated keystoneclient.session.Session object
+ :param url: endpoint
+ :param container: name of container to store object
+ :param object: local path to object
+ :returns: dict of returned headers
+ """
+
+ full_url = "%s/%s/%s" % (url, container, object)
+ response = session.put(full_url, data=open(object))
+ url_parts = urlparse(url)
+ data = {
+ 'account': url_parts.path.split('/')[-1],
+ 'container': container,
+ 'object': object,
+ 'x-trans-id': response.headers.get('X-Trans-Id', None),
+ 'etag': response.headers.get('Etag', None),
+ }
+
+ return data
+
+
+def delete_object(
+ session,
+ url,
+ container,
+ object,
+):
+ """Delete an object stored in a container
+
+ :param session: an authenticated keystoneclient.session.Session object
+ :param url: endpoint
+ :param container: name of container that stores object
+ :param container: name of object to delete
+ """
+
+ session.delete("%s/%s/%s" % (url, container, object))
def list_objects(
- api,
+ session,
url,
container,
marker=None,
@@ -38,7 +86,7 @@ def list_objects(
):
"""Get objects in a container
- :param api: a restapi object
+ :param session: an authenticated keystoneclient.session.Session object
:param url: endpoint
:param container: container name to get a listing for
:param marker: marker query
@@ -55,7 +103,7 @@ def list_objects(
if full_listing:
data = listing = list_objects(
- api,
+ session,
url,
container,
marker,
@@ -71,7 +119,7 @@ def list_objects(
else:
marker = listing[-1]['name']
listing = list_objects(
- api,
+ session,
url,
container,
marker,
@@ -85,7 +133,6 @@ def list_objects(
data.extend(listing)
return data
- object_url = url
params = {
'format': 'json',
}
@@ -101,32 +148,59 @@ def list_objects(
params['prefix'] = prefix
if path:
params['path'] = path
- url = "%s/%s" % (object_url, container)
- return api.list(url, params=params)
+ requrl = "%s/%s" % (url, container)
+ return session.get(requrl, params=params).json()
+
+
+def save_object(
+ session,
+ url,
+ container,
+ obj,
+ file=None
+):
+ """Save an object stored in a container
+
+ :param session: an authenticated keystoneclient.session.Session object
+ :param url: endpoint
+ :param container: name of container that stores object
+ :param object: name of object to save
+ :param file: local name of object
+ """
+
+ if not file:
+ file = obj
+
+ response = session.get("%s/%s/%s" % (url, container, obj), stream=True)
+ if response.status_code == 200:
+ if not os.path.exists(os.path.dirname(file)):
+ os.makedirs(os.path.dirname(file))
+ with open(file, 'wb') as f:
+ for chunk in response.iter_content():
+ f.write(chunk)
def show_object(
- api,
+ session,
url,
container,
obj,
):
"""Get object details
- :param api: a restapi object
+ :param session: an authenticated keystoneclient.session.Session object
:param url: endpoint
:param container: container name to get a listing for
:returns: dict of object properties
"""
- response = api.head("%s/%s/%s" % (url, container, obj))
- url_parts = urlparse(url)
+ response = session.head("%s/%s/%s" % (url, container, obj))
data = {
- 'account': url_parts.path.split('/')[-1],
+ 'account': response.headers.get('x-container-meta-owner', None),
'container': container,
'object': obj,
+ 'content-type': response.headers.get('content-type', None),
}
- data['content-type'] = response.headers.get('content-type', None)
if 'content-length' in response.headers:
data['content-length'] = response.headers.get('content-length', None)
if 'last-modified' in response.headers:
@@ -138,10 +212,10 @@ def show_object(
'x-object-manifest', None)
for key, value in six.iteritems(response.headers):
if key.startswith('x-object-meta-'):
- data[key[len('x-object-meta-'):].title()] = value
+ data[key[len('x-object-meta-'):].lower()] = value
elif key not in (
'content-type', 'content-length', 'last-modified',
- 'etag', 'date', 'x-object-manifest'):
- data[key.title()] = value
+ 'etag', 'date', 'x-object-manifest', 'x-container-meta-owner'):
+ data[key.lower()] = value
return data
diff --git a/openstackclient/object/v1/object.py b/openstackclient/object/v1/object.py
index ee30c842..f0ea7633 100644
--- a/openstackclient/object/v1/object.py
+++ b/openstackclient/object/v1/object.py
@@ -19,6 +19,7 @@
import logging
import six
+from cliff import command
from cliff import lister
from cliff import show
@@ -26,6 +27,80 @@ from openstackclient.common import utils
from openstackclient.object.v1.lib import object as lib_object
+class CreateObject(lister.Lister):
+ """Upload an object to a container"""
+
+ log = logging.getLogger(__name__ + '.CreateObject')
+
+ def get_parser(self, prog_name):
+ parser = super(CreateObject, self).get_parser(prog_name)
+ parser.add_argument(
+ 'container',
+ metavar='<container>',
+ help='Container to store new object',
+ )
+ parser.add_argument(
+ 'objects',
+ metavar='<object-name>',
+ nargs="+",
+ help='Local path of object(s) to upload',
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ self.log.debug('take_action(%s)', parsed_args)
+
+ results = []
+ for obj in parsed_args.objects:
+ data = lib_object.create_object(
+ self.app.client_manager.session,
+ self.app.client_manager.object_store.endpoint,
+ parsed_args.container,
+ obj,
+ )
+ results.append(data)
+
+ columns = ("object", "container", "etag")
+ return (columns,
+ (utils.get_dict_properties(
+ s, columns,
+ formatters={},
+ ) for s in results))
+
+
+class DeleteObject(command.Command):
+ """Delete an object within a container"""
+
+ log = logging.getLogger(__name__ + '.DeleteObject')
+
+ def get_parser(self, prog_name):
+ parser = super(DeleteObject, self).get_parser(prog_name)
+ parser.add_argument(
+ 'container',
+ metavar='<container>',
+ help='Container that stores the object to delete',
+ )
+ parser.add_argument(
+ 'objects',
+ metavar='<object-name>',
+ nargs="+",
+ help='Object(s) to delete',
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ self.log.debug('take_action(%s)', parsed_args)
+
+ for obj in parsed_args.objects:
+ lib_object.delete_object(
+ self.app.restapi,
+ self.app.client_manager.session,
+ self.app.client_manager.object_store.endpoint,
+ parsed_args.container,
+ obj,
+ )
+
+
class ListObject(lister.Lister):
"""List objects"""
@@ -107,7 +182,7 @@ class ListObject(lister.Lister):
kwargs['full_listing'] = True
data = lib_object.list_objects(
- self.app.restapi,
+ self.app.client_manager.session,
self.app.client_manager.object_store.endpoint,
parsed_args.container,
**kwargs
@@ -120,6 +195,42 @@ class ListObject(lister.Lister):
) for s in data))
+class SaveObject(command.Command):
+ """Save an object locally"""
+
+ log = logging.getLogger(__name__ + ".SaveObject")
+
+ def get_parser(self, prog_name):
+ parser = super(SaveObject, self).get_parser(prog_name)
+ parser.add_argument(
+ "--file",
+ metavar="<filename>",
+ help="Downloaded object filename [defaults to object name]",
+ )
+ parser.add_argument(
+ 'container',
+ metavar='<container>',
+ help='Container name that has the object',
+ )
+ parser.add_argument(
+ "object",
+ metavar="<object>",
+ help="Name of the object to save",
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ self.log.debug("take_action(%s)", parsed_args)
+
+ lib_object.save_object(
+ self.app.client_manager.session,
+ self.app.client_manager.object_store.endpoint,
+ parsed_args.container,
+ parsed_args.object,
+ parsed_args.file,
+ )
+
+
class ShowObject(show.ShowOne):
"""Show object information"""
@@ -143,7 +254,7 @@ class ShowObject(show.ShowOne):
self.log.debug('take_action(%s)', parsed_args)
data = lib_object.show_object(
- self.app.restapi,
+ self.app.client_manager.session,
self.app.client_manager.object_store.endpoint,
parsed_args.container,
parsed_args.object,
diff --git a/openstackclient/openstack/common/gettextutils.py b/openstackclient/openstack/common/gettextutils.py
index 6f573a7f..0c82634b 100644
--- a/openstackclient/openstack/common/gettextutils.py
+++ b/openstackclient/openstack/common/gettextutils.py
@@ -23,7 +23,6 @@ Usual usage in an openstack.common module:
"""
import copy
-import functools
import gettext
import locale
from logging import handlers
@@ -42,7 +41,7 @@ class TranslatorFactory(object):
"""Create translator functions
"""
- def __init__(self, domain, lazy=False, localedir=None):
+ def __init__(self, domain, localedir=None):
"""Establish a set of translation functions for the domain.
:param domain: Name of translation domain,
@@ -55,7 +54,6 @@ class TranslatorFactory(object):
:type localedir: str
"""
self.domain = domain
- self.lazy = lazy
if localedir is None:
localedir = os.environ.get(domain.upper() + '_LOCALEDIR')
self.localedir = localedir
@@ -75,16 +73,19 @@ class TranslatorFactory(object):
"""
if domain is None:
domain = self.domain
- if self.lazy:
- return functools.partial(Message, domain=domain)
- t = gettext.translation(
- domain,
- localedir=self.localedir,
- fallback=True,
- )
- if six.PY3:
- return t.gettext
- return t.ugettext
+ t = gettext.translation(domain,
+ localedir=self.localedir,
+ fallback=True)
+ # Use the appropriate method of the translation object based
+ # on the python version.
+ m = t.gettext if six.PY3 else t.ugettext
+
+ def f(msg):
+ """oslo.i18n.gettextutils translation function."""
+ if USE_LAZY:
+ return Message(msg, domain=domain)
+ return m(msg)
+ return f
@property
def primary(self):
@@ -147,19 +148,11 @@ def enable_lazy():
your project is importing _ directly instead of using the
gettextutils.install() way of importing the _ function.
"""
- # FIXME(dhellmann): This function will be removed in oslo.i18n,
- # because the TranslatorFactory makes it superfluous.
- global _, _LI, _LW, _LE, _LC, USE_LAZY
- tf = TranslatorFactory('openstackclient', lazy=True)
- _ = tf.primary
- _LI = tf.log_info
- _LW = tf.log_warning
- _LE = tf.log_error
- _LC = tf.log_critical
+ global USE_LAZY
USE_LAZY = True
-def install(domain, lazy=False):
+def install(domain):
"""Install a _() function using the given translation domain.
Given a translation domain, install a _() function using gettext's
@@ -170,26 +163,14 @@ def install(domain, lazy=False):
a translation-domain-specific environment variable (e.g.
NOVA_LOCALEDIR).
+ Note that to enable lazy translation, enable_lazy must be
+ called.
+
:param domain: the translation domain
- :param lazy: indicates whether or not to install the lazy _() function.
- The lazy _() introduces a way to do deferred translation
- of messages by installing a _ that builds Message objects,
- instead of strings, which can then be lazily translated into
- any available locale.
"""
- if lazy:
- from six import moves
- tf = TranslatorFactory(domain, lazy=True)
- moves.builtins.__dict__['_'] = tf.primary
- else:
- localedir = '%s_LOCALEDIR' % domain.upper()
- if six.PY3:
- gettext.install(domain,
- localedir=os.environ.get(localedir))
- else:
- gettext.install(domain,
- localedir=os.environ.get(localedir),
- unicode=True)
+ from six import moves
+ tf = TranslatorFactory(domain)
+ moves.builtins.__dict__['_'] = tf.primary
class Message(six.text_type):
@@ -373,8 +354,8 @@ def get_available_languages(domain):
'zh_Hant_HK': 'zh_HK',
'zh_Hant': 'zh_TW',
'fil': 'tl_PH'}
- for (locale, alias) in six.iteritems(aliases):
- if locale in language_list and alias not in language_list:
+ for (locale_, alias) in six.iteritems(aliases):
+ if locale_ in language_list and alias not in language_list:
language_list.append(alias)
_AVAILABLE_LANGUAGES[domain] = language_list
diff --git a/openstackclient/openstack/common/importutils.py b/openstackclient/openstack/common/importutils.py
new file mode 100644
index 00000000..69e8d8f1
--- /dev/null
+++ b/openstackclient/openstack/common/importutils.py
@@ -0,0 +1,73 @@
+# 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.
+
+"""
+Import related utilities and helper functions.
+"""
+
+import sys
+import traceback
+
+
+def import_class(import_str):
+ """Returns a class from a string including module and class."""
+ mod_str, _sep, class_str = import_str.rpartition('.')
+ __import__(mod_str)
+ try:
+ return getattr(sys.modules[mod_str], class_str)
+ except AttributeError:
+ raise ImportError('Class %s cannot be found (%s)' %
+ (class_str,
+ traceback.format_exception(*sys.exc_info())))
+
+
+def import_object(import_str, *args, **kwargs):
+ """Import a class and return an instance of it."""
+ return import_class(import_str)(*args, **kwargs)
+
+
+def import_object_ns(name_space, import_str, *args, **kwargs):
+ """Tries to import object from default namespace.
+
+ Imports a class and return an instance of it, first by trying
+ to find the class in a default namespace, then failing back to
+ a full path if not found in the default namespace.
+ """
+ import_value = "%s.%s" % (name_space, import_str)
+ try:
+ return import_class(import_value)(*args, **kwargs)
+ except ImportError:
+ return import_class(import_str)(*args, **kwargs)
+
+
+def import_module(import_str):
+ """Import a module."""
+ __import__(import_str)
+ return sys.modules[import_str]
+
+
+def import_versioned_module(version, submodule=None):
+ module = 'openstackclient.v%s' % version
+ if submodule:
+ module = '.'.join((module, submodule))
+ return import_module(module)
+
+
+def try_import(import_str, default=None):
+ """Try to import a module and if it fails return default."""
+ try:
+ return import_module(import_str)
+ except ImportError:
+ return default
diff --git a/openstackclient/openstack/common/strutils.py b/openstackclient/openstack/common/strutils.py
index 9d70264f..ad3cb44c 100644
--- a/openstackclient/openstack/common/strutils.py
+++ b/openstackclient/openstack/common/strutils.py
@@ -50,6 +50,39 @@ SLUGIFY_STRIP_RE = re.compile(r"[^\w\s-]")
SLUGIFY_HYPHENATE_RE = re.compile(r"[-\s]+")
+# NOTE(flaper87): The following globals are used by `mask_password`
+_SANITIZE_KEYS = ['adminPass', 'admin_pass', 'password', 'admin_password']
+
+# NOTE(ldbragst): Let's build a list of regex objects using the list of
+# _SANITIZE_KEYS we already have. This way, we only have to add the new key
+# to the list of _SANITIZE_KEYS and we can generate regular expressions
+# for XML and JSON automatically.
+_SANITIZE_PATTERNS_2 = []
+_SANITIZE_PATTERNS_1 = []
+
+# NOTE(amrith): Some regular expressions have only one parameter, some
+# have two parameters. Use different lists of patterns here.
+_FORMAT_PATTERNS_1 = [r'(%(key)s\s*[=]\s*)[^\s^\'^\"]+']
+_FORMAT_PATTERNS_2 = [r'(%(key)s\s*[=]\s*[\"\']).*?([\"\'])',
+ r'(%(key)s\s+[\"\']).*?([\"\'])',
+ r'([-]{2}%(key)s\s+)[^\'^\"^=^\s]+([\s]*)',
+ r'(<%(key)s>).*?(</%(key)s>)',
+ r'([\"\']%(key)s[\"\']\s*:\s*[\"\']).*?([\"\'])',
+ r'([\'"].*?%(key)s[\'"]\s*:\s*u?[\'"]).*?([\'"])',
+ r'([\'"].*?%(key)s[\'"]\s*,\s*\'--?[A-z]+\'\s*,\s*u?'
+ '[\'"]).*?([\'"])',
+ r'(%(key)s\s*--?[A-z]+\s*)\S+(\s*)']
+
+for key in _SANITIZE_KEYS:
+ for pattern in _FORMAT_PATTERNS_2:
+ reg_ex = re.compile(pattern % {'key': key}, re.DOTALL)
+ _SANITIZE_PATTERNS_2.append(reg_ex)
+
+ for pattern in _FORMAT_PATTERNS_1:
+ reg_ex = re.compile(pattern % {'key': key}, re.DOTALL)
+ _SANITIZE_PATTERNS_1.append(reg_ex)
+
+
def int_from_bool_as_string(subject):
"""Interpret a string as a boolean and return either 1 or 0.
@@ -237,3 +270,42 @@ def to_slug(value, incoming=None, errors="strict"):
"ascii", "ignore").decode("ascii")
value = SLUGIFY_STRIP_RE.sub("", value).strip().lower()
return SLUGIFY_HYPHENATE_RE.sub("-", value)
+
+
+def mask_password(message, secret="***"):
+ """Replace password with 'secret' in message.
+
+ :param message: The string which includes security information.
+ :param secret: value with which to replace passwords.
+ :returns: The unicode value of message with the password fields masked.
+
+ For example:
+
+ >>> mask_password("'adminPass' : 'aaaaa'")
+ "'adminPass' : '***'"
+ >>> mask_password("'admin_pass' : 'aaaaa'")
+ "'admin_pass' : '***'"
+ >>> mask_password('"password" : "aaaaa"')
+ '"password" : "***"'
+ >>> mask_password("'original_password' : 'aaaaa'")
+ "'original_password' : '***'"
+ >>> mask_password("u'original_password' : u'aaaaa'")
+ "u'original_password' : u'***'"
+ """
+ message = six.text_type(message)
+
+ # NOTE(ldbragst): Check to see if anything in message contains any key
+ # specified in _SANITIZE_KEYS, if not then just return the message since
+ # we don't have to mask any passwords.
+ if not any(key in message for key in _SANITIZE_KEYS):
+ return message
+
+ substitute = r'\g<1>' + secret + r'\g<2>'
+ for pattern in _SANITIZE_PATTERNS_2:
+ message = re.sub(pattern, substitute, message)
+
+ substitute = r'\g<1>' + secret
+ for pattern in _SANITIZE_PATTERNS_1:
+ message = re.sub(pattern, substitute, message)
+
+ return message
diff --git a/openstackclient/shell.py b/openstackclient/shell.py
index 1d0c5771..24804343 100644
--- a/openstackclient/shell.py
+++ b/openstackclient/shell.py
@@ -31,9 +31,8 @@ import openstackclient
from openstackclient.common import clientmanager
from openstackclient.common import commandmanager
from openstackclient.common import exceptions as exc
-from openstackclient.common import restapi
+from openstackclient.common import timing
from openstackclient.common import utils
-from openstackclient.identity import client as identity_client
KEYRING_SERVICE = 'openstack'
@@ -60,6 +59,7 @@ class OpenStackShell(app.App):
CONSOLE_MESSAGE_FORMAT = '%(levelname)s: %(name)s %(message)s'
log = logging.getLogger(__name__)
+ timing_data = []
def __init__(self):
# Patch command.Command to add a default auth_required = True
@@ -74,6 +74,8 @@ class OpenStackShell(app.App):
version=openstackclient.__version__,
command_manager=commandmanager.CommandManager('openstack.cli'))
+ self.api_version = {}
+
# Until we have command line arguments parsed, dump any stack traces
self.dump_stack_trace = True
@@ -84,10 +86,14 @@ class OpenStackShell(app.App):
# Assume TLS host certificate verification is enabled
self.verify = True
- # Get list of extension modules
+ # Get list of base modules
self.ext_modules = clientmanager.get_extension_modules(
- 'openstack.cli.extension',
+ 'openstack.cli.base',
)
+ # Append list of extension modules
+ self.ext_modules.extend(clientmanager.get_extension_modules(
+ 'openstack.cli.extension',
+ ))
# Loop through extensions to get parser additions
for mod in self.ext_modules:
@@ -303,23 +309,12 @@ class OpenStackShell(app.App):
metavar='<url>',
default=env('OS_URL'),
help='Defaults to env[OS_URL]')
-
parser.add_argument(
- '--os-identity-api-version',
- metavar='<identity-api-version>',
- default=env(
- 'OS_IDENTITY_API_VERSION',
- default=identity_client.DEFAULT_IDENTITY_API_VERSION),
- help='Identity API version, default=' +
- identity_client.DEFAULT_IDENTITY_API_VERSION +
- ' (Env: OS_IDENTITY_API_VERSION)')
- parser.add_argument(
- '--os-trust-id',
- metavar='<trust-id>',
- default=utils.env('OS_TRUST_ID'),
- help='Trust ID to use when authenticating. '
- 'This can only be used with Keystone v3 API '
- '(Env: OS_TRUST_ID)')
+ '--timing',
+ default=False,
+ action='store_true',
+ help="Print API call timing info",
+ )
return parser
@@ -410,6 +405,7 @@ class OpenStackShell(app.App):
password=self.options.os_password,
region_name=self.options.os_region_name,
verify=self.verify,
+ timing=self.options.timing,
api_version=self.api_version,
trust_id=self.options.os_trust_id,
)
@@ -428,24 +424,19 @@ class OpenStackShell(app.App):
# Save default domain
self.default_domain = self.options.os_default_domain
- # Stash selected API versions for later
- self.api_version = {
- 'identity': self.options.os_identity_api_version,
- }
# Loop through extensions to get API versions
for mod in self.ext_modules:
- ver = getattr(self.options, mod.API_VERSION_OPTION, None)
- if ver:
- self.api_version[mod.API_NAME] = ver
- self.log.debug('%(name)s API version %(version)s',
- {'name': mod.API_NAME, 'version': ver})
-
- # Add the API version-specific commands
- for api in self.api_version.keys():
- version = '.v' + self.api_version[api].replace('.', '_')
- cmd_group = 'openstack.' + api.replace('-', '_') + version
- self.log.debug('command group %s', cmd_group)
- self.command_manager.add_command_group(cmd_group)
+ version_opt = getattr(self.options, mod.API_VERSION_OPTION, None)
+ if version_opt:
+ api = mod.API_NAME
+ self.api_version[api] = version_opt
+ version = '.v' + version_opt.replace('.', '_')
+ cmd_group = 'openstack.' + api.replace('-', '_') + version
+ self.command_manager.add_command_group(cmd_group)
+ self.log.debug(
+ '%(name)s API version %(version)s, cmd group %(group)s',
+ {'name': api, 'version': version_opt, 'group': cmd_group}
+ )
# Commands that span multiple APIs
self.command_manager.add_command_group(
@@ -475,10 +466,6 @@ class OpenStackShell(app.App):
self.verify = self.options.os_cacert
else:
self.verify = not self.options.insecure
- self.restapi = restapi.RESTApi(
- verify=self.verify,
- debug=self.options.debug,
- )
def prepare_to_run_command(self, cmd):
"""Set up auth and API versions"""
@@ -489,24 +476,45 @@ class OpenStackShell(app.App):
if cmd.best_effort:
try:
self.authenticate_user()
- self.restapi.set_auth(self.client_manager.identity.auth_token)
except Exception:
pass
else:
self.authenticate_user()
- self.restapi.set_auth(self.client_manager.identity.auth_token)
return
def clean_up(self, cmd, result, err):
self.log.debug('clean_up %s', cmd.__class__.__name__)
+
if err:
self.log.debug('got an error: %s', err)
+ # Process collected timing data
+ if self.options.timing:
+ # Loop through extensions
+ for mod in self.ext_modules:
+ client = getattr(self.client_manager, mod.API_NAME)
+ if hasattr(client, 'get_timings'):
+ self.timing_data.extend(client.get_timings())
+
+ # Use the Timing pseudo-command to generate the output
+ tcmd = timing.Timing(self, self.options)
+ tparser = tcmd.get_parser('Timing')
+
+ # If anything other than prettytable is specified, force csv
+ format = 'table'
+ # Check the formatter used in the actual command
+ if hasattr(cmd, 'formatter') \
+ and cmd.formatter != cmd._formatter_plugins['table'].obj:
+ format = 'csv'
+
+ sys.stdout.write('\n')
+ targs = tparser.parse_args(['-f', format])
+ tcmd.run(targs)
+
def interact(self):
# NOTE(dtroyer): Maintain the old behaviour for interactive use as
# this path does not call prepare_to_run_command()
self.authenticate_user()
- self.restapi.set_auth(self.client_manager.identity.auth_token)
super(OpenStackShell, self).interact()
diff --git a/openstackclient/tests/common/test_clientmanager.py b/openstackclient/tests/common/test_clientmanager.py
index 6aee711d..0bb657ad 100644
--- a/openstackclient/tests/common/test_clientmanager.py
+++ b/openstackclient/tests/common/test_clientmanager.py
@@ -13,12 +13,33 @@
# under the License.
#
+import mock
+
+from keystoneclient.auth.identity import v2 as auth_v2
from openstackclient.common import clientmanager
from openstackclient.tests import utils
+AUTH_REF = {'a': 1}
AUTH_TOKEN = "foobar"
AUTH_URL = "http://0.0.0.0"
+USERNAME = "itchy"
+PASSWORD = "scratchy"
+SERVICE_CATALOG = {'sc': '123'}
+
+API_VERSION = {
+ 'identity': '2.0',
+}
+
+
+def FakeMakeClient(instance):
+ return FakeClient()
+
+
+class FakeClient(object):
+ auth_ref = AUTH_REF
+ auth_token = AUTH_TOKEN
+ service_catalog = SERVICE_CATALOG
class Container(object):
@@ -28,31 +49,86 @@ class Container(object):
pass
+class TestClientCache(utils.TestCase):
+
+ def test_singleton(self):
+ # NOTE(dtroyer): Verify that the ClientCache descriptor only invokes
+ # the factory one time and always returns the same value after that.
+ c = Container()
+ self.assertEqual(c.attr, c.attr)
+
+
+@mock.patch('keystoneclient.session.Session')
class TestClientManager(utils.TestCase):
def setUp(self):
super(TestClientManager, self).setUp()
- api_version = {"identity": "2.0"}
+ clientmanager.ClientManager.identity = \
+ clientmanager.ClientCache(FakeMakeClient)
- self.client_manager = clientmanager.ClientManager(
+ def test_client_manager_token(self, mock):
+
+ client_manager = clientmanager.ClientManager(
token=AUTH_TOKEN,
url=AUTH_URL,
- auth_url=AUTH_URL,
- api_version=api_version,
+ verify=True,
+ api_version=API_VERSION,
)
- def test_singleton(self):
- # NOTE(dtroyer): Verify that the ClientCache descriptor only invokes
- # the factory one time and always returns the same value after that.
- c = Container()
- self.assertEqual(c.attr, c.attr)
-
- def test_make_client_identity_default(self):
self.assertEqual(
- self.client_manager.identity.auth_token,
AUTH_TOKEN,
+ client_manager._token,
+ )
+ self.assertEqual(
+ AUTH_URL,
+ client_manager._url,
+ )
+ self.assertIsInstance(
+ client_manager.auth,
+ auth_v2.Token,
)
+ self.assertFalse(client_manager._insecure)
+ self.assertTrue(client_manager._verify)
+
+ def test_client_manager_password(self, mock):
+
+ client_manager = clientmanager.ClientManager(
+ auth_url=AUTH_URL,
+ username=USERNAME,
+ password=PASSWORD,
+ verify=False,
+ api_version=API_VERSION,
+ )
+
self.assertEqual(
- self.client_manager.identity.management_url,
AUTH_URL,
+ client_manager._auth_url,
)
+ self.assertEqual(
+ USERNAME,
+ client_manager._username,
+ )
+ self.assertEqual(
+ PASSWORD,
+ client_manager._password,
+ )
+ self.assertIsInstance(
+ client_manager.auth,
+ auth_v2.Password,
+ )
+ self.assertTrue(client_manager._insecure)
+ self.assertFalse(client_manager._verify)
+
+ def test_client_manager_password_verify_ca(self, mock):
+
+ client_manager = clientmanager.ClientManager(
+ auth_url=AUTH_URL,
+ username=USERNAME,
+ password=PASSWORD,
+ verify='cafile',
+ api_version=API_VERSION,
+ )
+
+ self.assertFalse(client_manager._insecure)
+ self.assertTrue(client_manager._verify)
+ self.assertEqual('cafile', client_manager._cacert)
diff --git a/openstackclient/tests/common/test_restapi.py b/openstackclient/tests/common/test_restapi.py
deleted file mode 100644
index d4fe2d3d..00000000
--- a/openstackclient/tests/common/test_restapi.py
+++ /dev/null
@@ -1,341 +0,0 @@
-# Copyright 2013 Nebula 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.
-#
-
-"""Test rest module"""
-
-import json
-import mock
-
-import requests
-import six
-
-from openstackclient.common import restapi
-from openstackclient.tests import utils
-
-fake_user_agent = 'test_rapi'
-
-fake_auth = '11223344556677889900'
-fake_url = 'http://gopher.com'
-fake_key = 'gopher'
-fake_keys = 'gophers'
-fake_gopher_mac = {
- 'id': 'g1',
- 'name': 'mac',
- 'actor': 'Mel Blanc',
-}
-fake_gopher_tosh = {
- 'id': 'g2',
- 'name': 'tosh',
- 'actor': 'Stan Freeberg',
-}
-fake_gopher_single = {
- fake_key: fake_gopher_mac,
-}
-fake_gopher_list = {
- fake_keys:
- [
- fake_gopher_mac,
- fake_gopher_tosh,
- ]
-}
-fake_headers = {
- 'User-Agent': fake_user_agent,
-}
-
-
-class FakeResponse(requests.Response):
- def __init__(self, headers={}, status_code=200, data=None, encoding=None):
- super(FakeResponse, self).__init__()
-
- self.status_code = status_code
-
- self.headers.update(headers)
- self._content = json.dumps(data)
- if not isinstance(self._content, six.binary_type):
- self._content = self._content.encode()
-
-
-@mock.patch('openstackclient.common.restapi.requests.Session')
-class TestRESTApi(utils.TestCase):
-
- def test_request_get(self, session_mock):
- resp = FakeResponse(status_code=200, data=fake_gopher_single)
- session_mock.return_value = mock.MagicMock(
- request=mock.MagicMock(return_value=resp),
- )
-
- api = restapi.RESTApi(
- user_agent=fake_user_agent,
- )
- gopher = api.request('GET', fake_url)
- session_mock.return_value.request.assert_called_with(
- 'GET',
- fake_url,
- headers={},
- allow_redirects=True,
- )
- self.assertEqual(gopher.status_code, 200)
- self.assertEqual(gopher.json(), fake_gopher_single)
-
- def test_request_get_return_300(self, session_mock):
- resp = FakeResponse(status_code=300, data=fake_gopher_single)
- session_mock.return_value = mock.MagicMock(
- request=mock.MagicMock(return_value=resp),
- )
-
- api = restapi.RESTApi(
- user_agent=fake_user_agent,
- )
- gopher = api.request('GET', fake_url)
- session_mock.return_value.request.assert_called_with(
- 'GET',
- fake_url,
- headers={},
- allow_redirects=True,
- )
- self.assertEqual(gopher.status_code, 300)
- self.assertEqual(gopher.json(), fake_gopher_single)
-
- def test_request_get_fail_404(self, session_mock):
- resp = FakeResponse(status_code=404, data=fake_gopher_single)
- session_mock.return_value = mock.MagicMock(
- request=mock.MagicMock(return_value=resp),
- )
-
- api = restapi.RESTApi(
- user_agent=fake_user_agent,
- )
- self.assertRaises(requests.HTTPError, api.request, 'GET', fake_url)
- session_mock.return_value.request.assert_called_with(
- 'GET',
- fake_url,
- headers={},
- allow_redirects=True,
- )
-
- def test_request_get_auth(self, session_mock):
- resp = FakeResponse(data=fake_gopher_single)
- session_mock.return_value = mock.MagicMock(
- request=mock.MagicMock(return_value=resp),
- headers=mock.MagicMock(return_value={}),
- )
-
- api = restapi.RESTApi(
- auth_header=fake_auth,
- user_agent=fake_user_agent,
- )
- gopher = api.request('GET', fake_url)
- session_mock.return_value.request.assert_called_with(
- 'GET',
- fake_url,
- headers={
- 'X-Auth-Token': fake_auth,
- },
- allow_redirects=True,
- )
- self.assertEqual(gopher.json(), fake_gopher_single)
-
- def test_request_post(self, session_mock):
- resp = FakeResponse(data=fake_gopher_single)
- session_mock.return_value = mock.MagicMock(
- request=mock.MagicMock(return_value=resp),
- )
-
- api = restapi.RESTApi(
- user_agent=fake_user_agent,
- )
- data = fake_gopher_tosh
- gopher = api.request('POST', fake_url, json=data)
- session_mock.return_value.request.assert_called_with(
- 'POST',
- fake_url,
- headers={
- 'Content-Type': 'application/json',
- },
- allow_redirects=True,
- data=json.dumps(data),
- )
- self.assertEqual(gopher.json(), fake_gopher_single)
-
- # Methods
- # TODO(dtroyer): add the other method methods
-
- def test_delete(self, session_mock):
- resp = FakeResponse(status_code=200, data=None)
- session_mock.return_value = mock.MagicMock(
- request=mock.MagicMock(return_value=resp),
- )
-
- api = restapi.RESTApi()
- gopher = api.delete(fake_url)
- session_mock.return_value.request.assert_called_with(
- 'DELETE',
- fake_url,
- headers=mock.ANY,
- allow_redirects=True,
- )
- self.assertEqual(gopher.status_code, 200)
-
- # Commands
-
- def test_create(self, session_mock):
- resp = FakeResponse(data=fake_gopher_single)
- session_mock.return_value = mock.MagicMock(
- request=mock.MagicMock(return_value=resp),
- )
-
- api = restapi.RESTApi()
- data = fake_gopher_mac
-
- # Test no key
- gopher = api.create(fake_url, data=data)
- session_mock.return_value.request.assert_called_with(
- 'POST',
- fake_url,
- headers=mock.ANY,
- allow_redirects=True,
- data=json.dumps(data),
- )
- self.assertEqual(gopher, fake_gopher_single)
-
- # Test with key
- gopher = api.create(fake_url, data=data, response_key=fake_key)
- session_mock.return_value.request.assert_called_with(
- 'POST',
- fake_url,
- headers=mock.ANY,
- allow_redirects=True,
- data=json.dumps(data),
- )
- self.assertEqual(gopher, fake_gopher_mac)
-
- def test_list(self, session_mock):
- resp = FakeResponse(data=fake_gopher_list)
- session_mock.return_value = mock.MagicMock(
- request=mock.MagicMock(return_value=resp),
- )
-
- # test base
- api = restapi.RESTApi()
- gopher = api.list(fake_url, response_key=fake_keys)
- session_mock.return_value.request.assert_called_with(
- 'GET',
- fake_url,
- headers=mock.ANY,
- allow_redirects=True,
- )
- self.assertEqual(gopher, [fake_gopher_mac, fake_gopher_tosh])
-
- # test body
- api = restapi.RESTApi()
- data = {'qwerty': 1}
- gopher = api.list(fake_url, response_key=fake_keys, data=data)
- session_mock.return_value.request.assert_called_with(
- 'POST',
- fake_url,
- headers=mock.ANY,
- allow_redirects=True,
- data=json.dumps(data),
- )
- self.assertEqual(gopher, [fake_gopher_mac, fake_gopher_tosh])
-
- # test query params
- api = restapi.RESTApi()
- params = {'qaz': '123'}
- gophers = api.list(fake_url, response_key=fake_keys, params=params)
- session_mock.return_value.request.assert_called_with(
- 'GET',
- fake_url,
- headers=mock.ANY,
- allow_redirects=True,
- params=params,
- )
- self.assertEqual(gophers, [fake_gopher_mac, fake_gopher_tosh])
-
- def test_set(self, session_mock):
- new_gopher = fake_gopher_single
- new_gopher[fake_key]['name'] = 'Chip'
- resp = FakeResponse(data=fake_gopher_single)
- session_mock.return_value = mock.MagicMock(
- request=mock.MagicMock(return_value=resp),
- )
-
- api = restapi.RESTApi()
- data = fake_gopher_mac
- data['name'] = 'Chip'
-
- # Test no data, no key
- gopher = api.set(fake_url)
- session_mock.return_value.request.assert_called_with(
- 'PUT',
- fake_url,
- headers=mock.ANY,
- allow_redirects=True,
- json=None,
- )
- self.assertEqual(gopher, None)
-
- # Test data, no key
- gopher = api.set(fake_url, data=data)
- session_mock.return_value.request.assert_called_with(
- 'PUT',
- fake_url,
- headers=mock.ANY,
- allow_redirects=True,
- data=json.dumps(data),
- )
- self.assertEqual(gopher, fake_gopher_single)
-
- # NOTE:(dtroyer): Key and no data is not tested as without data
- # the response_key is moot
-
- # Test data and key
- gopher = api.set(fake_url, data=data, response_key=fake_key)
- session_mock.return_value.request.assert_called_with(
- 'PUT',
- fake_url,
- headers=mock.ANY,
- allow_redirects=True,
- data=json.dumps(data),
- )
- self.assertEqual(gopher, fake_gopher_mac)
-
- def test_show(self, session_mock):
- resp = FakeResponse(data=fake_gopher_single)
- session_mock.return_value = mock.MagicMock(
- request=mock.MagicMock(return_value=resp),
- )
-
- api = restapi.RESTApi()
-
- # Test no key
- gopher = api.show(fake_url)
- session_mock.return_value.request.assert_called_with(
- 'GET',
- fake_url,
- headers=mock.ANY,
- allow_redirects=True,
- )
- self.assertEqual(gopher, fake_gopher_single)
-
- # Test with key
- gopher = api.show(fake_url, response_key=fake_key)
- session_mock.return_value.request.assert_called_with(
- 'GET',
- fake_url,
- headers=mock.ANY,
- allow_redirects=True,
- )
- self.assertEqual(gopher, fake_gopher_mac)
diff --git a/openstackclient/tests/common/test_timing.py b/openstackclient/tests/common/test_timing.py
new file mode 100644
index 00000000..aa910b91
--- /dev/null
+++ b/openstackclient/tests/common/test_timing.py
@@ -0,0 +1,87 @@
+# 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.
+#
+
+"""Test Timing pseudo-command"""
+
+from openstackclient.common import timing
+from openstackclient.tests import fakes
+from openstackclient.tests import utils
+
+
+timing_url = 'GET http://localhost:5000'
+timing_start = 1404802774.872809
+timing_end = 1404802775.724802
+
+
+class FakeGenericClient(object):
+
+ def __init__(self, **kwargs):
+ self.auth_token = kwargs['token']
+ self.management_url = kwargs['endpoint']
+
+
+class TestTiming(utils.TestCommand):
+
+ def setUp(self):
+ super(TestTiming, self).setUp()
+
+ self.app.timing_data = []
+
+ self.app.client_manager.compute = FakeGenericClient(
+ endpoint=fakes.AUTH_URL,
+ token=fakes.AUTH_TOKEN,
+ )
+
+ self.app.client_manager.volume = FakeGenericClient(
+ endpoint=fakes.AUTH_URL,
+ token=fakes.AUTH_TOKEN,
+ )
+
+ # Get the command object to test
+ self.cmd = timing.Timing(self.app, None)
+
+ def test_timing_list_no_data(self):
+ arglist = []
+ verifylist = []
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # DisplayCommandBase.take_action() returns two tuples
+ columns, data = self.cmd.take_action(parsed_args)
+
+ collist = ('URL', 'Seconds')
+ self.assertEqual(collist, columns)
+ datalist = [
+ ('Total', 0.0,)
+ ]
+ self.assertEqual(datalist, data)
+
+ def test_timing_list(self):
+ self.app.timing_data = [
+ (timing_url, timing_start, timing_end),
+ ]
+
+ arglist = []
+ verifylist = []
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # DisplayCommandBase.take_action() returns two tuples
+ columns, data = self.cmd.take_action(parsed_args)
+
+ collist = ('URL', 'Seconds')
+ self.assertEqual(collist, columns)
+ timing_sec = timing_end - timing_start
+ datalist = [
+ (timing_url, timing_sec),
+ ('Total', timing_sec)
+ ]
+ self.assertEqual(datalist, data)
diff --git a/openstackclient/tests/common/test_utils.py b/openstackclient/tests/common/test_utils.py
index 6d75a9b5..e782d410 100644
--- a/openstackclient/tests/common/test_utils.py
+++ b/openstackclient/tests/common/test_utils.py
@@ -130,3 +130,15 @@ class TestFindResource(test_utils.TestCase):
str(result))
self.manager.get.assert_called_with(self.name)
self.manager.find.assert_called_with(name=self.name)
+
+ def test_format_dict(self):
+ expected = "a='b', c='d', e='f'"
+ self.assertEqual(expected,
+ utils.format_dict({'a': 'b', 'c': 'd', 'e': 'f'}))
+ self.assertEqual(expected,
+ utils.format_dict({'e': 'f', 'c': 'd', 'a': 'b'}))
+
+ def test_format_list(self):
+ expected = 'a, b, c'
+ self.assertEqual(expected, utils.format_list(['a', 'b', 'c']))
+ self.assertEqual(expected, utils.format_list(['c', 'b', 'a']))
diff --git a/openstackclient/tests/compute/v2/fakes.py b/openstackclient/tests/compute/v2/fakes.py
index cef5ee90..9a7964db 100644
--- a/openstackclient/tests/compute/v2/fakes.py
+++ b/openstackclient/tests/compute/v2/fakes.py
@@ -47,6 +47,18 @@ EXTENSION = {
'links': extension_links,
}
+flavor_id = 'm1.large'
+flavor_name = 'Large'
+flavor_ram = 8192
+flavor_vcpus = 4
+
+FLAVOR = {
+ 'id': flavor_id,
+ 'name': flavor_name,
+ 'ram': flavor_ram,
+ 'vcpus': flavor_vcpus,
+}
+
class FakeComputev2Client(object):
def __init__(self, **kwargs):
@@ -56,6 +68,8 @@ class FakeComputev2Client(object):
self.servers.resource_class = fakes.FakeResource(None, {})
self.extensions = mock.Mock()
self.extensions.resource_class = fakes.FakeResource(None, {})
+ self.flavors = mock.Mock()
+ self.flavors.resource_class = fakes.FakeResource(None, {})
self.auth_token = kwargs['token']
self.management_url = kwargs['endpoint']
diff --git a/openstackclient/tests/compute/v2/test_server.py b/openstackclient/tests/compute/v2/test_server.py
index efe4c58b..a98cd156 100644
--- a/openstackclient/tests/compute/v2/test_server.py
+++ b/openstackclient/tests/compute/v2/test_server.py
@@ -30,6 +30,10 @@ class TestServer(compute_fakes.TestComputev2):
self.servers_mock = self.app.client_manager.compute.servers
self.servers_mock.reset_mock()
+ # Get a shortcut to the FlavorManager Mock
+ self.flavors_mock = self.app.client_manager.compute.flavors
+ self.flavors_mock.reset_mock()
+
# Get a shortcut to the ImageManager Mock
self.images_mock = self.app.client_manager.image.images
self.images_mock.reset_mock()
@@ -148,3 +152,134 @@ class TestServerImageCreate(TestServer):
image_fakes.image_owner,
)
self.assertEqual(data, datalist)
+
+
+class TestServerResize(TestServer):
+
+ def setUp(self):
+ super(TestServerResize, self).setUp()
+
+ # This is the return value for utils.find_resource()
+ self.servers_get_return_value = fakes.FakeResource(
+ None,
+ copy.deepcopy(compute_fakes.SERVER),
+ loaded=True,
+ )
+ self.servers_mock.get.return_value = self.servers_get_return_value
+
+ self.servers_mock.resize.return_value = None
+ self.servers_mock.confirm_resize.return_value = None
+ self.servers_mock.revert_resize.return_value = None
+
+ # This is the return value for utils.find_resource()
+ self.flavors_get_return_value = fakes.FakeResource(
+ None,
+ copy.deepcopy(compute_fakes.FLAVOR),
+ loaded=True,
+ )
+ self.flavors_mock.get.return_value = self.flavors_get_return_value
+
+ # Get the command object to test
+ self.cmd = server.ResizeServer(self.app, None)
+
+ def test_server_resize_no_options(self):
+ arglist = [
+ compute_fakes.server_id,
+ ]
+ verifylist = [
+ ('verify', False),
+ ('revert', False),
+ ('server', compute_fakes.server_id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # DisplayCommandBase.take_action() returns two tuples
+ self.cmd.take_action(parsed_args)
+
+ self.servers_mock.get.assert_called_with(
+ compute_fakes.server_id,
+ )
+
+ self.assertNotCalled(self.servers_mock.resize)
+ self.assertNotCalled(self.servers_mock.confirm_resize)
+ self.assertNotCalled(self.servers_mock.revert_resize)
+
+ def test_server_resize(self):
+ arglist = [
+ '--flavor', compute_fakes.flavor_id,
+ compute_fakes.server_id,
+ ]
+ verifylist = [
+ ('flavor', compute_fakes.flavor_id),
+ ('verify', False),
+ ('revert', False),
+ ('server', compute_fakes.server_id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # DisplayCommandBase.take_action() returns two tuples
+ self.cmd.take_action(parsed_args)
+
+ self.servers_mock.get.assert_called_with(
+ compute_fakes.server_id,
+ )
+ self.flavors_mock.get.assert_called_with(
+ compute_fakes.flavor_id,
+ )
+
+ self.servers_mock.resize.assert_called_with(
+ self.servers_get_return_value,
+ self.flavors_get_return_value,
+ )
+ self.assertNotCalled(self.servers_mock.confirm_resize)
+ self.assertNotCalled(self.servers_mock.revert_resize)
+
+ def test_server_resize_confirm(self):
+ arglist = [
+ '--verify',
+ compute_fakes.server_id,
+ ]
+ verifylist = [
+ ('verify', True),
+ ('revert', False),
+ ('server', compute_fakes.server_id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # DisplayCommandBase.take_action() returns two tuples
+ self.cmd.take_action(parsed_args)
+
+ self.servers_mock.get.assert_called_with(
+ compute_fakes.server_id,
+ )
+
+ self.assertNotCalled(self.servers_mock.resize)
+ self.servers_mock.confirm_resize.assert_called_with(
+ self.servers_get_return_value,
+ )
+ self.assertNotCalled(self.servers_mock.revert_resize)
+
+ def test_server_resize_revert(self):
+ arglist = [
+ '--revert',
+ compute_fakes.server_id,
+ ]
+ verifylist = [
+ ('verify', False),
+ ('revert', True),
+ ('server', compute_fakes.server_id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # DisplayCommandBase.take_action() returns two tuples
+ self.cmd.take_action(parsed_args)
+
+ self.servers_mock.get.assert_called_with(
+ compute_fakes.server_id,
+ )
+
+ self.assertNotCalled(self.servers_mock.resize)
+ self.assertNotCalled(self.servers_mock.confirm_resize)
+ self.servers_mock.revert_resize.assert_called_with(
+ self.servers_get_return_value,
+ )
diff --git a/openstackclient/tests/fakes.py b/openstackclient/tests/fakes.py
index fb27ef94..263640ee 100644
--- a/openstackclient/tests/fakes.py
+++ b/openstackclient/tests/fakes.py
@@ -13,9 +13,12 @@
# under the License.
#
+import json
import six
import sys
+import requests
+
AUTH_TOKEN = "foobar"
AUTH_URL = "http://0.0.0.0"
@@ -42,7 +45,6 @@ class FakeApp(object):
self.stdin = sys.stdin
self.stdout = _stdout or sys.stdout
self.stderr = sys.stderr
- self.restapi = None
class FakeClientManager(object):
@@ -53,6 +55,7 @@ class FakeClientManager(object):
self.object = None
self.volume = None
self.network = None
+ self.session = None
self.auth_ref = None
@@ -78,3 +81,15 @@ class FakeResource(object):
k != 'manager')
info = ", ".join("%s=%s" % (k, getattr(self, k)) for k in reprkeys)
return "<%s %s>" % (self.__class__.__name__, info)
+
+
+class FakeResponse(requests.Response):
+ def __init__(self, headers={}, status_code=200, data=None, encoding=None):
+ super(FakeResponse, self).__init__()
+
+ self.status_code = status_code
+
+ self.headers.update(headers)
+ self._content = json.dumps(data)
+ if not isinstance(self._content, six.binary_type):
+ self._content = self._content.encode()
diff --git a/openstackclient/tests/identity/v2_0/fakes.py b/openstackclient/tests/identity/v2_0/fakes.py
index a8438e96..b136f841 100644
--- a/openstackclient/tests/identity/v2_0/fakes.py
+++ b/openstackclient/tests/identity/v2_0/fakes.py
@@ -125,7 +125,6 @@ class FakeIdentityv2Client(object):
def __init__(self, **kwargs):
self.roles = mock.Mock()
self.roles.resource_class = fakes.FakeResource(None, {})
- self.service_catalog = mock.Mock()
self.services = mock.Mock()
self.services.resource_class = fakes.FakeResource(None, {})
self.tenants = mock.Mock()
diff --git a/openstackclient/tests/identity/v2_0/test_token.py b/openstackclient/tests/identity/v2_0/test_token.py
index e094ad4a..4184326c 100644
--- a/openstackclient/tests/identity/v2_0/test_token.py
+++ b/openstackclient/tests/identity/v2_0/test_token.py
@@ -13,6 +13,8 @@
# under the License.
#
+import mock
+
from openstackclient.identity.v2_0 import token
from openstackclient.tests.identity.v2_0 import fakes as identity_fakes
@@ -23,8 +25,9 @@ class TestToken(identity_fakes.TestIdentityv2):
super(TestToken, self).setUp()
# Get a shortcut to the Service Catalog Mock
- self.sc_mock = self.app.client_manager.identity.service_catalog
- self.sc_mock.reset_mock()
+ self.sc_mock = mock.Mock()
+ self.app.client_manager.auth_ref = mock.Mock()
+ self.app.client_manager.auth_ref.service_catalog = self.sc_mock
class TestTokenIssue(TestToken):
diff --git a/openstackclient/tests/identity/v3/fakes.py b/openstackclient/tests/identity/v3/fakes.py
index 8143409d..e9cda9ff 100644
--- a/openstackclient/tests/identity/v3/fakes.py
+++ b/openstackclient/tests/identity/v3/fakes.py
@@ -21,10 +21,13 @@ from openstackclient.tests import utils
domain_id = 'd1'
domain_name = 'oftheking'
+domain_description = 'domain description'
DOMAIN = {
'id': domain_id,
'name': domain_name,
+ 'description': domain_description,
+ 'enabled': True,
}
group_id = 'gr-010'
@@ -74,6 +77,20 @@ SERVICE = {
'enabled': True,
}
+endpoint_id = 'e-123'
+endpoint_url = 'http://127.0.0.1:35357'
+endpoint_region = 'RegionOne'
+endpoint_interface = 'admin'
+
+ENDPOINT = {
+ 'id': endpoint_id,
+ 'url': endpoint_url,
+ 'region': endpoint_region,
+ 'interface': endpoint_interface,
+ 'service_id': service_id,
+ 'enabled': True,
+}
+
user_id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'
user_name = 'paul'
user_description = 'Sir Paul'
@@ -182,6 +199,8 @@ class FakeIdentityv3Client(object):
def __init__(self, **kwargs):
self.domains = mock.Mock()
self.domains.resource_class = fakes.FakeResource(None, {})
+ self.endpoints = mock.Mock()
+ self.endpoints.resource_class = fakes.FakeResource(None, {})
self.groups = mock.Mock()
self.groups.resource_class = fakes.FakeResource(None, {})
self.oauth1 = mock.Mock()
@@ -201,14 +220,18 @@ class FakeIdentityv3Client(object):
self.management_url = kwargs['endpoint']
-class FakeFederatedClient(FakeIdentityv3Client):
+class FakeFederationManager(object):
def __init__(self, **kwargs):
- super(FakeFederatedClient, self).__init__(**kwargs)
-
self.identity_providers = mock.Mock()
self.identity_providers.resource_class = fakes.FakeResource(None, {})
+class FakeFederatedClient(FakeIdentityv3Client):
+ def __init__(self, **kwargs):
+ super(FakeFederatedClient, self).__init__(**kwargs)
+ self.federation = FakeFederationManager()
+
+
class FakeOAuth1Client(FakeIdentityv3Client):
def __init__(self, **kwargs):
super(FakeOAuth1Client, self).__init__(**kwargs)
diff --git a/openstackclient/tests/identity/v3/test_domain.py b/openstackclient/tests/identity/v3/test_domain.py
new file mode 100644
index 00000000..8dad5bcc
--- /dev/null
+++ b/openstackclient/tests/identity/v3/test_domain.py
@@ -0,0 +1,414 @@
+# 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 copy
+
+from openstackclient.identity.v3 import domain
+from openstackclient.tests import fakes
+from openstackclient.tests.identity.v3 import fakes as identity_fakes
+
+
+class TestDomain(identity_fakes.TestIdentityv3):
+
+ def setUp(self):
+ super(TestDomain, self).setUp()
+
+ # Get a shortcut to the DomainManager Mock
+ self.domains_mock = self.app.client_manager.identity.domains
+ self.domains_mock.reset_mock()
+
+
+class TestDomainCreate(TestDomain):
+
+ def setUp(self):
+ super(TestDomainCreate, self).setUp()
+
+ self.domains_mock.create.return_value = fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.DOMAIN),
+ loaded=True,
+ )
+
+ # Get the command object to test
+ self.cmd = domain.CreateDomain(self.app, None)
+
+ def test_domain_create_no_options(self):
+ arglist = [
+ identity_fakes.domain_name,
+ ]
+ verifylist = [
+ ('enabled', True),
+ ('name', identity_fakes.domain_name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # DisplayCommandBase.take_action() returns two tuples
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'name': identity_fakes.domain_name,
+ 'description': None,
+ 'enabled': True,
+ }
+ self.domains_mock.create.assert_called_with(
+ **kwargs
+ )
+
+ collist = ('description', 'enabled', 'id', 'name')
+ self.assertEqual(collist, columns)
+ datalist = (
+ identity_fakes.domain_description,
+ True,
+ identity_fakes.domain_id,
+ identity_fakes.domain_name,
+ )
+ self.assertEqual(datalist, data)
+
+ def test_domain_create_description(self):
+ arglist = [
+ '--description', 'new desc',
+ identity_fakes.domain_name,
+ ]
+ verifylist = [
+ ('description', 'new desc'),
+ ('enabled', True),
+ ('name', identity_fakes.domain_name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # DisplayCommandBase.take_action() returns two tuples
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'name': identity_fakes.domain_name,
+ 'description': 'new desc',
+ 'enabled': True,
+ }
+ self.domains_mock.create.assert_called_with(
+ **kwargs
+ )
+
+ collist = ('description', 'enabled', 'id', 'name')
+ self.assertEqual(collist, columns)
+ datalist = (
+ identity_fakes.domain_description,
+ True,
+ identity_fakes.domain_id,
+ identity_fakes.domain_name,
+ )
+ self.assertEqual(datalist, data)
+
+ def test_domain_create_enable(self):
+ arglist = [
+ '--enable',
+ identity_fakes.domain_name,
+ ]
+ verifylist = [
+ ('enabled', True),
+ ('name', identity_fakes.domain_name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # DisplayCommandBase.take_action() returns two tuples
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'name': identity_fakes.domain_name,
+ 'description': None,
+ 'enabled': True,
+ }
+ self.domains_mock.create.assert_called_with(
+ **kwargs
+ )
+
+ collist = ('description', 'enabled', 'id', 'name')
+ self.assertEqual(collist, columns)
+ datalist = (
+ identity_fakes.domain_description,
+ True,
+ identity_fakes.domain_id,
+ identity_fakes.domain_name,
+ )
+ self.assertEqual(datalist, data)
+
+ def test_domain_create_disable(self):
+ arglist = [
+ '--disable',
+ identity_fakes.domain_name,
+ ]
+ verifylist = [
+ ('enabled', False),
+ ('name', identity_fakes.domain_name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # DisplayCommandBase.take_action() returns two tuples
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'name': identity_fakes.domain_name,
+ 'description': None,
+ 'enabled': False,
+ }
+ self.domains_mock.create.assert_called_with(
+ **kwargs
+ )
+
+ collist = ('description', 'enabled', 'id', 'name')
+ self.assertEqual(collist, columns)
+ datalist = (
+ identity_fakes.domain_description,
+ True,
+ identity_fakes.domain_id,
+ identity_fakes.domain_name,
+ )
+ self.assertEqual(datalist, data)
+
+
+class TestDomainDelete(TestDomain):
+
+ def setUp(self):
+ super(TestDomainDelete, self).setUp()
+
+ # This is the return value for utils.find_resource()
+ self.domains_mock.get.return_value = fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.DOMAIN),
+ loaded=True,
+ )
+ self.domains_mock.delete.return_value = None
+
+ # Get the command object to test
+ self.cmd = domain.DeleteDomain(self.app, None)
+
+ def test_domain_delete(self):
+ arglist = [
+ identity_fakes.domain_id,
+ ]
+ verifylist = [
+ ('domain', identity_fakes.domain_id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.run(parsed_args)
+ self.assertEqual(0, result)
+
+ self.domains_mock.delete.assert_called_with(
+ identity_fakes.domain_id,
+ )
+
+
+class TestDomainList(TestDomain):
+
+ def setUp(self):
+ super(TestDomainList, self).setUp()
+
+ self.domains_mock.list.return_value = [
+ fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.DOMAIN),
+ loaded=True,
+ ),
+ ]
+
+ # Get the command object to test
+ self.cmd = domain.ListDomain(self.app, None)
+
+ def test_domain_list_no_options(self):
+ arglist = []
+ verifylist = []
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # DisplayCommandBase.take_action() returns two tuples
+ columns, data = self.cmd.take_action(parsed_args)
+ self.domains_mock.list.assert_called_with()
+
+ collist = ('ID', 'Name', 'Enabled', 'Description')
+ self.assertEqual(collist, columns)
+ datalist = ((
+ identity_fakes.domain_id,
+ identity_fakes.domain_name,
+ True,
+ identity_fakes.domain_description,
+ ), )
+ self.assertEqual(datalist, tuple(data))
+
+
+class TestDomainSet(TestDomain):
+
+ def setUp(self):
+ super(TestDomainSet, self).setUp()
+
+ self.domains_mock.get.return_value = fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.DOMAIN),
+ loaded=True,
+ )
+
+ self.domains_mock.update.return_value = fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.DOMAIN),
+ loaded=True,
+ )
+
+ # Get the command object to test
+ self.cmd = domain.SetDomain(self.app, None)
+
+ def test_domain_set_no_options(self):
+ arglist = [
+ identity_fakes.domain_name,
+ ]
+ verifylist = [
+ ('domain', identity_fakes.domain_name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.run(parsed_args)
+ self.assertEqual(0, result)
+
+ self.assertNotCalled(self.domains_mock.update)
+
+ def test_domain_set_name(self):
+ arglist = [
+ '--name', 'qwerty',
+ identity_fakes.domain_id,
+ ]
+ verifylist = [
+ ('name', 'qwerty'),
+ ('domain', identity_fakes.domain_id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.run(parsed_args)
+ self.assertEqual(0, result)
+
+ # Set expected values
+ kwargs = {
+ 'name': 'qwerty',
+ }
+ self.domains_mock.update.assert_called_with(
+ identity_fakes.domain_id,
+ **kwargs
+ )
+
+ def test_domain_set_description(self):
+ arglist = [
+ '--description', 'new desc',
+ identity_fakes.domain_id,
+ ]
+ verifylist = [
+ ('description', 'new desc'),
+ ('domain', identity_fakes.domain_id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.run(parsed_args)
+ self.assertEqual(0, result)
+
+ # Set expected values
+ kwargs = {
+ 'description': 'new desc',
+ }
+ self.domains_mock.update.assert_called_with(
+ identity_fakes.domain_id,
+ **kwargs
+ )
+
+ def test_domain_set_enable(self):
+ arglist = [
+ '--enable',
+ identity_fakes.domain_id,
+ ]
+ verifylist = [
+ ('enabled', True),
+ ('domain', identity_fakes.domain_id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.run(parsed_args)
+ self.assertEqual(0, result)
+
+ # Set expected values
+ kwargs = {
+ 'enabled': True,
+ }
+ self.domains_mock.update.assert_called_with(
+ identity_fakes.domain_id,
+ **kwargs
+ )
+
+ def test_domain_set_disable(self):
+ arglist = [
+ '--disable',
+ identity_fakes.domain_id,
+ ]
+ verifylist = [
+ ('disabled', True),
+ ('domain', identity_fakes.domain_id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.run(parsed_args)
+ self.assertEqual(0, result)
+
+ # Set expected values
+ kwargs = {
+ 'enabled': False,
+ }
+ self.domains_mock.update.assert_called_with(
+ identity_fakes.domain_id,
+ **kwargs
+ )
+
+
+class TestDomainShow(TestDomain):
+
+ def setUp(self):
+ super(TestDomainShow, self).setUp()
+
+ self.domains_mock.get.return_value = fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.DOMAIN),
+ loaded=True,
+ )
+
+ # Get the command object to test
+ self.cmd = domain.ShowDomain(self.app, None)
+
+ def test_domain_show(self):
+ arglist = [
+ identity_fakes.domain_id,
+ ]
+ verifylist = [
+ ('domain', identity_fakes.domain_id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # DisplayCommandBase.take_action() returns two tuples
+ columns, data = self.cmd.take_action(parsed_args)
+ self.domains_mock.get.assert_called_with(
+ identity_fakes.domain_id,
+ )
+
+ collist = ('description', 'enabled', 'id', 'name')
+ self.assertEqual(collist, columns)
+ datalist = (
+ identity_fakes.domain_description,
+ True,
+ identity_fakes.domain_id,
+ identity_fakes.domain_name,
+ )
+ self.assertEqual(datalist, data)
diff --git a/openstackclient/tests/identity/v3/test_endpoint.py b/openstackclient/tests/identity/v3/test_endpoint.py
new file mode 100644
index 00000000..ea05326e
--- /dev/null
+++ b/openstackclient/tests/identity/v3/test_endpoint.py
@@ -0,0 +1,671 @@
+# 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 copy
+
+from openstackclient.identity.v3 import endpoint
+from openstackclient.tests import fakes
+from openstackclient.tests.identity.v3 import fakes as identity_fakes
+
+
+class TestEndpoint(identity_fakes.TestIdentityv3):
+
+ def setUp(self):
+ super(TestEndpoint, self).setUp()
+
+ # Get a shortcut to the EndpointManager Mock
+ self.endpoints_mock = self.app.client_manager.identity.endpoints
+ self.endpoints_mock.reset_mock()
+
+ # Get a shortcut to the ServiceManager Mock
+ self.services_mock = self.app.client_manager.identity.services
+ self.services_mock.reset_mock()
+
+
+class TestEndpointCreate(TestEndpoint):
+
+ def setUp(self):
+ super(TestEndpointCreate, self).setUp()
+
+ self.endpoints_mock.create.return_value = fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.ENDPOINT),
+ loaded=True,
+ )
+
+ # This is the return value for common.find_resource(service)
+ self.services_mock.get.return_value = fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.SERVICE),
+ loaded=True,
+ )
+
+ # Get the command object to test
+ self.cmd = endpoint.CreateEndpoint(self.app, None)
+
+ def test_endpoint_create_no_options(self):
+ arglist = [
+ identity_fakes.service_id,
+ identity_fakes.endpoint_interface,
+ identity_fakes.endpoint_url,
+ ]
+ verifylist = [
+ ('enabled', True),
+ ('service', identity_fakes.service_id),
+ ('interface', identity_fakes.endpoint_interface),
+ ('url', identity_fakes.endpoint_url),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # DisplayCommandBase.take_action() returns two tuples
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'service': identity_fakes.service_id,
+ 'url': identity_fakes.endpoint_url,
+ 'interface': identity_fakes.endpoint_interface,
+ 'enabled': True,
+ 'region': None,
+ }
+
+ self.endpoints_mock.create.assert_called_with(
+ **kwargs
+ )
+
+ collist = ('enabled', 'id', 'interface', 'region', 'service_id',
+ 'service_name', 'service_type', 'url')
+ self.assertEqual(collist, columns)
+ datalist = (
+ True,
+ identity_fakes.endpoint_id,
+ identity_fakes.endpoint_interface,
+ identity_fakes.endpoint_region,
+ identity_fakes.service_id,
+ identity_fakes.service_name,
+ identity_fakes.service_type,
+ identity_fakes.endpoint_url,
+ )
+ self.assertEqual(datalist, data)
+
+ def test_endpoint_create_region(self):
+ arglist = [
+ identity_fakes.service_id,
+ identity_fakes.endpoint_interface,
+ identity_fakes.endpoint_url,
+ '--region', identity_fakes.endpoint_region,
+ ]
+ verifylist = [
+ ('enabled', True),
+ ('service', identity_fakes.service_id),
+ ('interface', identity_fakes.endpoint_interface),
+ ('url', identity_fakes.endpoint_url),
+ ('region', identity_fakes.endpoint_region),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # DisplayCommandBase.take_action() returns two tuples
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'service': identity_fakes.service_id,
+ 'url': identity_fakes.endpoint_url,
+ 'interface': identity_fakes.endpoint_interface,
+ 'enabled': True,
+ 'region': identity_fakes.endpoint_region,
+ }
+
+ self.endpoints_mock.create.assert_called_with(
+ **kwargs
+ )
+
+ collist = ('enabled', 'id', 'interface', 'region', 'service_id',
+ 'service_name', 'service_type', 'url')
+ self.assertEqual(collist, columns)
+ datalist = (
+ True,
+ identity_fakes.endpoint_id,
+ identity_fakes.endpoint_interface,
+ identity_fakes.endpoint_region,
+ identity_fakes.service_id,
+ identity_fakes.service_name,
+ identity_fakes.service_type,
+ identity_fakes.endpoint_url,
+ )
+ self.assertEqual(datalist, data)
+
+ def test_endpoint_create_enable(self):
+ arglist = [
+ identity_fakes.service_id,
+ identity_fakes.endpoint_interface,
+ identity_fakes.endpoint_url,
+ '--enable'
+ ]
+ verifylist = [
+ ('enabled', True),
+ ('service', identity_fakes.service_id),
+ ('interface', identity_fakes.endpoint_interface),
+ ('url', identity_fakes.endpoint_url),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # DisplayCommandBase.take_action() returns two tuples
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'service': identity_fakes.service_id,
+ 'url': identity_fakes.endpoint_url,
+ 'interface': identity_fakes.endpoint_interface,
+ 'enabled': True,
+ 'region': None,
+ }
+
+ self.endpoints_mock.create.assert_called_with(
+ **kwargs
+ )
+
+ collist = ('enabled', 'id', 'interface', 'region', 'service_id',
+ 'service_name', 'service_type', 'url')
+ self.assertEqual(collist, columns)
+ datalist = (
+ True,
+ identity_fakes.endpoint_id,
+ identity_fakes.endpoint_interface,
+ identity_fakes.endpoint_region,
+ identity_fakes.service_id,
+ identity_fakes.service_name,
+ identity_fakes.service_type,
+ identity_fakes.endpoint_url,
+ )
+ self.assertEqual(datalist, data)
+
+ def test_endpoint_create_disable(self):
+ arglist = [
+ identity_fakes.service_id,
+ identity_fakes.endpoint_interface,
+ identity_fakes.endpoint_url,
+ '--disable',
+ ]
+ verifylist = [
+ ('enabled', False),
+ ('service', identity_fakes.service_id),
+ ('interface', identity_fakes.endpoint_interface),
+ ('url', identity_fakes.endpoint_url),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # DisplayCommandBase.take_action() returns two tuples
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'service': identity_fakes.service_id,
+ 'url': identity_fakes.endpoint_url,
+ 'interface': identity_fakes.endpoint_interface,
+ 'enabled': False,
+ 'region': None,
+ }
+
+ self.endpoints_mock.create.assert_called_with(
+ **kwargs
+ )
+
+ collist = ('enabled', 'id', 'interface', 'region', 'service_id',
+ 'service_name', 'service_type', 'url')
+ self.assertEqual(collist, columns)
+ datalist = (
+ True,
+ identity_fakes.endpoint_id,
+ identity_fakes.endpoint_interface,
+ identity_fakes.endpoint_region,
+ identity_fakes.service_id,
+ identity_fakes.service_name,
+ identity_fakes.service_type,
+ identity_fakes.endpoint_url,
+ )
+ self.assertEqual(datalist, data)
+
+
+class TestEndpointDelete(TestEndpoint):
+
+ def setUp(self):
+ super(TestEndpointDelete, self).setUp()
+
+ # This is the return value for utils.find_resource(endpoint)
+ self.endpoints_mock.get.return_value = fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.ENDPOINT),
+ loaded=True,
+ )
+ self.endpoints_mock.delete.return_value = None
+
+ # Get the command object to test
+ self.cmd = endpoint.DeleteEndpoint(self.app, None)
+
+ def test_endpoint_delete(self):
+ arglist = [
+ identity_fakes.endpoint_id,
+ ]
+ verifylist = [
+ ('endpoint', identity_fakes.endpoint_id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.run(parsed_args)
+ self.assertEqual(0, result)
+
+ self.endpoints_mock.delete.assert_called_with(
+ identity_fakes.endpoint_id,
+ )
+
+
+class TestEndpointList(TestEndpoint):
+
+ def setUp(self):
+ super(TestEndpointList, self).setUp()
+
+ self.endpoints_mock.list.return_value = [
+ fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.ENDPOINT),
+ loaded=True,
+ ),
+ ]
+
+ # This is the return value for common.find_resource(service)
+ self.services_mock.get.return_value = fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.SERVICE),
+ loaded=True,
+ )
+
+ # Get the command object to test
+ self.cmd = endpoint.ListEndpoint(self.app, None)
+
+ def test_endpoint_list_no_options(self):
+ arglist = []
+ verifylist = []
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # DisplayCommandBase.take_action() returns two tuples
+ columns, data = self.cmd.take_action(parsed_args)
+ self.endpoints_mock.list.assert_called_with()
+
+ collist = ('ID', 'Region', 'Service Name', 'Service Type',
+ 'Enabled', 'Interface', 'URL')
+ self.assertEqual(collist, columns)
+ datalist = ((
+ identity_fakes.endpoint_id,
+ identity_fakes.endpoint_region,
+ identity_fakes.service_name,
+ identity_fakes.service_type,
+ True,
+ identity_fakes.endpoint_interface,
+ identity_fakes.endpoint_url,
+ ),)
+ self.assertEqual(datalist, tuple(data))
+
+ def test_endpoint_list_service(self):
+ arglist = [
+ '--service', identity_fakes.service_name,
+ ]
+ verifylist = [
+ ('service', identity_fakes.service_name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # DisplayCommandBase.take_action() returns two tuples
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'service': identity_fakes.service_id,
+ }
+ self.endpoints_mock.list.assert_called_with(**kwargs)
+
+ collist = ('ID', 'Region', 'Service Name', 'Service Type',
+ 'Enabled', 'Interface', 'URL')
+ self.assertEqual(collist, columns)
+ datalist = ((
+ identity_fakes.endpoint_id,
+ identity_fakes.endpoint_region,
+ identity_fakes.service_name,
+ identity_fakes.service_type,
+ True,
+ identity_fakes.endpoint_interface,
+ identity_fakes.endpoint_url,
+ ),)
+ self.assertEqual(datalist, tuple(data))
+
+ def test_endpoint_list_interface(self):
+ arglist = [
+ '--interface', identity_fakes.endpoint_interface,
+ ]
+ verifylist = [
+ ('interface', identity_fakes.endpoint_interface),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # DisplayCommandBase.take_action() returns two tuples
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'interface': identity_fakes.endpoint_interface,
+ }
+ self.endpoints_mock.list.assert_called_with(**kwargs)
+
+ collist = ('ID', 'Region', 'Service Name', 'Service Type',
+ 'Enabled', 'Interface', 'URL')
+ self.assertEqual(collist, columns)
+ datalist = ((
+ identity_fakes.endpoint_id,
+ identity_fakes.endpoint_region,
+ identity_fakes.service_name,
+ identity_fakes.service_type,
+ True,
+ identity_fakes.endpoint_interface,
+ identity_fakes.endpoint_url,
+ ),)
+ self.assertEqual(datalist, tuple(data))
+
+ def test_endpoint_list_region(self):
+ arglist = [
+ '--region', identity_fakes.endpoint_region,
+ ]
+ verifylist = [
+ ('region', identity_fakes.endpoint_region),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # DisplayCommandBase.take_action() returns two tuples
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'region': identity_fakes.endpoint_region,
+ }
+ self.endpoints_mock.list.assert_called_with(**kwargs)
+
+ collist = ('ID', 'Region', 'Service Name', 'Service Type',
+ 'Enabled', 'Interface', 'URL')
+ self.assertEqual(collist, columns)
+ datalist = ((
+ identity_fakes.endpoint_id,
+ identity_fakes.endpoint_region,
+ identity_fakes.service_name,
+ identity_fakes.service_type,
+ True,
+ identity_fakes.endpoint_interface,
+ identity_fakes.endpoint_url,
+ ),)
+ self.assertEqual(datalist, tuple(data))
+
+
+class TestEndpointSet(TestEndpoint):
+
+ def setUp(self):
+ super(TestEndpointSet, self).setUp()
+
+ # This is the return value for utils.find_resource(endpoint)
+ self.endpoints_mock.get.return_value = fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.ENDPOINT),
+ loaded=True,
+ )
+
+ self.endpoints_mock.update.return_value = fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.ENDPOINT),
+ loaded=True,
+ )
+
+ # This is the return value for common.find_resource(service)
+ self.services_mock.get.return_value = fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.SERVICE),
+ loaded=True,
+ )
+
+ # Get the command object to test
+ self.cmd = endpoint.SetEndpoint(self.app, None)
+
+ def test_endpoint_set_no_options(self):
+ arglist = [
+ identity_fakes.endpoint_id,
+ ]
+ verifylist = [
+ ('endpoint', identity_fakes.endpoint_id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.run(parsed_args)
+ self.assertEqual(0, result)
+
+ self.assertNotCalled(self.endpoints_mock.update)
+
+ def test_endpoint_set_interface(self):
+ arglist = [
+ '--interface', 'public',
+ identity_fakes.endpoint_id
+ ]
+ verifylist = [
+ ('interface', 'public'),
+ ('endpoint', identity_fakes.endpoint_id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.run(parsed_args)
+ self.assertEqual(0, result)
+
+ # Set expected values
+ kwargs = {
+ 'enabled': None,
+ 'interface': 'public',
+ 'url': None,
+ 'region': None,
+ 'service': None,
+ }
+ self.endpoints_mock.update.assert_called_with(
+ identity_fakes.endpoint_id,
+ **kwargs
+ )
+
+ def test_endpoint_set_url(self):
+ arglist = [
+ '--url', 'http://localhost:5000',
+ identity_fakes.endpoint_id
+ ]
+ verifylist = [
+ ('url', 'http://localhost:5000'),
+ ('endpoint', identity_fakes.endpoint_id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.run(parsed_args)
+ self.assertEqual(0, result)
+
+ # Set expected values
+ kwargs = {
+ 'enabled': None,
+ 'interface': None,
+ 'url': 'http://localhost:5000',
+ 'region': None,
+ 'service': None,
+ }
+ self.endpoints_mock.update.assert_called_with(
+ identity_fakes.endpoint_id,
+ **kwargs
+ )
+
+ def test_endpoint_set_service(self):
+ arglist = [
+ '--service', identity_fakes.service_id,
+ identity_fakes.endpoint_id
+ ]
+ verifylist = [
+ ('service', identity_fakes.service_id),
+ ('endpoint', identity_fakes.endpoint_id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.run(parsed_args)
+ self.assertEqual(0, result)
+
+ # Set expected values
+ kwargs = {
+ 'enabled': None,
+ 'interface': None,
+ 'url': None,
+ 'region': None,
+ 'service': identity_fakes.service_id,
+ }
+ self.endpoints_mock.update.assert_called_with(
+ identity_fakes.endpoint_id,
+ **kwargs
+ )
+
+ def test_endpoint_set_region(self):
+ arglist = [
+ '--region', 'e-rzzz',
+ identity_fakes.endpoint_id
+ ]
+ verifylist = [
+ ('region', 'e-rzzz'),
+ ('endpoint', identity_fakes.endpoint_id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.run(parsed_args)
+ self.assertEqual(0, result)
+
+ # Set expected values
+ kwargs = {
+ 'enabled': None,
+ 'interface': None,
+ 'url': None,
+ 'region': 'e-rzzz',
+ 'service': None,
+ }
+ self.endpoints_mock.update.assert_called_with(
+ identity_fakes.endpoint_id,
+ **kwargs
+ )
+
+ def test_endpoint_set_enable(self):
+ arglist = [
+ '--enable',
+ identity_fakes.endpoint_id
+ ]
+ verifylist = [
+ ('enabled', True),
+ ('endpoint', identity_fakes.endpoint_id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.run(parsed_args)
+ self.assertEqual(0, result)
+
+ # Set expected values
+ kwargs = {
+ 'enabled': True,
+ 'interface': None,
+ 'url': None,
+ 'region': None,
+ 'service': None,
+ }
+ self.endpoints_mock.update.assert_called_with(
+ identity_fakes.endpoint_id,
+ **kwargs
+ )
+
+ def test_endpoint_set_disable(self):
+ arglist = [
+ '--disable',
+ identity_fakes.endpoint_id
+ ]
+ verifylist = [
+ ('disabled', True),
+ ('endpoint', identity_fakes.endpoint_id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.run(parsed_args)
+ self.assertEqual(0, result)
+
+ # Set expected values
+ kwargs = {
+ 'enabled': False,
+ 'interface': None,
+ 'url': None,
+ 'region': None,
+ 'service': None,
+ }
+ self.endpoints_mock.update.assert_called_with(
+ identity_fakes.endpoint_id,
+ **kwargs
+ )
+
+
+class TestEndpointShow(TestEndpoint):
+
+ def setUp(self):
+ super(TestEndpointShow, self).setUp()
+
+ self.endpoints_mock.get.return_value = fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.ENDPOINT),
+ loaded=True,
+ )
+
+ # This is the return value for common.find_resource(service)
+ self.services_mock.get.return_value = fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.SERVICE),
+ loaded=True,
+ )
+
+ # Get the command object to test
+ self.cmd = endpoint.ShowEndpoint(self.app, None)
+
+ def test_endpoint_show(self):
+ arglist = [
+ identity_fakes.endpoint_id,
+ ]
+ verifylist = [
+ ('endpoint', identity_fakes.endpoint_id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # DisplayCommandBase.take_action() returns two tuples
+ columns, data = self.cmd.take_action(parsed_args)
+ self.endpoints_mock.get.assert_called_with(
+ identity_fakes.endpoint_id,
+ )
+
+ collist = ('enabled', 'id', 'interface', 'region', 'service_id',
+ 'service_name', 'service_type', 'url')
+ self.assertEqual(collist, columns)
+ datalist = (
+ True,
+ identity_fakes.endpoint_id,
+ identity_fakes.endpoint_interface,
+ identity_fakes.endpoint_region,
+ identity_fakes.service_id,
+ identity_fakes.service_name,
+ identity_fakes.service_type,
+ identity_fakes.endpoint_url,
+ )
+ self.assertEqual(datalist, data)
diff --git a/openstackclient/tests/identity/v3/test_identity_provider.py b/openstackclient/tests/identity/v3/test_identity_provider.py
index 280d9227..c74bce8e 100644
--- a/openstackclient/tests/identity/v3/test_identity_provider.py
+++ b/openstackclient/tests/identity/v3/test_identity_provider.py
@@ -24,8 +24,8 @@ class TestIdentityProvider(identity_fakes.TestFederatedIdentity):
def setUp(self):
super(TestIdentityProvider, self).setUp()
- identity_lib = self.app.client_manager.identity
- self.identity_providers_mock = identity_lib.identity_providers
+ federation_lib = self.app.client_manager.identity.federation
+ self.identity_providers_mock = federation_lib.identity_providers
self.identity_providers_mock.reset_mock()
@@ -56,7 +56,7 @@ class TestIdentityProviderCreate(TestIdentityProvider):
}
self.identity_providers_mock.create.assert_called_with(
- identity_fakes.idp_id,
+ id=identity_fakes.idp_id,
**kwargs
)
@@ -88,7 +88,7 @@ class TestIdentityProviderCreate(TestIdentityProvider):
}
self.identity_providers_mock.create.assert_called_with(
- identity_fakes.idp_id,
+ id=identity_fakes.idp_id,
**kwargs
)
@@ -128,7 +128,7 @@ class TestIdentityProviderCreate(TestIdentityProvider):
}
self.identity_providers_mock.create.assert_called_with(
- identity_fakes.idp_id,
+ id=identity_fakes.idp_id,
**kwargs
)
@@ -217,12 +217,12 @@ class TestIdentityProviderShow(TestIdentityProvider):
def setUp(self):
super(TestIdentityProviderShow, self).setUp()
- self.identity_providers_mock.get.return_value = fakes.FakeResource(
+ ret = fakes.FakeResource(
None,
copy.deepcopy(identity_fakes.IDENTITY_PROVIDER),
loaded=True,
)
-
+ self.identity_providers_mock.get.return_value = ret
# Get the command object to test
self.cmd = identity_provider.ShowIdentityProvider(self.app, None)
diff --git a/openstackclient/tests/identity/v3/test_role.py b/openstackclient/tests/identity/v3/test_role.py
index fa02ecb9..3d2a402b 100644
--- a/openstackclient/tests/identity/v3/test_role.py
+++ b/openstackclient/tests/identity/v3/test_role.py
@@ -715,7 +715,7 @@ class TestRoleShow(TestRole):
# Get the command object to test
self.cmd = role.ShowRole(self.app, None)
- def test_service_show(self):
+ def test_role_show(self):
arglist = [
identity_fakes.role_name,
]
diff --git a/openstackclient/tests/identity/v3/test_user.py b/openstackclient/tests/identity/v3/test_user.py
index 569d9140..42df5773 100644
--- a/openstackclient/tests/identity/v3/test_user.py
+++ b/openstackclient/tests/identity/v3/test_user.py
@@ -13,7 +13,9 @@
# under the License.
#
+import contextlib
import copy
+
import mock
from openstackclient.identity.v3 import user
@@ -944,6 +946,52 @@ class TestUserSet(TestUser):
)
+class TestUserSetPassword(TestUser):
+
+ def setUp(self):
+ super(TestUserSetPassword, self).setUp()
+ self.cmd = user.SetPasswordUser(self.app, None)
+
+ @staticmethod
+ @contextlib.contextmanager
+ def _mock_get_password(*passwords):
+ mocker = mock.Mock(side_effect=passwords)
+ with mock.patch("openstackclient.common.utils.get_password", mocker):
+ yield
+
+ def test_user_password_change(self):
+ current_pass = 'old_pass'
+ new_pass = 'new_pass'
+ arglist = [
+ '--password', new_pass,
+ ]
+ verifylist = [
+ ('password', new_pass),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # Mock getting user current password.
+ with self._mock_get_password(current_pass):
+ self.cmd.take_action(parsed_args)
+
+ self.users_mock.update_password.assert_called_with(
+ current_pass, new_pass
+ )
+
+ def test_user_create_password_prompt(self):
+ current_pass = 'old_pass'
+ new_pass = 'new_pass'
+ parsed_args = self.check_parser(self.cmd, [], [])
+
+ # Mock getting user current and new password.
+ with self._mock_get_password(current_pass, new_pass):
+ self.cmd.take_action(parsed_args)
+
+ self.users_mock.update_password.assert_called_with(
+ current_pass, new_pass
+ )
+
+
class TestUserShow(TestUser):
def setUp(self):
diff --git a/openstackclient/tests/image/v1/test_image.py b/openstackclient/tests/image/v1/test_image.py
index b014482a..3f97b151 100644
--- a/openstackclient/tests/image/v1/test_image.py
+++ b/openstackclient/tests/image/v1/test_image.py
@@ -446,3 +446,48 @@ class TestImageSet(TestImage):
image_fakes.image_id,
**kwargs
)
+
+
+class TestImageList(TestImage):
+
+ def setUp(self):
+ super(TestImageList, self).setUp()
+
+ # This is the return value for utils.find_resource()
+ self.images_mock.list.return_value = [
+ fakes.FakeResource(
+ None,
+ copy.deepcopy(image_fakes.IMAGE),
+ loaded=True,
+ ),
+ ]
+
+ # Get the command object to test
+ self.cmd = image.ListImage(self.app, None)
+
+ def test_image_list_long_option(self):
+ arglist = [
+ '--long',
+ ]
+ verifylist = [
+ ('long', True),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # DisplayCommandBase.take_action() returns two tuples
+ columns, data = self.cmd.take_action(parsed_args)
+ self.images_mock.list.assert_called_with()
+
+ collist = ('ID', 'Name', 'Disk Format', 'Container Format',
+ 'Size', 'Status')
+
+ self.assertEqual(columns, collist)
+ datalist = ((
+ image_fakes.image_id,
+ image_fakes.image_name,
+ '',
+ '',
+ '',
+ '',
+ ), )
+ self.assertEqual(datalist, tuple(data))
diff --git a/openstackclient/tests/image/v2/test_image.py b/openstackclient/tests/image/v2/test_image.py
index ef84e2c0..3e9eeebb 100644
--- a/openstackclient/tests/image/v2/test_image.py
+++ b/openstackclient/tests/image/v2/test_image.py
@@ -15,7 +15,7 @@
import copy
-from openstackclient.image.v1 import image
+from openstackclient.image.v2 import image
from openstackclient.tests import fakes
from openstackclient.tests.image.v2 import fakes as image_fakes
@@ -61,3 +61,48 @@ class TestImageDelete(TestImage):
self.images_mock.delete.assert_called_with(
image_fakes.image_id,
)
+
+
+class TestImageList(TestImage):
+
+ def setUp(self):
+ super(TestImageList, self).setUp()
+
+ # This is the return value for utils.find_resource()
+ self.images_mock.list.return_value = [
+ fakes.FakeResource(
+ None,
+ copy.deepcopy(image_fakes.IMAGE),
+ loaded=True,
+ ),
+ ]
+
+ # Get the command object to test
+ self.cmd = image.ListImage(self.app, None)
+
+ def test_image_list_long_option(self):
+ arglist = [
+ '--long',
+ ]
+ verifylist = [
+ ('long', True),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # DisplayCommandBase.take_action() returns two tuples
+ columns, data = self.cmd.take_action(parsed_args)
+ self.images_mock.list.assert_called_with()
+
+ collist = ('ID', 'Name', 'Disk Format', 'Container Format',
+ 'Size', 'Status')
+
+ self.assertEqual(columns, collist)
+ datalist = ((
+ image_fakes.image_id,
+ image_fakes.image_name,
+ '',
+ '',
+ '',
+ '',
+ ), )
+ self.assertEqual(datalist, tuple(data))
diff --git a/openstackclient/tests/network/v2/test_network.py b/openstackclient/tests/network/v2/test_network.py
index 08b61a0a..468db5e0 100644
--- a/openstackclient/tests/network/v2/test_network.py
+++ b/openstackclient/tests/network/v2/test_network.py
@@ -61,7 +61,7 @@ class TestCreateNetwork(common.TestNetworkBase):
def test_create_all_options(self):
arglist = [
- "--admin-state-down",
+ "--disable",
"--share",
FAKE_NAME,
] + self.given_show_options
@@ -88,7 +88,7 @@ class TestCreateNetwork(common.TestNetworkBase):
def test_create_other_options(self):
arglist = [
- "--admin-state-up",
+ "--enable",
"--no-share",
FAKE_NAME,
]
@@ -220,7 +220,7 @@ class TestSetNetwork(common.TestNetworkBase):
def test_set_this(self):
arglist = [
FAKE_NAME,
- '--admin-state-up',
+ '--enable',
'--name', 'noob',
'--share',
]
@@ -247,7 +247,7 @@ class TestSetNetwork(common.TestNetworkBase):
def test_set_that(self):
arglist = [
FAKE_NAME,
- '--admin-state-down',
+ '--disable',
'--no-share',
]
verifylist = [
diff --git a/openstackclient/tests/object/v1/lib/test_container.py b/openstackclient/tests/object/v1/lib/test_container.py
index f7355592..ce70b835 100644
--- a/openstackclient/tests/object/v1/lib/test_container.py
+++ b/openstackclient/tests/object/v1/lib/test_container.py
@@ -18,7 +18,7 @@
import mock
from openstackclient.object.v1.lib import container as lib_container
-from openstackclient.tests.common import test_restapi as restapi
+from openstackclient.tests import fakes
from openstackclient.tests.object.v1 import fakes as object_fakes
@@ -39,156 +39,158 @@ class TestContainer(object_fakes.TestObjectv1):
def setUp(self):
super(TestContainer, self).setUp()
- self.app.restapi = mock.MagicMock()
+ self.app.client_manager.session = mock.MagicMock()
class TestContainerList(TestContainer):
def test_container_list_no_options(self):
resp = [{'name': 'is-name'}]
- self.app.restapi.list.return_value = resp
+ self.app.client_manager.session.get().json.return_value = resp
data = lib_container.list_containers(
- self.app.restapi,
+ self.app.client_manager.session,
fake_url,
)
# Check expected values
- self.app.restapi.list.assert_called_with(
+ self.app.client_manager.session.get.assert_called_with(
fake_url,
params={
'format': 'json',
}
)
- self.assertEqual(data, resp)
+ self.assertEqual(resp, data)
def test_container_list_marker(self):
resp = [{'name': 'is-name'}]
- self.app.restapi.list.return_value = resp
+ self.app.client_manager.session.get().json.return_value = resp
data = lib_container.list_containers(
- self.app.restapi,
+ self.app.client_manager.session,
fake_url,
marker='next',
)
# Check expected values
- self.app.restapi.list.assert_called_with(
+ self.app.client_manager.session.get.assert_called_with(
fake_url,
params={
'format': 'json',
'marker': 'next',
}
)
- self.assertEqual(data, resp)
+ self.assertEqual(resp, data)
def test_container_list_limit(self):
resp = [{'name': 'is-name'}]
- self.app.restapi.list.return_value = resp
+ self.app.client_manager.session.get().json.return_value = resp
data = lib_container.list_containers(
- self.app.restapi,
+ self.app.client_manager.session,
fake_url,
limit=5,
)
# Check expected values
- self.app.restapi.list.assert_called_with(
+ self.app.client_manager.session.get.assert_called_with(
fake_url,
params={
'format': 'json',
'limit': 5,
}
)
- self.assertEqual(data, resp)
+ self.assertEqual(resp, data)
def test_container_list_end_marker(self):
resp = [{'name': 'is-name'}]
- self.app.restapi.list.return_value = resp
+ self.app.client_manager.session.get().json.return_value = resp
data = lib_container.list_containers(
- self.app.restapi,
+ self.app.client_manager.session,
fake_url,
end_marker='last',
)
# Check expected values
- self.app.restapi.list.assert_called_with(
+ self.app.client_manager.session.get.assert_called_with(
fake_url,
params={
'format': 'json',
'end_marker': 'last',
}
)
- self.assertEqual(data, resp)
+ self.assertEqual(resp, data)
def test_container_list_prefix(self):
resp = [{'name': 'is-name'}]
- self.app.restapi.list.return_value = resp
+ self.app.client_manager.session.get().json.return_value = resp
data = lib_container.list_containers(
- self.app.restapi,
+ self.app.client_manager.session,
fake_url,
prefix='foo/',
)
# Check expected values
- self.app.restapi.list.assert_called_with(
+ self.app.client_manager.session.get.assert_called_with(
fake_url,
params={
'format': 'json',
'prefix': 'foo/',
}
)
- self.assertEqual(data, resp)
+ self.assertEqual(resp, data)
def test_container_list_full_listing(self):
+ sess = self.app.client_manager.session
def side_effect(*args, **kwargs):
- rv = self.app.restapi.list.return_value
- self.app.restapi.list.return_value = []
- self.app.restapi.list.side_effect = None
+ rv = sess.get().json.return_value
+ sess.get().json.return_value = []
+ sess.get().json.side_effect = None
return rv
resp = [{'name': 'is-name'}]
- self.app.restapi.list.return_value = resp
- self.app.restapi.list.side_effect = side_effect
+ sess.get().json.return_value = resp
+ sess.get().json.side_effect = side_effect
data = lib_container.list_containers(
- self.app.restapi,
+ self.app.client_manager.session,
fake_url,
full_listing=True,
)
# Check expected values
- self.app.restapi.list.assert_called_with(
+ sess.get.assert_called_with(
fake_url,
params={
'format': 'json',
'marker': 'is-name',
}
)
- self.assertEqual(data, resp)
+ self.assertEqual(resp, data)
class TestContainerShow(TestContainer):
def test_container_show_no_options(self):
resp = {
+ 'X-Container-Meta-Owner': fake_account,
'x-container-object-count': 1,
'x-container-bytes-used': 577,
}
- self.app.restapi.head.return_value = \
- restapi.FakeResponse(headers=resp)
+ self.app.client_manager.session.head.return_value = \
+ fakes.FakeResponse(headers=resp)
data = lib_container.show_container(
- self.app.restapi,
+ self.app.client_manager.session,
fake_url,
'is-name',
)
# Check expected values
- self.app.restapi.head.assert_called_with(
+ self.app.client_manager.session.head.assert_called_with(
fake_url + '/is-name',
)
@@ -202,4 +204,4 @@ class TestContainerShow(TestContainer):
'sync_to': None,
'sync_key': None,
}
- self.assertEqual(data, data_expected)
+ self.assertEqual(data_expected, data)
diff --git a/openstackclient/tests/object/v1/lib/test_object.py b/openstackclient/tests/object/v1/lib/test_object.py
index 064efb53..f96732b4 100644
--- a/openstackclient/tests/object/v1/lib/test_object.py
+++ b/openstackclient/tests/object/v1/lib/test_object.py
@@ -18,7 +18,7 @@
import mock
from openstackclient.object.v1.lib import object as lib_object
-from openstackclient.tests.common import test_restapi as restapi
+from openstackclient.tests import fakes
from openstackclient.tests.object.v1 import fakes as object_fakes
@@ -40,99 +40,99 @@ class TestObject(object_fakes.TestObjectv1):
def setUp(self):
super(TestObject, self).setUp()
- self.app.restapi = mock.MagicMock()
+ self.app.client_manager.session = mock.MagicMock()
class TestObjectListObjects(TestObject):
def test_list_objects_no_options(self):
resp = [{'name': 'is-name'}]
- self.app.restapi.list.return_value = resp
+ self.app.client_manager.session.get().json.return_value = resp
data = lib_object.list_objects(
- self.app.restapi,
+ self.app.client_manager.session,
fake_url,
fake_container,
)
# Check expected values
- self.app.restapi.list.assert_called_with(
+ self.app.client_manager.session.get.assert_called_with(
fake_url + '/' + fake_container,
params={
'format': 'json',
}
)
- self.assertEqual(data, resp)
+ self.assertEqual(resp, data)
def test_list_objects_marker(self):
resp = [{'name': 'is-name'}]
- self.app.restapi.list.return_value = resp
+ self.app.client_manager.session.get().json.return_value = resp
data = lib_object.list_objects(
- self.app.restapi,
+ self.app.client_manager.session,
fake_url,
fake_container,
marker='next',
)
# Check expected values
- self.app.restapi.list.assert_called_with(
+ self.app.client_manager.session.get.assert_called_with(
fake_url + '/' + fake_container,
params={
'format': 'json',
'marker': 'next',
}
)
- self.assertEqual(data, resp)
+ self.assertEqual(resp, data)
def test_list_objects_limit(self):
resp = [{'name': 'is-name'}]
- self.app.restapi.list.return_value = resp
+ self.app.client_manager.session.get().json.return_value = resp
data = lib_object.list_objects(
- self.app.restapi,
+ self.app.client_manager.session,
fake_url,
fake_container,
limit=5,
)
# Check expected values
- self.app.restapi.list.assert_called_with(
+ self.app.client_manager.session.get.assert_called_with(
fake_url + '/' + fake_container,
params={
'format': 'json',
'limit': 5,
}
)
- self.assertEqual(data, resp)
+ self.assertEqual(resp, data)
def test_list_objects_end_marker(self):
resp = [{'name': 'is-name'}]
- self.app.restapi.list.return_value = resp
+ self.app.client_manager.session.get().json.return_value = resp
data = lib_object.list_objects(
- self.app.restapi,
+ self.app.client_manager.session,
fake_url,
fake_container,
end_marker='last',
)
# Check expected values
- self.app.restapi.list.assert_called_with(
+ self.app.client_manager.session.get.assert_called_with(
fake_url + '/' + fake_container,
params={
'format': 'json',
'end_marker': 'last',
}
)
- self.assertEqual(data, resp)
+ self.assertEqual(resp, data)
def test_list_objects_delimiter(self):
resp = [{'name': 'is-name'}]
- self.app.restapi.list.return_value = resp
+ self.app.client_manager.session.get().json.return_value = resp
data = lib_object.list_objects(
- self.app.restapi,
+ self.app.client_manager.session,
fake_url,
fake_container,
delimiter='|',
@@ -142,85 +142,86 @@ class TestObjectListObjects(TestObject):
# NOTE(dtroyer): requests handles the URL encoding and we're
# mocking that so use the otherwise-not-legal
# pipe '|' char in the response.
- self.app.restapi.list.assert_called_with(
+ self.app.client_manager.session.get.assert_called_with(
fake_url + '/' + fake_container,
params={
'format': 'json',
'delimiter': '|',
}
)
- self.assertEqual(data, resp)
+ self.assertEqual(resp, data)
def test_list_objects_prefix(self):
resp = [{'name': 'is-name'}]
- self.app.restapi.list.return_value = resp
+ self.app.client_manager.session.get().json.return_value = resp
data = lib_object.list_objects(
- self.app.restapi,
+ self.app.client_manager.session,
fake_url,
fake_container,
prefix='foo/',
)
# Check expected values
- self.app.restapi.list.assert_called_with(
+ self.app.client_manager.session.get.assert_called_with(
fake_url + '/' + fake_container,
params={
'format': 'json',
'prefix': 'foo/',
}
)
- self.assertEqual(data, resp)
+ self.assertEqual(resp, data)
def test_list_objects_path(self):
resp = [{'name': 'is-name'}]
- self.app.restapi.list.return_value = resp
+ self.app.client_manager.session.get().json.return_value = resp
data = lib_object.list_objects(
- self.app.restapi,
+ self.app.client_manager.session,
fake_url,
fake_container,
path='next',
)
# Check expected values
- self.app.restapi.list.assert_called_with(
+ self.app.client_manager.session.get.assert_called_with(
fake_url + '/' + fake_container,
params={
'format': 'json',
'path': 'next',
}
)
- self.assertEqual(data, resp)
+ self.assertEqual(resp, data)
def test_list_objects_full_listing(self):
+ sess = self.app.client_manager.session
def side_effect(*args, **kwargs):
- rv = self.app.restapi.list.return_value
- self.app.restapi.list.return_value = []
- self.app.restapi.list.side_effect = None
+ rv = sess.get().json.return_value
+ sess.get().json.return_value = []
+ sess.get().json.side_effect = None
return rv
resp = [{'name': 'is-name'}]
- self.app.restapi.list.return_value = resp
- self.app.restapi.list.side_effect = side_effect
+ sess.get().json.return_value = resp
+ sess.get().json.side_effect = side_effect
data = lib_object.list_objects(
- self.app.restapi,
+ sess,
fake_url,
fake_container,
full_listing=True,
)
# Check expected values
- self.app.restapi.list.assert_called_with(
+ sess.get.assert_called_with(
fake_url + '/' + fake_container,
params={
'format': 'json',
'marker': 'is-name',
}
)
- self.assertEqual(data, resp)
+ self.assertEqual(resp, data)
class TestObjectShowObjects(TestObject):
@@ -228,19 +229,20 @@ class TestObjectShowObjects(TestObject):
def test_object_show_no_options(self):
resp = {
'content-type': 'text/alpha',
+ 'x-container-meta-owner': fake_account,
}
- self.app.restapi.head.return_value = \
- restapi.FakeResponse(headers=resp)
+ self.app.client_manager.session.head.return_value = \
+ fakes.FakeResponse(headers=resp)
data = lib_object.show_object(
- self.app.restapi,
+ self.app.client_manager.session,
fake_url,
fake_container,
fake_object,
)
# Check expected values
- self.app.restapi.head.assert_called_with(
+ self.app.client_manager.session.head.assert_called_with(
fake_url + '/%s/%s' % (fake_container, fake_object),
)
@@ -250,7 +252,7 @@ class TestObjectShowObjects(TestObject):
'object': fake_object,
'content-type': 'text/alpha',
}
- self.assertEqual(data, data_expected)
+ self.assertEqual(data_expected, data)
def test_object_show_all_options(self):
resp = {
@@ -258,22 +260,23 @@ class TestObjectShowObjects(TestObject):
'content-length': 577,
'last-modified': '20130101',
'etag': 'qaz',
+ 'x-container-meta-owner': fake_account,
'x-object-manifest': None,
'x-object-meta-wife': 'Wilma',
'x-tra-header': 'yabba-dabba-do',
}
- self.app.restapi.head.return_value = \
- restapi.FakeResponse(headers=resp)
+ self.app.client_manager.session.head.return_value = \
+ fakes.FakeResponse(headers=resp)
data = lib_object.show_object(
- self.app.restapi,
+ self.app.client_manager.session,
fake_url,
fake_container,
fake_object,
)
# Check expected values
- self.app.restapi.head.assert_called_with(
+ self.app.client_manager.session.head.assert_called_with(
fake_url + '/%s/%s' % (fake_container, fake_object),
)
@@ -286,7 +289,7 @@ class TestObjectShowObjects(TestObject):
'last-modified': '20130101',
'etag': 'qaz',
'x-object-manifest': None,
- 'Wife': 'Wilma',
- 'X-Tra-Header': 'yabba-dabba-do',
+ 'wife': 'Wilma',
+ 'x-tra-header': 'yabba-dabba-do',
}
- self.assertEqual(data, data_expected)
+ self.assertEqual(data_expected, data)
diff --git a/openstackclient/tests/object/v1/test_container.py b/openstackclient/tests/object/v1/test_container.py
index 4afb1006..b72c79d6 100644
--- a/openstackclient/tests/object/v1/test_container.py
+++ b/openstackclient/tests/object/v1/test_container.py
@@ -77,7 +77,7 @@ class TestContainerList(TestObject):
kwargs = {
}
c_mock.assert_called_with(
- self.app.restapi,
+ self.app.client_manager.session,
AUTH_URL,
**kwargs
)
@@ -113,7 +113,7 @@ class TestContainerList(TestObject):
'prefix': 'bit',
}
c_mock.assert_called_with(
- self.app.restapi,
+ self.app.client_manager.session,
AUTH_URL,
**kwargs
)
@@ -148,7 +148,7 @@ class TestContainerList(TestObject):
'marker': object_fakes.container_name,
}
c_mock.assert_called_with(
- self.app.restapi,
+ self.app.client_manager.session,
AUTH_URL,
**kwargs
)
@@ -183,7 +183,7 @@ class TestContainerList(TestObject):
'end_marker': object_fakes.container_name_3,
}
c_mock.assert_called_with(
- self.app.restapi,
+ self.app.client_manager.session,
AUTH_URL,
**kwargs
)
@@ -218,7 +218,7 @@ class TestContainerList(TestObject):
'limit': 2,
}
c_mock.assert_called_with(
- self.app.restapi,
+ self.app.client_manager.session,
AUTH_URL,
**kwargs
)
@@ -252,7 +252,7 @@ class TestContainerList(TestObject):
kwargs = {
}
c_mock.assert_called_with(
- self.app.restapi,
+ self.app.client_manager.session,
AUTH_URL,
**kwargs
)
@@ -296,7 +296,7 @@ class TestContainerList(TestObject):
'full_listing': True,
}
c_mock.assert_called_with(
- self.app.restapi,
+ self.app.client_manager.session,
AUTH_URL,
**kwargs
)
@@ -341,7 +341,7 @@ class TestContainerShow(TestObject):
}
# lib.container.show_container(api, url, container)
c_mock.assert_called_with(
- self.app.restapi,
+ self.app.client_manager.session,
AUTH_URL,
object_fakes.container_name,
**kwargs
diff --git a/openstackclient/tests/object/v1/test_object.py b/openstackclient/tests/object/v1/test_object.py
index bea0d270..26d07b2c 100644
--- a/openstackclient/tests/object/v1/test_object.py
+++ b/openstackclient/tests/object/v1/test_object.py
@@ -71,7 +71,7 @@ class TestObjectList(TestObject):
columns, data = self.cmd.take_action(parsed_args)
o_mock.assert_called_with(
- self.app.restapi,
+ self.app.client_manager.session,
AUTH_URL,
object_fakes.container_name,
)
@@ -107,7 +107,7 @@ class TestObjectList(TestObject):
'prefix': 'floppy',
}
o_mock.assert_called_with(
- self.app.restapi,
+ self.app.client_manager.session,
AUTH_URL,
object_fakes.container_name_2,
**kwargs
@@ -143,7 +143,7 @@ class TestObjectList(TestObject):
'delimiter': '=',
}
o_mock.assert_called_with(
- self.app.restapi,
+ self.app.client_manager.session,
AUTH_URL,
object_fakes.container_name_2,
**kwargs
@@ -179,7 +179,7 @@ class TestObjectList(TestObject):
'marker': object_fakes.object_name_2,
}
o_mock.assert_called_with(
- self.app.restapi,
+ self.app.client_manager.session,
AUTH_URL,
object_fakes.container_name_2,
**kwargs
@@ -215,7 +215,7 @@ class TestObjectList(TestObject):
'end_marker': object_fakes.object_name_2,
}
o_mock.assert_called_with(
- self.app.restapi,
+ self.app.client_manager.session,
AUTH_URL,
object_fakes.container_name_2,
**kwargs
@@ -251,7 +251,7 @@ class TestObjectList(TestObject):
'limit': 2,
}
o_mock.assert_called_with(
- self.app.restapi,
+ self.app.client_manager.session,
AUTH_URL,
object_fakes.container_name_2,
**kwargs
@@ -287,7 +287,7 @@ class TestObjectList(TestObject):
kwargs = {
}
o_mock.assert_called_with(
- self.app.restapi,
+ self.app.client_manager.session,
AUTH_URL,
object_fakes.container_name,
**kwargs
@@ -337,7 +337,7 @@ class TestObjectList(TestObject):
'full_listing': True,
}
o_mock.assert_called_with(
- self.app.restapi,
+ self.app.client_manager.session,
AUTH_URL,
object_fakes.container_name,
**kwargs
@@ -384,7 +384,7 @@ class TestObjectShow(TestObject):
}
# lib.container.show_container(api, url, container)
c_mock.assert_called_with(
- self.app.restapi,
+ self.app.client_manager.session,
AUTH_URL,
object_fakes.container_name,
object_fakes.object_name_1,
diff --git a/openstackclient/tests/utils.py b/openstackclient/tests/utils.py
index 307abd7b..38d47250 100644
--- a/openstackclient/tests/utils.py
+++ b/openstackclient/tests/utils.py
@@ -37,6 +37,14 @@ class TestCase(testtools.TestCase):
stderr = self.useFixture(fixtures.StringStream("stderr")).stream
self.useFixture(fixtures.MonkeyPatch("sys.stderr", stderr))
+ def assertNotCalled(self, m, msg=None):
+ """Assert a function was not called"""
+
+ if m.called:
+ if not msg:
+ msg = 'method %s should not have been called' % m
+ self.fail(msg)
+
# 2.6 doesn't have the assert dict equals so make sure that it exists
if tuple(sys.version_info)[0:2] < (2, 7):
diff --git a/openstackclient/tests/volume/test_find_resource.py b/openstackclient/tests/volume/test_find_resource.py
index 8539070f..56081966 100644
--- a/openstackclient/tests/volume/test_find_resource.py
+++ b/openstackclient/tests/volume/test_find_resource.py
@@ -21,6 +21,7 @@ from cinderclient.v1 import volumes
from openstackclient.common import exceptions
from openstackclient.common import utils
from openstackclient.tests import utils as test_utils
+from openstackclient.volume import client # noqa
ID = '1after909'
diff --git a/openstackclient/tests/volume/v1/test_volume.py b/openstackclient/tests/volume/v1/test_volume.py
index cb006b10..f020791a 100644
--- a/openstackclient/tests/volume/v1/test_volume.py
+++ b/openstackclient/tests/volume/v1/test_volume.py
@@ -101,7 +101,7 @@ class TestVolumeCreate(TestVolume):
'status',
'type',
)
- self.assertEqual(columns, collist)
+ self.assertEqual(collist, columns)
datalist = (
'detached',
volume_fakes.volume_zone,
@@ -113,7 +113,7 @@ class TestVolumeCreate(TestVolume):
'',
volume_fakes.volume_type,
)
- self.assertEqual(data, datalist)
+ self.assertEqual(datalist, data)
def test_volume_create_options(self):
arglist = [
@@ -165,7 +165,7 @@ class TestVolumeCreate(TestVolume):
'status',
'type',
)
- self.assertEqual(columns, collist)
+ self.assertEqual(collist, columns)
datalist = (
'detached',
volume_fakes.volume_zone,
@@ -177,7 +177,7 @@ class TestVolumeCreate(TestVolume):
'',
volume_fakes.volume_type,
)
- self.assertEqual(data, datalist)
+ self.assertEqual(datalist, data)
def test_volume_create_user_project_id(self):
# Return a project
@@ -240,7 +240,7 @@ class TestVolumeCreate(TestVolume):
'status',
'type',
)
- self.assertEqual(columns, collist)
+ self.assertEqual(collist, columns)
datalist = (
'detached',
volume_fakes.volume_zone,
@@ -252,7 +252,7 @@ class TestVolumeCreate(TestVolume):
'',
volume_fakes.volume_type,
)
- self.assertEqual(data, datalist)
+ self.assertEqual(datalist, data)
def test_volume_create_user_project_name(self):
# Return a project
@@ -315,7 +315,7 @@ class TestVolumeCreate(TestVolume):
'status',
'type',
)
- self.assertEqual(columns, collist)
+ self.assertEqual(collist, columns)
datalist = (
'detached',
volume_fakes.volume_zone,
@@ -327,7 +327,7 @@ class TestVolumeCreate(TestVolume):
'',
volume_fakes.volume_type,
)
- self.assertEqual(data, datalist)
+ self.assertEqual(datalist, data)
def test_volume_create_properties(self):
arglist = [
@@ -376,7 +376,7 @@ class TestVolumeCreate(TestVolume):
'status',
'type',
)
- self.assertEqual(columns, collist)
+ self.assertEqual(collist, columns)
datalist = (
'detached',
volume_fakes.volume_zone,
@@ -388,4 +388,4 @@ class TestVolumeCreate(TestVolume):
'',
volume_fakes.volume_type,
)
- self.assertEqual(data, datalist)
+ self.assertEqual(datalist, data)
diff --git a/openstackclient/volume/v1/volume.py b/openstackclient/volume/v1/volume.py
index 3e4af56c..99abac52 100644
--- a/openstackclient/volume/v1/volume.py
+++ b/openstackclient/volume/v1/volume.py
@@ -36,7 +36,7 @@ class CreateVolume(show.ShowOne):
parser.add_argument(
'name',
metavar='<name>',
- help='Name of the volume',
+ help='Name of the new volume',
)
parser.add_argument(
'--size',
@@ -48,7 +48,7 @@ class CreateVolume(show.ShowOne):
parser.add_argument(
'--snapshot-id',
metavar='<snapshot-id>',
- help='ID of the snapshot',
+ help='Use <snapshot-id> as source of new volume',
)
parser.add_argument(
'--description',
@@ -73,7 +73,7 @@ class CreateVolume(show.ShowOne):
parser.add_argument(
'--availability-zone',
metavar='<availability-zone>',
- help='Availability zone to use',
+ help='Create new volume in <availability-zone>',
)
parser.add_argument(
'--property',
@@ -85,12 +85,12 @@ class CreateVolume(show.ShowOne):
parser.add_argument(
'--image',
metavar='<image>',
- help='Reference to a stored image',
+ help='Use <image> as source of new volume',
)
parser.add_argument(
'--source',
metavar='<volume>',
- help='Source for volume clone',
+ help='Volume to clone (name or ID)',
)
return parser
@@ -143,7 +143,7 @@ class CreateVolume(show.ShowOne):
class DeleteVolume(command.Command):
- """Delete volume"""
+ """Delete a volume"""
log = logging.getLogger(__name__ + '.DeleteVolume')
@@ -152,7 +152,7 @@ class DeleteVolume(command.Command):
parser.add_argument(
'volume',
metavar='<volume>',
- help='Name or ID of volume to delete',
+ help='Volume to delete (name or ID)',
)
parser.add_argument(
'--force',
@@ -271,7 +271,7 @@ class SetVolume(command.Command):
parser.add_argument(
'volume',
metavar='<volume>',
- help='Name or ID of volume to change',
+ help='Volume to change (name or ID)',
)
parser.add_argument(
'--name',
@@ -315,7 +315,7 @@ class SetVolume(command.Command):
class ShowVolume(show.ShowOne):
- """Show specific volume"""
+ """Show volume details"""
log = logging.getLogger(__name__ + '.ShowVolume')
@@ -324,7 +324,7 @@ class ShowVolume(show.ShowOne):
parser.add_argument(
'volume',
metavar='<volume>',
- help='Name or ID of volume to display',
+ help='Volume to display (name or ID)',
)
return parser
@@ -357,7 +357,7 @@ class UnsetVolume(command.Command):
parser.add_argument(
'volume',
metavar='<volume>',
- help='Name or ID of volume to change',
+ help='Volume to change (name or ID)',
)
parser.add_argument(
'--property',