From 30a64579b600362d5915877a8c80da6a24b380e2 Mon Sep 17 00:00:00 2001 From: Stephen Finucane <stephenfin@redhat.com> Date: Tue, 9 Jul 2024 14:37:07 +0100 Subject: [PATCH] compute: Migrate 'server create' to SDK The final step. Future changes will clean up the remnants of the novaclient usage. This is a rather large patch, owing to the number of things that novaclient was handling for us which SDK does not, but the combination of unit and functional tests mean we should be handling all of these differences. Change-Id: I623e8c772235438a3d1590e1bbd832748d6e62ea Signed-off-by: Stephen Finucane <stephenfin@redhat.com> --- openstackclient/api/compute_v2.py | 37 + openstackclient/compute/v2/server.py | 240 +- .../tests/unit/api/test_compute_v2.py | 99 + .../tests/unit/compute/v2/test_server.py | 2561 ++++++++--------- .../tests/unit/network/v2/fakes.py | 54 +- 5 files changed, 1535 insertions(+), 1456 deletions(-) diff --git a/openstackclient/api/compute_v2.py b/openstackclient/api/compute_v2.py index af0c69e587..4fcf3f1344 100644 --- a/openstackclient/api/compute_v2.py +++ b/openstackclient/api/compute_v2.py @@ -621,3 +621,40 @@ def find_security_group(compute_client, name_or_id): raise exceptions.NotFound(f'{name_or_id} not found') return found + + +def find_network(compute_client, name_or_id): + """Find the ID for a given network name or ID + + https://docs.openstack.org/api-ref/compute/#show-network-details + + :param compute_client: A compute client + :param name_or_id: The name or ID of the network to look up + :returns: A network object + :raises exception.NotFound: If a matching network could not be found or + more than one match was found + """ + response = compute_client.get( + f'/os-networks/{name_or_id}', microversion='2.1' + ) + if response.status_code != http.HTTPStatus.NOT_FOUND: + # there might be other, non-404 errors + sdk_exceptions.raise_from_response(response) + return response.json()['network'] + + response = compute_client.get('/os-networks', microversion='2.1') + sdk_exceptions.raise_from_response(response) + found = None + networks = response.json()['networks'] + for network in networks: + if network['label'] == name_or_id: + if found: + raise exceptions.NotFound( + f'multiple matches found for {name_or_id}' + ) + found = network + + if not found: + raise exceptions.NotFound(f'{name_or_id} not found') + + return found diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index ceac614d61..22ab4f11c6 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -21,10 +21,10 @@ import getpass import json import logging import os +import typing as ty from cliff import columns as cliff_columns import iso8601 -from novaclient import api_versions from openstack import exceptions as sdk_exceptions from openstack import utils as sdk_utils from osc_lib.cli import format_columns @@ -82,7 +82,7 @@ class AddressesColumn(cliff_columns.FormattableColumn): def machine_readable(self): return { k: [i['addr'] for i in v if 'addr' in i] - for k, v in self._value.items() + for k, v in (self._value.items() if self._value else []) } @@ -1069,7 +1069,6 @@ class BDMAction(parseractions.MultiKeyValueAction): super().__call__(parser, namespace, values, option_string) -# TODO(stephenfin): Migrate to SDK class CreateServer(command.ShowOne): _description = _("Create a new server") @@ -1173,7 +1172,7 @@ class CreateServer(command.ShowOne): ) parser.add_argument( '--block-device', - metavar='', + metavar='<block-device>', action=BDMAction, dest='block_devices', default=[], @@ -1507,7 +1506,7 @@ class CreateServer(command.ShowOne): self.app.stdout.write('\rProgress: %s' % progress) self.app.stdout.flush() - compute_client = self.app.client_manager.compute + compute_client = self.app.client_manager.sdk_connection.compute volume_client = self.app.client_manager.volume image_client = self.app.client_manager.image @@ -1602,12 +1601,12 @@ class CreateServer(command.ShowOne): parsed_args.snapshot, ).id - flavor = utils.find_resource( - compute_client.flavors, parsed_args.flavor + flavor = compute_client.find_flavor( + parsed_args.flavor, ignore_missing=False ) if parsed_args.file: - if compute_client.api_version >= api_versions.APIVersion('2.57'): + if sdk_utils.supports_microversion(compute_client, '2.57'): msg = _( 'Personality files are deprecated and are not supported ' 'for --os-compute-api-version greater than 2.56; use ' @@ -1638,10 +1637,12 @@ class CreateServer(command.ShowOne): msg = _("max instances should be > 0") raise exceptions.CommandError(msg) - userdata = None + user_data = None if parsed_args.user_data: try: - userdata = open(parsed_args.user_data) + with open(parsed_args.user_data, 'rb') as fh: + # TODO(stephenfin): SDK should do this for us + user_data = base64.b64encode(fh.read()).decode('utf-8') except OSError as e: msg = _("Can't open '%(data)s': %(exception)s") raise exceptions.CommandError( @@ -1649,7 +1650,7 @@ class CreateServer(command.ShowOne): ) if parsed_args.description: - if compute_client.api_version < api_versions.APIVersion("2.19"): + if not sdk_utils.supports_microversion(compute_client, '2.19'): msg = _( '--os-compute-api-version 2.19 or greater is ' 'required to support the --description option' @@ -1657,26 +1658,7 @@ class CreateServer(command.ShowOne): raise exceptions.CommandError(msg) block_device_mapping_v2 = [] - if volume: - block_device_mapping_v2 = [ - { - 'uuid': volume, - 'boot_index': 0, - 'source_type': 'volume', - 'destination_type': 'volume', - } - ] - elif snapshot: - block_device_mapping_v2 = [ - { - 'uuid': snapshot, - 'boot_index': 0, - 'source_type': 'snapshot', - 'destination_type': 'volume', - 'delete_on_termination': False, - } - ] - elif parsed_args.boot_from_volume: + if parsed_args.boot_from_volume: # Tell nova to create a root volume from the image provided. if not image: msg = _( @@ -1695,6 +1677,35 @@ class CreateServer(command.ShowOne): ] # If booting from volume we do not pass an image to compute. image = None + elif image: + block_device_mapping_v2 = [ + { + 'uuid': image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + } + ] + elif volume: + block_device_mapping_v2 = [ + { + 'uuid': volume, + 'boot_index': 0, + 'source_type': 'volume', + 'destination_type': 'volume', + } + ] + elif snapshot: + block_device_mapping_v2 = [ + { + 'uuid': snapshot, + 'boot_index': 0, + 'source_type': 'snapshot', + 'destination_type': 'volume', + 'delete_on_termination': False, + } + ] if parsed_args.swap: block_device_mapping_v2.append( @@ -1770,7 +1781,7 @@ class CreateServer(command.ShowOne): raise exceptions.CommandError(msg) if 'tag' in mapping and ( - compute_client.api_version < api_versions.APIVersion('2.42') + not sdk_utils.supports_microversion(compute_client, '2.42') ): msg = _( '--os-compute-api-version 2.42 or greater is ' @@ -1779,7 +1790,7 @@ class CreateServer(command.ShowOne): raise exceptions.CommandError(msg) if 'volume_type' in mapping and ( - compute_client.api_version < api_versions.APIVersion('2.67') + not sdk_utils.supports_microversion(compute_client, '2.67') ): msg = _( '--os-compute-api-version 2.67 or greater is ' @@ -1835,7 +1846,7 @@ class CreateServer(command.ShowOne): block_device_mapping_v2.append(mapping) - if not image and not any( + if not any( [bdm.get('boot_index') == 0 for bdm in block_device_mapping_v2] ): msg = _( @@ -1844,10 +1855,12 @@ class CreateServer(command.ShowOne): ) raise exceptions.CommandError(msg) - nics = parsed_args.nics + # Default to empty list if nothing was specified and let nova + # decide the default behavior. + networks: ty.Union[str, ty.List[ty.Dict[str, str]], None] = [] - if 'auto' in nics or 'none' in nics: - if len(nics) > 1: + if 'auto' in parsed_args.nics or 'none' in parsed_args.nics: + if len(parsed_args.nics) > 1: msg = _( 'Specifying a --nic of auto or none cannot ' 'be used with any other --nic, --network ' @@ -1855,7 +1868,7 @@ class CreateServer(command.ShowOne): ) raise exceptions.CommandError(msg) - if compute_client.api_version < api_versions.APIVersion('2.37'): + if not sdk_utils.supports_microversion(compute_client, '2.37'): msg = _( '--os-compute-api-version 2.37 or greater is ' 'required to support explicit auto-allocation of a ' @@ -1863,12 +1876,12 @@ class CreateServer(command.ShowOne): ) raise exceptions.CommandError(msg) - nics = nics[0] + networks = parsed_args.nics[0] else: - for nic in nics: + for nic in parsed_args.nics: if 'tag' in nic: - if compute_client.api_version < api_versions.APIVersion( - '2.43' + if not sdk_utils.supports_microversion( + compute_client, '2.43' ): msg = _( '--os-compute-api-version 2.43 or greater is ' @@ -1894,9 +1907,11 @@ class CreateServer(command.ShowOne): nic['port-id'] = port.id else: if nic['net-id']: - nic['net-id'] = compute_client.api.network_find( + net = compute_v2.find_network( + compute_client, nic['net-id'], - )['id'] + ) + nic['net-id'] = net['id'] if nic['port-id']: msg = _( @@ -1905,18 +1920,35 @@ class CreateServer(command.ShowOne): ) raise exceptions.CommandError(msg) - if not nics: + # convert from the novaclient-derived "NIC" view to the actual + # "network" view + network = {} + + if nic['net-id']: + network['uuid'] = nic['net-id'] + + if nic['port-id']: + network['port'] = nic['port-id'] + + if nic['v4-fixed-ip']: + network['fixed'] = nic['v4-fixed-ip'] + elif nic['v6-fixed-ip']: + network['fixed'] = nic['v6-fixed-ip'] + + if nic.get('tag'): # tags are optional + network['tag'] = nic['tag'] + + networks.append(network) + + if not parsed_args.nics and sdk_utils.supports_microversion( + compute_client, '2.37' + ): # Compute API version >= 2.37 requires a value, so default to # 'auto' to maintain legacy behavior if a nic wasn't specified. - if compute_client.api_version >= api_versions.APIVersion('2.37'): - nics = 'auto' - else: - # Default to empty list if nothing was specified and let nova - # decide the default behavior. - nics = [] + networks = 'auto' # Check security group exist and convert ID to name - security_group_names = [] + security_groups = [] if self.app.client_manager.is_network_endpoint_enabled(): network_client = self.app.client_manager.network for each_sg in parsed_args.security_group: @@ -1925,12 +1957,12 @@ class CreateServer(command.ShowOne): ) # Use security group ID to avoid multiple security group have # same name in neutron networking backend - security_group_names.append(sg.id) + security_groups.append({'name': sg.id}) else: # Handle nova-network case for each_sg in parsed_args.security_group: - sg = compute_client.api.security_group_find(each_sg) - security_group_names.append(sg['name']) + sg = compute_v2.find_security_group(compute_client, each_sg) + security_groups.append({'name': sg['name']}) hints = {} for key, values in parsed_args.hints.items(): @@ -1941,9 +1973,8 @@ class CreateServer(command.ShowOne): hints[key] = values if parsed_args.server_group: - server_group_obj = utils.find_resource( - compute_client.server_groups, - parsed_args.server_group, + server_group_obj = compute_client.find_server_group( + parsed_args.server_group, ignore_missing=False ) hints['group'] = server_group_obj.id @@ -1965,69 +1996,89 @@ class CreateServer(command.ShowOne): else: config_drive = parsed_args.config_drive - boot_args = [parsed_args.server_name, image, flavor] - - boot_kwargs = dict( - meta=parsed_args.properties, - files=files, - reservation_id=None, - min_count=parsed_args.min, - max_count=parsed_args.max, - security_groups=security_group_names, - userdata=userdata, - key_name=parsed_args.key_name, - availability_zone=parsed_args.availability_zone, - admin_pass=parsed_args.password, - block_device_mapping_v2=block_device_mapping_v2, - nics=nics, - scheduler_hints=hints, - config_drive=config_drive, - ) + kwargs = { + 'name': parsed_args.server_name, + 'image_id': image.id if image else '', + 'flavor_id': flavor.id, + 'min_count': parsed_args.min, + 'max_count': parsed_args.max, + } if parsed_args.description: - boot_kwargs['description'] = parsed_args.description + kwargs['description'] = parsed_args.description + + if parsed_args.availability_zone: + kwargs['availability_zone'] = parsed_args.availability_zone + + if parsed_args.password: + kwargs['admin_password'] = parsed_args.password + + if parsed_args.properties: + kwargs['metadata'] = parsed_args.properties + + if parsed_args.key_name: + kwargs['key_name'] = parsed_args.key_name + + if user_data: + kwargs['user_data'] = user_data + + if files: + kwargs['personality'] = files + + if security_groups: + kwargs['security_groups'] = security_groups + + if block_device_mapping_v2: + kwargs['block_device_mapping'] = block_device_mapping_v2 + + if hints: + kwargs['scheduler_hints'] = hints + + if networks is not None: + kwargs['networks'] = networks + + if config_drive is not None: + kwargs['config_drive'] = config_drive if parsed_args.tags: - if compute_client.api_version < api_versions.APIVersion('2.52'): + if not sdk_utils.supports_microversion(compute_client, '2.52'): msg = _( '--os-compute-api-version 2.52 or greater is required to ' 'support the --tag option' ) raise exceptions.CommandError(msg) - boot_kwargs['tags'] = parsed_args.tags + kwargs['tags'] = parsed_args.tags if parsed_args.host: - if compute_client.api_version < api_versions.APIVersion("2.74"): + if not sdk_utils.supports_microversion(compute_client, '2.74'): msg = _( '--os-compute-api-version 2.74 or greater is required to ' 'support the --host option' ) raise exceptions.CommandError(msg) - boot_kwargs['host'] = parsed_args.host + kwargs['host'] = parsed_args.host if parsed_args.hypervisor_hostname: - if compute_client.api_version < api_versions.APIVersion("2.74"): + if not sdk_utils.supports_microversion(compute_client, '2.74'): msg = _( '--os-compute-api-version 2.74 or greater is required to ' 'support the --hypervisor-hostname option' ) raise exceptions.CommandError(msg) - boot_kwargs['hypervisor_hostname'] = ( - parsed_args.hypervisor_hostname - ) + kwargs['hypervisor_hostname'] = parsed_args.hypervisor_hostname if parsed_args.hostname: - if compute_client.api_version < api_versions.APIVersion("2.90"): + if not sdk_utils.supports_microversion(compute_client, '2.90'): msg = _( '--os-compute-api-version 2.90 or greater is required to ' 'support the --hostname option' ) raise exceptions.CommandError(msg) - boot_kwargs['hostname'] = parsed_args.hostname + kwargs['hostname'] = parsed_args.hostname # TODO(stephenfin): Handle OS_TRUSTED_IMAGE_CERTIFICATE_IDS if parsed_args.trusted_image_certs: @@ -2037,7 +2088,7 @@ class CreateServer(command.ShowOne): 'servers booted directly from images' ) raise exceptions.CommandError(msg) - if compute_client.api_version < api_versions.APIVersion('2.63'): + if not sdk_utils.supports_microversion(compute_client, '2.63'): msg = _( '--os-compute-api-version 2.63 or greater is required to ' 'support the --trusted-image-cert option' @@ -2045,25 +2096,22 @@ class CreateServer(command.ShowOne): raise exceptions.CommandError(msg) certs = parsed_args.trusted_image_certs - boot_kwargs['trusted_image_certificates'] = certs + kwargs['trusted_image_certificates'] = certs - LOG.debug('boot_args: %s', boot_args) - LOG.debug('boot_kwargs: %s', boot_kwargs) + LOG.debug('boot_kwargs: %s', kwargs) # Wrap the call to catch exceptions in order to close files try: - server = compute_client.servers.create(*boot_args, **boot_kwargs) + server = compute_client.create_server(**kwargs) finally: # Clean up open files - make sure they are not strings for f in files: if hasattr(f, 'close'): f.close() - if hasattr(userdata, 'close'): - userdata.close() if parsed_args.wait: if utils.wait_for_status( - compute_client.servers.get, + compute_client.get_server, server.id, callback=_show_progress, ): @@ -2072,8 +2120,6 @@ class CreateServer(command.ShowOne): msg = _('Error creating server: %s') % parsed_args.server_name raise exceptions.CommandError(msg) - # TODO(stephenfin): Remove when the whole command is using SDK - compute_client = self.app.client_manager.sdk_connection.compute data = _prep_server_detail(compute_client, image_client, server) return zip(*sorted(data.items())) diff --git a/openstackclient/tests/unit/api/test_compute_v2.py b/openstackclient/tests/unit/api/test_compute_v2.py index 5a8442602b..fd2c521c7a 100644 --- a/openstackclient/tests/unit/api/test_compute_v2.py +++ b/openstackclient/tests/unit/api/test_compute_v2.py @@ -763,3 +763,102 @@ class TestFindSecurityGroup(utils.TestCase): self.compute_sdk_client, sg_name, ) + + +class TestFindNetwork(utils.TestCase): + + def setUp(self): + super().setUp() + + self.compute_sdk_client = mock.Mock(_proxy.Proxy) + + def test_find_network_by_id(self): + net_id = uuid.uuid4().hex + net_name = 'name-' + uuid.uuid4().hex + data = { + 'network': { + 'id': net_id, + 'label': net_name, + # other fields omitted for brevity + } + } + self.compute_sdk_client.get.side_effect = [ + fakes.FakeResponse(data=data), + ] + + result = compute.find_network(self.compute_sdk_client, net_id) + + self.compute_sdk_client.get.assert_has_calls( + [ + mock.call(f'/os-networks/{net_id}', microversion='2.1'), + ] + ) + self.assertEqual(data['network'], result) + + def test_find_network_by_name(self): + net_id = uuid.uuid4().hex + net_name = 'name-' + uuid.uuid4().hex + data = { + 'networks': [ + { + 'id': net_id, + 'label': net_name, + # other fields omitted for brevity + } + ], + } + self.compute_sdk_client.get.side_effect = [ + fakes.FakeResponse(status_code=http.HTTPStatus.NOT_FOUND), + fakes.FakeResponse(data=data), + ] + + result = compute.find_network(self.compute_sdk_client, net_name) + + self.compute_sdk_client.get.assert_has_calls( + [ + mock.call(f'/os-networks/{net_name}', microversion='2.1'), + mock.call('/os-networks', microversion='2.1'), + ] + ) + self.assertEqual(data['networks'][0], result) + + def test_find_network_not_found(self): + data = {'networks': []} + self.compute_sdk_client.get.side_effect = [ + fakes.FakeResponse(status_code=http.HTTPStatus.NOT_FOUND), + fakes.FakeResponse(data=data), + ] + self.assertRaises( + osc_lib_exceptions.NotFound, + compute.find_network, + self.compute_sdk_client, + 'invalid-net', + ) + + def test_find_network_by_name_duplicate(self): + net_name = 'name-' + uuid.uuid4().hex + data = { + 'networks': [ + { + 'id': uuid.uuid4().hex, + 'label': net_name, + # other fields omitted for brevity + }, + { + 'id': uuid.uuid4().hex, + 'label': net_name, + # other fields omitted for brevity + }, + ], + } + self.compute_sdk_client.get.side_effect = [ + fakes.FakeResponse(status_code=http.HTTPStatus.NOT_FOUND), + fakes.FakeResponse(data=data), + ] + + self.assertRaises( + osc_lib_exceptions.NotFound, + compute.find_network, + self.compute_sdk_client, + net_name, + ) diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py index 858e4eca66..8902b65015 100644 --- a/openstackclient/tests/unit/compute/v2/test_server.py +++ b/openstackclient/tests/unit/compute/v2/test_server.py @@ -14,11 +14,11 @@ import base64 import collections -import copy import getpass import json import tempfile from unittest import mock +import uuid import iso8601 from openstack import exceptions as sdk_exceptions @@ -64,10 +64,6 @@ class TestServer(compute_fakes.TestComputev2): def setUp(self): super().setUp() - # Get a shortcut to the compute client ServerManager Mock - self.servers_mock = self.compute_client.servers - self.servers_mock.reset_mock() - # Get a shortcut to the compute client ServerMigrationsManager Mock self.server_migrations_mock = self.compute_client.server_migrations self.server_migrations_mock.reset_mock() @@ -84,41 +80,12 @@ class TestServer(compute_fakes.TestComputev2): self.flavors_mock = self.compute_client.flavors self.flavors_mock.reset_mock() - # Get a shortcut to the volume client VolumeManager Mock - self.volumes_mock = self.volume_client.volumes - self.volumes_mock.reset_mock() - - # Get a shortcut to the volume client VolumeManager Mock - self.snapshots_mock = self.volume_client.volume_snapshots - self.snapshots_mock.reset_mock() - # Set object attributes to be tested. Could be overwritten in subclass. self.attrs = {} # Set object methods to be tested. Could be overwritten in subclass. self.methods = {} - def setup_servers_mock(self, count): - # If we are creating more than one server, make one of them - # boot-from-volume - include_bfv = count > 1 - servers = compute_fakes.create_servers( - attrs=self.attrs, - methods=self.methods, - count=count - 1 if include_bfv else count, - ) - if include_bfv: - attrs = copy.deepcopy(self.attrs) - attrs['image'] = '' - bfv_server = compute_fakes.create_one_server( - attrs=attrs, methods=self.methods - ) - servers.append(bfv_server) - - # This is the return value for utils.find_resource() - self.servers_mock.get = compute_fakes.get_servers(servers, 0) - return servers - def setup_sdk_servers_mock(self, count): servers = compute_fakes.create_sdk_servers( attrs=self.attrs, @@ -760,7 +727,7 @@ class TestServerAddPort(TestServer): def test_server_add_port_with_tag_pre_v249(self): self.set_compute_api_version('2.48') - servers = self.setup_servers_mock(count=1) + servers = self.setup_sdk_servers_mock(count=1) self.find_port.return_value.id = 'fake-port' arglist = [ servers[0].id, @@ -1341,7 +1308,7 @@ class TestServerCreate(TestServer): None, # OS-EXT-SRV-ATTR:user_data server.PowerStateColumn( self.server.power_state - ), # OS-EXT-STS:power_state # noqa: E501 + ), # OS-EXT-STS:power_state None, # OS-EXT-STS:task_state None, # OS-EXT-STS:vm_state None, # OS-SRV-USG:launched_at @@ -1382,7 +1349,6 @@ class TestServerCreate(TestServer): self.image_client.get_image.return_value = self.image self.flavor = compute_fakes.create_one_flavor() - self.flavors_mock.get.return_value = self.flavor self.compute_sdk_client.find_flavor.return_value = self.flavor attrs = { @@ -1391,31 +1357,23 @@ class TestServerCreate(TestServer): 'image': self.image, 'flavor': self.flavor, } - self.new_server = compute_fakes.create_one_server(attrs=attrs) - self.servers_mock.create.return_value = self.new_server - - # We need an SDK-style server object also for the get_server call in - # _prep_server_detail - # TODO(stephenfin): Remove when the whole command is using SDK self.server = compute_fakes.create_one_sdk_server(attrs=attrs) + + self.compute_sdk_client.create_server.return_value = self.server self.compute_sdk_client.get_server.return_value = self.server self.volume = volume_fakes.create_one_volume() - self.volume_alt = volume_fakes.create_one_volume() - self.volumes_mock.get.return_value = self.volume - self.snapshot = volume_fakes.create_one_snapshot() - self.snapshots_mock.get.return_value = self.snapshot # Get the command object to test self.cmd = server.CreateServer(self.app, None) def test_server_create_no_options(self): arglist = [ - self.new_server.name, + self.server.name, ] verifylist = [ - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] self.assertRaises( @@ -1429,63 +1387,65 @@ class TestServerCreate(TestServer): def test_server_create_minimal(self): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', - self.new_server.name, + self.flavor.id, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = dict( - meta=None, - files={}, - reservation_id=None, + self.compute_sdk_client.find_flavor.assert_has_calls( + [mock.call(self.flavor.id, ignore_missing=False)] * 2 + ) + self.image_client.find_image.assert_called_once_with( + self.image.id, ignore_missing=False + ) + self.compute_sdk_client.create_server.assert_called_once_with( + name=self.server.name, + image_id=self.image.id, + flavor_id=self.flavor.id, min_count=1, max_count=1, - security_groups=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[], - nics=[], - scheduler_hints={}, - config_drive=None, + networks=[], + block_device_mapping=[ + { + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, + ], ) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs - ) - self.assertEqual(self.columns, columns) self.assertEqual(self.datalist(), data) - self.assertFalse(self.image_client.images.called) - self.assertFalse(self.flavors_mock.called) def test_server_create_with_options(self): + server_group = compute_fakes.create_one_server_group() + self.compute_sdk_client.find_server_group.return_value = server_group + + security_group = network_fakes.create_one_security_group() + self.network_client.find_security_group.return_value = security_group + arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--key-name', 'keyname', '--property', 'Beta=b', '--security-group', - 'securitygroup', + security_group.id, '--use-config-drive', '--password', 'passw0rd', @@ -1494,329 +1454,283 @@ class TestServerCreate(TestServer): '--hint', 'a=c', '--server-group', - 'servergroup', - self.new_server.name, + server_group.id, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('key_name', 'keyname'), ('properties', {'Beta': 'b'}), - ('security_group', ['securitygroup']), + ('security_group', [security_group.id]), ('hints', {'a': ['b', 'c']}), - ('server_group', 'servergroup'), + ('server_group', server_group.id), ('config_drive', True), ('password', 'passw0rd'), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - fake_server_group = compute_fakes.create_one_server_group() - self.compute_client.server_groups.get.return_value = fake_server_group - - fake_sg = network_fakes.FakeSecurityGroup.create_security_groups() - mock_find_sg = network_fakes.FakeSecurityGroup.get_security_groups( - fake_sg - ) - self.app.client_manager.network.find_security_group = mock_find_sg - - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. columns, data = self.cmd.take_action(parsed_args) - mock_find_sg.assert_called_once_with( - 'securitygroup', ignore_missing=False + self.compute_sdk_client.find_flavor.assert_has_calls( + [mock.call(self.flavor.id, ignore_missing=False)] * 2 ) - # Set expected values - kwargs = dict( - meta={'Beta': 'b'}, - files={}, - reservation_id=None, + self.network_client.find_security_group.assert_called_once_with( + security_group.id, ignore_missing=False + ) + self.image_client.find_image.assert_called_once_with( + self.image.id, ignore_missing=False + ) + self.compute_sdk_client.find_server_group.assert_called_once_with( + server_group.id, ignore_missing=False + ) + self.compute_sdk_client.create_server.assert_called_once_with( + name=self.server.name, + image_id=self.image.id, + flavor_id=self.flavor.id, + metadata={'Beta': 'b'}, min_count=1, max_count=1, - security_groups=[fake_sg[0].id], - userdata=None, + security_groups=[{'name': security_group.id}], key_name='keyname', - availability_zone=None, - admin_pass='passw0rd', - block_device_mapping_v2=[], - nics=[], - scheduler_hints={'a': ['b', 'c'], 'group': fake_server_group.id}, + admin_password='passw0rd', + networks=[], + scheduler_hints={'a': ['b', 'c'], 'group': server_group.id}, config_drive=True, - ) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + block_device_mapping=[ + { + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, + ], ) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist(), data) def test_server_create_with_not_exist_security_group(self): + self.network_client.find_security_group.side_effect = ( + sdk_exceptions.NotFoundException() + ) + arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--key-name', 'keyname', '--security-group', - 'securitygroup', - '--security-group', 'not_exist_sg', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('key_name', 'keyname'), - ('security_group', ['securitygroup', 'not_exist_sg']), - ('server_name', self.new_server.name), + ('security_group', ['not_exist_sg']), + ('server_name', self.server.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - fake_sg = network_fakes.FakeSecurityGroup.create_security_groups( - count=1 - ) - fake_sg.append(exceptions.NotFound(code=404)) - mock_find_sg = network_fakes.FakeSecurityGroup.get_security_groups( - fake_sg - ) - self.app.client_manager.network.find_security_group = mock_find_sg - self.assertRaises( - exceptions.NotFound, self.cmd.take_action, parsed_args + sdk_exceptions.NotFoundException, self.cmd.take_action, parsed_args + ) + self.network_client.find_security_group.assert_called_once_with( + 'not_exist_sg', ignore_missing=False ) - mock_find_sg.assert_called_with('not_exist_sg', ignore_missing=False) def test_server_create_with_security_group_in_nova_network(self): + sg_name = 'nova-net-sec-group' arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', - '--key-name', - 'keyname', + self.flavor.id, '--security-group', - 'securitygroup', - self.new_server.name, + sg_name, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), - ('key_name', 'keyname'), - ('security_group', ['securitygroup']), - ('server_name', self.new_server.name), + ('image', self.image.id), + ('flavor', self.flavor.id), + ('security_group', [sg_name]), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) with mock.patch.object( self.app.client_manager, 'is_network_endpoint_enabled', return_value=False, ): with mock.patch.object( - self.compute_client.api, - 'security_group_find', - return_value={'name': 'fake_sg'}, + compute_v2, + 'find_security_group', + return_value={'name': sg_name}, ) as mock_find: columns, data = self.cmd.take_action(parsed_args) - mock_find.assert_called_once_with('securitygroup') - # Set expected values - kwargs = dict( - meta=None, - files={}, - reservation_id=None, + mock_find.assert_called_once_with(self.compute_sdk_client, sg_name) + self.compute_sdk_client.create_server.assert_called_once_with( + name=self.server.name, + image_id=self.image.id, + flavor_id=self.flavor.id, min_count=1, max_count=1, - security_groups=['fake_sg'], - userdata=None, - key_name='keyname', - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[], - nics=[], - scheduler_hints={}, - config_drive=None, - ) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + security_groups=[{'name': sg_name}], + networks=[], + block_device_mapping=[ + { + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, + ], ) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist(), data) def test_server_create_with_network(self): + network_net1 = network_fakes.create_one_network() + network_net2 = network_fakes.create_one_network() + network_auto = network_fakes.create_one_network({'name': 'auto'}) + port_port1 = network_fakes.create_one_port() + port_port2 = network_fakes.create_one_port() + + def find_network(name_or_id, ignore_missing): + assert ignore_missing is False + return { + network_net1.id: network_net1, + network_net2.id: network_net2, + network_auto.name: network_auto, + }[name_or_id] + + def find_port(name_or_id, ignore_missing): + assert ignore_missing is False + return { + port_port1.name: port_port1, + port_port2.id: port_port2, + }[name_or_id] + + self.app.client_manager.network.find_network.side_effect = find_network + self.app.client_manager.network.find_port.side_effect = find_port + arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--network', - 'net1', + network_net1.id, '--nic', - 'net-id=net1,v4-fixed-ip=10.0.0.2', + f'net-id={network_net2.id},v4-fixed-ip=10.0.0.2', '--port', - 'port1', + port_port1.name, '--network', - 'net1', - '--network', - 'auto', # this is a network called 'auto' + network_auto.name, '--nic', - 'port-id=port2', - self.new_server.name, + f'port-id={port_port2.id}', + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ( 'nics', [ { - 'net-id': 'net1', + 'net-id': network_net1.id, 'port-id': '', 'v4-fixed-ip': '', 'v6-fixed-ip': '', }, { - 'net-id': 'net1', + 'net-id': network_net2.id, 'port-id': '', 'v4-fixed-ip': '10.0.0.2', 'v6-fixed-ip': '', }, { 'net-id': '', - 'port-id': 'port1', + 'port-id': port_port1.name, 'v4-fixed-ip': '', 'v6-fixed-ip': '', }, { - 'net-id': 'net1', - 'port-id': '', - 'v4-fixed-ip': '', - 'v6-fixed-ip': '', - }, - { - 'net-id': 'auto', + 'net-id': network_auto.name, 'port-id': '', 'v4-fixed-ip': '', 'v6-fixed-ip': '', }, { 'net-id': '', - 'port-id': 'port2', + 'port-id': port_port2.id, 'v4-fixed-ip': '', 'v6-fixed-ip': '', }, ], ), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - get_endpoints = mock.Mock() - get_endpoints.return_value = {'network': []} - self.app.client_manager.auth_ref = mock.Mock() - self.app.client_manager.auth_ref.service_catalog = mock.Mock() - self.app.client_manager.auth_ref.service_catalog.get_endpoints = ( - get_endpoints - ) - - network_resource = mock.Mock(id='net1_uuid') - port1_resource = mock.Mock(id='port1_uuid') - port2_resource = mock.Mock(id='port2_uuid') - self.network_client.find_network.return_value = network_resource - self.network_client.find_port.side_effect = ( - lambda port_id, ignore_missing: { - "port1": port1_resource, - "port2": port2_resource, - }[port_id] - ) - - # Mock sdk APIs. - _network_1 = mock.Mock(id='net1_uuid') - _network_auto = mock.Mock(id='auto_uuid') - _port1 = mock.Mock(id='port1_uuid') - _port2 = mock.Mock(id='port2_uuid') - find_network = mock.Mock() - find_port = mock.Mock() - find_network.side_effect = lambda net_id, ignore_missing: { - "net1": _network_1, - "auto": _network_auto, - }[net_id] - find_port.side_effect = lambda port_id, ignore_missing: { - "port1": _port1, - "port2": _port2, - }[port_id] - self.app.client_manager.network.find_network = find_network - self.app.client_manager.network.find_port = find_port - - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = dict( - meta=None, - files={}, - reservation_id=None, + self.network_client.find_network.assert_has_calls( + [ + mock.call(network_net1.id, ignore_missing=False), + mock.call(network_net2.id, ignore_missing=False), + mock.call(network_auto.name, ignore_missing=False), + ] + ) + self.network_client.find_port.assert_has_calls( + [ + mock.call(port_port1.name, ignore_missing=False), + mock.call(port_port2.id, ignore_missing=False), + ] + ) + self.compute_sdk_client.create_server.assert_called_once_with( + name=self.server.name, + image_id=self.image.id, + flavor_id=self.flavor.id, min_count=1, max_count=1, - security_groups=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[], - nics=[ + networks=[ { - 'net-id': 'net1_uuid', - 'v4-fixed-ip': '', - 'v6-fixed-ip': '', - 'port-id': '', + 'uuid': network_net1.id, }, { - 'net-id': 'net1_uuid', - 'v4-fixed-ip': '10.0.0.2', - 'v6-fixed-ip': '', - 'port-id': '', + 'uuid': network_net2.id, + 'fixed': '10.0.0.2', }, { - 'net-id': '', - 'v4-fixed-ip': '', - 'v6-fixed-ip': '', - 'port-id': 'port1_uuid', + 'port': port_port1.id, }, { - 'net-id': 'net1_uuid', - 'v4-fixed-ip': '', - 'v6-fixed-ip': '', - 'port-id': '', + 'uuid': network_auto.id, }, { - 'net-id': 'auto_uuid', - 'v4-fixed-ip': '', - 'v6-fixed-ip': '', - 'port-id': '', - }, - { - 'net-id': '', - 'v4-fixed-ip': '', - 'v6-fixed-ip': '', - 'port-id': 'port2_uuid', + 'port': port_port2.id, + }, + ], + block_device_mapping=[ + { + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, }, ], - scheduler_hints={}, - config_drive=None, - ) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs ) self.assertEqual(self.columns, columns) @@ -1825,23 +1739,26 @@ class TestServerCreate(TestServer): def test_server_create_with_network_tag(self): self.set_compute_api_version('2.43') + network = network_fakes.create_one_network() + self.app.client_manager.network.find_network.return_value = network + arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--nic', - 'net-id=net1,tag=foo', - self.new_server.name, + f'net-id={network.id},tag=foo', + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ( 'nics', [ { - 'net-id': 'net1', + 'net-id': network.id, 'port-id': '', 'v4-fixed-ip': '', 'v6-fixed-ip': '', @@ -1849,70 +1766,55 @@ class TestServerCreate(TestServer): }, ], ), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # Mock sdk APIs. - _network = mock.Mock(id='net1_uuid') - self.network_client.find_network.return_value = _network - - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = dict( - meta=None, - files={}, - reservation_id=None, + self.network_client.find_network.assert_called_once_with( + network.id, ignore_missing=False + ) + self.compute_sdk_client.create_server.assert_called_once_with( + name=self.server.name, + image_id=self.image.id, + flavor_id=self.flavor.id, min_count=1, max_count=1, - security_groups=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[], - nics=[ + networks=[ { - 'net-id': 'net1_uuid', - 'v4-fixed-ip': '', - 'v6-fixed-ip': '', - 'port-id': '', + 'uuid': network.id, 'tag': 'foo', }, ], - scheduler_hints={}, - config_drive=None, - ) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + block_device_mapping=[ + { + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, + ], ) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist(), data) - self.network_client.find_network.assert_called_once() - self.app.client_manager.network.find_network.assert_called_once() - def test_server_create_with_network_tag_pre_v243(self): self.set_compute_api_version('2.42') arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--nic', 'net-id=net1,tag=foo', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ( 'nics', [ @@ -1925,49 +1827,48 @@ class TestServerCreate(TestServer): }, ], ), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) + self.network_client.find_network.assert_not_called() + self.compute_sdk_client.create_server.assert_not_called() def _test_server_create_with_auto_network(self, arglist): # requires API microversion 2.37 or later self.set_compute_api_version('2.37') verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('nics', ['auto']), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = dict( - meta=None, - files={}, - reservation_id=None, + self.network_client.find_network.assert_not_called() + self.compute_sdk_client.create_server.assert_called_once_with( + name=self.server.name, + image_id=self.image.id, + flavor_id=self.flavor.id, min_count=1, max_count=1, - security_groups=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[], - nics='auto', - scheduler_hints={}, - config_drive=None, - ) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + networks='auto', + block_device_mapping=[ + { + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, + ], ) self.assertEqual(self.columns, columns) @@ -1979,23 +1880,23 @@ class TestServerCreate(TestServer): def test_server_create_with_auto_network_legacy(self): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--nic', 'auto', - self.new_server.name, + self.server.name, ] self._test_server_create_with_auto_network(arglist) def test_server_create_with_auto_network(self): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--auto-network', - self.new_server.name, + self.server.name, ] self._test_server_create_with_auto_network(arglist) @@ -2005,19 +1906,19 @@ class TestServerCreate(TestServer): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--nic', 'auto', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('nics', ['auto']), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -2032,50 +1933,48 @@ class TestServerCreate(TestServer): 'allocation', str(exc), ) - self.assertNotCalled(self.servers_mock.create) + self.compute_sdk_client.create_server.assert_not_called() - def test_server_create_with_auto_network_default_v2_37(self): + def test_server_create_with_auto_network_default(self): """Tests creating a server without specifying --nic using 2.37.""" # requires API microversion 2.37 or later self.set_compute_api_version('2.37') arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', - self.new_server.name, + self.flavor.id, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), + ('nics', []), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = dict( - meta=None, - files={}, - reservation_id=None, + self.network_client.find_network.assert_not_called() + self.compute_sdk_client.create_server.assert_called_once_with( + name=self.server.name, + image_id=self.image.id, + flavor_id=self.flavor.id, min_count=1, max_count=1, - security_groups=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[], - nics='auto', - scheduler_hints={}, - config_drive=None, - ) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + networks='auto', + block_device_mapping=[ + { + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, + ], ) self.assertEqual(self.columns, columns) @@ -2086,36 +1985,33 @@ class TestServerCreate(TestServer): self.set_compute_api_version('2.37') verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('nics', ['none']), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = dict( - meta=None, - files={}, - reservation_id=None, + self.network_client.find_network.assert_not_called() + self.compute_sdk_client.create_server.assert_called_once_with( + name=self.server.name, + image_id=self.image.id, + flavor_id=self.flavor.id, min_count=1, max_count=1, - security_groups=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[], - nics='none', - scheduler_hints={}, - config_drive=None, - ) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + networks='none', + block_device_mapping=[ + { + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, + ], ) self.assertEqual(self.columns, columns) @@ -2127,23 +2023,23 @@ class TestServerCreate(TestServer): def test_server_create_with_none_network_legacy(self): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--nic', 'none', - self.new_server.name, + self.server.name, ] self._test_server_create_with_none_network(arglist) def test_server_create_with_none_network(self): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--no-network', - self.new_server.name, + self.server.name, ] self._test_server_create_with_none_network(arglist) @@ -2153,23 +2049,22 @@ class TestServerCreate(TestServer): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--nic', 'none', - self.new_server.name, + self.server.name, ] - verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('nics', ['none']), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) exc = self.assertRaises( exceptions.CommandError, self.cmd.take_action, @@ -2181,25 +2076,25 @@ class TestServerCreate(TestServer): 'allocation', str(exc), ) - self.assertNotCalled(self.servers_mock.create) + self.compute_sdk_client.create_server.assert_not_called() - def test_server_create_with_conflict_network_options(self): + def test_server_create_with_conflicting_network_options(self): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--nic', 'none', '--nic', 'auto', '--nic', 'port-id=port1', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ( 'nics', [ @@ -2214,101 +2109,181 @@ class TestServerCreate(TestServer): ], ), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - get_endpoints = mock.Mock() - get_endpoints.return_value = {'network': []} - self.app.client_manager.auth_ref = mock.Mock() - self.app.client_manager.auth_ref.service_catalog = mock.Mock() - self.app.client_manager.auth_ref.service_catalog.get_endpoints = ( - get_endpoints - ) - - port_resource = mock.Mock(id='port1_uuid') - self.network_client.find_port.return_value = port_resource - - self.assertRaises( + exc = self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) - self.assertNotCalled(self.servers_mock.create) + self.assertIn( + 'Specifying a --nic of auto or none cannot be used with any ' + 'other --nic, --network or --port value.', + str(exc), + ) + self.compute_sdk_client.create_server.assert_not_called() def test_server_create_with_invalid_network_options(self): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--nic', 'abcdefgh', - self.new_server.name, + self.server.name, ] - self.assertRaises( + exc = self.assertRaises( test_utils.ParserException, self.check_parser, self.cmd, arglist, [], ) - self.assertNotCalled(self.servers_mock.create) + self.assertIn( + 'Invalid argument abcdefgh; argument must be of form ', + str(exc), + ) + self.compute_sdk_client.create_server.assert_not_called() def test_server_create_with_invalid_network_key(self): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--nic', 'abcdefgh=12324', - self.new_server.name, + self.server.name, ] - self.assertRaises( + exc = self.assertRaises( test_utils.ParserException, self.check_parser, self.cmd, arglist, [], ) - self.assertNotCalled(self.servers_mock.create) + self.assertIn( + 'Invalid argument abcdefgh=12324; argument must be of form ', + str(exc), + ) + self.compute_sdk_client.create_server.assert_not_called() def test_server_create_with_empty_network_key_value(self): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--nic', 'net-id=', - self.new_server.name, + self.server.name, ] - self.assertRaises( + exc = self.assertRaises( test_utils.ParserException, self.check_parser, self.cmd, arglist, [], ) - self.assertNotCalled(self.servers_mock.create) + self.assertIn( + 'Invalid argument net-id=; argument must be of form ', + str(exc), + ) + self.compute_sdk_client.create_server.assert_not_called() def test_server_create_with_only_network_key(self): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--nic', 'net-id', - self.new_server.name, + self.server.name, ] - self.assertRaises( + exc = self.assertRaises( test_utils.ParserException, self.check_parser, self.cmd, arglist, [], ) - self.assertNotCalled(self.servers_mock.create) + self.assertIn( + 'Invalid argument net-id; argument must be of form ', + str(exc), + ) + self.compute_sdk_client.create_server.assert_not_called() + + def test_server_create_with_network_in_nova_network(self): + net_name = 'nova-net-net' + net_id = uuid.uuid4().hex + + arglist = [ + '--image', + self.image.id, + '--flavor', + self.flavor.id, + '--network', + net_name, + self.server.name, + ] + verifylist = [ + ('image', self.image.id), + ('flavor', self.flavor.id), + ( + 'nics', + [ + { + 'net-id': net_name, + 'port-id': '', + 'v4-fixed-ip': '', + 'v6-fixed-ip': '', + }, + ], + ), + ('config_drive', False), + ('server_name', self.server.name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + with mock.patch.object( + self.app.client_manager, + 'is_network_endpoint_enabled', + return_value=False, + ): + with mock.patch.object( + compute_v2, + 'find_network', + return_value={'id': net_id, 'name': net_name}, + ) as mock_find: + columns, data = self.cmd.take_action(parsed_args) + + mock_find.assert_called_once_with(self.compute_sdk_client, net_name) + self.compute_sdk_client.create_server.assert_called_once_with( + name=self.server.name, + image_id=self.image.id, + flavor_id=self.flavor.id, + min_count=1, + max_count=1, + networks=[ + { + 'uuid': net_id, + }, + ], + block_device_mapping=[ + { + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, + ], + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.datalist(), data) def test_server_create_with_conflicting_net_port_filters(self): arglist = [ @@ -2318,7 +2293,7 @@ class TestServerCreate(TestServer): 'flavor1', '--nic', 'net-id=abc,port-id=xyz', - self.new_server.name, + self.server.name, ] exc = self.assertRaises( test_utils.ParserException, @@ -2328,7 +2303,7 @@ class TestServerCreate(TestServer): [], ) self.assertIn("either 'network' or 'port'", str(exc)) - self.assertNotCalled(self.servers_mock.create) + self.compute_sdk_client.create_server.assert_not_called() def test_server_create_with_conflicting_fixed_ip_filters(self): arglist = [ @@ -2338,7 +2313,7 @@ class TestServerCreate(TestServer): 'flavor1', '--nic', 'net-id=abc,v4-fixed-ip=1.2.3.4,v6-fixed-ip=2001:db8:abcd', - self.new_server.name, + self.server.name, ] exc = self.assertRaises( test_utils.ParserException, @@ -2348,53 +2323,52 @@ class TestServerCreate(TestServer): [], ) self.assertIn("either 'v4-fixed-ip' or 'v6-fixed-ip'", str(exc)) - self.assertNotCalled(self.servers_mock.create) + self.compute_sdk_client.create_server.assert_not_called() @mock.patch.object(common_utils, 'wait_for_status', return_value=True) def test_server_create_with_wait_ok(self, mock_wait_for_status): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--wait', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('config_drive', False), ('wait', True), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) + + self.compute_sdk_client.create_server.assert_called_once_with( + name=self.server.name, + image_id=self.image.id, + flavor_id=self.flavor.id, + min_count=1, + max_count=1, + networks=[], + block_device_mapping=[ + { + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, + ], + ) mock_wait_for_status.assert_called_once_with( - self.servers_mock.get, - self.new_server.id, + self.compute_sdk_client.get_server, + self.server.id, callback=mock.ANY, ) - kwargs = dict( - meta=None, - files={}, - reservation_id=None, - min_count=1, - max_count=1, - security_groups=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[], - nics=[], - scheduler_hints={}, - config_drive=None, - ) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs - ) self.assertEqual(self.columns, columns) self.assertTupleEqual(self.datalist(), data) @@ -2402,143 +2376,126 @@ class TestServerCreate(TestServer): def test_server_create_with_wait_fails(self, mock_wait_for_status): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--wait', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('config_drive', False), ('wait', True), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) + self.compute_sdk_client.create_server.assert_called_once_with( + name=self.server.name, + image_id=self.image.id, + flavor_id=self.flavor.id, + min_count=1, + max_count=1, + networks=[], + block_device_mapping=[ + { + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, + ], + ) mock_wait_for_status.assert_called_once_with( - self.servers_mock.get, - self.new_server.id, + self.compute_sdk_client.get_server, + self.server.id, callback=mock.ANY, ) - kwargs = dict( - meta=None, - files={}, - reservation_id=None, - min_count=1, - max_count=1, - security_groups=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[], - nics=[], - scheduler_hints={}, - config_drive=None, - ) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs - ) - - @mock.patch('openstackclient.compute.v2.server.open') - def test_server_create_userdata(self, mock_open): - mock_file = mock.Mock(name='File') - mock_open.return_value = mock_file - mock_open.read.return_value = '#!/bin/sh' - + def test_server_create_userdata(self): + user_data = b'#!/bin/sh' arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--user-data', 'userdata.sh', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('user_data', 'userdata.sh'), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + with mock.patch( + 'openstackclient.compute.v2.server.open', + mock.mock_open(read_data=user_data), + ) as mock_file: + columns, data = self.cmd.take_action(parsed_args) - # In base command class ShowOne in cliff, abstract method take_action() - # returns a two-part tuple with a tuple of column names and a tuple of - # data to be shown. - columns, data = self.cmd.take_action(parsed_args) - - # Ensure the userdata file is opened - mock_open.assert_called_with('userdata.sh') - - # Ensure the userdata file is closed - mock_file.close.assert_called_with() - - # Set expected values - kwargs = dict( - meta=None, - files={}, - reservation_id=None, + mock_file.assert_called_with('userdata.sh', 'rb') + self.compute_sdk_client.create_server.assert_called_once_with( + name=self.server.name, + image_id=self.image.id, + flavor_id=self.flavor.id, min_count=1, max_count=1, - security_groups=[], - userdata=mock_file, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[], - nics=[], - scheduler_hints={}, - config_drive=None, - ) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + networks=[], + user_data=base64.b64encode(user_data).decode('utf-8'), + block_device_mapping=[ + { + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, + ], ) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist(), data) def test_server_create_with_volume(self): + self.volume_client.volumes.get.return_value = self.volume + arglist = [ '--flavor', self.flavor.id, '--volume', self.volume.name, - self.new_server.name, + self.server.name, ] verifylist = [ ('flavor', self.flavor.id), ('volume', self.volume.name), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # CreateServer.take_action() returns two tuples + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = { - 'meta': None, - 'files': {}, - 'reservation_id': None, - 'min_count': 1, - 'max_count': 1, - 'security_groups': [], - 'userdata': None, - 'key_name': None, - 'availability_zone': None, - 'admin_pass': None, - 'block_device_mapping_v2': [ + self.volume_client.volumes.get.assert_called_once_with( + self.volume.name + ) + self.compute_sdk_client.create_server.assert_called_once_with( + name=self.server.name, + image_id='', + flavor_id=self.flavor.id, + min_count=1, + max_count=1, + block_device_mapping=[ { 'uuid': self.volume.id, 'boot_index': 0, @@ -2546,50 +2503,41 @@ class TestServerCreate(TestServer): 'destination_type': 'volume', } ], - 'nics': [], - 'scheduler_hints': {}, - 'config_drive': None, - } - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, None, self.flavor, **kwargs + networks=[], ) - self.volumes_mock.get.assert_called_once_with(self.volume.name) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist(), data) def test_server_create_with_snapshot(self): + self.volume_client.volume_snapshots.get.return_value = self.snapshot + arglist = [ '--flavor', self.flavor.id, '--snapshot', self.snapshot.name, - self.new_server.name, + self.server.name, ] verifylist = [ ('flavor', self.flavor.id), ('snapshot', self.snapshot.name), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # CreateServer.take_action() returns two tuples + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = { - 'meta': None, - 'files': {}, - 'reservation_id': None, - 'min_count': 1, - 'max_count': 1, - 'security_groups': [], - 'userdata': None, - 'key_name': None, - 'availability_zone': None, - 'admin_pass': None, - 'block_device_mapping_v2': [ + self.volume_client.volume_snapshots.get.assert_called_once_with( + self.snapshot.name + ) + self.compute_sdk_client.create_server.assert_called_once_with( + name=self.server.name, + image_id='', + flavor_id=self.flavor.id, + min_count=1, + max_count=1, + block_device_mapping=[ { 'uuid': self.snapshot.id, 'boot_index': 0, @@ -2598,15 +2546,8 @@ class TestServerCreate(TestServer): 'delete_on_termination': False, } ], - 'nics': [], - 'scheduler_hints': {}, - 'config_drive': None, - } - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, None, self.flavor, **kwargs + networks=[], ) - self.snapshots_mock.get.assert_called_once_with(self.snapshot.name) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist(), data) @@ -2618,7 +2559,7 @@ class TestServerCreate(TestServer): self.flavor.id, '--block-device', block_device, - self.new_server.name, + self.server.name, ] verifylist = [ ('image', None), @@ -2633,40 +2574,29 @@ class TestServerCreate(TestServer): }, ], ), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # CreateServer.take_action() returns two tuples + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = { - 'meta': None, - 'files': {}, - 'reservation_id': None, - 'min_count': 1, - 'max_count': 1, - 'security_groups': [], - 'userdata': None, - 'key_name': None, - 'availability_zone': None, - 'admin_pass': None, - 'block_device_mapping_v2': [ + # we don't do any validation of IDs when using the legacy option + self.volume_client.volumes.get.assert_not_called() + self.compute_sdk_client.create_server.assert_called_once_with( + name=self.server.name, + image_id='', + flavor_id=self.flavor.id, + min_count=1, + max_count=1, + block_device_mapping=[ { 'uuid': self.volume.id, + 'boot_index': 0, 'source_type': 'volume', 'destination_type': 'volume', - 'boot_index': 0, - }, + } ], - 'nics': [], - 'scheduler_hints': {}, - 'config_drive': None, - } - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, None, self.flavor, **kwargs + networks=[], ) self.assertEqual(self.columns, columns) @@ -2675,6 +2605,7 @@ class TestServerCreate(TestServer): def test_server_create_with_block_device_full(self): self.set_compute_api_version('2.67') + self.volume_alt = volume_fakes.create_one_volume() block_device = ( f'uuid={self.volume.id},source_type=volume,' f'destination_type=volume,disk_bus=ide,device_type=disk,' @@ -2686,17 +2617,17 @@ class TestServerCreate(TestServer): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', self.flavor.id, '--block-device', block_device, '--block-device', block_device_alt, - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), + ('image', self.image.id), ('flavor', self.flavor.id), ( 'block_devices', @@ -2721,26 +2652,28 @@ class TestServerCreate(TestServer): }, ], ), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # CreateServer.take_action() returns two tuples + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = { - 'meta': None, - 'files': {}, - 'reservation_id': None, - 'min_count': 1, - 'max_count': 1, - 'security_groups': [], - 'userdata': None, - 'key_name': None, - 'availability_zone': None, - 'admin_pass': None, - 'block_device_mapping_v2': [ + # we don't do any validation of IDs when using the legacy option + self.volume_client.volumes.get.assert_not_called() + self.compute_sdk_client.create_server.assert_called_once_with( + name=self.server.name, + image_id=self.image.id, + flavor_id=self.flavor.id, + min_count=1, + max_count=1, + block_device_mapping=[ + { + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, { 'uuid': self.volume.id, 'source_type': 'volume', @@ -2761,13 +2694,7 @@ class TestServerCreate(TestServer): 'destination_type': 'volume', }, ], - 'nics': 'auto', - 'scheduler_hints': {}, - 'config_drive': None, - } - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + networks='auto', ) self.assertEqual(self.columns, columns) @@ -2797,37 +2724,38 @@ class TestServerCreate(TestServer): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', self.flavor.id, '--block-device', fp.name, - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), + ('image', self.image.id), ('flavor', self.flavor.id), ('block_devices', [block_device]), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - # CreateServer.take_action() returns two tuples columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = { - 'meta': None, - 'files': {}, - 'reservation_id': None, - 'min_count': 1, - 'max_count': 1, - 'security_groups': [], - 'userdata': None, - 'key_name': None, - 'availability_zone': None, - 'admin_pass': None, - 'block_device_mapping_v2': [ + # we don't do any validation of IDs when using the legacy option + self.volume_client.volumes.get.assert_not_called() + self.compute_sdk_client.create_server.assert_called_once_with( + name=self.server.name, + image_id=self.image.id, + flavor_id=self.flavor.id, + min_count=1, + max_count=1, + block_device_mapping=[ + { + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, { 'uuid': self.volume.id, 'source_type': 'volume', @@ -2841,15 +2769,9 @@ class TestServerCreate(TestServer): 'delete_on_termination': True, 'tag': 'foo', 'volume_type': 'foo', - } + }, ], - 'nics': 'auto', - 'scheduler_hints': {}, - 'config_drive': None, - } - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + networks='auto', ) self.assertEqual(self.columns, columns) @@ -2861,63 +2783,66 @@ class TestServerCreate(TestServer): ) arglist = [ '--image', - 'image1', + self.image.id, '--flavor', self.flavor.id, '--block-device', block_device, - self.new_server.name, + self.server.name, ] parsed_args = self.check_parser(self.cmd, arglist, []) ex = self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) self.assertIn('The boot_index key of --block-device ', str(ex)) + self.compute_sdk_client.create_server.assert_not_called() def test_server_create_with_block_device_invalid_source_type(self): block_device = f'uuid={self.volume.name},source_type=foo' arglist = [ '--image', - 'image1', + self.image.id, '--flavor', self.flavor.id, '--block-device', block_device, - self.new_server.name, + self.server.name, ] parsed_args = self.check_parser(self.cmd, arglist, []) ex = self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) self.assertIn('The source_type key of --block-device ', str(ex)) + self.compute_sdk_client.create_server.assert_not_called() def test_server_create_with_block_device_invalid_destination_type(self): block_device = f'uuid={self.volume.name},destination_type=foo' arglist = [ '--image', - 'image1', + self.image.id, '--flavor', self.flavor.id, '--block-device', block_device, - self.new_server.name, + self.server.name, ] parsed_args = self.check_parser(self.cmd, arglist, []) ex = self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) self.assertIn('The destination_type key of --block-device ', str(ex)) + self.compute_sdk_client.create_server.assert_not_called() def test_server_create_with_block_device_invalid_shutdown(self): block_device = f'uuid={self.volume.name},delete_on_termination=foo' arglist = [ '--image', - 'image1', + self.image.id, '--flavor', self.flavor.id, '--block-device', block_device, - self.new_server.name, + self.server.name, ] parsed_args = self.check_parser(self.cmd, arglist, []) ex = self.assertRaises( @@ -2926,6 +2851,7 @@ class TestServerCreate(TestServer): self.assertIn( 'The delete_on_termination key of --block-device ', str(ex) ) + self.compute_sdk_client.create_server.assert_not_called() def test_server_create_with_block_device_tag_pre_v242(self): self.set_compute_api_version('2.41') @@ -2933,12 +2859,12 @@ class TestServerCreate(TestServer): block_device = f'uuid={self.volume.name},tag=foo' arglist = [ '--image', - 'image1', + self.image.id, '--flavor', self.flavor.id, '--block-device', block_device, - self.new_server.name, + self.server.name, ] parsed_args = self.check_parser(self.cmd, arglist, []) ex = self.assertRaises( @@ -2947,6 +2873,7 @@ class TestServerCreate(TestServer): self.assertIn( '--os-compute-api-version 2.42 or greater is required', str(ex) ) + self.compute_sdk_client.create_server.assert_not_called() def test_server_create_with_block_device_volume_type_pre_v267(self): self.set_compute_api_version('2.66') @@ -2954,12 +2881,12 @@ class TestServerCreate(TestServer): block_device = f'uuid={self.volume.name},volume_type=foo' arglist = [ '--image', - 'image1', + self.image.id, '--flavor', self.flavor.id, '--block-device', block_device, - self.new_server.name, + self.server.name, ] parsed_args = self.check_parser(self.cmd, arglist, []) ex = self.assertRaises( @@ -2968,19 +2895,22 @@ class TestServerCreate(TestServer): self.assertIn( '--os-compute-api-version 2.67 or greater is required', str(ex) ) + self.compute_sdk_client.create_server.assert_not_called() def test_server_create_with_block_device_mapping(self): + self.volume_client.volumes.get.return_value = self.volume + arglist = [ '--image', - 'image1', + self.image.id, '--flavor', self.flavor.id, '--block-device-mapping', 'vda=' + self.volume.name + ':::false', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), + ('image', self.image.id), ('flavor', self.flavor.id), ( 'block_device_mapping', @@ -2995,58 +2925,57 @@ class TestServerCreate(TestServer): ], ), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # CreateServer.take_action() returns two tuples + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = dict( - meta=None, - files={}, - reservation_id=None, + self.volume_client.volumes.get.assert_called_once_with( + self.volume.name + ) + self.compute_sdk_client.create_server.assert_called_once_with( + name=self.server.name, + image_id=self.image.id, + flavor_id=self.flavor.id, min_count=1, max_count=1, - security_groups=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[ + block_device_mapping=[ + { + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, { 'device_name': 'vda', 'uuid': self.volume.id, 'destination_type': 'volume', 'source_type': 'volume', 'delete_on_termination': 'false', - } + }, ], - nics=[], - scheduler_hints={}, - config_drive=None, - ) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + networks=[], ) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist(), data) def test_server_create_with_block_device_mapping_min_input(self): + self.volume_client.volumes.get.return_value = self.volume + arglist = [ '--image', - 'image1', + self.image.id, '--flavor', self.flavor.id, '--block-device-mapping', 'vdf=' + self.volume.name, - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), + ('image', self.image.id), ('flavor', self.flavor.id), ( 'block_device_mapping', @@ -3060,57 +2989,56 @@ class TestServerCreate(TestServer): ], ), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # CreateServer.take_action() returns two tuples + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = dict( - meta=None, - files={}, - reservation_id=None, + self.volume_client.volumes.get.assert_called_once_with( + self.volume.name + ) + self.compute_sdk_client.create_server.assert_called_once_with( + name=self.server.name, + image_id=self.image.id, + flavor_id=self.flavor.id, min_count=1, max_count=1, - security_groups=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[ + block_device_mapping=[ + { + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, { 'device_name': 'vdf', 'uuid': self.volume.id, 'destination_type': 'volume', 'source_type': 'volume', - } + }, ], - nics=[], - scheduler_hints={}, - config_drive=None, - ) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + networks=[], ) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist(), data) def test_server_create_with_block_device_mapping_default_input(self): + self.volume_client.volumes.get.return_value = self.volume + arglist = [ '--image', - 'image1', + self.image.id, '--flavor', self.flavor.id, '--block-device-mapping', 'vdf=' + self.volume.name + ':::', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), + ('image', self.image.id), ('flavor', self.flavor.id), ( 'block_device_mapping', @@ -3124,57 +3052,56 @@ class TestServerCreate(TestServer): ], ), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # CreateServer.take_action() returns two tuples + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = dict( - meta=None, - files={}, - reservation_id=None, + self.volume_client.volumes.get.assert_called_once_with( + self.volume.name + ) + self.compute_sdk_client.create_server.assert_called_once_with( + name=self.server.name, + image_id=self.image.id, + flavor_id=self.flavor.id, min_count=1, max_count=1, - security_groups=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[ + block_device_mapping=[ + { + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, { 'device_name': 'vdf', 'uuid': self.volume.id, 'destination_type': 'volume', 'source_type': 'volume', - } + }, ], - nics=[], - scheduler_hints={}, - config_drive=None, - ) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + networks=[], ) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist(), data) def test_server_create_with_block_device_mapping_full_input(self): + self.volume_client.volumes.get.return_value = self.volume + arglist = [ '--image', - 'image1', + self.image.id, '--flavor', self.flavor.id, '--block-device-mapping', 'vde=' + self.volume.name + ':volume:3:true', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), + ('image', self.image.id), ('flavor', self.flavor.id), ( 'block_device_mapping', @@ -3190,26 +3117,29 @@ class TestServerCreate(TestServer): ], ), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # CreateServer.take_action() returns two tuples + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = dict( - meta=None, - files={}, - reservation_id=None, + self.volume_client.volumes.get.assert_called_once_with( + self.volume.name + ) + self.compute_sdk_client.create_server.assert_called_once_with( + name=self.server.name, + image_id=self.image.id, + flavor_id=self.flavor.id, min_count=1, max_count=1, - security_groups=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[ + block_device_mapping=[ + { + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, { 'device_name': 'vde', 'uuid': self.volume.id, @@ -3217,39 +3147,36 @@ class TestServerCreate(TestServer): 'source_type': 'volume', 'delete_on_termination': 'true', 'volume_size': '3', - } + }, ], - nics=[], - scheduler_hints={}, - config_drive=None, - ) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + networks=[], ) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist(), data) def test_server_create_with_block_device_mapping_snapshot(self): + self.snapshot = volume_fakes.create_one_snapshot() + self.volume_client.volume_snapshots.get.return_value = self.snapshot + arglist = [ '--image', - 'image1', + self.image.id, '--flavor', self.flavor.id, '--block-device-mapping', - 'vds=' + self.volume.name + ':snapshot:5:true', - self.new_server.name, + 'vds=' + self.snapshot.name + ':snapshot:5:true', + self.server.name, ] verifylist = [ - ('image', 'image1'), + ('image', self.image.id), ('flavor', self.flavor.id), ( 'block_device_mapping', [ { 'device_name': 'vds', - 'uuid': self.volume.name, + 'uuid': self.snapshot.name, 'source_type': 'snapshot', 'volume_size': '5', 'destination_type': 'volume', @@ -3258,26 +3185,29 @@ class TestServerCreate(TestServer): ], ), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # CreateServer.take_action() returns two tuples + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = dict( - meta=None, - files={}, - reservation_id=None, + self.volume_client.volume_snapshots.get.assert_called_once_with( + self.snapshot.name + ) + self.compute_sdk_client.create_server.assert_called_once_with( + name=self.server.name, + image_id=self.image.id, + flavor_id=self.flavor.id, min_count=1, max_count=1, - security_groups=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[ + block_device_mapping=[ + { + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, { 'device_name': 'vds', 'uuid': self.snapshot.id, @@ -3285,34 +3215,30 @@ class TestServerCreate(TestServer): 'source_type': 'snapshot', 'delete_on_termination': 'true', 'volume_size': '5', - } + }, ], - nics=[], - scheduler_hints={}, - config_drive=None, - ) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + networks=[], ) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist(), data) def test_server_create_with_block_device_mapping_multiple(self): + self.volume_client.volumes.get.return_value = self.volume + arglist = [ '--image', - 'image1', + self.image.id, '--flavor', self.flavor.id, '--block-device-mapping', 'vdb=' + self.volume.name + ':::false', '--block-device-mapping', 'vdc=' + self.volume.name + ':::true', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), + ('image', self.image.id), ('flavor', self.flavor.id), ( 'block_device_mapping', @@ -3334,26 +3260,29 @@ class TestServerCreate(TestServer): ], ), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # CreateServer.take_action() returns two tuples + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = dict( - meta=None, - files={}, - reservation_id=None, + self.volume_client.volumes.get.assert_has_calls( + [mock.call(self.volume.name)] * 2 + ) + self.compute_sdk_client.create_server.assert_called_once_with( + name=self.server.name, + image_id=self.image.id, + flavor_id=self.flavor.id, min_count=1, max_count=1, - security_groups=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[ + block_device_mapping=[ + { + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, { 'device_name': 'vdb', 'uuid': self.volume.id, @@ -3369,13 +3298,7 @@ class TestServerCreate(TestServer): 'delete_on_termination': 'true', }, ], - nics=[], - scheduler_hints={}, - config_drive=None, - ) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + networks=[], ) self.assertEqual(self.columns, columns) @@ -3385,56 +3308,68 @@ class TestServerCreate(TestServer): # block device mapping don't contain equal sign "=" arglist = [ '--image', - 'image1', + self.image.id, '--flavor', self.flavor.id, '--block-device-mapping', 'not_contain_equal_sign', - self.new_server.name, + self.server.name, ] - self.assertRaises( + exc = self.assertRaises( test_utils.ParserException, self.check_parser, self.cmd, arglist, [], ) + self.assertIn( + 'argument --block-device-mapping: Invalid argument ', str(exc) + ) + self.compute_sdk_client.create_server.assert_not_called() # block device mapping don't contain device name "=uuid:::true" arglist = [ '--image', - 'image1', + self.image.id, '--flavor', self.flavor.id, '--block-device-mapping', '=uuid:::true', - self.new_server.name, + self.server.name, ] - self.assertRaises( + exc = self.assertRaises( test_utils.ParserException, self.check_parser, self.cmd, arglist, [], ) + self.assertIn( + 'argument --block-device-mapping: Invalid argument ', str(exc) + ) + self.compute_sdk_client.create_server.assert_not_called() def test_server_create_with_block_device_mapping_no_uuid(self): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', self.flavor.id, '--block-device-mapping', 'vdb=', - self.new_server.name, + self.server.name, ] - self.assertRaises( + exc = self.assertRaises( test_utils.ParserException, self.check_parser, self.cmd, arglist, [], ) + self.assertIn( + 'argument --block-device-mapping: Invalid argument ', str(exc) + ) + self.compute_sdk_client.create_server.assert_not_called() def test_server_create_volume_boot_from_volume_conflict(self): # Tests that specifying --volume and --boot-from-volume results in @@ -3449,14 +3384,14 @@ class TestServerCreate(TestServer): 'volume1', '--boot-from-volume', '1', - self.new_server.name, + self.server.name, ] verifylist = [ ('flavor', self.flavor.id), ('volume', 'volume1'), ('boot_from_volume', 1), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -3467,6 +3402,7 @@ class TestServerCreate(TestServer): self.assertIn( '--volume is not allowed with --boot-from-volume', str(ex) ) + self.compute_sdk_client.create_server.assert_not_called() def test_server_create_boot_from_volume_no_image(self): # Test --boot-from-volume option without --image or @@ -3476,218 +3412,202 @@ class TestServerCreate(TestServer): self.flavor.id, '--boot-from-volume', '1', - self.new_server.name, + self.server.name, ] verifylist = [ ('flavor', self.flavor.id), ('boot_from_volume', 1), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) ex = self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) - # Assert it is the error we expect. self.assertIn( 'An image (--image or --image-property) is required ' 'to support --boot-from-volume option', str(ex), ) + self.compute_sdk_client.create_server.assert_not_called() def test_server_create_image_property(self): + image = image_fakes.create_one_image({'hypervisor_type': 'qemu'}) + self.image_client.images.return_value = [image] + arglist = [ '--image-property', 'hypervisor_type=qemu', '--flavor', - 'flavor1', - self.new_server.name, + self.flavor.id, + self.server.name, ] verifylist = [ ('image_properties', {'hypervisor_type': 'qemu'}), - ('flavor', 'flavor1'), + ('flavor', self.flavor.id), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - # create a image_info as the side_effect of the fake image_list() - image_info = { - 'hypervisor_type': 'qemu', - } - - _image = image_fakes.create_one_image(image_info) - self.image_client.images.return_value = [_image] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = dict( - files={}, - reservation_id=None, + self.image_client.images.assert_called_once_with() + self.compute_sdk_client.create_server.assert_called_once_with( + name=self.server.name, + image_id=image.id, + flavor_id=self.flavor.id, min_count=1, max_count=1, - security_groups=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[], - nics=[], - meta=None, - scheduler_hints={}, - config_drive=None, - ) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, _image, self.flavor, **kwargs + networks=[], + block_device_mapping=[ + { + 'uuid': image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, + ], ) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist(), data) def test_server_create_image_property_multi(self): + image = image_fakes.create_one_image( + {'hypervisor_type': 'qemu', 'hw_disk_bus': 'ide'} + ) + self.image_client.images.return_value = [image] + arglist = [ '--image-property', 'hypervisor_type=qemu', '--image-property', 'hw_disk_bus=ide', '--flavor', - 'flavor1', - self.new_server.name, + self.flavor.id, + self.server.name, ] verifylist = [ ( 'image_properties', {'hypervisor_type': 'qemu', 'hw_disk_bus': 'ide'}, ), - ('flavor', 'flavor1'), + ('flavor', self.flavor.id), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - # create a image_info as the side_effect of the fake image_list() - image_info = { - 'hypervisor_type': 'qemu', - 'hw_disk_bus': 'ide', - } - _image = image_fakes.create_one_image(image_info) - self.image_client.images.return_value = [_image] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = dict( - files={}, - reservation_id=None, + self.image_client.images.assert_called_once_with() + self.compute_sdk_client.create_server.assert_called_once_with( + name=self.server.name, + image_id=image.id, + flavor_id=self.flavor.id, min_count=1, max_count=1, - security_groups=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[], - nics=[], - meta=None, - scheduler_hints={}, - config_drive=None, - ) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, _image, self.flavor, **kwargs + networks=[], + block_device_mapping=[ + { + 'uuid': image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, + ], ) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist(), data) def test_server_create_image_property_missed(self): + image = image_fakes.create_one_image( + {'hypervisor_type': 'qemu', 'hw_disk_bus': 'ide'} + ) + self.image_client.images.return_value = [image] + arglist = [ '--image-property', 'hypervisor_type=qemu', + # note the mismatch in the 'hw_disk_bus' property '--image-property', 'hw_disk_bus=virtio', '--flavor', - 'flavor1', - self.new_server.name, + self.flavor.id, + self.server.name, ] verifylist = [ ( 'image_properties', {'hypervisor_type': 'qemu', 'hw_disk_bus': 'virtio'}, ), - ('flavor', 'flavor1'), + ('flavor', self.flavor.id), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - # create a image_info as the side_effect of the fake image_list() - image_info = { - 'hypervisor_type': 'qemu', - 'hw_disk_bus': 'ide', - } - - _image = image_fakes.create_one_image(image_info) - self.image_client.images.return_value = [_image] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - self.assertRaises( + exc = self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) + self.assertIn( + 'No images match the property expected by --image-property', + str(exc), + ) + self.compute_sdk_client.create_server.assert_not_called() def test_server_create_image_property_with_image_list(self): + target_image = image_fakes.create_one_image( + { + 'properties': { + 'owner_specified.openstack.object': 'image/cirros' + } + } + ) + another_image = image_fakes.create_one_image() + self.image_client.images.return_value = [target_image, another_image] + arglist = [ '--image-property', 'owner_specified.openstack.object=image/cirros', '--flavor', - 'flavor1', - self.new_server.name, + self.flavor.id, + self.server.name, ] - verifylist = [ ( 'image_properties', {'owner_specified.openstack.object': 'image/cirros'}, ), - ('flavor', 'flavor1'), - ('server_name', self.new_server.name), + ('flavor', self.flavor.id), + ('server_name', self.server.name), ] - # create a image_info as the side_effect of the fake image_list() - image_info = { - 'properties': {'owner_specified.openstack.object': 'image/cirros'} - } - - target_image = image_fakes.create_one_image(image_info) - another_image = image_fakes.create_one_image({}) - self.image_client.images.return_value = [target_image, another_image] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = dict( - files={}, - reservation_id=None, + self.image_client.images.assert_called_once_with() + self.compute_sdk_client.create_server.assert_called_once_with( + name=self.server.name, + image_id=target_image.id, + flavor_id=self.flavor.id, min_count=1, max_count=1, - security_groups=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[], - nics=[], - meta=None, - scheduler_hints={}, - config_drive=None, - ) - - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, target_image, self.flavor, **kwargs + networks=[], + block_device_mapping=[ + { + 'uuid': target_image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, + ], ) self.assertEqual(self.columns, columns) @@ -3700,7 +3620,7 @@ class TestServerCreate(TestServer): block_device, '--flavor', self.flavor.id, - self.new_server.name, + self.server.name, ] verifylist = [ ('image', None), @@ -3715,7 +3635,7 @@ class TestServerCreate(TestServer): }, ], ), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) exc = self.assertRaises( @@ -3728,41 +3648,42 @@ class TestServerCreate(TestServer): '(--volume, --snapshot, --block-device) is required', str(exc), ) + self.compute_sdk_client.create_server.assert_not_called() def test_server_create_with_swap(self): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', self.flavor.id, '--swap', '1024', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), + ('image', self.image.id), ('flavor', self.flavor.id), ('swap', 1024), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # CreateServer.take_action() returns two tuples + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = { - 'meta': None, - 'files': {}, - 'reservation_id': None, - 'min_count': 1, - 'max_count': 1, - 'security_groups': [], - 'userdata': None, - 'key_name': None, - 'availability_zone': None, - 'admin_pass': None, - 'block_device_mapping_v2': [ + self.compute_sdk_client.create_server.assert_called_once_with( + name=self.server.name, + image_id=self.image.id, + flavor_id=self.flavor.id, + min_count=1, + max_count=1, + block_device_mapping=[ + { + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, { 'boot_index': -1, 'source_type': 'blank', @@ -3770,15 +3691,9 @@ class TestServerCreate(TestServer): 'guest_format': 'swap', 'volume_size': 1024, 'delete_on_termination': True, - } + }, ], - 'nics': [], - 'scheduler_hints': {}, - 'config_drive': None, - } - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + networks=[], ) self.assertEqual(self.columns, columns) @@ -3787,37 +3702,37 @@ class TestServerCreate(TestServer): def test_server_create_with_ephemeral(self): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', self.flavor.id, '--ephemeral', 'size=1024,format=ext4', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), + ('image', self.image.id), ('flavor', self.flavor.id), ('ephemerals', [{'size': '1024', 'format': 'ext4'}]), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # CreateServer.take_action() returns two tuples + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = { - 'meta': None, - 'files': {}, - 'reservation_id': None, - 'min_count': 1, - 'max_count': 1, - 'security_groups': [], - 'userdata': None, - 'key_name': None, - 'availability_zone': None, - 'admin_pass': None, - 'block_device_mapping_v2': [ + self.compute_sdk_client.create_server.assert_called_once_with( + name=self.server.name, + image_id=self.image.id, + flavor_id=self.flavor.id, + min_count=1, + max_count=1, + block_device_mapping=[ + { + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, { 'boot_index': -1, 'source_type': 'blank', @@ -3825,15 +3740,9 @@ class TestServerCreate(TestServer): 'guest_format': 'ext4', 'volume_size': '1024', 'delete_on_termination': True, - } + }, ], - 'nics': [], - 'scheduler_hints': {}, - 'config_drive': None, - } - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + networks=[], ) self.assertEqual(self.columns, columns) @@ -3842,75 +3751,83 @@ class TestServerCreate(TestServer): def test_server_create_with_ephemeral_missing_key(self): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', self.flavor.id, '--ephemeral', 'format=ext3', - self.new_server.name, + self.server.name, ] - self.assertRaises( + exc = self.assertRaises( test_utils.ParserException, self.check_parser, self.cmd, arglist, [], ) + self.assertIn('Argument parse failed', str(exc)) + self.compute_sdk_client.create_server.assert_not_called() def test_server_create_with_ephemeral_invalid_key(self): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', self.flavor.id, '--ephemeral', 'size=1024,foo=bar', - self.new_server.name, + self.server.name, ] - self.assertRaises( + exc = self.assertRaises( test_utils.ParserException, self.check_parser, self.cmd, arglist, [], ) + self.assertIn('Argument parse failed', str(exc)) + self.compute_sdk_client.create_server.assert_not_called() def test_server_create_invalid_hint(self): # Not a key-value pair arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--hint', 'a0cf03a5-d921-4877-bb5c-86d26cf818e1', - self.new_server.name, + self.server.name, ] - self.assertRaises( + exc = self.assertRaises( test_utils.ParserException, self.check_parser, self.cmd, arglist, [], ) + self.assertIn('Argument parse failed', str(exc)) + self.compute_sdk_client.create_server.assert_not_called() # Empty key arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--hint', '=a0cf03a5-d921-4877-bb5c-86d26cf818e1', - self.new_server.name, + self.server.name, ] - self.assertRaises( + exc = self.assertRaises( test_utils.ParserException, self.check_parser, self.cmd, arglist, [], ) + self.assertIn('Argument parse failed', str(exc)) + self.compute_sdk_client.create_server.assert_not_called() def test_server_create_with_description(self): # Description is supported for nova api version 2.19 or above @@ -3918,51 +3835,45 @@ class TestServerCreate(TestServer): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--description', 'description1', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('description', 'description1'), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = dict( - meta=None, - files={}, - reservation_id=None, + self.compute_sdk_client.create_server.assert_called_once_with( + name=self.server.name, + image_id=self.image.id, + flavor_id=self.flavor.id, min_count=1, max_count=1, - security_groups=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[], - nics=[], - scheduler_hints={}, - config_drive=None, + networks=[], description='description1', - ) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + block_device_mapping=[ + { + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, + ], ) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist(), data) - self.assertFalse(self.image_client.images.called) - self.assertFalse(self.flavors_mock.called) def test_server_create_with_description_pre_v219(self): # Description is not supported for nova api version below 2.19 @@ -3970,160 +3881,149 @@ class TestServerCreate(TestServer): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--description', 'description1', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('description', 'description1'), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) + self.compute_sdk_client.create_server.assert_not_called() def test_server_create_with_tag(self): self.set_compute_api_version('2.52') arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--tag', 'tag1', '--tag', 'tag2', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('tags', ['tag1', 'tag2']), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = { - 'meta': None, - 'files': {}, - 'reservation_id': None, - 'min_count': 1, - 'max_count': 1, - 'security_groups': [], - 'userdata': None, - 'key_name': None, - 'availability_zone': None, - 'block_device_mapping_v2': [], - 'admin_pass': None, - 'nics': 'auto', - 'scheduler_hints': {}, - 'config_drive': None, - 'tags': ['tag1', 'tag2'], - } - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + self.compute_sdk_client.create_server.assert_called_once_with( + name=self.server.name, + image_id=self.image.id, + flavor_id=self.flavor.id, + min_count=1, + max_count=1, + networks='auto', + tags=['tag1', 'tag2'], + block_device_mapping=[ + { + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, + ], ) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist(), data) - self.assertFalse(self.image_client.images.called) - self.assertFalse(self.flavors_mock.called) def test_server_create_with_tag_pre_v252(self): self.set_compute_api_version('2.51') arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--tag', 'tag1', '--tag', 'tag2', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('tags', ['tag1', 'tag2']), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - ex = self.assertRaises( + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + exc = self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) self.assertIn( - '--os-compute-api-version 2.52 or greater is required', str(ex) + '--os-compute-api-version 2.52 or greater is required', str(exc) ) + self.compute_sdk_client.create_server.assert_not_called() - def test_server_create_with_host_v274(self): + def test_server_create_with_host(self): # Explicit host is supported for nova api version 2.74 or above self.set_compute_api_version('2.74') arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--host', 'host1', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('host', 'host1'), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = dict( - meta=None, - files={}, - reservation_id=None, + self.compute_sdk_client.create_server.assert_called_once_with( + name=self.server.name, + image_id=self.image.id, + flavor_id=self.flavor.id, min_count=1, max_count=1, - security_groups=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[], - nics='auto', - scheduler_hints={}, - config_drive=None, + networks='auto', host='host1', + block_device_mapping=[ + { + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, + ], ) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs - ) - self.assertEqual(self.columns, columns) self.assertEqual(self.datalist(), data) - self.assertFalse(self.image_client.images.called) - self.assertFalse(self.flavors_mock.called) def test_server_create_with_host_pre_v274(self): # Host is not supported for nova api version below 2.74 @@ -4131,78 +4031,75 @@ class TestServerCreate(TestServer): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--host', 'host1', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('host', 'host1'), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.assertRaises( + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + exc = self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) + self.assertIn( + '--os-compute-api-version 2.74 or greater is required', str(exc) + ) + self.compute_sdk_client.create_server.assert_not_called() - def test_server_create_with_hypervisor_hostname_v274(self): + def test_server_create_with_hypervisor_hostname(self): # Explicit hypervisor_hostname is supported for nova api version # 2.74 or above self.set_compute_api_version('2.74') arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--hypervisor-hostname', 'node1', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('hypervisor_hostname', 'node1'), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = dict( - meta=None, - files={}, - reservation_id=None, + self.compute_sdk_client.create_server.assert_called_once_with( + name=self.server.name, + image_id=self.image.id, + flavor_id=self.flavor.id, min_count=1, max_count=1, - security_groups=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[], - nics='auto', - scheduler_hints={}, - config_drive=None, + networks='auto', hypervisor_hostname='node1', + block_device_mapping=[ + { + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, + ], ) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs - ) - self.assertEqual(self.columns, columns) self.assertEqual(self.datalist(), data) - self.assertFalse(self.image_client.images.called) - self.assertFalse(self.flavors_mock.called) def test_server_create_with_hypervisor_hostname_pre_v274(self): # Hypervisor_hostname is not supported for nova api version below 2.74 @@ -4210,315 +4107,281 @@ class TestServerCreate(TestServer): arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--hypervisor-hostname', 'node1', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('hypervisor_hostname', 'node1'), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.assertRaises( + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + exc = self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) - - def test_server_create_with_host_and_hypervisor_hostname_v274(self): - # Explicit host and hypervisor_hostname is supported for nova api - # version 2.74 or above - self.set_compute_api_version('2.74') - - arglist = [ - '--image', - 'image1', - '--flavor', - 'flavor1', - '--host', - 'host1', - '--hypervisor-hostname', - 'node1', - self.new_server.name, - ] - verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), - ('host', 'host1'), - ('hypervisor_hostname', 'node1'), - ('config_drive', False), - ('server_name', self.new_server.name), - ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - - columns, data = self.cmd.take_action(parsed_args) - - # Set expected values - kwargs = dict( - meta=None, - files={}, - reservation_id=None, - min_count=1, - max_count=1, - security_groups=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[], - nics='auto', - scheduler_hints={}, - config_drive=None, - host='host1', - hypervisor_hostname='node1', - ) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + self.assertIn( + '--os-compute-api-version 2.74 or greater is required', str(exc) ) + self.compute_sdk_client.create_server.assert_not_called() - self.assertEqual(self.columns, columns) - self.assertEqual(self.datalist(), data) - self.assertFalse(self.image_client.images.called) - self.assertFalse(self.flavors_mock.called) - - def test_server_create_with_hostname_v290(self): + def test_server_create_with_hostname(self): self.set_compute_api_version('2.90') arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--hostname', 'hostname', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('hostname', 'hostname'), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, - self.image, - self.flavor, - meta=None, - files={}, - reservation_id=None, + self.compute_sdk_client.create_server.assert_called_once_with( + name=self.server.name, + image_id=self.image.id, + flavor_id=self.flavor.id, min_count=1, max_count=1, - security_groups=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[], - nics='auto', - scheduler_hints={}, - config_drive=None, + networks='auto', hostname='hostname', + block_device_mapping=[ + { + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, + ], ) - self.assertEqual(self.columns, columns) self.assertEqual(self.datalist(), data) - self.assertFalse(self.image_client.images.called) - self.assertFalse(self.flavors_mock.called) def test_server_create_with_hostname_pre_v290(self): self.set_compute_api_version('2.89') arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--hostname', 'hostname', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('hostname', 'hostname'), ('config_drive', False), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.assertRaises( + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + exc = self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) + self.assertIn( + '--os-compute-api-version 2.90 or greater is required', str(exc) + ) + self.compute_sdk_client.create_server.assert_not_called() def test_server_create_with_trusted_image_cert(self): self.set_compute_api_version('2.63') arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--trusted-image-cert', 'foo', '--trusted-image-cert', 'bar', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('config_drive', False), ('trusted_image_certs', ['foo', 'bar']), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - # Set expected values - kwargs = dict( - meta=None, - files={}, - reservation_id=None, + self.compute_sdk_client.create_server.assert_called_once_with( + name=self.server.name, + image_id=self.image.id, + flavor_id=self.flavor.id, min_count=1, max_count=1, - security_groups=[], - userdata=None, - key_name=None, - availability_zone=None, - admin_pass=None, - block_device_mapping_v2=[], - nics='auto', - scheduler_hints={}, - config_drive=None, + networks='auto', trusted_image_certificates=['foo', 'bar'], - ) - # ServerManager.create(name, image, flavor, **kwargs) - self.servers_mock.create.assert_called_with( - self.new_server.name, self.image, self.flavor, **kwargs + block_device_mapping=[ + { + 'uuid': self.image.id, + 'boot_index': 0, + 'source_type': 'image', + 'destination_type': 'local', + 'delete_on_termination': True, + }, + ], ) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist(), data) - self.assertFalse(self.image_client.images.called) - self.assertFalse(self.flavors_mock.called) - def test_server_create_with_trusted_image_cert_prev263(self): + def test_server_create_with_trusted_image_cert_pre_v263(self): self.set_compute_api_version('2.62') arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--trusted-image-cert', 'foo', '--trusted-image-cert', 'bar', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('config_drive', False), ('trusted_image_certs', ['foo', 'bar']), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.assertRaises( + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + exc = self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) + self.assertIn( + '--os-compute-api-version 2.63 or greater is required', str(exc) + ) + self.compute_sdk_client.create_server.assert_not_called() def test_server_create_with_trusted_image_cert_from_volume(self): self.set_compute_api_version('2.63') + arglist = [ '--volume', 'volume1', '--flavor', - 'flavor1', + self.flavor.id, '--trusted-image-cert', 'foo', '--trusted-image-cert', 'bar', - self.new_server.name, + self.server.name, ] verifylist = [ ('volume', 'volume1'), - ('flavor', 'flavor1'), + ('flavor', self.flavor.id), ('config_drive', False), ('trusted_image_certs', ['foo', 'bar']), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.assertRaises( + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + exc = self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) + self.assertIn( + '--trusted-image-cert option is only supported for servers booted ' + 'directly from images', + str(exc), + ) + self.compute_sdk_client.create_server.assert_not_called() def test_server_create_with_trusted_image_cert_from_snapshot(self): self.set_compute_api_version('2.63') + arglist = [ '--snapshot', 'snapshot1', '--flavor', - 'flavor1', + self.flavor.id, '--trusted-image-cert', 'foo', '--trusted-image-cert', 'bar', - self.new_server.name, + self.server.name, ] verifylist = [ ('snapshot', 'snapshot1'), - ('flavor', 'flavor1'), + ('flavor', self.flavor.id), ('config_drive', False), ('trusted_image_certs', ['foo', 'bar']), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.assertRaises( + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + exc = self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) + self.assertIn( + '--trusted-image-cert option is only supported for servers booted ' + 'directly from images', + str(exc), + ) + self.compute_sdk_client.create_server.assert_not_called() def test_server_create_with_trusted_image_cert_boot_from_volume(self): self.set_compute_api_version('2.63') + arglist = [ '--image', - 'image1', + self.image.id, '--flavor', - 'flavor1', + self.flavor.id, '--boot-from-volume', '1', '--trusted-image-cert', 'foo', '--trusted-image-cert', 'bar', - self.new_server.name, + self.server.name, ] verifylist = [ - ('image', 'image1'), - ('flavor', 'flavor1'), + ('image', self.image.id), + ('flavor', self.flavor.id), ('boot_from_volume', 1), ('config_drive', False), ('trusted_image_certs', ['foo', 'bar']), - ('server_name', self.new_server.name), + ('server_name', self.server.name), ] - parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.assertRaises( + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + exc = self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) + self.assertIn( + '--trusted-image-cert option is only supported for servers booted ' + 'directly from images', + str(exc), + ) + self.compute_sdk_client.create_server.assert_not_called() class TestServerDelete(compute_fakes.TestComputev2): @@ -8983,7 +8846,7 @@ class TestServerStop(TestServer): self.run_method_with_sdk_servers('stop_server', 3) def test_server_start_with_all_projects(self): - servers = self.setup_servers_mock(count=1) + servers = self.setup_sdk_servers_mock(count=1) arglist = [ servers[0].id, @@ -9586,7 +9449,7 @@ class TestServerGeneral(TestServer): 'OS-SRV-USG:terminated_at': None, 'accessIPv4': None, 'accessIPv6': None, - 'addresses': format_columns.DictListColumn(_server.addresses), + 'addresses': server.AddressesColumn(_server.addresses), 'config_drive': None, 'created': None, 'description': None, @@ -9673,7 +9536,7 @@ class TestServerGeneral(TestServer): 'OS-SRV-USG:terminated_at': None, 'accessIPv4': None, 'accessIPv6': None, - 'addresses': format_columns.DictListColumn(_server.addresses), + 'addresses': server.AddressesColumn(_server.addresses), 'config_drive': None, 'created': None, 'description': None, diff --git a/openstackclient/tests/unit/network/v2/fakes.py b/openstackclient/tests/unit/network/v2/fakes.py index 4874bf856e..28af24287b 100644 --- a/openstackclient/tests/unit/network/v2/fakes.py +++ b/openstackclient/tests/unit/network/v2/fakes.py @@ -32,8 +32,9 @@ from openstack.network.v2 import network_ip_availability as _ip_availability from openstack.network.v2 import network_segment_range as _segment_range from openstack.network.v2 import port as _port from openstack.network.v2 import rbac_policy as network_rbac +from openstack.network.v2 import security_group as _security_group from openstack.network.v2 import segment as _segment -from openstack.network.v2 import service_profile as _flavor_profile +from openstack.network.v2 import service_profile as _service_profile from openstack.network.v2 import trunk as _trunk from openstackclient.tests.unit import fakes @@ -1943,11 +1944,44 @@ def get_network_rbacs(rbac_policies=None, count=2): return mock.Mock(side_effect=rbac_policies) -def create_one_service_profile(attrs=None): - """Create flavor profile.""" +def create_one_security_group(attrs=None): + """Create a security group.""" attrs = attrs or {} - flavor_profile_attrs = { + security_group_attrs = { + 'name': 'security-group-name-' + uuid.uuid4().hex, + 'id': 'security-group-id-' + uuid.uuid4().hex, + 'project_id': 'project-id-' + uuid.uuid4().hex, + 'description': 'security-group-description-' + uuid.uuid4().hex, + 'location': 'MUNCHMUNCHMUNCH', + } + + security_group_attrs.update(attrs) + + security_group = _security_group.SecurityGroup(**security_group_attrs) + + return security_group + + +def create_security_groups(attrs=None, count=2): + """Create multiple fake security groups. + + :param dict attrs: A dictionary with all attributes + :param int count: The number of security groups to fake + :return: A list of fake SecurityGroup objects + """ + security_groups = [] + for i in range(0, count): + security_groups.append(create_one_security_group(attrs)) + + return security_groups + + +def create_one_service_profile(attrs=None): + """Create service profile.""" + attrs = attrs or {} + + service_profile_attrs = { 'id': 'flavor-profile-id' + uuid.uuid4().hex, 'description': 'flavor-profile-description-' + uuid.uuid4().hex, 'project_id': 'project-id-' + uuid.uuid4().hex, @@ -1957,20 +1991,20 @@ def create_one_service_profile(attrs=None): 'location': 'MUNCHMUNCHMUNCH', } - flavor_profile_attrs.update(attrs) + service_profile_attrs.update(attrs) - flavor_profile = _flavor_profile.ServiceProfile(**flavor_profile_attrs) + flavor_profile = _service_profile.ServiceProfile(**service_profile_attrs) return flavor_profile def create_service_profile(attrs=None, count=2): - """Create multiple flavor profiles.""" + """Create multiple service profiles.""" - flavor_profiles = [] + service_profiles = [] for i in range(0, count): - flavor_profiles.append(create_one_service_profile(attrs)) - return flavor_profiles + service_profiles.append(create_one_service_profile(attrs)) + return service_profiles def get_service_profile(flavor_profile=None, count=2):