summaryrefslogtreecommitdiff
path: root/openstackclient
diff options
context:
space:
mode:
Diffstat (limited to 'openstackclient')
-rw-r--r--openstackclient/compute/v2/console.py54
-rw-r--r--openstackclient/compute/v2/server.py633
-rw-r--r--openstackclient/compute/v2/server_group.py25
-rw-r--r--openstackclient/image/v2/image.py2
-rw-r--r--openstackclient/tests/unit/compute/v2/test_console.py163
-rw-r--r--openstackclient/tests/unit/compute/v2/test_server.py511
-rw-r--r--openstackclient/tests/unit/compute/v2/test_server_group.py43
-rw-r--r--openstackclient/tests/unit/image/v2/test_image.py6
8 files changed, 1060 insertions, 377 deletions
diff --git a/openstackclient/compute/v2/console.py b/openstackclient/compute/v2/console.py
index 110b21b8..0ab5c8a2 100644
--- a/openstackclient/compute/v2/console.py
+++ b/openstackclient/compute/v2/console.py
@@ -22,6 +22,15 @@ from osc_lib import utils
from openstackclient.i18n import _
+def _get_console_columns(item):
+ # To maintain backwards compatibility we need to rename sdk props to
+ # whatever OSC was using before
+ column_map = {}
+ hidden_columns = ['id', 'links', 'location', 'name']
+ return utils.get_osc_show_columns_for_sdk_resource(
+ item, column_map, hidden_columns)
+
+
class ShowConsoleLog(command.Command):
_description = _("Show server's console output")
@@ -44,19 +53,18 @@ class ShowConsoleLog(command.Command):
return parser
def take_action(self, parsed_args):
- compute_client = self.app.client_manager.compute
+ compute_client = self.app.client_manager.sdk_connection.compute
- server = utils.find_resource(
- compute_client.servers,
- parsed_args.server,
+ server = compute_client.find_server(
+ name_or_id=parsed_args.server,
+ ignore_missing=False
)
- length = parsed_args.lines
- if length:
- # NOTE(dtroyer): get_console_output() appears to shortchange the
- # output by one line
- length += 1
- data = server.get_console_output(length=length)
+ output = compute_client.get_server_console_output(
+ server.id, length=parsed_args.lines)
+ data = None
+ if output:
+ data = output.get('output', None)
if data and data[-1] != '\n':
data += '\n'
@@ -120,21 +128,15 @@ class ShowConsoleURL(command.ShowOne):
return parser
def take_action(self, parsed_args):
- compute_client = self.app.client_manager.compute
- server = utils.find_resource(
- compute_client.servers,
+ compute_client = self.app.client_manager.sdk_connection.compute
+ server = compute_client.find_server(
parsed_args.server,
- )
+ ignore_missing=False)
+
+ data = compute_client.create_console(server.id,
+ console_type=parsed_args.url_type)
+
+ display_columns, columns = _get_console_columns(data)
+ data = utils.get_dict_properties(data, columns)
- data = server.get_console_url(parsed_args.url_type)
- if not data:
- return ({}, {})
-
- info = {}
- # NOTE(Rui Chen): Return 'remote_console' in compute microversion API
- # 2.6 and later, return 'console' in compute
- # microversion API from 2.0 to 2.5, do compatibility
- # handle for different microversion API.
- console_data = data.get('remote_console', data.get('console'))
- info.update(console_data)
- return zip(*sorted(info.items()))
+ return (display_columns, data)
diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py
index 89ea0067..294b4683 100644
--- a/openstackclient/compute/v2/server.py
+++ b/openstackclient/compute/v2/server.py
@@ -236,6 +236,14 @@ class AddFixedIP(command.Command):
metavar="<ip-address>",
help=_("Requested fixed IP address"),
)
+ parser.add_argument(
+ '--tag',
+ metavar='<tag>',
+ help=_(
+ 'Tag for the attached interface. '
+ '(supported by --os-compute-api-version 2.52 or above)'
+ )
+ )
return parser
def take_action(self, parsed_args):
@@ -246,11 +254,23 @@ class AddFixedIP(command.Command):
network = compute_client.api.network_find(parsed_args.network)
- server.interface_attach(
- port_id=None,
- net_id=network['id'],
- fixed_ip=parsed_args.fixed_ip_address,
- )
+ kwargs = {
+ 'port_id': None,
+ 'net_id': network['id'],
+ 'fixed_ip': parsed_args.fixed_ip_address,
+ }
+
+ if parsed_args.tag:
+ if compute_client.api_version < api_versions.APIVersion('2.49'):
+ msg = _(
+ '--os-compute-api-version 2.49 or greater is required to '
+ 'support the --tag option'
+ )
+ raise exceptions.CommandError(msg)
+
+ kwargs['tag'] = parsed_args.tag
+
+ server.interface_attach(**kwargs)
class AddFloatingIP(network_common.NetworkAndComputeCommand):
@@ -620,13 +640,15 @@ class CreateServer(command.ShowOne):
disk_group.add_argument(
'--volume',
metavar='<volume>',
- help=_('Create server using this volume as the boot disk (name '
- 'or ID).\n'
- 'This option automatically creates a block device mapping '
- 'with a boot index of 0. On many hypervisors (libvirt/kvm '
- 'for example) this will be device vda. Do not create a '
- 'duplicate mapping using --block-device-mapping for this '
- 'volume.'),
+ help=_(
+ 'Create server using this volume as the boot disk (name or ID)'
+ '\n'
+ 'This option automatically creates a block device mapping '
+ 'with a boot index of 0. On many hypervisors (libvirt/kvm '
+ 'for example) this will be device vda. Do not create a '
+ 'duplicate mapping using --block-device-mapping for this '
+ 'volume.'
+ ),
)
parser.add_argument(
'--password',
@@ -644,28 +666,34 @@ class CreateServer(command.ShowOne):
metavar='<security-group>',
action='append',
default=[],
- help=_('Security group to assign to this server (name or ID) '
- '(repeat option to set multiple groups)'),
+ help=_(
+ 'Security group to assign to this server (name or ID) '
+ '(repeat option to set multiple groups)'
+ ),
)
parser.add_argument(
'--key-name',
metavar='<key-name>',
- help=_('Keypair to inject into this server (optional extension)'),
+ help=_('Keypair to inject into this server'),
)
parser.add_argument(
'--property',
metavar='<key=value>',
action=parseractions.KeyValueAction,
- help=_('Set a property on this server '
- '(repeat option to set multiple values)'),
+ help=_(
+ 'Set a property on this server '
+ '(repeat option to set multiple values)'
+ ),
)
parser.add_argument(
'--file',
metavar='<dest-filename=source-filename>',
action='append',
default=[],
- help=_('File to inject into image before boot '
- '(repeat option to set multiple files)'),
+ help=_(
+ 'File to inject into image before boot '
+ '(repeat option to set multiple files)'
+ ),
)
parser.add_argument(
'--user-data',
@@ -675,8 +703,10 @@ class CreateServer(command.ShowOne):
parser.add_argument(
'--description',
metavar='<description>',
- help=_('Set description for the server (supported by '
- '--os-compute-api-version 2.19 or above)'),
+ help=_(
+ 'Set description for the server '
+ '(supported by --os-compute-api-version 2.19 or above)'
+ ),
)
parser.add_argument(
'--availability-zone',
@@ -686,29 +716,35 @@ class CreateServer(command.ShowOne):
parser.add_argument(
'--host',
metavar='<host>',
- help=_('Requested host to create servers. Admin only '
- 'by default. (supported by --os-compute-api-version 2.74 '
- 'or above)'),
+ help=_(
+ 'Requested host to create servers. '
+ '(admin only) '
+ '(supported by --os-compute-api-version 2.74 or above)'
+ ),
)
parser.add_argument(
'--hypervisor-hostname',
metavar='<hypervisor-hostname>',
- help=_('Requested hypervisor hostname to create servers. Admin '
- 'only by default. (supported by --os-compute-api-version '
- '2.74 or above)'),
+ help=_(
+ 'Requested hypervisor hostname to create servers. '
+ '(admin only) '
+ '(supported by --os-compute-api-version 2.74 or above)'
+ ),
)
parser.add_argument(
'--boot-from-volume',
metavar='<volume-size>',
type=int,
- help=_('When used in conjunction with the ``--image`` or '
- '``--image-property`` option, this option automatically '
- 'creates a block device mapping with a boot index of 0 '
- 'and tells the compute service to create a volume of the '
- 'given size (in GB) from the specified image and use it '
- 'as the root disk of the server. The root volume will not '
- 'be deleted when the server is deleted. This option is '
- 'mutually exclusive with the ``--volume`` option.')
+ help=_(
+ 'When used in conjunction with the ``--image`` or '
+ '``--image-property`` option, this option automatically '
+ 'creates a block device mapping with a boot index of 0 '
+ 'and tells the compute service to create a volume of the '
+ 'given size (in GB) from the specified image and use it '
+ 'as the root disk of the server. The root volume will not '
+ 'be deleted when the server is deleted. This option is '
+ 'mutually exclusive with the ``--volume`` option.'
+ )
)
parser.add_argument(
'--block-device-mapping',
@@ -718,37 +754,40 @@ class CreateServer(command.ShowOne):
# NOTE(RuiChen): Add '\n' at the end of line to put each item in
# the separated line, avoid the help message looks
# messy, see _SmartHelpFormatter in cliff.
- help=_('Create a block device on the server.\n'
- 'Block device mapping in the format\n'
- '<dev-name>=<id>:<type>:<size(GB)>:<delete-on-terminate>\n'
- '<dev-name>: block device name, like: vdb, xvdc '
- '(required)\n'
- '<id>: Name or ID of the volume, volume snapshot or image '
- '(required)\n'
- '<type>: volume, snapshot or image; default: volume '
- '(optional)\n'
- '<size(GB)>: volume size if create from image or snapshot '
- '(optional)\n'
- '<delete-on-terminate>: true or false; default: false '
- '(optional)\n'
- '(optional extension)'),
+ help=_(
+ 'Create a block device on the server.\n'
+ 'Block device mapping in the format\n'
+ '<dev-name>=<id>:<type>:<size(GB)>:<delete-on-terminate>\n'
+ '<dev-name>: block device name, like: vdb, xvdc '
+ '(required)\n'
+ '<id>: Name or ID of the volume, volume snapshot or image '
+ '(required)\n'
+ '<type>: volume, snapshot or image; default: volume '
+ '(optional)\n'
+ '<size(GB)>: volume size if create from image or snapshot '
+ '(optional)\n'
+ '<delete-on-terminate>: true or false; default: false '
+ '(optional)\n'
+ ),
)
parser.add_argument(
'--nic',
metavar="<net-id=net-uuid,v4-fixed-ip=ip-addr,v6-fixed-ip=ip-addr,"
"port-id=port-uuid,auto,none>",
action='append',
- help=_("Create a NIC on the server. "
- "Specify option multiple times to create multiple NICs. "
- "Either net-id or port-id must be provided, but not both. "
- "net-id: attach NIC to network with this UUID, "
- "port-id: attach NIC to port with this UUID, "
- "v4-fixed-ip: IPv4 fixed address for NIC (optional), "
- "v6-fixed-ip: IPv6 fixed address for NIC (optional), "
- "none: (v2.37+) no network is attached, "
- "auto: (v2.37+) the compute service will automatically "
- "allocate a network. Specifying a --nic of auto or none "
- "cannot be used with any other --nic value."),
+ help=_(
+ "Create a NIC on the server. "
+ "Specify option multiple times to create multiple NICs. "
+ "Either net-id or port-id must be provided, but not both. "
+ "net-id: attach NIC to network with this UUID, "
+ "port-id: attach NIC to port with this UUID, "
+ "v4-fixed-ip: IPv4 fixed address for NIC (optional), "
+ "v6-fixed-ip: IPv6 fixed address for NIC (optional), "
+ "none: (v2.37+) no network is attached, "
+ "auto: (v2.37+) the compute service will automatically "
+ "allocate a network. Specifying a --nic of auto or none "
+ "cannot be used with any other --nic value."
+ ),
)
parser.add_argument(
'--network',
@@ -756,13 +795,15 @@ class CreateServer(command.ShowOne):
action='append',
dest='nic',
type=_prefix_checked_value('net-id='),
- help=_("Create a NIC on the server and connect it to network. "
- "Specify option multiple times to create multiple NICs. "
- "This is a wrapper for the '--nic net-id=<network>' "
- "parameter that provides simple syntax for the standard "
- "use case of connecting a new server to a given network. "
- "For more advanced use cases, refer to the '--nic' "
- "parameter."),
+ help=_(
+ "Create a NIC on the server and connect it to network. "
+ "Specify option multiple times to create multiple NICs. "
+ "This is a wrapper for the '--nic net-id=<network>' "
+ "parameter that provides simple syntax for the standard "
+ "use case of connecting a new server to a given network. "
+ "For more advanced use cases, refer to the '--nic' "
+ "parameter."
+ ),
)
parser.add_argument(
'--port',
@@ -770,12 +811,14 @@ class CreateServer(command.ShowOne):
action='append',
dest='nic',
type=_prefix_checked_value('port-id='),
- help=_("Create a NIC on the server and connect it to port. "
- "Specify option multiple times to create multiple NICs. "
- "This is a wrapper for the '--nic port-id=<port>' "
- "parameter that provides simple syntax for the standard "
- "use case of connecting a new server to a given port. For "
- "more advanced use cases, refer to the '--nic' parameter."),
+ help=_(
+ "Create a NIC on the server and connect it to port. "
+ "Specify option multiple times to create multiple NICs. "
+ "This is a wrapper for the '--nic port-id=<port>' "
+ "parameter that provides simple syntax for the standard "
+ "use case of connecting a new server to a given port. For "
+ "more advanced use cases, refer to the '--nic' parameter."
+ ),
)
parser.add_argument(
'--hint',
@@ -828,6 +871,18 @@ class CreateServer(command.ShowOne):
action='store_true',
help=_('Wait for build to complete'),
)
+ parser.add_argument(
+ '--tag',
+ metavar='<tag>',
+ action='append',
+ default=[],
+ dest='tags',
+ help=_(
+ 'Tags for the server. '
+ 'Specify multiple times to add multiple tags. '
+ '(supported by --os-compute-api-version 2.52 or above)'
+ ),
+ )
return parser
def take_action(self, parsed_args):
@@ -850,10 +905,13 @@ class CreateServer(command.ShowOne):
if not image and parsed_args.image_property:
def emit_duplicated_warning(img, image_property):
img_uuid_list = [str(image.id) for image in img]
- LOG.warning(_('Multiple matching images: %(img_uuid_list)s\n'
- 'Using image: %(chosen_one)s') %
- {'img_uuid_list': img_uuid_list,
- 'chosen_one': img_uuid_list[0]})
+ LOG.warning(
+ 'Multiple matching images: %(img_uuid_list)s\n'
+ 'Using image: %(chosen_one)s',
+ {
+ 'img_uuid_list': img_uuid_list,
+ 'chosen_one': img_uuid_list[0],
+ })
def _match_image(image_api, wanted_properties):
image_list = image_api.images()
@@ -870,45 +928,52 @@ class CreateServer(command.ShowOne):
set([key, value])
except TypeError:
if key != 'properties':
- LOG.debug('Skipped the \'%s\' attribute. '
- 'That cannot be compared. '
- '(image: %s, value: %s)',
- key, img.id, value)
+ LOG.debug(
+ 'Skipped the \'%s\' attribute. '
+ 'That cannot be compared. '
+ '(image: %s, value: %s)',
+ key, img.id, value,
+ )
pass
else:
img_dict[key] = value
- if all(k in img_dict and img_dict[k] == v
- for k, v in wanted_properties.items()):
+ if all(
+ k in img_dict and img_dict[k] == v
+ for k, v in wanted_properties.items()
+ ):
images_matched.append(img)
+
return images_matched
images = _match_image(image_client, parsed_args.image_property)
if len(images) > 1:
- emit_duplicated_warning(images,
- parsed_args.image_property)
+ emit_duplicated_warning(images, parsed_args.image_property)
if images:
image = images[0]
else:
- raise exceptions.CommandError(_("No images match the "
- "property expected by "
- "--image-property"))
+ msg = _(
+ 'No images match the property expected by '
+ '--image-property'
+ )
+ raise exceptions.CommandError(msg)
# Lookup parsed_args.volume
volume = None
if parsed_args.volume:
# --volume and --boot-from-volume are mutually exclusive.
if parsed_args.boot_from_volume:
- raise exceptions.CommandError(
- _('--volume is not allowed with --boot-from-volume'))
+ msg = _('--volume is not allowed with --boot-from-volume')
+ raise exceptions.CommandError(msg)
+
volume = utils.find_resource(
volume_client.volumes,
parsed_args.volume,
).id
# Lookup parsed_args.flavor
- flavor = utils.find_resource(compute_client.flavors,
- parsed_args.flavor)
+ flavor = utils.find_resource(
+ compute_client.flavors, parsed_args.flavor)
files = {}
for f in parsed_args.file:
@@ -918,16 +983,17 @@ class CreateServer(command.ShowOne):
except IOError as e:
msg = _("Can't open '%(source)s': %(exception)s")
raise exceptions.CommandError(
- msg % {"source": src,
- "exception": e}
+ msg % {'source': src, 'exception': e}
)
if parsed_args.min > parsed_args.max:
msg = _("min instances should be <= max instances")
raise exceptions.CommandError(msg)
+
if parsed_args.min < 1:
msg = _("min instances should be > 0")
raise exceptions.CommandError(msg)
+
if parsed_args.max < 1:
msg = _("max instances should be > 0")
raise exceptions.CommandError(msg)
@@ -939,8 +1005,7 @@ class CreateServer(command.ShowOne):
except IOError as e:
msg = _("Can't open '%(data)s': %(exception)s")
raise exceptions.CommandError(
- msg % {"data": parsed_args.user_data,
- "exception": e}
+ msg % {'data': parsed_args.user_data, 'exception': e}
)
if parsed_args.description:
@@ -951,11 +1016,12 @@ class CreateServer(command.ShowOne):
block_device_mapping_v2 = []
if volume:
- block_device_mapping_v2 = [{'uuid': volume,
- 'boot_index': '0',
- 'source_type': 'volume',
- 'destination_type': 'volume'
- }]
+ block_device_mapping_v2 = [{
+ 'uuid': volume,
+ 'boot_index': '0',
+ 'source_type': 'volume',
+ 'destination_type': 'volume'
+ }]
elif parsed_args.boot_from_volume:
# Tell nova to create a root volume from the image provided.
block_device_mapping_v2 = [{
@@ -976,13 +1042,16 @@ class CreateServer(command.ShowOne):
dev_map = dev_map.split(':')
if dev_map[0]:
mapping = {'device_name': dev_name}
+
# 1. decide source and destination type
if (len(dev_map) > 1 and
dev_map[1] in ('volume', 'snapshot', 'image')):
mapping['source_type'] = dev_map[1]
else:
mapping['source_type'] = 'volume'
+
mapping['destination_type'] = 'volume'
+
# 2. check target exist, update target uuid according by
# source type
if mapping['source_type'] == 'volume':
@@ -1008,14 +1077,18 @@ class CreateServer(command.ShowOne):
image_id = image_client.find_image(dev_map[0],
ignore_missing=False).id
mapping['uuid'] = image_id
+
# 3. append size and delete_on_termination if exist
if len(dev_map) > 2 and dev_map[2]:
mapping['volume_size'] = dev_map[2]
+
if len(dev_map) > 3 and dev_map[3]:
mapping['delete_on_termination'] = dev_map[3]
else:
- msg = _("Volume, volume snapshot or image (name or ID) must "
- "be specified if --block-device-mapping is specified")
+ msg = _(
+ 'Volume, volume snapshot or image (name or ID) must '
+ 'be specified if --block-device-mapping is specified'
+ )
raise exceptions.CommandError(msg)
block_device_mapping_v2.append(mapping)
@@ -1029,22 +1102,32 @@ class CreateServer(command.ShowOne):
auto_or_none = True
nics.append(nic_str)
else:
- nic_info = {"net-id": "", "v4-fixed-ip": "",
- "v6-fixed-ip": "", "port-id": ""}
+ nic_info = {
+ 'net-id': '',
+ 'v4-fixed-ip': '',
+ 'v6-fixed-ip': '',
+ 'port-id': '',
+ }
for kv_str in nic_str.split(","):
k, sep, v = kv_str.partition("=")
if k in nic_info and v:
nic_info[k] = v
else:
- msg = (_("Invalid nic argument '%s'. Nic arguments "
- "must be of the form --nic <net-id=net-uuid"
- ",v4-fixed-ip=ip-addr,v6-fixed-ip=ip-addr,"
- "port-id=port-uuid>."))
+ msg = _(
+ "Invalid nic argument '%s'. Nic arguments "
+ "must be of the form --nic <net-id=net-uuid"
+ ",v4-fixed-ip=ip-addr,v6-fixed-ip=ip-addr,"
+ "port-id=port-uuid>."
+ )
raise exceptions.CommandError(msg % k)
+
if bool(nic_info["net-id"]) == bool(nic_info["port-id"]):
- msg = _("either network or port should be specified "
- "but not both")
+ msg = _(
+ 'Either network or port should be specified '
+ 'but not both'
+ )
raise exceptions.CommandError(msg)
+
if self.app.client_manager.is_network_endpoint_enabled():
network_client = self.app.client_manager.network
if nic_info["net-id"]:
@@ -1061,17 +1144,22 @@ class CreateServer(command.ShowOne):
nic_info["net-id"]
)['id']
if nic_info["port-id"]:
- msg = _("can't create server with port specified "
- "since network endpoint not enabled")
+ msg = _(
+ "Can't create server with port specified "
+ "since network endpoint not enabled"
+ )
raise exceptions.CommandError(msg)
+
nics.append(nic_info)
if nics:
if auto_or_none:
if len(nics) > 1:
- msg = _('Specifying a --nic of auto or none cannot '
- 'be used with any other --nic, --network '
- 'or --port value.')
+ msg = _(
+ 'Specifying a --nic of auto or none cannot '
+ 'be used with any other --nic, --network '
+ 'or --port value.'
+ )
raise exceptions.CommandError(msg)
nics = nics[0]
else:
@@ -1141,18 +1229,34 @@ class CreateServer(command.ShowOne):
if parsed_args.description:
boot_kwargs['description'] = parsed_args.description
+ if parsed_args.tags:
+ if compute_client.api_version < api_versions.APIVersion('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
+
if parsed_args.host:
if compute_client.api_version < api_versions.APIVersion("2.74"):
- msg = _("Specifying --host is not supported for "
- "--os-compute-api-version less than 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
if parsed_args.hypervisor_hostname:
if compute_client.api_version < api_versions.APIVersion("2.74"):
- msg = _("Specifying --hypervisor-hostname is not supported "
- "for --os-compute-api-version less than 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)
@@ -1178,8 +1282,7 @@ class CreateServer(command.ShowOne):
):
self.app.stdout.write('\n')
else:
- LOG.error(_('Error creating server: %s'),
- parsed_args.server_name)
+ LOG.error('Error creating server: %s', parsed_args.server_name)
self.app.stdout.write(_('Error creating server\n'))
raise SystemExit
@@ -1408,6 +1511,30 @@ class ListServer(command.Lister):
help=_('Only display unlocked servers. '
'Requires ``--os-compute-api-version`` 2.73 or greater.'),
)
+ parser.add_argument(
+ '--tags',
+ metavar='<tag>',
+ action='append',
+ default=[],
+ dest='tags',
+ help=_(
+ 'Only list servers with the specified tag. '
+ 'Specify multiple times to filter on multiple tags. '
+ '(supported by --os-compute-api-version 2.26 or above)'
+ ),
+ )
+ parser.add_argument(
+ '--not-tags',
+ metavar='<tag>',
+ action='append',
+ default=[],
+ dest='not_tags',
+ help=_(
+ 'Only list servers without the specified tag. '
+ 'Specify multiple times to filter on multiple tags. '
+ '(supported by --os-compute-api-version 2.26 or above)'
+ ),
+ )
return parser
def take_action(self, parsed_args):
@@ -1463,6 +1590,27 @@ class ListServer(command.Lister):
'changes-before': parsed_args.changes_before,
'changes-since': parsed_args.changes_since,
}
+
+ if parsed_args.tags:
+ if compute_client.api_version < api_versions.APIVersion('2.26'):
+ msg = _(
+ '--os-compute-api-version 2.26 or greater is required to '
+ 'support the --tag option'
+ )
+ raise exceptions.CommandError(msg)
+
+ search_opts['tags'] = parsed_args.tags
+
+ if parsed_args.not_tags:
+ if compute_client.api_version < api_versions.APIVersion('2.26'):
+ msg = _(
+ '--os-compute-api-version 2.26 or greater is required to '
+ 'support the --not-tag option'
+ )
+ raise exceptions.CommandError(msg)
+
+ search_opts['not-tags'] = parsed_args.not_tags
+
support_locked = (compute_client.api_version >=
api_versions.APIVersion('2.73'))
if not support_locked and (parsed_args.locked or parsed_args.unlocked):
@@ -1894,99 +2042,99 @@ revert to release the new server and restart the old one.""")
class ListMigration(command.Command):
- _description = _("""List server migrations.""")
+ _description = _("""List server migrations""")
def get_parser(self, prog_name):
parser = super(ListMigration, self).get_parser(prog_name)
parser.add_argument(
- "--server",
- metavar="<server>",
- dest='server',
- default=None,
- help=_('Server to show migration details (name or ID).')
+ '--server',
+ metavar='<server>',
+ help=_(
+ 'Filter migrations by server (name or ID)'
+ )
)
parser.add_argument(
- "--host",
- metavar="<host>",
- default=None,
- help=_('Fetch migrations for the given host.')
+ '--host',
+ metavar='<host>',
+ help=_(
+ 'Filter migrations by source or destination host'
+ ),
)
parser.add_argument(
- "--status",
- metavar="<status>",
- default=None,
- help=_('Fetch migrations for the given status.')
+ '--status',
+ metavar='<status>',
+ help=_('Filter migrations by status')
)
parser.add_argument(
- "--marker",
- metavar="<marker>",
- dest='marker',
- default=None,
- help=_("The last migration of the previous page; displays list "
- "of migrations after 'marker'. Note that the marker is "
- "the migration UUID. (Supported with "
- "``--os-compute-api-version`` 2.59 or greater.)")
+ '--marker',
+ metavar='<marker>',
+ help=_(
+ "The last migration of the previous page; displays list "
+ "of migrations after 'marker'. Note that the marker is "
+ "the migration UUID. "
+ "(supported with --os-compute-api-version 2.59 or above)"
+ ),
)
parser.add_argument(
- "--limit",
- metavar="<limit>",
- dest='limit',
+ '--limit',
+ metavar='<limit>',
type=int,
- default=None,
- help=_("Maximum number of migrations to display. Note that there "
- "is a configurable max limit on the server, and the limit "
- "that is used will be the minimum of what is requested "
- "here and what is configured in the server. "
- "(Supported with ``--os-compute-api-version`` 2.59 "
- "or greater.)")
+ help=_(
+ "Maximum number of migrations to display. Note that there "
+ "is a configurable max limit on the server, and the limit "
+ "that is used will be the minimum of what is requested "
+ "here and what is configured in the server. "
+ "(supported with --os-compute-api-version 2.59 or above)"
+ ),
)
parser.add_argument(
'--changes-since',
dest='changes_since',
metavar='<changes-since>',
- default=None,
- help=_("List only migrations changed later or equal to a certain "
- "point of time. The provided time should be an ISO 8061 "
- "formatted time, e.g. ``2016-03-04T06:27:59Z``. "
- "(Supported with ``--os-compute-api-version`` 2.59 "
- "or greater.)")
+ help=_(
+ "List only migrations changed later or equal to a certain "
+ "point of time. The provided time should be an ISO 8061 "
+ "formatted time, e.g. ``2016-03-04T06:27:59Z``. "
+ "(supported with --os-compute-api-version 2.59 or above)"
+ ),
)
parser.add_argument(
'--changes-before',
dest='changes_before',
metavar='<changes-before>',
- default=None,
- help=_("List only migrations changed earlier or equal to a "
- "certain point of time. The provided time should be an ISO "
- "8061 formatted time, e.g. ``2016-03-04T06:27:59Z``. "
- "(Supported with ``--os-compute-api-version`` 2.66 or "
- "greater.)")
+ help=_(
+ "List only migrations changed earlier or equal to a "
+ "certain point of time. The provided time should be an ISO "
+ "8061 formatted time, e.g. ``2016-03-04T06:27:59Z``. "
+ "(supported with --os-compute-api-version 2.66 or above)"
+ ),
)
parser.add_argument(
'--project',
metavar='<project>',
dest='project_id',
- default=None,
- help=_("Filter the migrations by the given project ID. "
- "(Supported with ``--os-compute-api-version`` 2.80 "
- "or greater.)"),
+ help=_(
+ "Filter migrations by project (ID) "
+ "(supported with --os-compute-api-version 2.80 or above)"
+ ),
)
parser.add_argument(
'--user',
metavar='<user>',
dest='user_id',
- default=None,
- help=_("Filter the migrations by the given user ID. "
- "(Supported with ``--os-compute-api-version`` 2.80 "
- "or greater.)"),
+ help=_(
+ "Filter migrations by user (ID) "
+ "(supported with --os-compute-api-version 2.80 or above)"
+ ),
)
return parser
def print_migrations(self, parsed_args, compute_client, migrations):
- columns = ['Source Node', 'Dest Node', 'Source Compute',
- 'Dest Compute', 'Dest Host', 'Status',
- 'Server UUID', 'Old Flavor', 'New Flavor',
- 'Created At', 'Updated At']
+ columns = [
+ 'Source Node', 'Dest Node', 'Source Compute', 'Dest Compute',
+ 'Dest Host', 'Status', 'Server UUID', 'Old Flavor', 'New Flavor',
+ 'Created At', 'Updated At',
+ ]
# Insert migrations UUID after ID
if compute_client.api_version >= api_versions.APIVersion("2.59"):
@@ -2006,48 +2154,73 @@ class ListMigration(command.Command):
if parsed_args.user_id:
columns.insert(len(columns) - 2, "User")
- columns_header = columns
- return (columns_header, (utils.get_item_properties(
- mig, columns) for mig in migrations))
+ return (
+ columns,
+ (utils.get_item_properties(mig, columns) for mig in migrations),
+ )
def take_action(self, parsed_args):
compute_client = self.app.client_manager.compute
search_opts = {
- "host": parsed_args.host,
- "server": parsed_args.server,
- "status": parsed_args.status,
+ 'host': parsed_args.host,
+ 'server': parsed_args.server,
+ 'status': parsed_args.status,
}
- if (parsed_args.marker or parsed_args.limit or
- parsed_args.changes_since):
- if compute_client.api_version < api_versions.APIVersion("2.59"):
- msg = _("marker, limit and/or changes_since is not supported "
- "for --os-compute-api-version less than 2.59")
+ if parsed_args.marker:
+ if compute_client.api_version < api_versions.APIVersion('2.59'):
+ msg = _(
+ '--os-compute-api-version 2.59 or greater is required to '
+ 'support the --marker option'
+ )
raise exceptions.CommandError(msg)
- if parsed_args.marker:
- search_opts['marker'] = parsed_args.marker
- if parsed_args.limit:
- search_opts['limit'] = parsed_args.limit
- if parsed_args.changes_since:
- search_opts['changes_since'] = parsed_args.changes_since
+ search_opts['marker'] = parsed_args.marker
+
+ if parsed_args.limit:
+ if compute_client.api_version < api_versions.APIVersion('2.59'):
+ msg = _(
+ '--os-compute-api-version 2.59 or greater is required to '
+ 'support the --limit option'
+ )
+ raise exceptions.CommandError(msg)
+ search_opts['limit'] = parsed_args.limit
+
+ if parsed_args.changes_since:
+ if compute_client.api_version < api_versions.APIVersion('2.59'):
+ msg = _(
+ '--os-compute-api-version 2.59 or greater is required to '
+ 'support the --changes-since option'
+ )
+ raise exceptions.CommandError(msg)
+ search_opts['changes_since'] = parsed_args.changes_since
if parsed_args.changes_before:
- if compute_client.api_version < api_versions.APIVersion("2.66"):
- msg = _("changes_before is not supported for "
- "--os-compute-api-version less than 2.66")
+ if compute_client.api_version < api_versions.APIVersion('2.66'):
+ msg = _(
+ '--os-compute-api-version 2.66 or greater is required to '
+ 'support the --changes-before option'
+ )
raise exceptions.CommandError(msg)
search_opts['changes_before'] = parsed_args.changes_before
- if parsed_args.project_id or parsed_args.user_id:
- if compute_client.api_version < api_versions.APIVersion("2.80"):
- msg = _("Project and/or user is not supported for "
- "--os-compute-api-version less than 2.80")
+ if parsed_args.project_id:
+ if compute_client.api_version < api_versions.APIVersion('2.80'):
+ msg = _(
+ '--os-compute-api-version 2.80 or greater is required to '
+ 'support the --project option'
+ )
raise exceptions.CommandError(msg)
- if parsed_args.project_id:
- search_opts['project_id'] = parsed_args.project_id
- if parsed_args.user_id:
- search_opts['user_id'] = parsed_args.user_id
+ search_opts['project_id'] = parsed_args.project_id
+
+ if parsed_args.user_id:
+ if compute_client.api_version < api_versions.APIVersion('2.80'):
+ msg = _(
+ '--os-compute-api-version 2.80 or greater is required to '
+ 'support the --user option'
+ )
+ raise exceptions.CommandError(msg)
+ search_opts['user_id'] = parsed_args.user_id
migrations = compute_client.migrations.list(**search_opts)
@@ -2795,6 +2968,18 @@ class SetServer(command.Command):
help=_('New server description (supported by '
'--os-compute-api-version 2.19 or above)'),
)
+ parser.add_argument(
+ '--tag',
+ metavar='<tag>',
+ action='append',
+ default=[],
+ dest='tags',
+ help=_(
+ 'Tag for the server. '
+ 'Specify multiple times to add multiple tags. '
+ '(supported by --os-compute-api-version 2.26 or above)'
+ ),
+ )
return parser
def take_action(self, parsed_args):
@@ -2833,6 +3018,17 @@ class SetServer(command.Command):
raise exceptions.CommandError(msg)
server.update(description=parsed_args.description)
+ if parsed_args.tags:
+ if server.api_version < api_versions.APIVersion('2.26'):
+ msg = _(
+ '--os-compute-api-version 2.26 or greater is required to '
+ 'support the --tag option'
+ )
+ raise exceptions.CommandError(msg)
+
+ for tag in parsed_args.tags:
+ server.add_tag(tag=tag)
+
class ShelveServer(command.Command):
_description = _("Shelve server(s)")
@@ -3174,7 +3370,7 @@ class UnrescueServer(command.Command):
class UnsetServer(command.Command):
- _description = _("Unset server properties")
+ _description = _("Unset server properties and tags")
def get_parser(self, prog_name):
parser = super(UnsetServer, self).get_parser(prog_name)
@@ -3198,6 +3394,18 @@ class UnsetServer(command.Command):
help=_('Unset server description (supported by '
'--os-compute-api-version 2.19 or above)'),
)
+ parser.add_argument(
+ '--tag',
+ metavar='<tag>',
+ action='append',
+ default=[],
+ dest='tags',
+ help=_(
+ 'Tag to remove from the server. '
+ 'Specify multiple times to remove multiple tags. '
+ '(supported by --os-compute-api-version 2.26 or later'
+ ),
+ )
return parser
def take_action(self, parsed_args):
@@ -3223,6 +3431,17 @@ class UnsetServer(command.Command):
description="",
)
+ if parsed_args.tags:
+ if compute_client.api_version < api_versions.APIVersion('2.26'):
+ msg = _(
+ '--os-compute-api-version 2.26 or greater is required to '
+ 'support the --tag option'
+ )
+ raise exceptions.CommandError(msg)
+
+ for tag in parsed_args.tags:
+ compute_client.servers.delete_tag(server, tag=tag)
+
class UnshelveServer(command.Command):
_description = _("Unshelve server(s)")
diff --git a/openstackclient/compute/v2/server_group.py b/openstackclient/compute/v2/server_group.py
index 1af6e28d..a3363244 100644
--- a/openstackclient/compute/v2/server_group.py
+++ b/openstackclient/compute/v2/server_group.py
@@ -56,12 +56,18 @@ class CreateServerGroup(command.ShowOne):
parser.add_argument(
'--policy',
metavar='<policy>',
+ choices=[
+ 'affinity',
+ 'anti-affinity',
+ 'soft-affinity',
+ 'soft-anti-affinity',
+ ],
default='affinity',
- help=_("Add a policy to <name> "
- "('affinity' or 'anti-affinity', "
- "defaults to 'affinity'). Specify --os-compute-api-version "
- "2.15 or higher for the 'soft-affinity' or "
- "'soft-anti-affinity' policy.")
+ help=_(
+ "Add a policy to <name> "
+ "Specify --os-compute-api-version 2.15 or higher for the "
+ "'soft-affinity' or 'soft-anti-affinity' policy."
+ )
)
return parser
@@ -69,9 +75,18 @@ class CreateServerGroup(command.ShowOne):
compute_client = self.app.client_manager.compute
info = {}
+ if parsed_args.policy in ('soft-affinity', 'soft-anti-affinity'):
+ if compute_client.api_version < api_versions.APIVersion('2.15'):
+ msg = _(
+ '--os-compute-api-version 2.15 or greater is required to '
+ 'support the %s policy'
+ )
+ raise exceptions.CommandError(msg % parsed_args.policy)
+
policy_arg = {'policies': [parsed_args.policy]}
if compute_client.api_version >= api_versions.APIVersion("2.64"):
policy_arg = {'policy': parsed_args.policy}
+
server_group = compute_client.server_groups.create(
name=parsed_args.name, **policy_arg)
diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py
index 029f57a3..4f3e9d0b 100644
--- a/openstackclient/image/v2/image.py
+++ b/openstackclient/image/v2/image.py
@@ -354,7 +354,7 @@ class CreateImage(command.ShowOne):
# Build an attribute dict from the parsed args, only include
# attributes that were actually set on the command line
- kwargs = {}
+ kwargs = {'allow_duplicates': True}
copy_attrs = ('name', 'id',
'container_format', 'disk_format',
'min_disk', 'min_ram', 'tags', 'visibility')
diff --git a/openstackclient/tests/unit/compute/v2/test_console.py b/openstackclient/tests/unit/compute/v2/test_console.py
index 99a14f04..db9603c9 100644
--- a/openstackclient/tests/unit/compute/v2/test_console.py
+++ b/openstackclient/tests/unit/compute/v2/test_console.py
@@ -17,29 +17,103 @@ from unittest import mock
from openstackclient.compute.v2 import console
from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes
+from openstackclient.tests.unit import utils
class TestConsole(compute_fakes.TestComputev2):
def setUp(self):
super(TestConsole, self).setUp()
- self.servers_mock = self.app.client_manager.compute.servers
- self.servers_mock.reset_mock()
+
+ # SDK mock
+ self.app.client_manager.sdk_connection = mock.Mock()
+ self.app.client_manager.sdk_connection.compute = mock.Mock()
+ self.sdk_client = self.app.client_manager.sdk_connection.compute
+ self.sdk_client.find_server = mock.Mock()
+ self.sdk_client.get_server_console_output = mock.Mock()
+
+
+class TestConsoleLog(TestConsole):
+ _server = compute_fakes.FakeServer.create_one_server()
+
+ def setUp(self):
+ super(TestConsoleLog, self).setUp()
+
+ self.sdk_client.find_server.return_value = self._server
+
+ self.cmd = console.ShowConsoleLog(self.app, None)
+
+ def test_show_no_args(self):
+ arglist = [
+ ]
+ verifylist = [
+ ]
+ self.assertRaises(utils.ParserException,
+ self.check_parser,
+ self.cmd,
+ arglist,
+ verifylist)
+
+ def test_show(self):
+ arglist = [
+ 'fake_server'
+ ]
+ verifylist = [
+ ('server', 'fake_server')
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ output = {
+ 'output': '1st line\n2nd line\n'
+ }
+ self.sdk_client.get_server_console_output.return_value = output
+ self.cmd.take_action(parsed_args)
+
+ self.sdk_client.find_server.assert_called_with(
+ name_or_id='fake_server', ignore_missing=False)
+ self.sdk_client.get_server_console_output.assert_called_with(
+ self._server.id,
+ length=None
+ )
+ stdout = self.app.stdout.content
+ self.assertEqual(stdout[0], output['output'])
+
+ def test_show_lines(self):
+ arglist = [
+ 'fake_server',
+ '--lines', '15'
+ ]
+ verifylist = [
+ ('server', 'fake_server'),
+ ('lines', 15)
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ output = {
+ 'output': '1st line\n2nd line'
+ }
+ self.sdk_client.get_server_console_output.return_value = output
+ self.cmd.take_action(parsed_args)
+
+ self.sdk_client.find_server.assert_called_with(
+ name_or_id='fake_server', ignore_missing=False)
+ self.sdk_client.get_server_console_output.assert_called_with(
+ self._server.id,
+ length=15
+ )
class TestConsoleUrlShow(TestConsole):
+ _server = compute_fakes.FakeServer.create_one_server()
def setUp(self):
super(TestConsoleUrlShow, self).setUp()
- fake_console_data = {'remote_console': {'url': 'http://localhost',
- 'protocol': 'fake_protocol',
- 'type': 'fake_type'}}
- methods = {
- 'get_console_url': fake_console_data
- }
- self.fake_server = compute_fakes.FakeServer.create_one_server(
- methods=methods)
- self.servers_mock.get.return_value = self.fake_server
+ self.sdk_client.find_server.return_value = self._server
+ fake_console_data = {'url': 'http://localhost',
+ 'protocol': 'fake_protocol',
+ 'type': 'fake_type'}
+ self.sdk_client.create_console = mock.Mock(
+ return_value=fake_console_data)
self.columns = (
'protocol',
@@ -47,9 +121,9 @@ class TestConsoleUrlShow(TestConsole):
'url',
)
self.data = (
- fake_console_data['remote_console']['protocol'],
- fake_console_data['remote_console']['type'],
- fake_console_data['remote_console']['url']
+ fake_console_data['protocol'],
+ fake_console_data['type'],
+ fake_console_data['url']
)
self.cmd = console.ShowConsoleURL(self.app, None)
@@ -64,7 +138,9 @@ class TestConsoleUrlShow(TestConsole):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- self.fake_server.get_console_url.assert_called_once_with('novnc')
+ self.sdk_client.create_console.assert_called_once_with(
+ self._server.id,
+ console_type='novnc')
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
@@ -79,7 +155,9 @@ class TestConsoleUrlShow(TestConsole):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- self.fake_server.get_console_url.assert_called_once_with('novnc')
+ self.sdk_client.create_console.assert_called_once_with(
+ self._server.id,
+ console_type='novnc')
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
@@ -94,7 +172,9 @@ class TestConsoleUrlShow(TestConsole):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- self.fake_server.get_console_url.assert_called_once_with('xvpvnc')
+ self.sdk_client.create_console.assert_called_once_with(
+ self._server.id,
+ console_type='xvpvnc')
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
@@ -109,41 +189,12 @@ class TestConsoleUrlShow(TestConsole):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- self.fake_server.get_console_url.assert_called_once_with(
- 'spice-html5')
+ self.sdk_client.create_console.assert_called_once_with(
+ self._server.id,
+ console_type='spice-html5')
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
- def test_console_url_show_compatible(self):
- methods = {
- 'get_console_url': {'console': {'url': 'http://localhost',
- 'type': 'fake_type'}},
- }
- old_fake_server = compute_fakes.FakeServer.create_one_server(
- methods=methods)
- old_columns = (
- 'type',
- 'url',
- )
- old_data = (
- methods['get_console_url']['console']['type'],
- methods['get_console_url']['console']['url']
- )
- arglist = [
- 'foo_vm',
- ]
- verifylist = [
- ('url_type', 'novnc'),
- ('server', 'foo_vm'),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- with mock.patch.object(self.servers_mock, 'get',
- return_value=old_fake_server):
- columns, data = self.cmd.take_action(parsed_args)
- old_fake_server.get_console_url.assert_called_once_with('novnc')
- self.assertEqual(old_columns, columns)
- self.assertEqual(old_data, data)
-
def test_console_url_show_with_rdp(self):
arglist = [
'--rdp',
@@ -155,8 +206,9 @@ class TestConsoleUrlShow(TestConsole):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- self.fake_server.get_console_url.assert_called_once_with(
- 'rdp-html5')
+ self.sdk_client.create_console.assert_called_once_with(
+ self._server.id,
+ console_type='rdp-html5')
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
@@ -171,8 +223,9 @@ class TestConsoleUrlShow(TestConsole):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- self.fake_server.get_console_url.assert_called_once_with(
- 'serial')
+ self.sdk_client.create_console.assert_called_once_with(
+ self._server.id,
+ console_type='serial')
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
@@ -187,6 +240,8 @@ class TestConsoleUrlShow(TestConsole):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- self.fake_server.get_console_url.assert_called_once_with('webmks')
+ self.sdk_client.create_console.assert_called_once_with(
+ self._server.id,
+ console_type='webmks')
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py
index 5f1d5d06..380ef66b 100644
--- a/openstackclient/tests/unit/compute/v2/test_server.py
+++ b/openstackclient/tests/unit/compute/v2/test_server.py
@@ -47,10 +47,14 @@ class TestServer(compute_fakes.TestComputev2):
self.app.client_manager.compute.server_migrations
self.server_migrations_mock.reset_mock()
- # Get a shortcut to the compute client volumeManager Mock
+ # Get a shortcut to the compute client VolumeManager mock
self.servers_volumes_mock = self.app.client_manager.compute.volumes
self.servers_volumes_mock.reset_mock()
+ # Get a shortcut to the compute client MigrationManager mock
+ self.migrations_mock = self.app.client_manager.compute.migrations
+ self.migrations_mock.reset_mock()
+
# Get a shortcut to the compute client FlavorManager Mock
self.flavors_mock = self.app.client_manager.compute.flavors
self.flavors_mock.reset_mock()
@@ -182,6 +186,72 @@ class TestServerAddFixedIP(TestServer):
extralist = ['--fixed-ip-address', '5.6.7.8']
self._test_server_add_fixed_ip(extralist, '5.6.7.8')
+ def test_server_add_fixed_ip_with_tag(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.49')
+
+ servers = self.setup_servers_mock(count=1)
+ network = compute_fakes.FakeNetwork.create_one_network()
+ with mock.patch(
+ 'openstackclient.api.compute_v2.APIv2.network_find'
+ ) as net_mock:
+ net_mock.return_value = network
+
+ arglist = [
+ servers[0].id,
+ network['id'],
+ '--fixed-ip-address', '5.6.7.8',
+ '--tag', 'tag1',
+ ]
+ verifylist = [
+ ('server', servers[0].id),
+ ('network', network['id']),
+ ('fixed_ip_address', '5.6.7.8'),
+ ('tag', 'tag1'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+
+ servers[0].interface_attach.assert_called_once_with(
+ port_id=None,
+ net_id=network['id'],
+ fixed_ip='5.6.7.8',
+ tag='tag1'
+ )
+ self.assertIsNone(result)
+
+ def test_server_add_fixed_ip_with_tag_pre_v249(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.48')
+
+ servers = self.setup_servers_mock(count=1)
+ network = compute_fakes.FakeNetwork.create_one_network()
+ with mock.patch(
+ 'openstackclient.api.compute_v2.APIv2.network_find'
+ ) as net_mock:
+ net_mock.return_value = network
+
+ arglist = [
+ servers[0].id,
+ network['id'],
+ '--fixed-ip-address', '5.6.7.8',
+ '--tag', 'tag1',
+ ]
+ verifylist = [
+ ('server', servers[0].id),
+ ('network', network['id']),
+ ('fixed_ip_address', '5.6.7.8'),
+ ('tag', 'tag1'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ ex = self.assertRaises(
+ exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args)
+ self.assertIn(
+ '--os-compute-api-version 2.49 or greater is required',
+ str(ex))
+
@mock.patch(
'openstackclient.api.compute_v2.APIv2.floating_ip_add'
@@ -2434,6 +2504,87 @@ class TestServerCreate(TestServer):
self.assertRaises(exceptions.CommandError, self.cmd.take_action,
parsed_args)
+ def test_server_create_with_tag(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.52')
+
+ arglist = [
+ '--image', 'image1',
+ '--flavor', 'flavor1',
+ '--tag', 'tag1',
+ '--tag', 'tag2',
+ self.new_server.name,
+ ]
+ verifylist = [
+ ('image', 'image1'),
+ ('flavor', 'flavor1'),
+ ('tags', ['tag1', 'tag2']),
+ ('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 = {
+ '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.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist(), data)
+ self.assertFalse(self.images_mock.called)
+ self.assertFalse(self.flavors_mock.called)
+
+ def test_server_create_with_tag_pre_v252(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.51')
+
+ arglist = [
+ '--image', 'image1',
+ '--flavor', 'flavor1',
+ '--tag', 'tag1',
+ '--tag', 'tag2',
+ self.new_server.name,
+ ]
+ verifylist = [
+ ('image', 'image1'),
+ ('flavor', 'flavor1'),
+ ('tags', ['tag1', 'tag2']),
+ ('config_drive', False),
+ ('server_name', self.new_server.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ ex = self.assertRaises(
+ exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args)
+ self.assertIn(
+ '--os-compute-api-version 2.52 or greater is required',
+ str(ex))
+
def test_server_create_with_host_v274(self):
# Explicit host is supported for nova api version 2.74 or above
@@ -3206,6 +3357,7 @@ class TestServerList(TestServer):
self.search_opts['changes-before'] = '2016-03-05T06:27:59Z'
self.search_opts['deleted'] = True
+
self.servers_mock.list.assert_called_with(**self.kwargs)
self.assertEqual(self.columns, columns)
@@ -3298,6 +3450,92 @@ class TestServerList(TestServer):
'UNKNOWN', '', '', '')
self.assertEqual(expected_row, partial_server)
+ def test_server_list_with_tag(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.26')
+
+ arglist = [
+ '--tag', 'tag1',
+ '--tag', 'tag2',
+ ]
+ verifylist = [
+ ('tags', ['tag1', 'tag2']),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.search_opts['tags'] = ['tag1', 'tag2']
+
+ self.servers_mock.list.assert_called_with(**self.kwargs)
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(tuple(self.data), tuple(data))
+
+ def test_server_list_with_tag_pre_v225(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.25')
+
+ arglist = [
+ '--tag', 'tag1',
+ '--tag', 'tag2',
+ ]
+ verifylist = [
+ ('tags', ['tag1', 'tag2']),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ ex = self.assertRaises(
+ exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args)
+ self.assertIn(
+ '--os-compute-api-version 2.26 or greater is required',
+ str(ex))
+
+ def test_server_list_with_not_tag(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.26')
+
+ arglist = [
+ '--not-tag', 'tag1',
+ '--not-tag', 'tag2',
+ ]
+ verifylist = [
+ ('not_tags', ['tag1', 'tag2']),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.search_opts['not-tags'] = ['tag1', 'tag2']
+
+ self.servers_mock.list.assert_called_with(**self.kwargs)
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(tuple(self.data), tuple(data))
+
+ def test_server_list_with_not_tag_pre_v226(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.25')
+
+ arglist = [
+ '--not-tag', 'tag1',
+ '--not-tag', 'tag2',
+ ]
+ verifylist = [
+ ('not_tags', ['tag1', 'tag2']),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ ex = self.assertRaises(
+ exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args)
+ self.assertIn(
+ '--os-compute-api-version 2.26 or greater is required',
+ str(ex))
+
class TestServerLock(TestServer):
@@ -3805,34 +4043,7 @@ class TestServerMigrate(TestServer):
self.assertNotCalled(self.servers_mock.live_migrate)
-class TestServerMigration(TestServer):
-
- def setUp(self):
- super(TestServerMigration, self).setUp()
-
- # Get a shortcut to the compute client ServerManager Mock
- self.servers_mock = self.app.client_manager.compute.servers
- self.servers_mock.reset_mock()
-
- self.migrations_mock = (
- self.app.client_manager.compute.migrations)
- self.migrations_mock.reset_mock()
-
- self.server = self.setup_servers_mock(1)[0]
-
- def setup_servers_mock(self, count):
- servers = compute_fakes.FakeServer.create_servers(count=count)
-
- # This is the return value for utils.find_resource()
- self.servers_mock.get = compute_fakes.FakeServer.get_servers(servers)
- return servers
-
- def setup_server_migrations_mock(self, count):
- return compute_fakes.FakeServerMigration.create_server_migrations(
- count=count)
-
-
-class TestListMigration(TestServerMigration):
+class TestListMigration(TestServer):
"""Test fetch all migrations."""
MIGRATION_COLUMNS = [
@@ -3844,24 +4055,48 @@ class TestListMigration(TestServerMigration):
def setUp(self):
super(TestListMigration, self).setUp()
- self.cmd = server.ListMigration(self.app, None)
- self.migrations = self.setup_server_migrations_mock(3)
- self.migrations_mock.list.return_value = self.migrations
- self.setup_server_migrations_data(self.migrations)
+ self.server = compute_fakes.FakeServer.create_one_server()
+ self.servers_mock.get.return_value = self.server
- self.app.client_manager.compute.api_version = api_versions.APIVersion(
- '2.20')
+ self.migrations = compute_fakes.FakeServerMigration\
+ .create_server_migrations(count=3)
+ self.migrations_mock.list.return_value = self.migrations
- def setup_server_migrations_data(self, migrations):
self.data = (common_utils.get_item_properties(
- s, self.MIGRATION_COLUMNS) for s in migrations)
+ s, self.MIGRATION_COLUMNS) for s in self.migrations)
- def test_server_migraton_list(self):
+ # Get the command object to test
+ self.cmd = server.ListMigration(self.app, None)
+
+ def test_server_migration_list_no_options(self):
+ arglist = []
+ verifylist = []
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'status': None,
+ 'host': None,
+ 'server': None,
+ }
+
+ self.migrations_mock.list.assert_called_with(**kwargs)
+
+ self.assertEqual(self.MIGRATION_COLUMNS, columns)
+ self.assertEqual(tuple(self.data), tuple(data))
+
+ def test_server_migration_list(self):
arglist = [
- '--status', 'migrating'
+ '--server', 'server1',
+ '--host', 'host1',
+ '--status', 'migrating',
]
verifylist = [
- ('status', 'migrating')
+ ('server', 'server1'),
+ ('host', 'host1'),
+ ('status', 'migrating'),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
@@ -3869,8 +4104,8 @@ class TestListMigration(TestServerMigration):
# Set expected values
kwargs = {
'status': 'migrating',
- 'host': None,
- 'server': None,
+ 'host': 'host1',
+ 'server': 'server1',
}
self.migrations_mock.list.assert_called_with(**kwargs)
@@ -3890,15 +4125,11 @@ class TestListMigrationV223(TestListMigration):
def setUp(self):
super(TestListMigrationV223, self).setUp()
- self.cmd = server.ListMigration(self.app, None)
- self.migrations = self.setup_server_migrations_mock(3)
- self.migrations_mock.list.return_value = self.migrations
- self.setup_server_migrations_data(self.migrations)
self.app.client_manager.compute.api_version = api_versions.APIVersion(
'2.23')
- def test_server_migraton_list(self):
+ def test_server_migration_list(self):
arglist = [
'--status', 'migrating'
]
@@ -3935,15 +4166,11 @@ class TestListMigrationV259(TestListMigration):
def setUp(self):
super(TestListMigrationV259, self).setUp()
- self.cmd = server.ListMigration(self.app, None)
- self.migrations = self.setup_server_migrations_mock(3)
- self.migrations_mock.list.return_value = self.migrations
- self.setup_server_migrations_data(self.migrations)
self.app.client_manager.compute.api_version = api_versions.APIVersion(
'2.59')
- def test_server_migraton_list(self):
+ def test_server_migration_list(self):
arglist = [
'--status', 'migrating',
'--limit', '1',
@@ -3974,7 +4201,7 @@ class TestListMigrationV259(TestListMigration):
self.assertEqual(self.MIGRATION_COLUMNS, columns)
self.assertEqual(tuple(self.data), tuple(data))
- def test_server_migraton_list_with_limit_pre_v259(self):
+ def test_server_migration_list_with_limit_pre_v259(self):
self.app.client_manager.compute.api_version = api_versions.APIVersion(
'2.58')
arglist = [
@@ -3986,10 +4213,15 @@ class TestListMigrationV259(TestListMigration):
('limit', 1)
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.assertRaises(exceptions.CommandError, self.cmd.take_action,
- parsed_args)
+ ex = self.assertRaises(
+ exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args)
+ self.assertIn(
+ '--os-compute-api-version 2.59 or greater is required',
+ str(ex))
- def test_server_migraton_list_with_marker_pre_v259(self):
+ def test_server_migration_list_with_marker_pre_v259(self):
self.app.client_manager.compute.api_version = api_versions.APIVersion(
'2.58')
arglist = [
@@ -4001,10 +4233,15 @@ class TestListMigrationV259(TestListMigration):
('marker', 'test_kp')
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.assertRaises(exceptions.CommandError, self.cmd.take_action,
- parsed_args)
+ ex = self.assertRaises(
+ exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args)
+ self.assertIn(
+ '--os-compute-api-version 2.59 or greater is required',
+ str(ex))
- def test_server_migraton_list_with_changes_since_pre_v259(self):
+ def test_server_migration_list_with_changes_since_pre_v259(self):
self.app.client_manager.compute.api_version = api_versions.APIVersion(
'2.58')
arglist = [
@@ -4016,8 +4253,13 @@ class TestListMigrationV259(TestListMigration):
('changes_since', '2019-08-09T08:03:25Z')
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.assertRaises(exceptions.CommandError, self.cmd.take_action,
- parsed_args)
+ ex = self.assertRaises(
+ exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args)
+ self.assertIn(
+ '--os-compute-api-version 2.59 or greater is required',
+ str(ex))
class TestListMigrationV266(TestListMigration):
@@ -4031,15 +4273,11 @@ class TestListMigrationV266(TestListMigration):
def setUp(self):
super(TestListMigrationV266, self).setUp()
- self.cmd = server.ListMigration(self.app, None)
- self.migrations = self.setup_server_migrations_mock(3)
- self.migrations_mock.list.return_value = self.migrations
- self.setup_server_migrations_data(self.migrations)
self.app.client_manager.compute.api_version = api_versions.APIVersion(
'2.66')
- def test_server_migraton_list_with_changes_before(self):
+ def test_server_migration_list_with_changes_before(self):
arglist = [
'--status', 'migrating',
'--limit', '1',
@@ -4073,7 +4311,7 @@ class TestListMigrationV266(TestListMigration):
self.assertEqual(self.MIGRATION_COLUMNS, columns)
self.assertEqual(tuple(self.data), tuple(data))
- def test_server_migraton_list_with_changes_before_pre_v266(self):
+ def test_server_migration_list_with_changes_before_pre_v266(self):
self.app.client_manager.compute.api_version = api_versions.APIVersion(
'2.65')
arglist = [
@@ -4085,8 +4323,13 @@ class TestListMigrationV266(TestListMigration):
('changes_before', '2019-08-09T08:03:25Z')
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.assertRaises(exceptions.CommandError, self.cmd.take_action,
- parsed_args)
+ ex = self.assertRaises(
+ exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args)
+ self.assertIn(
+ '--os-compute-api-version 2.66 or greater is required',
+ str(ex))
class TestListMigrationV280(TestListMigration):
@@ -4100,15 +4343,11 @@ class TestListMigrationV280(TestListMigration):
def setUp(self):
super(TestListMigrationV280, self).setUp()
- self.cmd = server.ListMigration(self.app, None)
- self.migrations = self.setup_server_migrations_mock(3)
- self.migrations_mock.list.return_value = self.migrations
- self.setup_server_migrations_data(self.migrations)
self.app.client_manager.compute.api_version = api_versions.APIVersion(
'2.80')
- def test_server_migraton_list_with_project(self):
+ def test_server_migration_list_with_project(self):
arglist = [
'--status', 'migrating',
'--limit', '1',
@@ -4163,10 +4402,15 @@ class TestListMigrationV280(TestListMigration):
('project_id', '0c2accde-644a-45fa-8c10-e76debc7fbc3')
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.assertRaises(exceptions.CommandError, self.cmd.take_action,
- parsed_args)
+ ex = self.assertRaises(
+ exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args)
+ self.assertIn(
+ '--os-compute-api-version 2.80 or greater is required',
+ str(ex))
- def test_server_migraton_list_with_user(self):
+ def test_server_migration_list_with_user(self):
arglist = [
'--status', 'migrating',
'--limit', '1',
@@ -4221,11 +4465,15 @@ class TestListMigrationV280(TestListMigration):
('user_id', 'dd214878-ca12-40fb-b035-fa7d2c1e86d6')
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ ex = self.assertRaises(
+ exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args)
+ self.assertIn(
+ '--os-compute-api-version 2.80 or greater is required',
+ str(ex))
- self.assertRaises(exceptions.CommandError, self.cmd.take_action,
- parsed_args)
-
- def test_server_migraton_list_with_project_and_user(self):
+ def test_server_migration_list_with_project_and_user(self):
arglist = [
'--status', 'migrating',
'--limit', '1',
@@ -4285,8 +4533,13 @@ class TestListMigrationV280(TestListMigration):
('user_id', 'dd214878-ca12-40fb-b035-fa7d2c1e86d6')
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.assertRaises(exceptions.CommandError, self.cmd.take_action,
- parsed_args)
+ ex = self.assertRaises(
+ exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args)
+ self.assertIn(
+ '--os-compute-api-version 2.80 or greater is required',
+ str(ex))
class TestServerMigrationAbort(TestServer):
@@ -5388,6 +5641,8 @@ class TestServerSet(TestServer):
'update': None,
'reset_state': None,
'change_password': None,
+ 'add_tag': None,
+ 'set_tags': None,
}
self.fake_servers = self.setup_servers_mock(2)
@@ -5528,6 +5783,50 @@ class TestServerSet(TestServer):
self.assertRaises(exceptions.CommandError, self.cmd.take_action,
parsed_args)
+ def test_server_set_with_tag(self):
+ self.fake_servers[0].api_version = api_versions.APIVersion('2.26')
+
+ arglist = [
+ '--tag', 'tag1',
+ '--tag', 'tag2',
+ 'foo_vm',
+ ]
+ verifylist = [
+ ('tags', ['tag1', 'tag2']),
+ ('server', 'foo_vm'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ self.fake_servers[0].add_tag.assert_has_calls([
+ mock.call(tag='tag1'),
+ mock.call(tag='tag2'),
+ ])
+ self.assertIsNone(result)
+
+ def test_server_set_with_tag_pre_v226(self):
+ self.fake_servers[0].api_version = api_versions.APIVersion('2.25')
+
+ arglist = [
+ '--tag', 'tag1',
+ '--tag', 'tag2',
+ 'foo_vm',
+ ]
+ verifylist = [
+ ('tags', ['tag1', 'tag2']),
+ ('server', 'foo_vm'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ ex = self.assertRaises(
+ exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args)
+ self.assertIn(
+ '--os-compute-api-version 2.26 or greater is required',
+ str(ex))
+
class TestServerShelve(TestServer):
@@ -5853,6 +6152,52 @@ class TestServerUnset(TestServer):
self.assertRaises(exceptions.CommandError, self.cmd.take_action,
parsed_args)
+ def test_server_unset_with_tag(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.26')
+
+ arglist = [
+ '--tag', 'tag1',
+ '--tag', 'tag2',
+ 'foo_vm',
+ ]
+ verifylist = [
+ ('tags', ['tag1', 'tag2']),
+ ('server', 'foo_vm'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+ self.assertIsNone(result)
+
+ self.servers_mock.delete_tag.assert_has_calls([
+ mock.call(self.fake_server, tag='tag1'),
+ mock.call(self.fake_server, tag='tag2'),
+ ])
+
+ def test_server_unset_with_tag_pre_v226(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.25')
+
+ arglist = [
+ '--tag', 'tag1',
+ '--tag', 'tag2',
+ 'foo_vm',
+ ]
+ verifylist = [
+ ('tags', ['tag1', 'tag2']),
+ ('server', 'foo_vm'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ ex = self.assertRaises(
+ exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args)
+ self.assertIn(
+ '--os-compute-api-version 2.26 or greater is required',
+ str(ex))
+
class TestServerUnshelve(TestServer):
diff --git a/openstackclient/tests/unit/compute/v2/test_server_group.py b/openstackclient/tests/unit/compute/v2/test_server_group.py
index 359cd2bd..bf0ea0ba 100644
--- a/openstackclient/tests/unit/compute/v2/test_server_group.py
+++ b/openstackclient/tests/unit/compute/v2/test_server_group.py
@@ -91,6 +91,28 @@ class TestServerGroupCreate(TestServerGroup):
def test_server_group_create(self):
arglist = [
+ '--policy', 'anti-affinity',
+ 'affinity_group',
+ ]
+ verifylist = [
+ ('policy', 'anti-affinity'),
+ ('name', 'affinity_group'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+ self.server_groups_mock.create.assert_called_once_with(
+ name=parsed_args.name,
+ policies=[parsed_args.policy],
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
+ def test_server_group_create_with_soft_policies(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.15')
+
+ arglist = [
'--policy', 'soft-anti-affinity',
'affinity_group',
]
@@ -108,6 +130,27 @@ class TestServerGroupCreate(TestServerGroup):
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
+ def test_server_group_create_with_soft_policies_pre_v215(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.14')
+
+ arglist = [
+ '--policy', 'soft-anti-affinity',
+ 'affinity_group',
+ ]
+ verifylist = [
+ ('policy', 'soft-anti-affinity'),
+ ('name', 'affinity_group'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ ex = self.assertRaises(
+ exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args)
+ self.assertIn(
+ '--os-compute-api-version 2.15 or greater is required',
+ str(ex))
+
def test_server_group_create_v264(self):
self.app.client_manager.compute.api_version = api_versions.APIVersion(
'2.64')
diff --git a/openstackclient/tests/unit/image/v2/test_image.py b/openstackclient/tests/unit/image/v2/test_image.py
index 310f6b76..b094817e 100644
--- a/openstackclient/tests/unit/image/v2/test_image.py
+++ b/openstackclient/tests/unit/image/v2/test_image.py
@@ -100,6 +100,7 @@ class TestImageCreate(TestImage):
# ImageManager.create(name=, **)
self.client.create_image.assert_called_with(
name=self.new_image.name,
+ allow_duplicates=True,
container_format=image.DEFAULT_CONTAINER_FORMAT,
disk_format=image.DEFAULT_DISK_FORMAT,
)
@@ -152,6 +153,7 @@ class TestImageCreate(TestImage):
# ImageManager.create(name=, **)
self.client.create_image.assert_called_with(
name=self.new_image.name,
+ allow_duplicates=True,
container_format='ovf',
disk_format='ami',
min_disk=10,
@@ -239,6 +241,7 @@ class TestImageCreate(TestImage):
# ImageManager.create(name=, **)
self.client.create_image.assert_called_with(
name=self.new_image.name,
+ allow_duplicates=True,
container_format=image.DEFAULT_CONTAINER_FORMAT,
disk_format=image.DEFAULT_DISK_FORMAT,
is_protected=self.new_image.is_protected,
@@ -246,7 +249,7 @@ class TestImageCreate(TestImage):
Alpha='1',
Beta='2',
tags=self.new_image.tags,
- filename=imagefile.name
+ filename=imagefile.name,
)
self.assertEqual(
@@ -288,6 +291,7 @@ class TestImageCreate(TestImage):
# ImageManager.create(name=, **)
self.client.create_image.assert_called_with(
name=self.new_image.name,
+ allow_duplicates=True,
container_format=image.DEFAULT_CONTAINER_FORMAT,
disk_format=image.DEFAULT_DISK_FORMAT,
use_import=True