diff options
Diffstat (limited to 'openstackclient')
24 files changed, 386 insertions, 167 deletions
diff --git a/openstackclient/common/clientmanager.py b/openstackclient/common/clientmanager.py index 0159ad7d..fae95630 100644 --- a/openstackclient/common/clientmanager.py +++ b/openstackclient/common/clientmanager.py @@ -86,6 +86,7 @@ class ClientManager(object): self._pw_callback = pw_func self._url = self._cli_options.auth.get('url', None) self._region_name = self._cli_options.region_name + self._endpoint_type = self._cli_options.endpoint_type self.timing = self._cli_options.timing @@ -183,18 +184,23 @@ class ClientManager(object): self._auth_ref = self.auth.get_auth_ref(self.session) return self._auth_ref - def get_endpoint_for_service_type(self, service_type, region_name=None): + def get_endpoint_for_service_type(self, service_type, region_name=None, + endpoint_type='public'): """Return the endpoint URL for the service type.""" + if not endpoint_type: + endpoint_type = 'public' # See if we are using password flow auth, i.e. we have a # service catalog to select endpoints from if self.auth_ref: endpoint = self.auth_ref.service_catalog.url_for( service_type=service_type, region_name=region_name, + endpoint_type=endpoint_type, ) else: # Get the passed endpoint directly from the auth plugin - endpoint = self.auth.get_endpoint(self.session) + endpoint = self.auth.get_endpoint(self.session, + interface=endpoint_type) return endpoint diff --git a/openstackclient/common/utils.py b/openstackclient/common/utils.py index c824678e..6cd35c05 100644 --- a/openstackclient/common/utils.py +++ b/openstackclient/common/utils.py @@ -368,3 +368,11 @@ def read_blob_file_contents(blob_file): except IOError: msg = "Error occurred trying to read from file %s" raise exceptions.CommandError(msg % blob_file) + + +def build_kwargs_dict(arg_name, value): + """Return a dictionary containing `arg_name` if `value` is set.""" + kwargs = {} + if value: + kwargs[arg_name] = value + return kwargs diff --git a/openstackclient/compute/client.py b/openstackclient/compute/client.py index 93a7b715..27d63a95 100644 --- a/openstackclient/compute/client.py +++ b/openstackclient/compute/client.py @@ -48,12 +48,17 @@ def make_client(instance): extensions = [extension.Extension('list_extensions', list_extensions)] + # Remember endpoint_type only if it is set + kwargs = utils.build_kwargs_dict('endpoint_type', + instance._endpoint_type) + client = compute_client( session=instance.session, extensions=extensions, http_log_debug=http_log_debug, timings=instance.timing, region_name=instance._region_name, + **kwargs ) return client diff --git a/openstackclient/compute/v2/hypervisor.py b/openstackclient/compute/v2/hypervisor.py index 65035d04..f33beb08 100644 --- a/openstackclient/compute/v2/hypervisor.py +++ b/openstackclient/compute/v2/hypervisor.py @@ -77,6 +77,29 @@ class ShowHypervisor(show.ShowOne): hypervisor = utils.find_resource(compute_client.hypervisors, parsed_args.hypervisor)._info.copy() + aggregates = compute_client.aggregates.list() + hypervisor["aggregates"] = list() + if aggregates: + # Hypervisors in nova cells are prefixed by "<cell>@" + if "@" in hypervisor['service']['host']: + cell, service_host = hypervisor['service']['host'].split('@', + 1) + else: + cell = None + service_host = hypervisor['service']['host'] + + if cell: + # The host aggregates are also prefixed by "<cell>@" + member_of = [aggregate.name + for aggregate in aggregates + if cell in aggregate.name and + service_host in aggregate.hosts] + else: + member_of = [aggregate.name + for aggregate in aggregates + if service_host in aggregate.hosts] + hypervisor["aggregates"] = member_of + uptime = compute_client.hypervisors.uptime(hypervisor['id'])._info # Extract data from uptime value # format: 0 up 0, 0 users, load average: 0, 0, 0 diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 5007b072..4efef975 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -55,6 +55,39 @@ def _format_servers_list_networks(networks): return '; '.join(output) +def _get_ip_address(addresses, address_type, ip_address_family): + # Old style addresses + if address_type in addresses: + for addy in addresses[address_type]: + if int(addy['version']) in ip_address_family: + return addy['addr'] + + # New style addresses + new_address_type = address_type + if address_type == 'public': + new_address_type = 'floating' + if address_type == 'private': + new_address_type = 'fixed' + for network in addresses: + for addy in addresses[network]: + # Case where it is list of strings + if isinstance(addy, six.string_types): + if new_address_type == 'fixed': + return addresses[network][0] + else: + return addresses[network][-1] + # Case where it is a dict + if 'OS-EXT-IPS:type' not in addy: + continue + if addy['OS-EXT-IPS:type'] == new_address_type: + if int(addy['version']) in ip_address_family: + return addy['addr'] + raise exceptions.CommandError( + "ERROR: No %s IP version %s address found" % + (address_type, ip_address_family) + ) + + def _prep_server_detail(compute_client, server): """Prepare the detailed server dict for printing @@ -1283,6 +1316,7 @@ class SshServer(command.Command): ) parser.add_argument( '-l', + dest='login', metavar='<login-name>', help=argparse.SUPPRESS, ) @@ -1381,13 +1415,6 @@ class SshServer(command.Command): # Build the command cmd = "ssh" - # Look for address type - if parsed_args.address_type: - address_type = parsed_args.address_type - if address_type not in server.addresses: - raise SystemExit("ERROR: No %s IP address found" % address_type) - - # Set up desired address family ip_address_family = [4, 6] if parsed_args.ipv4: ip_address_family = [4] @@ -1396,14 +1423,6 @@ class SshServer(command.Command): ip_address_family = [6] cmd += " -6" - # Grab the first matching IP address - ip_address = None - for addr in server.addresses[address_type]: - if int(addr['version']) in ip_address_family: - ip_address = addr['addr'] - if not ip_address: - raise SystemExit("ERROR: No IP address found") - if parsed_args.port: cmd += " -p %d" % parsed_args.port if parsed_args.identity: @@ -1418,6 +1437,9 @@ class SshServer(command.Command): cmd += " -v" cmd += " %s@%s" + ip_address = _get_ip_address(server.addresses, + parsed_args.address_type, + ip_address_family) self.log.debug("ssh command: %s", (cmd % (login, ip_address))) os.system(cmd % (login, ip_address)) diff --git a/openstackclient/identity/client.py b/openstackclient/identity/client.py index 4127a451..cc803511 100644 --- a/openstackclient/identity/client.py +++ b/openstackclient/identity/client.py @@ -46,10 +46,15 @@ def make_client(instance): API_VERSIONS) LOG.debug('Instantiating identity client: %s', identity_client) + # Remember interface only if endpoint_type is set + kwargs = utils.build_kwargs_dict('interface', + instance._endpoint_type) + client = identity_client( session=instance.session, region_name=instance._region_name, - ) + **kwargs + ) return client diff --git a/openstackclient/identity/v2_0/catalog.py b/openstackclient/identity/v2_0/catalog.py index 7d17fbf5..c10001d0 100644 --- a/openstackclient/identity/v2_0/catalog.py +++ b/openstackclient/identity/v2_0/catalog.py @@ -31,7 +31,7 @@ def _format_endpoints(eps=None): region = eps[index].get('region', '<none>') ret += region + '\n' for url in ['publicURL', 'internalURL', 'adminURL']: - ret += " %s: %s\n" % (url, eps[index]['publicURL']) + ret += " %s: %s\n" % (url, eps[index][url]) return ret diff --git a/openstackclient/image/client.py b/openstackclient/image/client.py index c78f4425..8e2d6cd9 100644 --- a/openstackclient/image/client.py +++ b/openstackclient/image/client.py @@ -46,6 +46,7 @@ def make_client(instance): endpoint = instance.get_endpoint_for_service_type( API_NAME, region_name=instance._region_name, + endpoint_type=instance._endpoint_type, ) client = image_client( @@ -68,6 +69,7 @@ def make_client(instance): endpoint=instance.get_endpoint_for_service_type( IMAGE_API_TYPE, region_name=instance._region_name, + endpoint_type=instance._endpoint_type, ) ) diff --git a/openstackclient/image/v1/image.py b/openstackclient/image/v1/image.py index 85a9e076..68c81cd5 100644 --- a/openstackclient/image/v1/image.py +++ b/openstackclient/image/v1/image.py @@ -33,7 +33,6 @@ from cliff import show from glanceclient.common import utils as gc_utils from openstackclient.api import utils as api_utils -from openstackclient.common import exceptions from openstackclient.common import parseractions from openstackclient.common import utils @@ -244,29 +243,7 @@ class CreateImage(show.ShowOne): # Wrap the call to catch exceptions in order to close files try: - try: - image = utils.find_resource( - image_client.images, - parsed_args.name, - ) - - # Preserve previous properties if any are being set now - if image.properties: - if parsed_args.properties: - image.properties.update(kwargs['properties']) - kwargs['properties'] = image.properties - - except exceptions.CommandError: - if not parsed_args.volume: - # This is normal for a create or reserve (create w/o - # an image), but skip for create from volume - image = image_client.images.create(**kwargs) - else: - # Update an existing reservation - - # If an image is specified via --file, --location or - # --copy-from let the API handle it - image = image_client.images.update(image.id, **kwargs) + image = image_client.images.create(**kwargs) finally: # Clean up open files - make sure data isn't a string if ('data' in kwargs and hasattr(kwargs['data'], 'close') and @@ -561,6 +538,51 @@ class SetImage(show.ShowOne): help="Set a property on this image " "(repeat option to set multiple properties)", ) + parser.add_argument( + "--store", + metavar="<store>", + help="Upload image to this store", + ) + parser.add_argument( + "--location", + metavar="<image-url>", + help="Download image from an existing URL", + ) + parser.add_argument( + "--copy-from", + metavar="<image-url>", + help="Copy image from the data store (similar to --location)", + ) + parser.add_argument( + "--file", + metavar="<file>", + help="Upload image from local file", + ) + parser.add_argument( + "--volume", + metavar="<volume>", + help="Create image from a volume", + ) + parser.add_argument( + "--force", + dest='force', + action='store_true', + default=False, + help="Force image change if volume is in use " + "(only meaningful with --volume)", + ) + parser.add_argument( + "--stdin", + dest='stdin', + action='store_true', + default=False, + help="Read image data from standard input", + ) + parser.add_argument( + "--checksum", + metavar="<checksum>", + help="Image hash used for verification", + ) return parser def take_action(self, parsed_args): @@ -569,7 +591,8 @@ class SetImage(show.ShowOne): kwargs = {} copy_attrs = ('name', 'owner', 'min_disk', 'min_ram', 'properties', - 'container_format', 'disk_format', 'size') + 'container_format', 'disk_format', 'size', 'store', + 'location', 'copy_from', 'volume', 'force', 'checksum') for attr in copy_attrs: if attr in parsed_args: val = getattr(parsed_args, attr, None) @@ -592,20 +615,63 @@ class SetImage(show.ShowOne): if parsed_args.private: kwargs['is_public'] = False - if not kwargs: - self.log.warning('no arguments specified') - return {}, {} - - image = utils.find_resource( - image_client.images, - parsed_args.image, - ) - - if image.properties and parsed_args.properties: - image.properties.update(kwargs['properties']) - kwargs['properties'] = image.properties + # Wrap the call to catch exceptions in order to close files + try: + image = utils.find_resource( + image_client.images, + parsed_args.image, + ) - image = image_client.images.update(image.id, **kwargs) + if not parsed_args.location and not parsed_args.copy_from: + if parsed_args.volume: + volume_client = self.app.client_manager.volume + source_volume = utils.find_resource( + volume_client.volumes, + parsed_args.volume, + ) + response, body = volume_client.volumes.upload_to_image( + source_volume.id, + parsed_args.force, + parsed_args.image, + (parsed_args.container_format + if parsed_args.container_format + else image.container_format), + (parsed_args.disk_format + if parsed_args.disk_format + else image.disk_format), + ) + info = body['os-volume_upload_image'] + elif parsed_args.file: + # Send an open file handle to glanceclient so it will + # do a chunked transfer + kwargs["data"] = io.open(parsed_args.file, "rb") + else: + # Read file from stdin + if sys.stdin.isatty() is not True: + if parsed_args.stdin: + if msvcrt: + msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY) + # Send an open file handle to glanceclient so it + # will do a chunked transfer + kwargs["data"] = sys.stdin + else: + self.log.warning('Use --stdin to enable read image' + ' data from standard input') + + if image.properties and parsed_args.properties: + image.properties.update(kwargs['properties']) + kwargs['properties'] = image.properties + + if not kwargs: + self.log.warning('no arguments specified') + return {}, {} + + image = image_client.images.update(image.id, **kwargs) + finally: + # Clean up open files - make sure data isn't a string + if ('data' in kwargs and hasattr(kwargs['data'], 'close') and + kwargs['data'] != sys.stdin): + kwargs['data'].close() info = {} info.update(image._info) diff --git a/openstackclient/network/client.py b/openstackclient/network/client.py index 870566aa..de08e5e2 100644 --- a/openstackclient/network/client.py +++ b/openstackclient/network/client.py @@ -47,11 +47,17 @@ def make_client(instance): endpoint = instance.get_endpoint_for_service_type( API_NAME, region_name=instance._region_name, + endpoint_type=instance._endpoint_type, ) + # Remember endpoint_type only if it is set + kwargs = utils.build_kwargs_dict('endpoint_type', + instance._endpoint_type) + client = network_client( session=instance.session, region_name=instance._region_name, + **kwargs ) network_api = utils.get_client_class( diff --git a/openstackclient/object/client.py b/openstackclient/object/client.py index beb7c04f..676f6642 100644 --- a/openstackclient/object/client.py +++ b/openstackclient/object/client.py @@ -36,6 +36,7 @@ def make_client(instance): endpoint = instance.get_endpoint_for_service_type( 'object-store', region_name=instance._region_name, + endpoint_type=instance._endpoint_type, ) client = object_store_v1.APIv1( diff --git a/openstackclient/shell.py b/openstackclient/shell.py index 4109b8bc..b4e5904c 100644 --- a/openstackclient/shell.py +++ b/openstackclient/shell.py @@ -209,6 +209,15 @@ class OpenStackShell(app.App): DEFAULT_DOMAIN + ' (Env: OS_DEFAULT_DOMAIN)') parser.add_argument( + '--os-endpoint-type', + metavar='<endpoint-type>', + dest='endpoint_type', + choices=['admin', 'public', 'internal'], + default=utils.env('OS_ENDPOINT_TYPE'), + help='Select an endpoint type.' + ' Valid endpoint types: [admin, public, internal].' + ' (Env: OS_ENDPOINT_TYPE)') + parser.add_argument( '--timing', default=False, action='store_true', @@ -254,7 +263,10 @@ class OpenStackShell(app.App): self.options.project_name = tenant_name # Do configuration file handling - cc = cloud_config.OpenStackConfig() + # Ignore the default value of endpoint_type. Only if it is set later + # will it be used. + cc = cloud_config.OpenStackConfig( + override_defaults={'endpoint_type': None, }) self.log.debug("defaults: %s", cc.defaults) self.cloud = cc.get_one_cloud( diff --git a/openstackclient/tests/common/test_clientmanager.py b/openstackclient/tests/common/test_clientmanager.py index 4e2f46b4..e86ef509 100644 --- a/openstackclient/tests/common/test_clientmanager.py +++ b/openstackclient/tests/common/test_clientmanager.py @@ -54,6 +54,7 @@ class FakeOptions(object): self.identity_api_version = '2.0' self.timing = None self.region_name = None + self.endpoint_type = None self.url = None self.auth = {} self.default_domain = 'default' @@ -123,6 +124,8 @@ class TestClientManager(utils.TestCase): auth_url=fakes.AUTH_URL, ), auth_type='v2token', + endpoint_type=fakes.ENDPOINT_TYPE, + region_name=fakes.REGION_NAME, ), api_version=API_VERSION, verify=True @@ -137,6 +140,14 @@ class TestClientManager(utils.TestCase): client_manager.auth, auth_v2.Token, ) + self.assertEqual( + fakes.ENDPOINT_TYPE, + client_manager._endpoint_type, + ) + self.assertEqual( + fakes.REGION_NAME, + client_manager._region_name, + ) self.assertFalse(client_manager._insecure) self.assertTrue(client_manager._verify) diff --git a/openstackclient/tests/common/test_utils.py b/openstackclient/tests/common/test_utils.py index d9f5b7a5..a25a5ba5 100644 --- a/openstackclient/tests/common/test_utils.py +++ b/openstackclient/tests/common/test_utils.py @@ -159,6 +159,16 @@ class TestUtils(test_utils.TestCase): self.assertFalse(utils.wait_for_delete(manager, res_id)) self.assertFalse(mock_sleep.called) + def test_build_kwargs_dict_value_set(self): + self.assertEqual({'arg_bla': 'bla'}, + utils.build_kwargs_dict('arg_bla', 'bla')) + + def test_build_kwargs_dict_value_None(self): + self.assertEqual({}, utils.build_kwargs_dict('arg_bla', None)) + + def test_build_kwargs_dict_value_empty_str(self): + self.assertEqual({}, utils.build_kwargs_dict('arg_bla', '')) + class NoUniqueMatch(Exception): pass diff --git a/openstackclient/tests/compute/v2/test_server.py b/openstackclient/tests/compute/v2/test_server.py index a8a1936d..4df18f05 100644 --- a/openstackclient/tests/compute/v2/test_server.py +++ b/openstackclient/tests/compute/v2/test_server.py @@ -15,7 +15,9 @@ import copy import mock +import testtools +from openstackclient.common import exceptions from openstackclient.common import utils as common_utils from openstackclient.compute.v2 import server from openstackclient.tests.compute.v2 import fakes as compute_fakes @@ -250,7 +252,7 @@ class TestServerCreate(TestServer): mock_open.assert_called_with('userdata.sh') # Ensure the userdata file is closed - mock_file.close.assert_called() + mock_file.close.assert_called_with() # Set expected values kwargs = dict( @@ -580,3 +582,55 @@ class TestServerResize(TestServer): self.servers_mock.revert_resize.assert_called_with( self.servers_get_return_value, ) + + +class TestServerGeneral(testtools.TestCase): + OLD = { + 'private': [ + { + 'addr': '192.168.0.3', + 'version': 4, + }, + ] + } + NEW = { + 'foo': [ + { + 'OS-EXT-IPS-MAC:mac_addr': 'fa:16:3e:93:b3:01', + 'version': 4, + 'addr': '10.10.1.2', + 'OS-EXT-IPS:type': 'fixed', + }, + { + 'OS-EXT-IPS-MAC:mac_addr': 'fa:16:3e:93:b3:02', + 'version': 6, + 'addr': '0:0:0:0:0:ffff:a0a:103', + 'OS-EXT-IPS:type': 'floating', + }, + ] + } + ODD = {'jenkins': ['10.3.3.18', '124.12.125.4']} + + def test_get_ip_address(self): + self.assertEqual("192.168.0.3", + server._get_ip_address(self.OLD, 'private', [4, 6])) + self.assertEqual("10.10.1.2", + server._get_ip_address(self.NEW, 'fixed', [4, 6])) + self.assertEqual("10.10.1.2", + server._get_ip_address(self.NEW, 'private', [4, 6])) + self.assertEqual("0:0:0:0:0:ffff:a0a:103", + server._get_ip_address(self.NEW, 'public', [6])) + self.assertEqual("0:0:0:0:0:ffff:a0a:103", + server._get_ip_address(self.NEW, 'floating', [6])) + self.assertEqual("124.12.125.4", + server._get_ip_address(self.ODD, 'public', [4, 6])) + self.assertEqual("10.3.3.18", + server._get_ip_address(self.ODD, 'private', [4, 6])) + self.assertRaises(exceptions.CommandError, + server._get_ip_address, self.NEW, 'public', [4]) + self.assertRaises(exceptions.CommandError, + server._get_ip_address, self.NEW, 'admin', [4]) + self.assertRaises(exceptions.CommandError, + server._get_ip_address, self.OLD, 'public', [4, 6]) + self.assertRaises(exceptions.CommandError, + server._get_ip_address, self.OLD, 'private', [6]) diff --git a/openstackclient/tests/fakes.py b/openstackclient/tests/fakes.py index 323f9543..a9322ec3 100644 --- a/openstackclient/tests/fakes.py +++ b/openstackclient/tests/fakes.py @@ -26,6 +26,8 @@ AUTH_URL = "http://0.0.0.0" USERNAME = "itchy" PASSWORD = "scratchy" PROJECT_NAME = "poochie" +REGION_NAME = "richie" +ENDPOINT_TYPE = "catchy" TEST_RESPONSE_DICT = fixture.V2Token(token_id=AUTH_TOKEN, user_name=USERNAME) diff --git a/openstackclient/tests/identity/v2_0/test_catalog.py b/openstackclient/tests/identity/v2_0/test_catalog.py index 50445954..fe13d78d 100644 --- a/openstackclient/tests/identity/v2_0/test_catalog.py +++ b/openstackclient/tests/identity/v2_0/test_catalog.py @@ -27,11 +27,13 @@ class TestCatalog(utils.TestCommand): { 'region': 'one', 'publicURL': 'https://public.one.example.com', + 'internalURL': 'https://internal.one.example.com', 'adminURL': 'https://admin.one.example.com', }, { 'region': 'two', 'publicURL': 'https://public.two.example.com', + 'internalURL': 'https://internal.two.example.com', 'adminURL': 'https://admin.two.example.com', }, ], @@ -74,11 +76,11 @@ class TestCatalogList(TestCatalog): 'supernova', 'compute', 'one\n publicURL: https://public.one.example.com\n ' - 'internalURL: https://public.one.example.com\n ' - 'adminURL: https://public.one.example.com\n' + 'internalURL: https://internal.one.example.com\n ' + 'adminURL: https://admin.one.example.com\n' 'two\n publicURL: https://public.two.example.com\n ' - 'internalURL: https://public.two.example.com\n ' - 'adminURL: https://public.two.example.com\n', + 'internalURL: https://internal.two.example.com\n ' + 'adminURL: https://admin.two.example.com\n', ), ) self.assertEqual(datalist, tuple(data)) @@ -108,11 +110,11 @@ class TestCatalogShow(TestCatalog): self.assertEqual(collist, columns) datalist = ( 'one\n publicURL: https://public.one.example.com\n ' - 'internalURL: https://public.one.example.com\n ' - 'adminURL: https://public.one.example.com\n' + 'internalURL: https://internal.one.example.com\n ' + 'adminURL: https://admin.one.example.com\n' 'two\n publicURL: https://public.two.example.com\n ' - 'internalURL: https://public.two.example.com\n ' - 'adminURL: https://public.two.example.com\n', + 'internalURL: https://internal.two.example.com\n ' + 'adminURL: https://admin.two.example.com\n', 'qwertyuiop', 'supernova', 'compute', diff --git a/openstackclient/tests/image/v1/test_image.py b/openstackclient/tests/image/v1/test_image.py index eec8cfa5..a79df8b4 100644 --- a/openstackclient/tests/image/v1/test_image.py +++ b/openstackclient/tests/image/v1/test_image.py @@ -178,8 +178,8 @@ class TestImageCreate(TestImage): # Ensure the input file is closed mock_file.close.assert_called_with() - # ImageManager.get(name) - self.images_mock.get.assert_called_with(image_fakes.image_name) + # ImageManager.get(name) not to be called since update action exists + self.images_mock.get.assert_not_called() # ImageManager.create(name=, **) self.images_mock.create.assert_called_with( @@ -201,71 +201,6 @@ class TestImageCreate(TestImage): self.assertEqual(image_fakes.IMAGE_columns, columns) self.assertEqual(image_fakes.IMAGE_data, data) - def test_image_create_volume(self): - # Set up VolumeManager Mock - volumes_mock = self.app.client_manager.volume.volumes - volumes_mock.reset_mock() - volumes_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy({'id': 'vol1', 'name': 'volly'}), - loaded=True, - ) - response = { - "id": 'volume_id', - "updated_at": 'updated_at', - "status": 'uploading', - "display_description": 'desc', - "size": 'size', - "volume_type": 'volume_type', - "image_id": 'image1', - "container_format": image.DEFAULT_CONTAINER_FORMAT, - "disk_format": image.DEFAULT_DISK_FORMAT, - "image_name": image_fakes.image_name, - } - full_response = {"os-volume_upload_image": response} - volumes_mock.upload_to_image.return_value = (201, full_response) - - arglist = [ - '--volume', 'volly', - image_fakes.image_name, - ] - verifylist = [ - ('private', False), - ('protected', False), - ('public', False), - ('unprotected', False), - ('volume', 'volly'), - ('force', False), - ('name', image_fakes.image_name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # DisplayCommandBase.take_action() returns two tuples - columns, data = self.cmd.take_action(parsed_args) - - # VolumeManager.upload_to_image(volume, force, image_name, - # container_format, disk_format) - volumes_mock.upload_to_image.assert_called_with( - 'vol1', - False, - image_fakes.image_name, - 'bare', - 'raw', - ) - - # ImageManager.update(image_id, remove_props=, **) - self.images_mock.update.assert_called_with( - image_fakes.image_id, - name=image_fakes.image_name, - container_format=image.DEFAULT_CONTAINER_FORMAT, - disk_format=image.DEFAULT_DISK_FORMAT, - properties=image_fakes.image_properties, - volume='volly', - ) - - self.assertEqual(image_fakes.IMAGE_columns, columns) - self.assertEqual(image_fakes.IMAGE_data, data) - class TestImageDelete(TestImage): @@ -669,6 +604,69 @@ class TestImageSet(TestImage): **kwargs ) + def test_image_update_volume(self): + # Set up VolumeManager Mock + volumes_mock = self.app.client_manager.volume.volumes + volumes_mock.reset_mock() + volumes_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy({'id': 'vol1', 'name': 'volly'}), + loaded=True, + ) + response = { + "id": 'volume_id', + "updated_at": 'updated_at', + "status": 'uploading', + "display_description": 'desc', + "size": 'size', + "volume_type": 'volume_type', + "container_format": image.DEFAULT_CONTAINER_FORMAT, + "disk_format": image.DEFAULT_DISK_FORMAT, + "image": image_fakes.image_name, + } + full_response = {"os-volume_upload_image": response} + volumes_mock.upload_to_image.return_value = (201, full_response) + + arglist = [ + '--volume', 'volly', + '--name', 'updated_image', + image_fakes.image_name, + ] + verifylist = [ + ('private', False), + ('protected', False), + ('public', False), + ('unprotected', False), + ('volume', 'volly'), + ('force', False), + ('name', 'updated_image'), + ('image', image_fakes.image_name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # DisplayCommandBase.take_action() returns two tuples + columns, data = self.cmd.take_action(parsed_args) + + # VolumeManager.upload_to_image(volume, force, image_name, + # container_format, disk_format) + volumes_mock.upload_to_image.assert_called_with( + 'vol1', + False, + image_fakes.image_name, + '', + '', + ) + + # ImageManager.update(image_id, remove_props=, **) + self.images_mock.update.assert_called_with( + image_fakes.image_id, + name='updated_image', + volume='volly', + ) + + self.assertEqual(image_fakes.IMAGE_columns, columns) + self.assertEqual(image_fakes.IMAGE_data, data) + class TestImageShow(TestImage): diff --git a/openstackclient/tests/network/common.py b/openstackclient/tests/network/common.py index 7162f97b..31fde257 100644 --- a/openstackclient/tests/network/common.py +++ b/openstackclient/tests/network/common.py @@ -33,30 +33,3 @@ class TestNetworkBase(utils.TestCommand): service_type="network", ) self.api = self.app.client_manager.network.api - - given_show_options = [ - '-f', - 'shell', - '-c', - 'id', - '--prefix', - 'TST', - ] - then_show_options = [ - ('formatter', 'shell'), - ('columns', ['id']), - ('prefix', 'TST'), - ] - given_list_options = [ - '-f', - 'csv', - '-c', - 'id', - '--quote', - 'all', - ] - then_list_options = [ - ('formatter', 'csv'), - ('columns', ['id']), - ('quote_mode', 'all'), - ] diff --git a/openstackclient/tests/network/v2/test_network.py b/openstackclient/tests/network/v2/test_network.py index 36133a3b..87a44066 100644 --- a/openstackclient/tests/network/v2/test_network.py +++ b/openstackclient/tests/network/v2/test_network.py @@ -92,14 +92,14 @@ class TestCreateNetwork(common.TestNetworkBase): "--project", identity_fakes_v3.project_name, "--project-domain", identity_fakes_v3.domain_name, FAKE_NAME, - ] + self.given_show_options + ] verifylist = [ ('admin_state', False), ('shared', True), ('project', identity_fakes_v3.project_name), ('project_domain', identity_fakes_v3.domain_name), ('name', FAKE_NAME), - ] + self.then_show_options + ] mocker = mock.Mock(return_value=copy.deepcopy(RESPONSE)) self.app.client_manager.network.create_network = mocker identity_client = identity_fakes_v3.FakeIdentityv3Client( @@ -519,8 +519,8 @@ class TestShowNetwork(common.TestNetworkBase): self.assertEqual(FILTERED, result) def test_show_all_options(self, n_mock): - arglist = [FAKE_NAME] + self.given_show_options - verifylist = [('identifier', FAKE_NAME)] + self.then_show_options + arglist = [FAKE_NAME] + verifylist = [('identifier', FAKE_NAME)] n_mock.return_value = copy.deepcopy(RECORD) self.cmd = network.ShowNetwork(self.app, self.namespace) diff --git a/openstackclient/tests/test_shell.py b/openstackclient/tests/test_shell.py index b080ae91..674d8345 100644 --- a/openstackclient/tests/test_shell.py +++ b/openstackclient/tests/test_shell.py @@ -38,6 +38,7 @@ DEFAULT_REGION_NAME = "ZZ9_Plural_Z_Alpha" DEFAULT_TOKEN = "token" DEFAULT_SERVICE_URL = "http://127.0.0.1:8771/v3.0/" DEFAULT_AUTH_PLUGIN = "v2password" +DEFAULT_ENDPOINT_TYPE = "internal" DEFAULT_COMPUTE_API_VERSION = "2" DEFAULT_IDENTITY_API_VERSION = "2" @@ -61,6 +62,7 @@ CLOUD_1 = { }, 'region_name': 'occ-cloud', 'donut': 'glazed', + 'endpoint_type': 'public', } } } @@ -104,6 +106,7 @@ global_options = { '--os-default-domain': (DEFAULT_DOMAIN_NAME, True, True), '--os-cacert': ('/dev/null', True, True), '--timing': (True, True, False), + '--os-endpoint-type': (DEFAULT_ENDPOINT_TYPE, True, True) } auth_options = { @@ -123,6 +126,7 @@ auth_options = { '--os-auth-type': ("v2password", True, True), '--os-token': (DEFAULT_TOKEN, True, True), '--os-url': (DEFAULT_SERVICE_URL, True, True), + '--os-endpoint-type': (DEFAULT_ENDPOINT_TYPE, True, True), } @@ -608,6 +612,10 @@ class TestShellCli(TestShell): 'glazed', _shell.cloud.config['donut'], ) + self.assertEqual( + 'public', + _shell.cloud.config['endpoint_type'], + ) @mock.patch("os_client_config.config.OpenStackConfig._load_vendor_file") @mock.patch("os_client_config.config.OpenStackConfig._load_config_file") diff --git a/openstackclient/tests/volume/v1/test_qos_specs.py b/openstackclient/tests/volume/v1/test_qos_specs.py index 0a5d14e6..226fe673 100644 --- a/openstackclient/tests/volume/v1/test_qos_specs.py +++ b/openstackclient/tests/volume/v1/test_qos_specs.py @@ -312,7 +312,7 @@ class TestQosList(TestQos): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.qos_mock.list.assert_called() + self.qos_mock.list.assert_called_with() collist = ( 'ID', diff --git a/openstackclient/tests/volume/v2/test_qos_specs.py b/openstackclient/tests/volume/v2/test_qos_specs.py index 92b3f179..6a550988 100644 --- a/openstackclient/tests/volume/v2/test_qos_specs.py +++ b/openstackclient/tests/volume/v2/test_qos_specs.py @@ -312,7 +312,7 @@ class TestQosList(TestQos): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - self.qos_mock.list.assert_called() + self.qos_mock.list.assert_called_with() collist = ( 'ID', diff --git a/openstackclient/volume/client.py b/openstackclient/volume/client.py index 1038c407..965c42ec 100644 --- a/openstackclient/volume/client.py +++ b/openstackclient/volume/client.py @@ -53,11 +53,16 @@ def make_client(instance): extensions = [extension.Extension('list_extensions', list_extensions)] + # Remember endpoint_type only if it is set + kwargs = utils.build_kwargs_dict('endpoint_type', + instance._endpoint_type) + client = volume_client( session=instance.session, extensions=extensions, http_log_debug=http_log_debug, region_name=instance._region_name, + **kwargs ) return client |
