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/flavor.py59
-rw-r--r--openstackclient/compute/v2/keypair.py109
-rw-r--r--openstackclient/compute/v2/server.py510
-rw-r--r--openstackclient/compute/v2/server_group.py25
-rw-r--r--openstackclient/image/v2/image.py2
-rw-r--r--openstackclient/network/v2/security_group_rule.py17
-rw-r--r--openstackclient/tests/functional/compute/v2/test_flavor.py43
-rw-r--r--openstackclient/tests/unit/compute/v2/fakes.py1
-rw-r--r--openstackclient/tests/unit/compute/v2/test_console.py163
-rw-r--r--openstackclient/tests/unit/compute/v2/test_flavor.py26
-rw-r--r--openstackclient/tests/unit/compute/v2/test_keypair.py283
-rw-r--r--openstackclient/tests/unit/compute/v2/test_server.py411
-rw-r--r--openstackclient/tests/unit/compute/v2/test_server_group.py43
-rw-r--r--openstackclient/tests/unit/image/v2/test_image.py6
-rw-r--r--openstackclient/tests/unit/network/v2/test_security_group_rule_compute.py1
-rw-r--r--openstackclient/tests/unit/network/v2/test_security_group_rule_network.py16
17 files changed, 1445 insertions, 324 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/flavor.py b/openstackclient/compute/v2/flavor.py
index 805e919e..00431b7b 100644
--- a/openstackclient/compute/v2/flavor.py
+++ b/openstackclient/compute/v2/flavor.py
@@ -18,6 +18,7 @@
import logging
from novaclient import api_versions
+from osc_lib.cli import format_columns
from osc_lib.cli import parseractions
from osc_lib.command import command
from osc_lib import exceptions
@@ -30,6 +31,31 @@ from openstackclient.identity import common as identity_common
LOG = logging.getLogger(__name__)
+_formatters = {
+ 'extra_specs': format_columns.DictColumn,
+ # Unless we finish switch to use SDK resources this need to be doubled this
+ # way
+ 'properties': format_columns.DictColumn,
+ 'Properties': format_columns.DictColumn
+}
+
+
+def _get_flavor_columns(item):
+ # To maintain backwards compatibility we need to rename sdk props to
+ # whatever OSC was using before
+ column_map = {
+ 'extra_specs': 'properties',
+ 'ephemeral': 'OS-FLV-EXT-DATA:ephemeral',
+ 'is_disabled': 'OS-FLV-DISABLED:disabled',
+ 'is_public': 'os-flavor-access:is_public'
+
+ }
+ hidden_columns = ['links', 'location']
+
+ return utils.get_osc_show_columns_for_sdk_resource(
+ item, column_map, hidden_columns)
+
+
def _find_flavor(compute_client, flavor):
try:
return compute_client.flavors.get(flavor)
@@ -191,10 +217,16 @@ class CreateFlavor(command.ShowOne):
LOG.error(_("Failed to set flavor property: %s"), e)
flavor_info = flavor._info.copy()
- flavor_info.pop("links")
- flavor_info['properties'] = utils.format_dict(flavor.get_keys())
+ flavor_info['properties'] = flavor.get_keys()
- return zip(*sorted(flavor_info.items()))
+ display_columns, columns = _get_flavor_columns(flavor_info)
+ data = utils.get_dict_properties(
+ flavor_info, columns,
+ formatters=_formatters,
+ mixed_case_fields=['OS-FLV-DISABLED:disabled',
+ 'OS-FLV-EXT-DATA:ephemeral'])
+
+ return (display_columns, data)
class DeleteFlavor(command.Command):
@@ -309,7 +341,7 @@ class ListFlavor(command.Lister):
return (column_headers,
(utils.get_item_properties(
- s, columns, formatters={'Properties': utils.format_dict},
+ s, columns, formatters=_formatters,
) for s in data))
@@ -428,11 +460,8 @@ class ShowFlavor(command.ShowOne):
try:
flavor_access = compute_client.flavor_access.list(
flavor=resource_flavor.id)
- projects = [utils.get_field(access, 'tenant_id')
- for access in flavor_access]
- # TODO(Huanxuan Ao): This format case can be removed after
- # patch https://review.opendev.org/#/c/330223/ merged.
- access_projects = utils.format_list(projects)
+ access_projects = [utils.get_field(access, 'tenant_id')
+ for access in flavor_access]
except Exception as e:
msg = _("Failed to get access projects list "
"for flavor '%(flavor)s': %(e)s")
@@ -442,11 +471,17 @@ class ShowFlavor(command.ShowOne):
flavor.update({
'access_project_ids': access_projects
})
- flavor.pop("links", None)
- flavor['properties'] = utils.format_dict(resource_flavor.get_keys())
+ flavor['properties'] = resource_flavor.get_keys()
+
+ display_columns, columns = _get_flavor_columns(flavor)
+ data = utils.get_dict_properties(
+ flavor, columns,
+ formatters=_formatters,
+ mixed_case_fields=['OS-FLV-DISABLED:disabled',
+ 'OS-FLV-EXT-DATA:ephemeral'])
- return zip(*sorted(flavor.items()))
+ return (display_columns, data)
class UnsetFlavor(command.Command):
diff --git a/openstackclient/compute/v2/keypair.py b/openstackclient/compute/v2/keypair.py
index 2b365ceb..8c365cf0 100644
--- a/openstackclient/compute/v2/keypair.py
+++ b/openstackclient/compute/v2/keypair.py
@@ -20,11 +20,13 @@ import logging
import os
import sys
+from novaclient import api_versions
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
from openstackclient.i18n import _
+from openstackclient.identity import common as identity_common
LOG = logging.getLogger(__name__)
@@ -53,6 +55,15 @@ class CreateKeypair(command.ShowOne):
help=_("Filename for private key to save. If not used, "
"print private key in console.")
)
+ parser.add_argument(
+ '--type',
+ metavar='<type>',
+ choices=['ssh', 'x509'],
+ help=_(
+ "Keypair type. Can be ssh or x509. "
+ "(Supported by API versions '2.2' - '2.latest')"
+ ),
+ )
return parser
def take_action(self, parsed_args):
@@ -70,17 +81,28 @@ class CreateKeypair(command.ShowOne):
"exception": e}
)
- keypair = compute_client.keypairs.create(
- parsed_args.name,
- public_key=public_key,
- )
+ kwargs = {
+ 'name': parsed_args.name,
+ 'public_key': public_key,
+ }
+ if parsed_args.type:
+ if compute_client.api_version < api_versions.APIVersion('2.2'):
+ msg = _(
+ '--os-compute-api-version 2.2 or greater is required to '
+ 'support the --type option.'
+ )
+ raise exceptions.CommandError(msg)
+
+ kwargs['key_type'] = parsed_args.type
+
+ keypair = compute_client.keypairs.create(**kwargs)
private_key = parsed_args.private_key
# Save private key into specified file
if private_key:
try:
with io.open(
- os.path.expanduser(parsed_args.private_key), 'w+'
+ os.path.expanduser(parsed_args.private_key), 'w+'
) as p:
p.write(keypair.private_key)
except IOError as e:
@@ -142,18 +164,85 @@ class DeleteKeypair(command.Command):
class ListKeypair(command.Lister):
_description = _("List key fingerprints")
+ def get_parser(self, prog_name):
+ parser = super().get_parser(prog_name)
+ user_group = parser.add_mutually_exclusive_group()
+ user_group.add_argument(
+ '--user',
+ metavar='<user>',
+ help=_(
+ 'Show keypairs for another user (admin only) (name or ID). '
+ 'Requires ``--os-compute-api-version`` 2.10 or greater.'
+ ),
+ )
+ identity_common.add_user_domain_option_to_parser(parser)
+ user_group.add_argument(
+ '--project',
+ metavar='<project>',
+ help=_(
+ 'Show keypairs for all users associated with project '
+ '(admin only) (name or ID). '
+ 'Requires ``--os-compute-api-version`` 2.10 or greater.'
+ ),
+ )
+ identity_common.add_project_domain_option_to_parser(parser)
+ return parser
+
def take_action(self, parsed_args):
compute_client = self.app.client_manager.compute
+ identity_client = self.app.client_manager.identity
+
+ if parsed_args.project:
+ if compute_client.api_version < api_versions.APIVersion('2.10'):
+ msg = _(
+ '--os-compute-api-version 2.10 or greater is required to '
+ 'support the --project option'
+ )
+ raise exceptions.CommandError(msg)
+
+ # NOTE(stephenfin): This is done client side because nova doesn't
+ # currently support doing so server-side. If this is slow, we can
+ # think about spinning up a threadpool or similar.
+ project = identity_common.find_project(
+ identity_client,
+ parsed_args.project,
+ parsed_args.project_domain,
+ ).id
+ users = identity_client.users.list(tenant_id=project)
+
+ data = []
+ for user in users:
+ data.extend(compute_client.keypairs.list(user_id=user.id))
+ elif parsed_args.user:
+ if compute_client.api_version < api_versions.APIVersion('2.10'):
+ msg = _(
+ '--os-compute-api-version 2.10 or greater is required to '
+ 'support the --user option'
+ )
+ raise exceptions.CommandError(msg)
+
+ user = identity_common.find_user(
+ identity_client,
+ parsed_args.user,
+ parsed_args.user_domain,
+ )
+
+ data = compute_client.keypairs.list(user_id=user.id)
+ else:
+ data = compute_client.keypairs.list()
+
columns = (
"Name",
"Fingerprint"
)
- data = compute_client.keypairs.list()
- return (columns,
- (utils.get_item_properties(
- s, columns,
- ) for s in data))
+ if compute_client.api_version >= api_versions.APIVersion('2.2'):
+ columns += ("Type", )
+
+ return (
+ columns,
+ (utils.get_item_properties(s, columns) for s in data),
+ )
class ShowKeypair(command.ShowOne):
diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py
index dec6eb62..fddafaee 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):
@@ -510,20 +530,32 @@ class AddServerVolume(command.Command):
metavar='<device>',
help=_('Server internal device name for volume'),
)
+ parser.add_argument(
+ '--tag',
+ metavar='<tag>',
+ help=_(
+ "Tag for the attached volume. "
+ "(Supported by API versions '2.49' - '2.latest')"
+ ),
+ )
termination_group = parser.add_mutually_exclusive_group()
termination_group.add_argument(
'--enable-delete-on-termination',
action='store_true',
- help=_("Specify if the attached volume should be deleted when "
- "the server is destroyed. (Supported with "
- "``--os-compute-api-version`` 2.79 or greater.)"),
+ help=_(
+ "Specify if the attached volume should be deleted when the "
+ "server is destroyed. "
+ "(Supported by API versions '2.79' - '2.latest')"
+ ),
)
termination_group.add_argument(
'--disable-delete-on-termination',
action='store_true',
- help=_("Specify if the attached volume should not be deleted "
- "when the server is destroyed. (Supported with "
- "``--os-compute-api-version`` 2.79 or greater.)"),
+ help=_(
+ "Specify if the attached volume should not be deleted when "
+ "the server is destroyed. "
+ "(Supported by API versions '2.79' - '2.latest')"
+ ),
)
return parser
@@ -540,28 +572,38 @@ class AddServerVolume(command.Command):
parsed_args.volume,
)
- support_set_delete_on_termination = (compute_client.api_version >=
- api_versions.APIVersion('2.79'))
-
- if not support_set_delete_on_termination:
- if parsed_args.enable_delete_on_termination:
- msg = _('--os-compute-api-version 2.79 or greater '
- 'is required to support the '
- '--enable-delete-on-termination option.')
- raise exceptions.CommandError(msg)
- if parsed_args.disable_delete_on_termination:
- msg = _('--os-compute-api-version 2.79 or greater '
- 'is required to support the '
- '--disable-delete-on-termination option.')
- raise exceptions.CommandError(msg)
-
kwargs = {
"device": parsed_args.device
}
+ 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
+
if parsed_args.enable_delete_on_termination:
+ if compute_client.api_version < api_versions.APIVersion('2.79'):
+ msg = _(
+ '--os-compute-api-version 2.79 or greater is required to '
+ 'support the --enable-delete-on-termination option.'
+ )
+ raise exceptions.CommandError(msg)
+
kwargs['delete_on_termination'] = True
+
if parsed_args.disable_delete_on_termination:
+ if compute_client.api_version < api_versions.APIVersion('2.79'):
+ msg = _(
+ '--os-compute-api-version 2.79 or greater is required to '
+ 'support the --disable-delete-on-termination option.'
+ )
+ raise exceptions.CommandError(msg)
+
kwargs['delete_on_termination'] = False
compute_client.volumes.create_server_volume(
@@ -598,13 +640,20 @@ 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',
+ metavar='<password>',
+ help=_("Set the password to this server"),
)
parser.add_argument(
'--flavor',
@@ -617,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',
@@ -648,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',
@@ -659,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',
@@ -691,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',
@@ -729,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',
@@ -743,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',
@@ -801,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):
@@ -823,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()
@@ -843,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:
@@ -891,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)
@@ -912,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:
@@ -924,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 = [{
@@ -949,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':
@@ -981,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)
@@ -1002,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"]:
@@ -1034,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:
@@ -1105,6 +1220,7 @@ class CreateServer(command.ShowOne):
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,
@@ -1113,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)
@@ -1150,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
@@ -1380,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):
@@ -1435,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):
@@ -2803,6 +2979,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):
@@ -2841,6 +3029,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)")
@@ -3182,7 +3381,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)
@@ -3206,6 +3405,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):
@@ -3231,6 +3442,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/network/v2/security_group_rule.py b/openstackclient/network/v2/security_group_rule.py
index 1fbd97ab..a7703933 100644
--- a/openstackclient/network/v2/security_group_rule.py
+++ b/openstackclient/network/v2/security_group_rule.py
@@ -474,7 +474,7 @@ class ListSecurityGroupRule(common.NetworkAndComputeLister):
action='store_true',
default=False,
help=self.enhance_help_neutron(
- _("List additional fields in output"))
+ _("**Deprecated** This argument is no longer needed"))
)
return parser
@@ -504,15 +504,19 @@ class ListSecurityGroupRule(common.NetworkAndComputeLister):
'Ethertype',
'IP Range',
'Port Range',
+ 'Direction',
+ 'Remote Security Group',
)
- if parsed_args.long:
- column_headers = column_headers + ('Direction',)
- column_headers = column_headers + ('Remote Security Group',)
if parsed_args.group is None:
column_headers = column_headers + ('Security Group',)
return column_headers
def take_action_network(self, client, parsed_args):
+ if parsed_args.long:
+ self.log.warning(_(
+ "The --long option has been deprecated and is no longer needed"
+ ))
+
column_headers = self._get_column_headers(parsed_args)
columns = (
'id',
@@ -520,10 +524,9 @@ class ListSecurityGroupRule(common.NetworkAndComputeLister):
'ether_type',
'remote_ip_prefix',
'port_range',
+ 'direction',
+ 'remote_group_id',
)
- if parsed_args.long:
- columns = columns + ('direction',)
- columns = columns + ('remote_group_id',)
# Get the security group rules using the requested query.
query = {}
diff --git a/openstackclient/tests/functional/compute/v2/test_flavor.py b/openstackclient/tests/functional/compute/v2/test_flavor.py
index c274adf2..162d4287 100644
--- a/openstackclient/tests/functional/compute/v2/test_flavor.py
+++ b/openstackclient/tests/functional/compute/v2/test_flavor.py
@@ -115,8 +115,8 @@ class FlavorTests(base.TestCase):
self.assertFalse(
cmd_output["os-flavor-access:is_public"],
)
- self.assertEqual(
- "a='b2', b='d2'",
+ self.assertDictEqual(
+ {"a": "b2", "b": "d2"},
cmd_output["properties"],
)
@@ -133,12 +133,18 @@ class FlavorTests(base.TestCase):
"flavor list -f json " +
"--long"
))
- col_name = [x["Name"] for x in cmd_output]
- col_properties = [x['Properties'] for x in cmd_output]
- self.assertIn(name1, col_name)
- self.assertIn("a='b', c='d'", col_properties)
- self.assertNotIn(name2, col_name)
- self.assertNotIn("b2', b='d2'", col_properties)
+ # We have list of complex json objects
+ # Iterate through the list setting flags
+ found_expected = False
+ for rec in cmd_output:
+ if rec['Name'] == name1:
+ found_expected = True
+ self.assertEqual('b', rec['Properties']['a'])
+ self.assertEqual('d', rec['Properties']['c'])
+ elif rec['Name'] == name2:
+ # We should have not seen private flavor
+ self.assertFalse(True)
+ self.assertTrue(found_expected)
# Test list --public
cmd_output = json.loads(self.openstack(
@@ -201,8 +207,8 @@ class FlavorTests(base.TestCase):
self.assertFalse(
cmd_output["os-flavor-access:is_public"],
)
- self.assertEqual(
- "a='first', b='second'",
+ self.assertDictEqual(
+ {"a": "first", "b": "second"},
cmd_output["properties"],
)
@@ -223,9 +229,14 @@ class FlavorTests(base.TestCase):
cmd_output["id"],
)
self.assertEqual(
- "a='third and 10', b='second', g='fourth'",
- cmd_output['properties'],
- )
+ 'third and 10',
+ cmd_output['properties']['a'])
+ self.assertEqual(
+ 'second',
+ cmd_output['properties']['b'])
+ self.assertEqual(
+ 'fourth',
+ cmd_output['properties']['g'])
raw_output = self.openstack(
"flavor unset " +
@@ -238,7 +249,5 @@ class FlavorTests(base.TestCase):
"flavor show -f json " +
name1
))
- self.assertEqual(
- "a='third and 10', g='fourth'",
- cmd_output["properties"],
- )
+
+ self.assertNotIn('b', cmd_output['properties'])
diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py
index ce556fa6..3a06d271 100644
--- a/openstackclient/tests/unit/compute/v2/fakes.py
+++ b/openstackclient/tests/unit/compute/v2/fakes.py
@@ -884,6 +884,7 @@ class FakeKeypair(object):
# Set default attributes.
keypair_info = {
'name': 'keypair-name-' + uuid.uuid4().hex,
+ 'type': 'ssh',
'fingerprint': 'dummy',
'public_key': 'dummy',
'user_id': 'user'
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_flavor.py b/openstackclient/tests/unit/compute/v2/test_flavor.py
index 4732cc82..2828d74e 100644
--- a/openstackclient/tests/unit/compute/v2/test_flavor.py
+++ b/openstackclient/tests/unit/compute/v2/test_flavor.py
@@ -17,8 +17,8 @@ from unittest import mock
from unittest.mock import call
import novaclient
+from osc_lib.cli import format_columns
from osc_lib import exceptions
-from osc_lib import utils
from openstackclient.compute.v2 import flavor
from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes
@@ -70,7 +70,7 @@ class TestFlavorCreate(TestFlavor):
flavor.id,
flavor.name,
flavor.is_public,
- utils.format_dict(flavor.properties),
+ format_columns.DictColumn(flavor.properties),
flavor.ram,
flavor.rxtx_factor,
flavor.swap,
@@ -111,7 +111,7 @@ class TestFlavorCreate(TestFlavor):
self.flavors_mock.create.assert_called_once_with(*default_args)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_flavor_create_all_options(self):
@@ -165,7 +165,7 @@ class TestFlavorCreate(TestFlavor):
self.flavor.get_keys.assert_called_once_with()
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_flavor_create_other_options(self):
@@ -226,7 +226,7 @@ class TestFlavorCreate(TestFlavor):
{'key1': 'value1', 'key2': 'value2'})
self.flavor.get_keys.assert_called_with()
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_public_flavor_create_with_project(self):
arglist = [
@@ -300,7 +300,7 @@ class TestFlavorCreate(TestFlavor):
self.flavors_mock.create.assert_called_once_with(*args)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_flavor_create_with_description_api_older(self):
arglist = [
@@ -429,7 +429,7 @@ class TestFlavorList(TestFlavor):
data_long = (data[0] + (
flavors[0].swap,
flavors[0].rxtx_factor,
- u'property=\'value\''
+ format_columns.DictColumn(flavors[0].properties)
), )
def setUp(self):
@@ -583,7 +583,7 @@ class TestFlavorList(TestFlavor):
)
self.assertEqual(self.columns_long, columns)
- self.assertEqual(tuple(self.data_long), tuple(data))
+ self.assertListItemEqual(self.data_long, tuple(data))
class TestFlavorSet(TestFlavor):
@@ -817,7 +817,7 @@ class TestFlavorShow(TestFlavor):
flavor.id,
flavor.name,
flavor.is_public,
- utils.format_dict(flavor.get_keys()),
+ format_columns.DictColumn(flavor.get_keys()),
flavor.ram,
flavor.rxtx_factor,
flavor.swap,
@@ -854,7 +854,7 @@ class TestFlavorShow(TestFlavor):
columns, data = self.cmd.take_action(parsed_args)
self.assertEqual(self.columns, columns)
- self.assertEqual(self.data, data)
+ self.assertItemEqual(self.data, data)
def test_private_flavor_show(self):
private_flavor = compute_fakes.FakeFlavor.create_one_flavor(
@@ -874,13 +874,13 @@ class TestFlavorShow(TestFlavor):
data_with_project = (
private_flavor.disabled,
private_flavor.ephemeral,
- self.flavor_access.tenant_id,
+ [self.flavor_access.tenant_id],
private_flavor.description,
private_flavor.disk,
private_flavor.id,
private_flavor.name,
private_flavor.is_public,
- utils.format_dict(private_flavor.get_keys()),
+ format_columns.DictColumn(private_flavor.get_keys()),
private_flavor.ram,
private_flavor.rxtx_factor,
private_flavor.swap,
@@ -894,7 +894,7 @@ class TestFlavorShow(TestFlavor):
self.flavor_access_mock.list.assert_called_with(
flavor=private_flavor.id)
self.assertEqual(self.columns, columns)
- self.assertEqual(data_with_project, data)
+ self.assertItemEqual(data_with_project, data)
class TestFlavorUnset(TestFlavor):
diff --git a/openstackclient/tests/unit/compute/v2/test_keypair.py b/openstackclient/tests/unit/compute/v2/test_keypair.py
index 1f3f56f9..286cbb09 100644
--- a/openstackclient/tests/unit/compute/v2/test_keypair.py
+++ b/openstackclient/tests/unit/compute/v2/test_keypair.py
@@ -13,15 +13,19 @@
# under the License.
#
+import copy
from unittest import mock
from unittest.mock import call
import uuid
+from novaclient import api_versions
from osc_lib import exceptions
from osc_lib import utils
from openstackclient.compute.v2 import keypair
from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes
+from openstackclient.tests.unit import fakes
+from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes
from openstackclient.tests.unit import utils as tests_utils
@@ -45,11 +49,13 @@ class TestKeypairCreate(TestKeypair):
self.columns = (
'fingerprint',
'name',
+ 'type',
'user_id'
)
self.data = (
self.keypair.fingerprint,
self.keypair.name,
+ self.keypair.type,
self.keypair.user_id
)
@@ -71,7 +77,7 @@ class TestKeypairCreate(TestKeypair):
columns, data = self.cmd.take_action(parsed_args)
self.keypairs_mock.create.assert_called_with(
- self.keypair.name,
+ name=self.keypair.name,
public_key=None
)
@@ -87,6 +93,7 @@ class TestKeypairCreate(TestKeypair):
self.data = (
self.keypair.fingerprint,
self.keypair.name,
+ self.keypair.type,
self.keypair.user_id
)
@@ -96,7 +103,7 @@ class TestKeypairCreate(TestKeypair):
]
verifylist = [
('public_key', self.keypair.public_key),
- ('name', self.keypair.name)
+ ('name', self.keypair.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -109,8 +116,8 @@ class TestKeypairCreate(TestKeypair):
columns, data = self.cmd.take_action(parsed_args)
self.keypairs_mock.create.assert_called_with(
- self.keypair.name,
- public_key=self.keypair.public_key
+ name=self.keypair.name,
+ public_key=self.keypair.public_key,
)
self.assertEqual(self.columns, columns)
@@ -124,7 +131,7 @@ class TestKeypairCreate(TestKeypair):
]
verifylist = [
('private_key', tmp_pk_file),
- ('name', self.keypair.name)
+ ('name', self.keypair.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -136,8 +143,8 @@ class TestKeypairCreate(TestKeypair):
columns, data = self.cmd.take_action(parsed_args)
self.keypairs_mock.create.assert_called_with(
- self.keypair.name,
- public_key=None
+ name=self.keypair.name,
+ public_key=None,
)
mock_open.assert_called_once_with(tmp_pk_file, 'w+')
@@ -146,6 +153,79 @@ class TestKeypairCreate(TestKeypair):
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
+ def test_keypair_create_with_key_type(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.2')
+
+ for key_type in ['x509', 'ssh']:
+ self.keypair = compute_fakes.FakeKeypair.create_one_keypair(
+ no_pri=True)
+ self.keypairs_mock.create.return_value = self.keypair
+
+ self.data = (
+ self.keypair.fingerprint,
+ self.keypair.name,
+ self.keypair.type,
+ self.keypair.user_id,
+ )
+ arglist = [
+ '--public-key', self.keypair.public_key,
+ self.keypair.name,
+ '--type', key_type,
+ ]
+ verifylist = [
+ ('public_key', self.keypair.public_key),
+ ('name', self.keypair.name),
+ ('type', key_type),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ with mock.patch('io.open') as mock_open:
+ mock_open.return_value = mock.MagicMock()
+ m_file = mock_open.return_value.__enter__.return_value
+ m_file.read.return_value = 'dummy'
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.keypairs_mock.create.assert_called_with(
+ name=self.keypair.name,
+ public_key=self.keypair.public_key,
+ key_type=key_type,
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
+ def test_keypair_create_with_key_type_pre_v22(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.1')
+
+ for key_type in ['x509', 'ssh']:
+ arglist = [
+ '--public-key', self.keypair.public_key,
+ self.keypair.name,
+ '--type', 'ssh',
+ ]
+ verifylist = [
+ ('public_key', self.keypair.public_key),
+ ('name', self.keypair.name),
+ ('type', 'ssh'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ with mock.patch('io.open') as mock_open:
+ mock_open.return_value = mock.MagicMock()
+ m_file = mock_open.return_value.__enter__.return_value
+ m_file.read.return_value = 'dummy'
+
+ ex = self.assertRaises(
+ exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args)
+
+ self.assertIn(
+ '--os-compute-api-version 2.2 or greater is required',
+ str(ex))
+
class TestKeypairDelete(TestKeypair):
@@ -227,16 +307,6 @@ class TestKeypairList(TestKeypair):
# Return value of self.keypairs_mock.list().
keypairs = compute_fakes.FakeKeypair.create_keypairs(count=1)
- columns = (
- "Name",
- "Fingerprint"
- )
-
- data = ((
- keypairs[0].name,
- keypairs[0].fingerprint
- ), )
-
def setUp(self):
super(TestKeypairList, self).setUp()
@@ -247,8 +317,7 @@ class TestKeypairList(TestKeypair):
def test_keypair_list_no_options(self):
arglist = []
- verifylist = [
- ]
+ verifylist = []
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -261,8 +330,175 @@ class TestKeypairList(TestKeypair):
self.keypairs_mock.list.assert_called_with()
- self.assertEqual(self.columns, columns)
- self.assertEqual(tuple(self.data), tuple(data))
+ self.assertEqual(('Name', 'Fingerprint'), columns)
+ self.assertEqual(
+ ((self.keypairs[0].name, self.keypairs[0].fingerprint), ),
+ tuple(data)
+ )
+
+ def test_keypair_list_v22(self):
+ self.app.client_manager.compute.api_version = \
+ api_versions.APIVersion('2.2')
+
+ arglist = []
+ verifylist = []
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class Lister in cliff, abstract method take_action()
+ # returns a tuple containing the column names and an iterable
+ # containing the data to be listed.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+
+ self.keypairs_mock.list.assert_called_with()
+
+ self.assertEqual(('Name', 'Fingerprint', 'Type'), columns)
+ self.assertEqual(
+ ((
+ self.keypairs[0].name,
+ self.keypairs[0].fingerprint,
+ self.keypairs[0].type,
+ ), ),
+ tuple(data)
+ )
+
+ def test_keypair_list_with_user(self):
+
+ # Filtering by user is support for nova api 2.10 or above
+ self.app.client_manager.compute.api_version = \
+ api_versions.APIVersion('2.10')
+
+ users_mock = self.app.client_manager.identity.users
+ users_mock.reset_mock()
+ users_mock.get.return_value = fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.USER),
+ loaded=True,
+ )
+
+ arglist = [
+ '--user', identity_fakes.user_name,
+ ]
+ verifylist = [
+ ('user', identity_fakes.user_name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ users_mock.get.assert_called_with(identity_fakes.user_name)
+ self.keypairs_mock.list.assert_called_with(
+ user_id=identity_fakes.user_id,
+ )
+
+ self.assertEqual(('Name', 'Fingerprint', 'Type'), columns)
+ self.assertEqual(
+ ((
+ self.keypairs[0].name,
+ self.keypairs[0].fingerprint,
+ self.keypairs[0].type,
+ ), ),
+ tuple(data)
+ )
+
+ def test_keypair_list_with_user_pre_v210(self):
+
+ self.app.client_manager.compute.api_version = \
+ api_versions.APIVersion('2.9')
+
+ arglist = [
+ '--user', identity_fakes.user_name,
+ ]
+ verifylist = [
+ ('user', identity_fakes.user_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.10 or greater is required', str(ex))
+
+ def test_keypair_list_with_project(self):
+
+ # Filtering by user is support for nova api 2.10 or above
+ self.app.client_manager.compute.api_version = \
+ api_versions.APIVersion('2.10')
+
+ projects_mock = self.app.client_manager.identity.tenants
+ projects_mock.reset_mock()
+ projects_mock.get.return_value = fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.PROJECT),
+ loaded=True,
+ )
+
+ users_mock = self.app.client_manager.identity.users
+ users_mock.reset_mock()
+ users_mock.list.return_value = [
+ fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.USER),
+ loaded=True,
+ ),
+ ]
+
+ arglist = ['--project', identity_fakes.project_name]
+ verifylist = [('project', identity_fakes.project_name)]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ projects_mock.get.assert_called_with(identity_fakes.project_name)
+ users_mock.list.assert_called_with(tenant_id=identity_fakes.project_id)
+ self.keypairs_mock.list.assert_called_with(
+ user_id=identity_fakes.user_id,
+ )
+
+ self.assertEqual(('Name', 'Fingerprint', 'Type'), columns)
+ self.assertEqual(
+ ((
+ self.keypairs[0].name,
+ self.keypairs[0].fingerprint,
+ self.keypairs[0].type,
+ ), ),
+ tuple(data)
+ )
+
+ def test_keypair_list_with_project_pre_v210(self):
+
+ self.app.client_manager.compute.api_version = \
+ api_versions.APIVersion('2.9')
+
+ arglist = ['--project', identity_fakes.project_name]
+ verifylist = [('project', identity_fakes.project_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.10 or greater is required', str(ex))
+
+ def test_keypair_list_conflicting_user_options(self):
+
+ # Filtering by user is support for nova api 2.10 or above
+ self.app.client_manager.compute.api_version = \
+ api_versions.APIVersion('2.10')
+
+ arglist = [
+ '--user', identity_fakes.user_name,
+ '--project', identity_fakes.project_name,
+ ]
+
+ self.assertRaises(
+ tests_utils.ParserException,
+ self.check_parser, self.cmd, arglist, None)
class TestKeypairShow(TestKeypair):
@@ -279,16 +515,18 @@ class TestKeypairShow(TestKeypair):
self.columns = (
"fingerprint",
"name",
+ "type",
"user_id"
)
self.data = (
self.keypair.fingerprint,
self.keypair.name,
+ self.keypair.type,
self.keypair.user_id
)
- def test_show_no_options(self):
+ def test_keypair_show_no_options(self):
arglist = []
verifylist = []
@@ -306,6 +544,7 @@ class TestKeypairShow(TestKeypair):
self.data = (
self.keypair.fingerprint,
self.keypair.name,
+ self.keypair.type,
self.keypair.user_id
)
diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py
index 51a3beb5..15cdbf16 100644
--- a/openstackclient/tests/unit/compute/v2/test_server.py
+++ b/openstackclient/tests/unit/compute/v2/test_server.py
@@ -186,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'
@@ -596,8 +662,57 @@ class TestServerVolume(TestServer):
servers[0].id, self.volume.id, device='/dev/sdb')
self.assertIsNone(result)
+ def test_server_add_volume_with_tag(self):
+ # requires API 2.49 or later
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.49')
+
+ servers = self.setup_servers_mock(count=1)
+ arglist = [
+ '--device', '/dev/sdb',
+ '--tag', 'foo',
+ servers[0].id,
+ self.volume.id,
+ ]
+ verifylist = [
+ ('server', servers[0].id),
+ ('volume', self.volume.id),
+ ('device', '/dev/sdb'),
+ ('tag', 'foo'),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ self.servers_volumes_mock.create_server_volume.assert_called_once_with(
+ servers[0].id, self.volume.id, device='/dev/sdb', tag='foo')
+ self.assertIsNone(result)
+
+ def test_server_add_volume_with_tag_pre_v249(self):
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.48')
-class TestServerVolumeV279(TestServerVolume):
+ servers = self.setup_servers_mock(count=1)
+ arglist = [
+ servers[0].id,
+ self.volume.id,
+ '--tag', 'foo',
+ ]
+ verifylist = [
+ ('server', servers[0].id),
+ ('volume', self.volume.id),
+ ('tag', 'foo'),
+ ]
+ 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))
def test_server_add_volume_with_enable_delete_on_termination(self):
self.app.client_manager.compute.api_version = api_versions.APIVersion(
@@ -654,7 +769,8 @@ class TestServerVolumeV279(TestServerVolume):
self.assertIsNone(result)
def test_server_add_volume_with_enable_delete_on_termination_pre_v279(
- self):
+ self,
+ ):
self.app.client_manager.compute.api_version = api_versions.APIVersion(
'2.78')
@@ -678,7 +794,8 @@ class TestServerVolumeV279(TestServerVolume):
str(ex))
def test_server_add_volume_with_disable_delete_on_termination_pre_v279(
- self):
+ self,
+ ):
self.app.client_manager.compute.api_version = api_versions.APIVersion(
'2.78')
@@ -702,7 +819,8 @@ class TestServerVolumeV279(TestServerVolume):
str(ex))
def test_server_add_volume_with_disable_and_enable_delete_on_termination(
- self):
+ self,
+ ):
self.app.client_manager.compute.api_version = api_versions.APIVersion(
'2.79')
@@ -981,6 +1099,7 @@ class TestServerCreate(TestServer):
userdata=None,
key_name=None,
availability_zone=None,
+ admin_pass=None,
block_device_mapping_v2=[],
nics=[],
scheduler_hints={},
@@ -1007,6 +1126,7 @@ class TestServerCreate(TestServer):
'--property', 'Beta=b',
'--security-group', 'securitygroup',
'--use-config-drive',
+ '--password', 'passw0rd',
'--hint', 'a=b',
'--hint', 'a=c',
self.new_server.name,
@@ -1019,6 +1139,7 @@ class TestServerCreate(TestServer):
('security_group', ['securitygroup']),
('hint', {'a': ['b', 'c']}),
('config_drive', True),
+ ('password', 'passw0rd'),
('server_name', self.new_server.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -1047,6 +1168,7 @@ class TestServerCreate(TestServer):
userdata=None,
key_name='keyname',
availability_zone=None,
+ admin_pass='passw0rd',
block_device_mapping_v2=[],
nics=[],
scheduler_hints={'a': ['b', 'c']},
@@ -1133,6 +1255,7 @@ class TestServerCreate(TestServer):
userdata=None,
key_name='keyname',
availability_zone=None,
+ admin_pass=None,
block_device_mapping_v2=[],
nics=[],
scheduler_hints={},
@@ -1219,6 +1342,7 @@ class TestServerCreate(TestServer):
userdata=None,
key_name=None,
availability_zone=None,
+ admin_pass=None,
block_device_mapping_v2=[],
nics=[{'net-id': 'net1_uuid',
'v4-fixed-ip': '',
@@ -1283,6 +1407,7 @@ class TestServerCreate(TestServer):
userdata=None,
key_name=None,
availability_zone=None,
+ admin_pass=None,
block_device_mapping_v2=[],
nics='auto',
scheduler_hints={},
@@ -1332,6 +1457,7 @@ class TestServerCreate(TestServer):
userdata=None,
key_name=None,
availability_zone=None,
+ admin_pass=None,
block_device_mapping_v2=[],
nics='auto',
scheduler_hints={},
@@ -1377,6 +1503,7 @@ class TestServerCreate(TestServer):
userdata=None,
key_name=None,
availability_zone=None,
+ admin_pass=None,
block_device_mapping_v2=[],
nics='none',
scheduler_hints={},
@@ -1542,6 +1669,7 @@ class TestServerCreate(TestServer):
userdata=None,
key_name=None,
availability_zone=None,
+ admin_pass=None,
block_device_mapping_v2=[],
nics=[],
scheduler_hints={},
@@ -1592,6 +1720,7 @@ class TestServerCreate(TestServer):
userdata=None,
key_name=None,
availability_zone=None,
+ admin_pass=None,
block_device_mapping_v2=[],
nics=[],
scheduler_hints={},
@@ -1647,6 +1776,7 @@ class TestServerCreate(TestServer):
userdata=mock_file,
key_name=None,
availability_zone=None,
+ admin_pass=None,
block_device_mapping_v2=[],
nics=[],
scheduler_hints={},
@@ -1693,6 +1823,7 @@ class TestServerCreate(TestServer):
userdata=None,
key_name=None,
availability_zone=None,
+ admin_pass=None,
block_device_mapping_v2=[{
'device_name': 'vda',
'uuid': self.volume.id,
@@ -1745,6 +1876,7 @@ class TestServerCreate(TestServer):
userdata=None,
key_name=None,
availability_zone=None,
+ admin_pass=None,
block_device_mapping_v2=[{
'device_name': 'vdf',
'uuid': self.volume.id,
@@ -1796,6 +1928,7 @@ class TestServerCreate(TestServer):
userdata=None,
key_name=None,
availability_zone=None,
+ admin_pass=None,
block_device_mapping_v2=[{
'device_name': 'vdf',
'uuid': self.volume.id,
@@ -1849,6 +1982,7 @@ class TestServerCreate(TestServer):
userdata=None,
key_name=None,
availability_zone=None,
+ admin_pass=None,
block_device_mapping_v2=[{
'device_name': 'vde',
'uuid': self.volume.id,
@@ -1904,6 +2038,7 @@ class TestServerCreate(TestServer):
userdata=None,
key_name=None,
availability_zone=None,
+ admin_pass=None,
block_device_mapping_v2=[{
'device_name': 'vds',
'uuid': self.snapshot.id,
@@ -1959,6 +2094,7 @@ class TestServerCreate(TestServer):
userdata=None,
key_name=None,
availability_zone=None,
+ admin_pass=None,
block_device_mapping_v2=[
{
'device_name': 'vdb',
@@ -2095,6 +2231,7 @@ class TestServerCreate(TestServer):
userdata=None,
key_name=None,
availability_zone=None,
+ admin_pass=None,
block_device_mapping_v2=[],
nics='none',
meta=None,
@@ -2150,6 +2287,7 @@ class TestServerCreate(TestServer):
userdata=None,
key_name=None,
availability_zone=None,
+ admin_pass=None,
block_device_mapping_v2=[],
nics='none',
meta=None,
@@ -2239,6 +2377,7 @@ class TestServerCreate(TestServer):
userdata=None,
key_name=None,
availability_zone=None,
+ admin_pass=None,
block_device_mapping_v2=[],
nics='none',
meta=None,
@@ -2319,6 +2458,7 @@ class TestServerCreate(TestServer):
userdata=None,
key_name=None,
availability_zone=None,
+ admin_pass=None,
block_device_mapping_v2=[],
nics='auto',
scheduler_hints={},
@@ -2364,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
@@ -2403,6 +2624,7 @@ class TestServerCreate(TestServer):
userdata=None,
key_name=None,
availability_zone=None,
+ admin_pass=None,
block_device_mapping_v2=[],
nics='auto',
scheduler_hints={},
@@ -2488,6 +2710,7 @@ class TestServerCreate(TestServer):
userdata=None,
key_name=None,
availability_zone=None,
+ admin_pass=None,
block_device_mapping_v2=[],
nics='auto',
scheduler_hints={},
@@ -2575,6 +2798,7 @@ class TestServerCreate(TestServer):
userdata=None,
key_name=None,
availability_zone=None,
+ admin_pass=None,
block_device_mapping_v2=[],
nics='auto',
scheduler_hints={},
@@ -3133,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)
@@ -3225,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):
@@ -5332,6 +5643,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)
@@ -5472,6 +5785,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):
@@ -5797,6 +6154,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
diff --git a/openstackclient/tests/unit/network/v2/test_security_group_rule_compute.py b/openstackclient/tests/unit/network/v2/test_security_group_rule_compute.py
index 5720e305..b7e38afb 100644
--- a/openstackclient/tests/unit/network/v2/test_security_group_rule_compute.py
+++ b/openstackclient/tests/unit/network/v2/test_security_group_rule_compute.py
@@ -362,6 +362,7 @@ class TestListSecurityGroupRuleCompute(TestSecurityGroupRuleCompute):
'Ethertype',
'IP Range',
'Port Range',
+ 'Direction',
'Remote Security Group',
)
expected_columns_no_group = \
diff --git a/openstackclient/tests/unit/network/v2/test_security_group_rule_network.py b/openstackclient/tests/unit/network/v2/test_security_group_rule_network.py
index 0a9522b0..01411611 100644
--- a/openstackclient/tests/unit/network/v2/test_security_group_rule_network.py
+++ b/openstackclient/tests/unit/network/v2/test_security_group_rule_network.py
@@ -870,7 +870,7 @@ class TestListSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
_security_group_rules = [_security_group_rule_tcp,
_security_group_rule_icmp]
- expected_columns_with_group_and_long = (
+ expected_columns_with_group = (
'ID',
'IP Protocol',
'Ethertype',
@@ -885,14 +885,15 @@ class TestListSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
'Ethertype',
'IP Range',
'Port Range',
+ 'Direction',
'Remote Security Group',
'Security Group',
)
- expected_data_with_group_and_long = []
+ expected_data_with_group = []
expected_data_no_group = []
for _security_group_rule in _security_group_rules:
- expected_data_with_group_and_long.append((
+ expected_data_with_group.append((
_security_group_rule.id,
_security_group_rule.protocol,
_security_group_rule.ether_type,
@@ -909,6 +910,7 @@ class TestListSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
_security_group_rule.remote_ip_prefix,
security_group_rule._format_network_port_range(
_security_group_rule),
+ _security_group_rule.direction,
_security_group_rule.remote_group_id,
_security_group_rule.security_group_id,
))
@@ -935,14 +937,12 @@ class TestListSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
self.assertEqual(self.expected_columns_no_group, columns)
self.assertEqual(self.expected_data_no_group, list(data))
- def test_list_with_group_and_long(self):
+ def test_list_with_group(self):
self._security_group_rule_tcp.port_range_min = 80
arglist = [
- '--long',
self._security_group.id,
]
verifylist = [
- ('long', True),
('group', self._security_group.id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -952,8 +952,8 @@ class TestListSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
self.network.security_group_rules.assert_called_once_with(**{
'security_group_id': self._security_group.id,
})
- self.assertEqual(self.expected_columns_with_group_and_long, columns)
- self.assertEqual(self.expected_data_with_group_and_long, list(data))
+ self.assertEqual(self.expected_columns_with_group, columns)
+ self.assertEqual(self.expected_data_with_group, list(data))
def test_list_with_ignored_options(self):
self._security_group_rule_tcp.port_range_min = 80