summaryrefslogtreecommitdiff
path: root/openstackclient
diff options
context:
space:
mode:
Diffstat (limited to 'openstackclient')
-rw-r--r--openstackclient/__init__.py4
-rw-r--r--openstackclient/api/compute_v2.py156
-rw-r--r--openstackclient/common/clientmanager.py19
-rw-r--r--openstackclient/common/extension.py6
-rw-r--r--openstackclient/common/limits.py32
-rw-r--r--openstackclient/compute/v2/console.py7
-rw-r--r--openstackclient/compute/v2/host.py18
-rw-r--r--openstackclient/compute/v2/server.py225
-rw-r--r--openstackclient/compute/v2/server_backup.py16
-rw-r--r--openstackclient/compute/v2/server_image.py15
-rw-r--r--openstackclient/compute/v2/service.py26
-rw-r--r--openstackclient/compute/v2/usage.py8
-rw-r--r--openstackclient/identity/common.py7
-rw-r--r--openstackclient/identity/v3/application_credential.py220
-rw-r--r--openstackclient/identity/v3/endpoint_group.py324
-rw-r--r--openstackclient/identity/v3/group.py5
-rw-r--r--openstackclient/identity/v3/implied_role.py6
-rw-r--r--openstackclient/identity/v3/project.py9
-rw-r--r--openstackclient/identity/v3/role.py41
-rw-r--r--openstackclient/identity/v3/role_assignment.py35
-rw-r--r--openstackclient/identity/v3/tag.py116
-rw-r--r--openstackclient/identity/v3/token.py6
-rw-r--r--openstackclient/image/v1/image.py10
-rw-r--r--openstackclient/image/v2/image.py36
-rw-r--r--openstackclient/locale/tr_TR/LC_MESSAGES/openstackclient.po50
-rw-r--r--openstackclient/network/common.py5
-rw-r--r--openstackclient/network/v2/floating_ip.py123
-rw-r--r--openstackclient/network/v2/floating_ip_pool.py25
-rw-r--r--openstackclient/network/v2/network.py14
-rw-r--r--openstackclient/network/v2/network_qos_rule.py8
-rw-r--r--openstackclient/network/v2/port.py8
-rw-r--r--openstackclient/network/v2/router.py4
-rw-r--r--openstackclient/network/v2/subnet.py18
-rw-r--r--openstackclient/tests/functional/common/test_help.py8
-rw-r--r--openstackclient/tests/functional/common/test_module.py2
-rw-r--r--openstackclient/tests/functional/identity/v3/common.py13
-rw-r--r--openstackclient/tests/functional/identity/v3/test_application_credential.py143
-rw-r--r--openstackclient/tests/functional/identity/v3/test_role.py25
-rw-r--r--openstackclient/tests/functional/network/v2/test_network.py70
-rw-r--r--openstackclient/tests/functional/network/v2/test_network_agent.py45
-rw-r--r--openstackclient/tests/functional/network/v2/test_network_flavor.py6
-rw-r--r--openstackclient/tests/functional/network/v2/test_network_qos_rule.py153
-rw-r--r--openstackclient/tests/functional/network/v2/test_network_segment.py17
-rw-r--r--openstackclient/tests/functional/network/v2/test_port.py90
-rw-r--r--openstackclient/tests/functional/object/v1/test_object.py32
-rw-r--r--openstackclient/tests/functional/volume/v1/test_volume_type.py119
-rw-r--r--openstackclient/tests/functional/volume/v2/test_qos.py5
-rw-r--r--openstackclient/tests/functional/volume/v2/test_volume_type.py136
-rw-r--r--openstackclient/tests/unit/api/test_compute_v2.py176
-rw-r--r--openstackclient/tests/unit/common/test_limits.py125
-rw-r--r--openstackclient/tests/unit/compute/v2/fakes.py115
-rw-r--r--openstackclient/tests/unit/compute/v2/test_host.py85
-rw-r--r--openstackclient/tests/unit/compute/v2/test_server.py433
-rw-r--r--openstackclient/tests/unit/compute/v2/test_service.py10
-rw-r--r--openstackclient/tests/unit/fakes.py8
-rw-r--r--openstackclient/tests/unit/identity/v3/fakes.py57
-rw-r--r--openstackclient/tests/unit/identity/v3/test_application_credential.py309
-rw-r--r--openstackclient/tests/unit/identity/v3/test_domain.py5
-rw-r--r--openstackclient/tests/unit/identity/v3/test_implied_role.py32
-rw-r--r--openstackclient/tests/unit/identity/v3/test_project.py103
-rw-r--r--openstackclient/tests/unit/identity/v3/test_role_assignment.py42
-rw-r--r--openstackclient/tests/unit/image/v2/test_image.py46
-rw-r--r--openstackclient/tests/unit/network/v2/fakes.py4
-rw-r--r--openstackclient/tests/unit/network/v2/test_floating_ip_network.py136
-rw-r--r--openstackclient/tests/unit/network/v2/test_network.py9
-rw-r--r--openstackclient/tests/unit/network/v2/test_network_qos_rule.py55
-rw-r--r--openstackclient/tests/unit/network/v2/test_port.py5
-rw-r--r--openstackclient/tests/unit/network/v2/test_subnet.py53
-rw-r--r--openstackclient/tests/unit/volume/v2/fakes.py100
-rw-r--r--openstackclient/volume/v1/volume_type.py8
-rw-r--r--openstackclient/volume/v2/volume_type.py8
71 files changed, 3636 insertions, 754 deletions
diff --git a/openstackclient/__init__.py b/openstackclient/__init__.py
index 89deee32..e6e7f7c0 100644
--- a/openstackclient/__init__.py
+++ b/openstackclient/__init__.py
@@ -11,10 +11,10 @@
# under the License.
#
-__all__ = ['__version__']
-
import pbr.version
+__all__ = ['__version__']
+
version_info = pbr.version.VersionInfo('python-openstackclient')
try:
__version__ = version_info.version_string()
diff --git a/openstackclient/api/compute_v2.py b/openstackclient/api/compute_v2.py
index 0ffed655..0c89e912 100644
--- a/openstackclient/api/compute_v2.py
+++ b/openstackclient/api/compute_v2.py
@@ -93,7 +93,51 @@ class APIv2(api.BaseAPI):
return ret
- # Flaoting IPs
+ # Floating IPs
+
+ def floating_ip_add(
+ self,
+ server,
+ address,
+ fixed_address=None,
+ ):
+ """Add a floating IP to a server
+
+ :param server:
+ The :class:`Server` (or its ID) to add an IP to.
+ :param address:
+ The FloatingIP or string floating address to add.
+ :param fixed_address:
+ The FixedIP the floatingIP should be associated with (optional)
+ """
+
+ url = '/servers'
+
+ server = self.find(
+ url,
+ attr='name',
+ value=server,
+ )
+
+ address = address.ip if hasattr(address, 'ip') else address
+ if fixed_address:
+ if hasattr(fixed_address, 'ip'):
+ fixed_address = fixed_address.ip
+
+ body = {
+ 'address': address,
+ 'fixed_address': fixed_address,
+ }
+ else:
+ body = {
+ 'address': address,
+ }
+
+ return self._request(
+ "POST",
+ "/%s/%s/action" % (url, server['id']),
+ json={'addFloatingIp': body},
+ )
def floating_ip_create(
self,
@@ -175,6 +219,38 @@ class APIv2(api.BaseAPI):
return self.list(url)["floating_ips"]
+ def floating_ip_remove(
+ self,
+ server,
+ address,
+ ):
+ """Remove a floating IP from a server
+
+ :param server:
+ The :class:`Server` (or its ID) to add an IP to.
+ :param address:
+ The FloatingIP or string floating address to add.
+ """
+
+ url = '/servers'
+
+ server = self.find(
+ url,
+ attr='name',
+ value=server,
+ )
+
+ address = address.ip if hasattr(address, 'ip') else address
+ body = {
+ 'address': address,
+ }
+
+ return self._request(
+ "POST",
+ "/%s/%s/action" % (url, server['id']),
+ json={'removeFloatingIp': body},
+ )
+
# Floating IP Pools
def floating_ip_pool_list(
@@ -192,6 +268,84 @@ class APIv2(api.BaseAPI):
return self.list(url)["floating_ip_pools"]
+ # Hosts
+
+ def host_list(
+ self,
+ zone=None,
+ ):
+ """Lists hypervisor Hosts
+
+ https://developer.openstack.org/api-ref/compute/#list-hosts
+ Valid for Compute 2.0 - 2.42
+
+ :param string zone:
+ Availability zone
+ :returns: A dict of the floating IP attributes
+ """
+
+ url = "/os-hosts"
+ if zone:
+ url = '/os-hosts?zone=%s' % zone
+
+ return self.list(url)["hosts"]
+
+ def host_set(
+ self,
+ host=None,
+ status=None,
+ maintenance_mode=None,
+ **params
+ ):
+ """Modify host properties
+
+ https://developer.openstack.org/api-ref/compute/#update-host-status
+ Valid for Compute 2.0 - 2.42
+
+ status
+ maintenance_mode
+ """
+
+ url = "/os-hosts"
+
+ params = {}
+ if status:
+ params['status'] = status
+ if maintenance_mode:
+ params['maintenance_mode'] = maintenance_mode
+ if params == {}:
+ # Don't bother calling if nothing given
+ return None
+ else:
+ return self._request(
+ "PUT",
+ "/%s/%s" % (url, host),
+ json=params,
+ ).json()
+
+ def host_show(
+ self,
+ host=None,
+ ):
+ """Show host
+
+ https://developer.openstack.org/api-ref/compute/#show-host-details
+ Valid for Compute 2.0 - 2.42
+ """
+
+ url = "/os-hosts"
+
+ r_host = self.find(
+ url,
+ attr='host_name',
+ value=host,
+ )
+
+ data = []
+ for h in r_host:
+ data.append(h['resource'])
+ return data
+
# Networks
def network_create(
diff --git a/openstackclient/common/clientmanager.py b/openstackclient/common/clientmanager.py
index 89781052..aa1045e4 100644
--- a/openstackclient/common/clientmanager.py
+++ b/openstackclient/common/clientmanager.py
@@ -125,6 +125,25 @@ class ClientManager(clientmanager.ClientManager):
# use Network API by default
return self.is_service_available('network') is not False
+ def is_compute_endpoint_enabled(self):
+ """Check if Compute endpoint is enabled"""
+
+ return self.is_service_available('compute') is not False
+
+ def is_volume_endpoint_enabled(self, volume_client):
+ """Check if volume endpoint is enabled"""
+ # NOTE(jcross): Cinder did some interesting things with their service
+ # name so we need to figure out which version to look
+ # for when calling is_service_available()
+ volume_version = volume_client.api_version.ver_major
+ if self.is_service_available(
+ "volumev%s" % volume_version) is not False:
+ return True
+ elif self.is_service_available('volume') is not False:
+ return True
+ else:
+ return False
+
# Plugin Support
diff --git a/openstackclient/common/extension.py b/openstackclient/common/extension.py
index 139f43ab..71206618 100644
--- a/openstackclient/common/extension.py
+++ b/openstackclient/common/extension.py
@@ -75,8 +75,10 @@ class ListExtension(command.Lister):
# by default we want to show everything, unless the
# user specifies one or more of the APIs to show
# for now, only identity and compute are supported.
- show_all = (not parsed_args.identity and not parsed_args.compute
- and not parsed_args.volume and not parsed_args.network)
+ show_all = (not parsed_args.identity and
+ not parsed_args.compute and
+ not parsed_args.volume and
+ not parsed_args.network)
if parsed_args.identity or show_all:
identity_client = self.app.client_manager.identity
diff --git a/openstackclient/common/limits.py b/openstackclient/common/limits.py
index 957f1d02..19db35d7 100644
--- a/openstackclient/common/limits.py
+++ b/openstackclient/common/limits.py
@@ -83,24 +83,34 @@ class ShowLimits(command.Lister):
project_id = utils.find_resource(identity_client.projects,
parsed_args.project).id
- compute_limits = compute_client.limits.get(parsed_args.is_reserved,
- tenant_id=project_id)
- volume_limits = volume_client.limits.get()
+ compute_limits = None
+ volume_limits = None
+ if self.app.client_manager.is_compute_endpoint_enabled():
+ compute_limits = compute_client.limits.get(parsed_args.is_reserved,
+ tenant_id=project_id)
+
+ if self.app.client_manager.is_volume_endpoint_enabled(volume_client):
+ volume_limits = volume_client.limits.get()
+
+ data = []
if parsed_args.is_absolute:
- compute_limits = compute_limits.absolute
- volume_limits = volume_limits.absolute
+ if compute_limits:
+ data.append(compute_limits.absolute)
+ if volume_limits:
+ data.append(volume_limits.absolute)
columns = ["Name", "Value"]
return (columns, (utils.get_item_properties(s, columns)
- for s in itertools.chain(compute_limits, volume_limits)))
+ for s in itertools.chain(*data)))
elif parsed_args.is_rate:
- compute_limits = compute_limits.rate
- volume_limits = volume_limits.rate
+ if compute_limits:
+ data.append(compute_limits.rate)
+ if volume_limits:
+ data.append(volume_limits.rate)
columns = ["Verb", "URI", "Value", "Remain", "Unit",
"Next Available"]
return (columns, (utils.get_item_properties(s, columns)
- for s in itertools.chain(compute_limits, volume_limits)))
-
+ for s in itertools.chain(*data)))
else:
- return ({}, {})
+ return {}, {}
diff --git a/openstackclient/compute/v2/console.py b/openstackclient/compute/v2/console.py
index 25f92108..b2f7288f 100644
--- a/openstackclient/compute/v2/console.py
+++ b/openstackclient/compute/v2/console.py
@@ -15,8 +15,6 @@
"""Compute v2 Console action implementations"""
-import sys
-
from osc_lib.cli import parseractions
from osc_lib.command import command
from osc_lib import utils
@@ -60,7 +58,10 @@ class ShowConsoleLog(command.Command):
length += 1
data = server.get_console_output(length=length)
- sys.stdout.write(data)
+
+ if data and data[-1] != '\n':
+ data += '\n'
+ self.app.stdout.write(data)
class ShowConsoleURL(command.ShowOne):
diff --git a/openstackclient/compute/v2/host.py b/openstackclient/compute/v2/host.py
index a495b367..9fdfd927 100644
--- a/openstackclient/compute/v2/host.py
+++ b/openstackclient/compute/v2/host.py
@@ -40,9 +40,9 @@ class ListHost(command.Lister):
"Service",
"Zone"
)
- data = compute_client.hosts.list_all(parsed_args.zone)
+ data = compute_client.api.host_list(parsed_args.zone)
return (columns,
- (utils.get_item_properties(
+ (utils.get_dict_properties(
s, columns,
) for s in data))
@@ -95,13 +95,7 @@ class SetHost(command.Command):
compute_client = self.app.client_manager.compute
- # More than one hosts will be returned by using find_resource()
- # so that the return value cannot be used in host update() method.
- # find_resource() is just used for checking existence of host and
- # keeping the exception message consistent with other commands.
- utils.find_resource(compute_client.hosts, parsed_args.host)
-
- compute_client.hosts.update(
+ compute_client.api.host_set(
parsed_args.host,
kwargs
)
@@ -128,8 +122,10 @@ class ShowHost(command.Lister):
"Memory MB",
"Disk GB"
)
- data = compute_client.hosts.get(parsed_args.host)
+
+ data = compute_client.api.host_show(parsed_args.host)
+
return (columns,
- (utils.get_item_properties(
+ (utils.get_dict_properties(
s, columns,
) for s in data))
diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py
index c08f5cae..a7b99306 100644
--- a/openstackclient/compute/v2/server.py
+++ b/openstackclient/compute/v2/server.py
@@ -20,7 +20,6 @@ import getpass
import io
import logging
import os
-import sys
from novaclient.v2 import servers
from osc_lib.cli import parseractions
@@ -32,6 +31,7 @@ import six
from openstackclient.i18n import _
from openstackclient.identity import common as identity_common
+from openstackclient.network import common as network_common
LOG = logging.getLogger(__name__)
@@ -120,17 +120,21 @@ def _prefix_checked_value(prefix):
return func
-def _prep_server_detail(compute_client, image_client, server):
+def _prep_server_detail(compute_client, image_client, server, refresh=True):
"""Prepare the detailed server dict for printing
:param compute_client: a compute client instance
+ :param image_client: an image client instance
:param server: a Server resource
+ :param refresh: Flag indicating if ``server`` is already the latest version
+ or if it needs to be refreshed, for example when showing
+ the latest details of a server after creating it.
:rtype: a dict of server details
"""
- info = server._info.copy()
-
- server = utils.find_resource(compute_client.servers, info['id'])
- info.update(server._info)
+ info = server.to_dict()
+ if refresh:
+ server = utils.find_resource(compute_client.servers, info['id'])
+ info.update(server.to_dict())
# Convert the image blob to a name
image_info = info.get('image', {})
@@ -144,12 +148,18 @@ def _prep_server_detail(compute_client, image_client, server):
# Convert the flavor blob to a name
flavor_info = info.get('flavor', {})
- flavor_id = flavor_info.get('id', '')
- try:
- flavor = utils.find_resource(compute_client.flavors, flavor_id)
- info['flavor'] = "%s (%s)" % (flavor.name, flavor_id)
- except Exception:
- info['flavor'] = flavor_id
+ # Microversion 2.47 puts the embedded flavor into the server response
+ # body but omits the id, so if not present we just expose the flavor
+ # dict in the server output.
+ if 'id' in flavor_info:
+ flavor_id = flavor_info.get('id', '')
+ try:
+ flavor = utils.find_resource(compute_client.flavors, flavor_id)
+ info['flavor'] = "%s (%s)" % (flavor.name, flavor_id)
+ except Exception:
+ info['flavor'] = flavor_id
+ else:
+ info['flavor'] = utils.format_dict(flavor_info)
if 'os-extended-volumes:volumes_attached' in info:
info.update(
@@ -178,7 +188,7 @@ def _prep_server_detail(compute_client, image_client, server):
if 'tenant_id' in info:
info['project_id'] = info.pop('tenant_id')
- # Map power state num to meanful string
+ # Map power state num to meaningful string
if 'OS-EXT-STS:power_state' in info:
info['OS-EXT-STS:power_state'] = _format_servers_list_power_state(
info['OS-EXT-STS:power_state'])
@@ -189,12 +199,6 @@ def _prep_server_detail(compute_client, image_client, server):
return info
-def _show_progress(progress):
- if progress:
- sys.stdout.write('\rProgress: %s' % progress)
- sys.stdout.flush()
-
-
class AddFixedIP(command.Command):
_description = _("Add fixed IP address to server")
@@ -234,11 +238,10 @@ class AddFixedIP(command.Command):
)
-class AddFloatingIP(command.Command):
+class AddFloatingIP(network_common.NetworkAndComputeCommand):
_description = _("Add floating IP address to server")
- def get_parser(self, prog_name):
- parser = super(AddFloatingIP, self).get_parser(prog_name)
+ def update_parser_common(self, parser):
parser.add_argument(
"server",
metavar="<server>",
@@ -252,19 +255,37 @@ class AddFloatingIP(command.Command):
parser.add_argument(
"--fixed-ip-address",
metavar="<ip-address>",
- help=_("Fixed IP address to associate with this floating IP "
- "address"),
+ help=_(
+ "Fixed IP address to associate with this floating IP address"
+ ),
)
return parser
- def take_action(self, parsed_args):
+ def take_action_network(self, client, parsed_args):
compute_client = self.app.client_manager.compute
+ attrs = {}
+ obj = client.find_ip(
+ parsed_args.ip_address,
+ ignore_missing=False,
+ )
server = utils.find_resource(
- compute_client.servers, parsed_args.server)
+ compute_client.servers,
+ parsed_args.server,
+ )
+ port = list(client.ports(device_id=server.id))[0]
+ attrs['port_id'] = port.id
+ if parsed_args.fixed_ip_address:
+ attrs['fixed_ip_address'] = parsed_args.fixed_ip_address
- server.add_floating_ip(parsed_args.ip_address,
- parsed_args.fixed_ip_address)
+ client.update_ip(obj, **attrs)
+
+ def take_action_compute(self, client, parsed_args):
+ client.api.floating_ip_add(
+ parsed_args.server,
+ parsed_args.ip_address,
+ fixed_address=parsed_args.fixed_ip_address,
+ )
class AddPort(command.Command):
@@ -425,6 +446,12 @@ class CreateServer(command.ShowOne):
help=_('Create server boot disk from this image (name or ID)'),
)
disk_group.add_argument(
+ '--image-property',
+ metavar='<key=value>',
+ action=parseractions.KeyValueAction,
+ help=_("Image property to be matched"),
+ )
+ disk_group.add_argument(
'--volume',
metavar='<volume>',
help=_('Create server using this volume as the boot disk (name '
@@ -580,6 +607,12 @@ class CreateServer(command.ShowOne):
return parser
def take_action(self, parsed_args):
+
+ def _show_progress(progress):
+ if progress:
+ self.app.stdout.write('\rProgress: %s' % progress)
+ self.app.stdout.flush()
+
compute_client = self.app.client_manager.compute
volume_client = self.app.client_manager.volume
image_client = self.app.client_manager.image
@@ -592,6 +625,45 @@ class CreateServer(command.ShowOne):
parsed_args.image,
)
+ 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]})
+
+ def _match_image(image_api, wanted_properties):
+ image_list = image_api.image_list()
+ images_matched = []
+ for img in image_list:
+ img_dict = {}
+ # exclude any unhashable entries
+ for key, value in img.items():
+ try:
+ set([key, value])
+ except TypeError:
+ pass
+ else:
+ img_dict[key] = value
+ if all(k in img_dict and img_dict[k] == v
+ for k, v in wanted_properties.items()):
+ images_matched.append(img)
+ else:
+ return []
+ return images_matched
+
+ images = _match_image(image_client.api, parsed_args.image_property)
+ if len(images) > 1:
+ 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"))
+
# Lookup parsed_args.volume
volume = None
if parsed_args.volume:
@@ -814,11 +886,11 @@ class CreateServer(command.ShowOne):
server.id,
callback=_show_progress,
):
- sys.stdout.write('\n')
+ self.app.stdout.write('\n')
else:
LOG.error(_('Error creating server: %s'),
parsed_args.server_name)
- sys.stdout.write(_('Error creating server\n'))
+ self.app.stdout.write(_('Error creating server\n'))
raise SystemExit
details = _prep_server_detail(compute_client, image_client, server)
@@ -872,6 +944,12 @@ class DeleteServer(command.Command):
return parser
def take_action(self, parsed_args):
+
+ def _show_progress(progress):
+ if progress:
+ self.app.stdout.write('\rProgress: %s' % progress)
+ self.app.stdout.flush()
+
compute_client = self.app.client_manager.compute
for server in parsed_args.server:
server_obj = utils.find_resource(
@@ -883,11 +961,11 @@ class DeleteServer(command.Command):
server_obj.id,
callback=_show_progress,
):
- sys.stdout.write('\n')
+ self.app.stdout.write('\n')
else:
LOG.error(_('Error deleting server: %s'),
server_obj.id)
- sys.stdout.write(_('Error deleting server\n'))
+ self.app.stdout.write(_('Error deleting server\n'))
raise SystemExit
@@ -1185,6 +1263,10 @@ class ListServer(command.Lister):
s.flavor_name = flavor.name
s.flavor_id = s.flavor['id']
else:
+ # TODO(mriedem): Fix this for microversion >= 2.47 where the
+ # flavor is embedded in the server response without the id.
+ # We likely need to drop the Flavor ID column in that case if
+ # --long is specified.
s.flavor_name = ''
s.flavor_id = ''
@@ -1290,6 +1372,11 @@ class MigrateServer(command.Command):
def take_action(self, parsed_args):
+ def _show_progress(progress):
+ if progress:
+ self.app.stdout.write('\rProgress: %s' % progress)
+ self.app.stdout.flush()
+
compute_client = self.app.client_manager.compute
server = utils.find_resource(
@@ -1313,13 +1400,14 @@ class MigrateServer(command.Command):
if utils.wait_for_status(
compute_client.servers.get,
server.id,
+ success_status=['active', 'verify_resize'],
callback=_show_progress,
):
- sys.stdout.write(_('Complete\n'))
+ self.app.stdout.write(_('Complete\n'))
else:
LOG.error(_('Error migrating server: %s'),
server.id)
- sys.stdout.write(_('Error migrating server\n'))
+ self.app.stdout.write(_('Error migrating server\n'))
raise SystemExit
@@ -1380,6 +1468,12 @@ class RebootServer(command.Command):
return parser
def take_action(self, parsed_args):
+
+ def _show_progress(progress):
+ if progress:
+ self.app.stdout.write('\rProgress: %s' % progress)
+ self.app.stdout.flush()
+
compute_client = self.app.client_manager.compute
server = utils.find_resource(
compute_client.servers, parsed_args.server)
@@ -1391,11 +1485,11 @@ class RebootServer(command.Command):
server.id,
callback=_show_progress,
):
- sys.stdout.write(_('Complete\n'))
+ self.app.stdout.write(_('Complete\n'))
else:
LOG.error(_('Error rebooting server: %s'),
server.id)
- sys.stdout.write(_('Error rebooting server\n'))
+ self.app.stdout.write(_('Error rebooting server\n'))
raise SystemExit
@@ -1428,6 +1522,12 @@ class RebuildServer(command.ShowOne):
return parser
def take_action(self, parsed_args):
+
+ def _show_progress(progress):
+ if progress:
+ self.app.stdout.write('\rProgress: %s' % progress)
+ self.app.stdout.flush()
+
compute_client = self.app.client_manager.compute
image_client = self.app.client_manager.image
@@ -1435,7 +1535,8 @@ class RebuildServer(command.ShowOne):
compute_client.servers, parsed_args.server)
# If parsed_args.image is not set, default to the currently used one.
- image_id = parsed_args.image or server._info.get('image', {}).get('id')
+ image_id = parsed_args.image or server.to_dict().get(
+ 'image', {}).get('id')
image = utils.find_resource(image_client.images, image_id)
server = server.rebuild(image, parsed_args.password)
@@ -1445,14 +1546,15 @@ class RebuildServer(command.ShowOne):
server.id,
callback=_show_progress,
):
- sys.stdout.write(_('Complete\n'))
+ self.app.stdout.write(_('Complete\n'))
else:
LOG.error(_('Error rebuilding server: %s'),
server.id)
- sys.stdout.write(_('Error rebuilding server\n'))
+ self.app.stdout.write(_('Error rebuilding server\n'))
raise SystemExit
- details = _prep_server_detail(compute_client, image_client, server)
+ details = _prep_server_detail(compute_client, image_client, server,
+ refresh=False)
return zip(*sorted(six.iteritems(details)))
@@ -1482,11 +1584,10 @@ class RemoveFixedIP(command.Command):
server.remove_fixed_ip(parsed_args.ip_address)
-class RemoveFloatingIP(command.Command):
+class RemoveFloatingIP(network_common.NetworkAndComputeCommand):
_description = _("Remove floating IP address from server")
- def get_parser(self, prog_name):
- parser = super(RemoveFloatingIP, self).get_parser(prog_name)
+ def update_parser_common(self, parser):
parser.add_argument(
"server",
metavar="<server>",
@@ -1501,13 +1602,21 @@ class RemoveFloatingIP(command.Command):
)
return parser
- def take_action(self, parsed_args):
- compute_client = self.app.client_manager.compute
+ def take_action_network(self, client, parsed_args):
+ attrs = {}
+ obj = client.find_ip(
+ parsed_args.ip_address,
+ ignore_missing=False,
+ )
+ attrs['port_id'] = None
- server = utils.find_resource(
- compute_client.servers, parsed_args.server)
+ client.update_ip(obj, **attrs)
- server.remove_floating_ip(parsed_args.ip_address)
+ def take_action_compute(self, client, parsed_args):
+ client.api.floating_ip_remove(
+ parsed_args.server,
+ parsed_args.ip_address,
+ )
class RemovePort(command.Command):
@@ -1727,6 +1836,11 @@ the new server and restart the old one.""")
def take_action(self, parsed_args):
+ def _show_progress(progress):
+ if progress:
+ self.app.stdout.write('\rProgress: %s' % progress)
+ self.app.stdout.flush()
+
compute_client = self.app.client_manager.compute
server = utils.find_resource(
compute_client.servers,
@@ -1745,11 +1859,11 @@ the new server and restart the old one.""")
success_status=['active', 'verify_resize'],
callback=_show_progress,
):
- sys.stdout.write(_('Complete\n'))
+ self.app.stdout.write(_('Complete\n'))
else:
LOG.error(_('Error resizing server: %s'),
server.id)
- sys.stdout.write(_('Error resizing server\n'))
+ self.app.stdout.write(_('Error resizing server\n'))
raise SystemExit
elif parsed_args.confirm:
compute_client.servers.confirm_resize(server)
@@ -1890,7 +2004,9 @@ class ShelveServer(command.Command):
class ShowServer(command.ShowOne):
- _description = _("Show server details")
+ _description = _(
+ "Show server details. Specify ``--os-compute-api-version 2.47`` "
+ "or higher to see the embedded flavor information for the server.")
def get_parser(self, prog_name):
parser = super(ShowServer, self).get_parser(prog_name)
@@ -1915,11 +2031,14 @@ class ShowServer(command.ShowOne):
if parsed_args.diagnostics:
(resp, data) = server.diagnostics()
if not resp.status_code == 200:
- sys.stderr.write(_("Error retrieving diagnostics data\n"))
+ self.app.stderr.write(_(
+ "Error retrieving diagnostics data\n"
+ ))
return ({}, {})
else:
data = _prep_server_detail(compute_client,
- self.app.client_manager.image, server)
+ self.app.client_manager.image, server,
+ refresh=False)
return zip(*sorted(six.iteritems(data)))
diff --git a/openstackclient/compute/v2/server_backup.py b/openstackclient/compute/v2/server_backup.py
index ddcf9101..a79f5f70 100644
--- a/openstackclient/compute/v2/server_backup.py
+++ b/openstackclient/compute/v2/server_backup.py
@@ -15,8 +15,6 @@
"""Compute v2 Server action implementations"""
-import sys
-
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
@@ -26,12 +24,6 @@ import six
from openstackclient.i18n import _
-def _show_progress(progress):
- if progress:
- sys.stderr.write('\rProgress: %s' % progress)
- sys.stderr.flush()
-
-
class CreateServerBackup(command.ShowOne):
_description = _("Create a server backup image")
@@ -74,6 +66,12 @@ class CreateServerBackup(command.ShowOne):
return parser
def take_action(self, parsed_args):
+
+ def _show_progress(progress):
+ if progress:
+ self.app.stderr.write('\rProgress: %s' % progress)
+ self.app.stderr.flush()
+
compute_client = self.app.client_manager.compute
server = utils.find_resource(
@@ -114,7 +112,7 @@ class CreateServerBackup(command.ShowOne):
image.id,
callback=_show_progress,
):
- sys.stdout.write('\n')
+ self.app.stdout.write('\n')
else:
msg = _('Error creating server backup: %s') % parsed_args.name
raise exceptions.CommandError(msg)
diff --git a/openstackclient/compute/v2/server_image.py b/openstackclient/compute/v2/server_image.py
index c66e0674..3bc5d94a 100644
--- a/openstackclient/compute/v2/server_image.py
+++ b/openstackclient/compute/v2/server_image.py
@@ -16,7 +16,6 @@
"""Compute v2 Server action implementations"""
import logging
-import sys
from osc_lib.command import command
from osc_lib import exceptions
@@ -30,12 +29,6 @@ from openstackclient.i18n import _
LOG = logging.getLogger(__name__)
-def _show_progress(progress):
- if progress:
- sys.stdout.write('\rProgress: %s' % progress)
- sys.stdout.flush()
-
-
class CreateServerImage(command.ShowOne):
_description = _("Create a new server disk image from an existing server")
@@ -64,6 +57,12 @@ class CreateServerImage(command.ShowOne):
return parser
def take_action(self, parsed_args):
+
+ def _show_progress(progress):
+ if progress:
+ self.app.stdout.write('\rProgress: %s' % progress)
+ self.app.stdout.flush()
+
compute_client = self.app.client_manager.compute
server = utils.find_resource(
@@ -92,7 +91,7 @@ class CreateServerImage(command.ShowOne):
image_id,
callback=_show_progress,
):
- sys.stdout.write('\n')
+ self.app.stdout.write('\n')
else:
LOG.error(_('Error creating server image: %s'),
parsed_args.server)
diff --git a/openstackclient/compute/v2/service.py b/openstackclient/compute/v2/service.py
index 7331d29d..18e6d9d9 100644
--- a/openstackclient/compute/v2/service.py
+++ b/openstackclient/compute/v2/service.py
@@ -17,6 +17,7 @@
import logging
+from novaclient import api_versions
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
@@ -192,18 +193,23 @@ class SetService(command.Command):
result += 1
force_down = None
- try:
- if parsed_args.down:
- force_down = True
- if parsed_args.up:
- force_down = False
- if force_down is not None:
+ if parsed_args.down:
+ force_down = True
+ if parsed_args.up:
+ force_down = False
+ if force_down is not None:
+ if compute_client.api_version < api_versions.APIVersion(
+ '2.11'):
+ msg = _('--os-compute-api-version 2.11 or later is '
+ 'required')
+ raise exceptions.CommandError(msg)
+ try:
cs.force_down(parsed_args.host, parsed_args.service,
force_down=force_down)
- except Exception:
- state = "down" if force_down else "up"
- LOG.error("Failed to set service state to %s", state)
- result += 1
+ except Exception:
+ state = "down" if force_down else "up"
+ LOG.error("Failed to set service state to %s", state)
+ result += 1
if result > 0:
msg = _("Compute service %(service)s of host %(host)s failed to "
diff --git a/openstackclient/compute/v2/usage.py b/openstackclient/compute/v2/usage.py
index 3edcffe4..4320bf90 100644
--- a/openstackclient/compute/v2/usage.py
+++ b/openstackclient/compute/v2/usage.py
@@ -16,7 +16,6 @@
"""Usage action implementations"""
import datetime
-import sys
from osc_lib.command import command
from osc_lib import utils
@@ -96,7 +95,7 @@ class ListUsage(command.Lister):
pass
if parsed_args.formatter == 'table' and len(usage_list) > 0:
- sys.stdout.write(_("Usage from %(start)s to %(end)s: \n") % {
+ self.app.stdout.write(_("Usage from %(start)s to %(end)s: \n") % {
"start": start.strftime(dateformat),
"end": end.strftime(dateformat),
})
@@ -168,8 +167,9 @@ class ShowUsage(command.ShowOne):
usage = compute_client.usage.get(project, start, end)
if parsed_args.formatter == 'table':
- sys.stdout.write(_("Usage from %(start)s to %(end)s on "
- "project %(project)s: \n") % {
+ self.app.stdout.write(_(
+ "Usage from %(start)s to %(end)s on project %(project)s: \n"
+ ) % {
"start": start.strftime(dateformat),
"end": end.strftime(dateformat),
"project": project,
diff --git a/openstackclient/identity/common.py b/openstackclient/identity/common.py
index e119f660..f36f5f73 100644
--- a/openstackclient/identity/common.py
+++ b/openstackclient/identity/common.py
@@ -101,6 +101,13 @@ def _get_token_resource(client, resource, parsed_name, parsed_domain=None):
# user/project under different domain may has a same name
if parsed_domain and parsed_domain not in obj['domain'].values():
return parsed_name
+ if isinstance(obj, list):
+ for item in obj:
+ if item['name'] == parsed_name:
+ return item['id']
+ if item['id'] == parsed_name:
+ return parsed_name
+ return parsed_name
return obj['id'] if obj['name'] == parsed_name else parsed_name
# diaper defense in case parsing the token fails
except Exception: # noqa
diff --git a/openstackclient/identity/v3/application_credential.py b/openstackclient/identity/v3/application_credential.py
new file mode 100644
index 00000000..747fa20e
--- /dev/null
+++ b/openstackclient/identity/v3/application_credential.py
@@ -0,0 +1,220 @@
+# Copyright 2018 SUSE Linux GmbH
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+
+"""Identity v3 Application Credential action implementations"""
+
+import datetime
+import logging
+
+from osc_lib.command import command
+from osc_lib import exceptions
+from osc_lib import utils
+import six
+
+from openstackclient.i18n import _
+from openstackclient.identity import common
+
+
+LOG = logging.getLogger(__name__)
+
+
+class CreateApplicationCredential(command.ShowOne):
+ _description = _("Create new application credential")
+
+ def get_parser(self, prog_name):
+ parser = super(CreateApplicationCredential, self).get_parser(prog_name)
+ parser.add_argument(
+ 'name',
+ metavar='<name>',
+ help=_('Name of the application credential'),
+ )
+ parser.add_argument(
+ '--secret',
+ metavar='<secret>',
+ help=_('Secret to use for authentication (if not provided, one'
+ ' will be generated)'),
+ )
+ parser.add_argument(
+ '--role',
+ metavar='<role>',
+ action='append',
+ default=[],
+ help=_('Roles to authorize (name or ID) (repeat option to set'
+ ' multiple values)'),
+ )
+ parser.add_argument(
+ '--expiration',
+ metavar='<expiration>',
+ help=_('Sets an expiration date for the application credential,'
+ ' format of YYYY-mm-ddTHH:MM:SS (if not provided, the'
+ ' application credential will not expire)'),
+ )
+ parser.add_argument(
+ '--description',
+ metavar='<description>',
+ help=_('Application credential description'),
+ )
+ parser.add_argument(
+ '--unrestricted',
+ action="store_true",
+ help=_('Enable application credential to create and delete other'
+ ' application credentials and trusts (this is potentially'
+ ' dangerous behavior and is disabled by default)'),
+ )
+ parser.add_argument(
+ '--restricted',
+ action="store_true",
+ help=_('Prohibit application credential from creating and deleting'
+ ' other application credentials and trusts (this is the'
+ ' default behavior)'),
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ identity_client = self.app.client_manager.identity
+
+ role_ids = []
+ for role in parsed_args.role:
+ # A user can only create an application credential for themself,
+ # not for another user even as an admin, and only on the project to
+ # which they are currently scoped with a subset of the role
+ # assignments they have on that project. Don't bother trying to
+ # look up roles via keystone, just introspect the token.
+ role_id = common._get_token_resource(identity_client, "roles",
+ role)
+ role_ids.append(role_id)
+
+ expires_at = None
+ if parsed_args.expiration:
+ expires_at = datetime.datetime.strptime(parsed_args.expiration,
+ '%Y-%m-%dT%H:%M:%S')
+
+ if parsed_args.restricted:
+ unrestricted = False
+ else:
+ unrestricted = parsed_args.unrestricted
+
+ app_cred_manager = identity_client.application_credentials
+ application_credential = app_cred_manager.create(
+ parsed_args.name,
+ roles=role_ids,
+ expires_at=expires_at,
+ description=parsed_args.description,
+ secret=parsed_args.secret,
+ unrestricted=unrestricted,
+ )
+
+ application_credential._info.pop('links', None)
+
+ # Format roles into something sensible
+ roles = application_credential._info.pop('roles')
+ msg = ' '.join(r['name'] for r in roles)
+ application_credential._info['roles'] = msg
+
+ return zip(*sorted(six.iteritems(application_credential._info)))
+
+
+class DeleteApplicationCredential(command.Command):
+ _description = _("Delete application credentials(s)")
+
+ def get_parser(self, prog_name):
+ parser = super(DeleteApplicationCredential, self).get_parser(prog_name)
+ parser.add_argument(
+ 'application_credential',
+ metavar='<application-credential>',
+ nargs="+",
+ help=_('Application credentials(s) to delete (name or ID)'),
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ identity_client = self.app.client_manager.identity
+
+ errors = 0
+ for ac in parsed_args.application_credential:
+ try:
+ app_cred = utils.find_resource(
+ identity_client.application_credentials, ac)
+ identity_client.application_credentials.delete(app_cred.id)
+ except Exception as e:
+ errors += 1
+ LOG.error(_("Failed to delete application credential with "
+ "name or ID '%(ac)s': %(e)s"),
+ {'ac': ac, 'e': e})
+
+ if errors > 0:
+ total = len(parsed_args.application_credential)
+ msg = (_("%(errors)s of %(total)s application credentials failed "
+ "to delete.") % {'errors': errors, 'total': total})
+ raise exceptions.CommandError(msg)
+
+
+class ListApplicationCredential(command.Lister):
+ _description = _("List application credentials")
+
+ def get_parser(self, prog_name):
+ parser = super(ListApplicationCredential, self).get_parser(prog_name)
+ parser.add_argument(
+ '--user',
+ metavar='<user>',
+ help=_('User whose application credentials to list (name or ID)'),
+ )
+ common.add_user_domain_option_to_parser(parser)
+ return parser
+
+ def take_action(self, parsed_args):
+ identity_client = self.app.client_manager.identity
+ if parsed_args.user:
+ user_id = common.find_user(identity_client,
+ parsed_args.user,
+ parsed_args.user_domain).id
+ else:
+ user_id = None
+
+ columns = ('ID', 'Name', 'Project ID', 'Description', 'Expires At')
+ data = identity_client.application_credentials.list(
+ user=user_id)
+ return (columns,
+ (utils.get_item_properties(
+ s, columns,
+ formatters={},
+ ) for s in data))
+
+
+class ShowApplicationCredential(command.ShowOne):
+ _description = _("Display application credential details")
+
+ def get_parser(self, prog_name):
+ parser = super(ShowApplicationCredential, self).get_parser(prog_name)
+ parser.add_argument(
+ 'application_credential',
+ metavar='<application-credential>',
+ help=_('Application credential to display (name or ID)'),
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ identity_client = self.app.client_manager.identity
+ app_cred = utils.find_resource(identity_client.application_credentials,
+ parsed_args.application_credential)
+
+ app_cred._info.pop('links', None)
+
+ # Format roles into something sensible
+ roles = app_cred._info.pop('roles')
+ msg = ' '.join(r['name'] for r in roles)
+ app_cred._info['roles'] = msg
+
+ return zip(*sorted(six.iteritems(app_cred._info)))
diff --git a/openstackclient/identity/v3/endpoint_group.py b/openstackclient/identity/v3/endpoint_group.py
new file mode 100644
index 00000000..e254973b
--- /dev/null
+++ b/openstackclient/identity/v3/endpoint_group.py
@@ -0,0 +1,324 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+
+"""Identity v3 Endpoint Group action implementations"""
+
+import json
+import logging
+
+from osc_lib.command import command
+from osc_lib import exceptions
+from osc_lib import utils
+import six
+
+from openstackclient.i18n import _
+from openstackclient.identity import common
+
+
+LOG = logging.getLogger(__name__)
+
+
+class _FiltersReader(object):
+ _description = _("Helper class capable of reading filters from files")
+
+ def _read_filters(self, path):
+ """Read and parse rules from path
+
+ Expect the file to contain a valid JSON structure.
+
+ :param path: path to the file
+ :return: loaded and valid dictionary with filters
+ :raises exception.CommandError: In case the file cannot be
+ accessed or the content is not a valid JSON.
+
+ Example of the content of the file:
+ {
+ "interface": "admin",
+ "service_id": "1b501a"
+ }
+ """
+ blob = utils.read_blob_file_contents(path)
+ try:
+ rules = json.loads(blob)
+ except ValueError as e:
+ msg = _("An error occurred when reading filters from file "
+ "%(path)s: %(error)s") % {"path": path, "error": e}
+ raise exceptions.CommandError(msg)
+ else:
+ return rules
+
+
+class AddProjectToEndpointGroup(command.Command):
+ _description = _("Add a project to an endpoint group")
+
+ def get_parser(self, prog_name):
+ parser = super(
+ AddProjectToEndpointGroup, self).get_parser(prog_name)
+ parser.add_argument(
+ 'endpointgroup',
+ metavar='<endpoint-group>',
+ help=_('Endpoint group (name or ID)'),
+ )
+ parser.add_argument(
+ 'project',
+ metavar='<project>',
+ help=_('Project to associate (name or ID)'),
+ )
+ common.add_project_domain_option_to_parser(parser)
+ return parser
+
+ def take_action(self, parsed_args):
+ client = self.app.client_manager.identity
+
+ endpointgroup = utils.find_resource(client.endpoint_groups,
+ parsed_args.endpointgroup)
+
+ project = common.find_project(client,
+ parsed_args.project,
+ parsed_args.project_domain)
+
+ client.endpoint_filter.add_endpoint_group_to_project(
+ endpoint_group=endpointgroup.id,
+ project=project.id)
+
+
+class CreateEndpointGroup(command.ShowOne, _FiltersReader):
+ _description = _("Create new endpoint group")
+
+ def get_parser(self, prog_name):
+ parser = super(CreateEndpointGroup, self).get_parser(prog_name)
+ parser.add_argument(
+ 'name',
+ metavar='<name>',
+ help=_('Name of the endpoint group'),
+ )
+ parser.add_argument(
+ 'filters',
+ metavar='<filename>',
+ help=_('Filename that contains a new set of filters'),
+ )
+ parser.add_argument(
+ '--description',
+ help=_('Description of the endpoint group'),
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ identity_client = self.app.client_manager.identity
+
+ filters = None
+ if parsed_args.filters:
+ filters = self._read_filters(parsed_args.filters)
+
+ endpoint_group = identity_client.endpoint_groups.create(
+ name=parsed_args.name,
+ filters=filters,
+ description=parsed_args.description
+ )
+
+ info = {}
+ endpoint_group._info.pop('links')
+ info.update(endpoint_group._info)
+ return zip(*sorted(six.iteritems(info)))
+
+
+class DeleteEndpointGroup(command.Command):
+ _description = _("Delete endpoint group(s)")
+
+ def get_parser(self, prog_name):
+ parser = super(DeleteEndpointGroup, self).get_parser(prog_name)
+ parser.add_argument(
+ 'endpointgroup',
+ metavar='<endpoint-group>',
+ nargs='+',
+ help=_('Endpoint group(s) to delete (name or ID)'),
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ identity_client = self.app.client_manager.identity
+ result = 0
+ for i in parsed_args.endpointgroup:
+ try:
+ endpoint_id = utils.find_resource(
+ identity_client.endpoint_groups, i).id
+ identity_client.endpoint_groups.delete(endpoint_id)
+ except Exception as e:
+ result += 1
+ LOG.error(_("Failed to delete endpoint group with "
+ "ID '%(endpointgroup)s': %(e)s"),
+ {'endpointgroup': i, 'e': e})
+
+ if result > 0:
+ total = len(parsed_args.endpointgroup)
+ msg = (_("%(result)s of %(total)s endpointgroups failed "
+ "to delete.") % {'result': result, 'total': total})
+ raise exceptions.CommandError(msg)
+
+
+class ListEndpointGroup(command.Lister):
+ _description = _("List endpoint groups")
+
+ def get_parser(self, prog_name):
+ parser = super(ListEndpointGroup, self).get_parser(prog_name)
+ list_group = parser.add_mutually_exclusive_group()
+ list_group.add_argument(
+ '--endpointgroup',
+ metavar='<endpoint-group>',
+ help=_('Endpoint Group (name or ID)'),
+ )
+ list_group.add_argument(
+ '--project',
+ metavar='<project>',
+ help=_('Project (name or ID)'),
+ )
+ parser.add_argument(
+ '--domain',
+ metavar='<domain>',
+ help=_('Domain owning <project> (name or ID)'),
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ client = self.app.client_manager.identity
+
+ endpointgroup = None
+ if parsed_args.endpointgroup:
+ endpointgroup = utils.find_resource(client.endpoint_groups,
+ parsed_args.endpointgroup)
+ project = None
+ if parsed_args.project:
+ project = common.find_project(client,
+ parsed_args.project,
+ parsed_args.domain)
+
+ if endpointgroup:
+ # List projects associated to the endpoint group
+ columns = ('ID', 'Name')
+ data = client.endpoint_filter.list_projects_for_endpoint_group(
+ endpoint_group=endpointgroup.id)
+ elif project:
+ columns = ('ID', 'Name')
+ data = client.endpoint_filter.list_endpoint_groups_for_project(
+ project=project.id)
+ else:
+ columns = ('ID', 'Name', 'Description')
+ data = client.endpoint_groups.list()
+
+ return (columns,
+ (utils.get_item_properties(
+ s, columns,
+ formatters={},
+ ) for s in data))
+
+
+class RemoveProjectFromEndpointGroup(command.Command):
+ _description = _("Remove project from endpoint group")
+
+ def get_parser(self, prog_name):
+ parser = super(
+ RemoveProjectFromEndpointGroup, self).get_parser(prog_name)
+ parser.add_argument(
+ 'endpointgroup',
+ metavar='<endpoint-group>',
+ help=_('Endpoint group (name or ID)'),
+ )
+ parser.add_argument(
+ 'project',
+ metavar='<project>',
+ help=_('Project to remove (name or ID)'),
+ )
+ common.add_project_domain_option_to_parser(parser)
+ return parser
+
+ def take_action(self, parsed_args):
+ client = self.app.client_manager.identity
+
+ endpointgroup = utils.find_resource(client.endpoint_groups,
+ parsed_args.endpointgroup)
+
+ project = common.find_project(client,
+ parsed_args.project,
+ parsed_args.project_domain)
+
+ client.endpoint_filter.delete_endpoint_group_to_project(
+ endpoint_group=endpointgroup.id,
+ project=project.id)
+
+
+class SetEndpointGroup(command.Command, _FiltersReader):
+ _description = _("Set endpoint group properties")
+
+ def get_parser(self, prog_name):
+ parser = super(SetEndpointGroup, self).get_parser(prog_name)
+ parser.add_argument(
+ 'endpointgroup',
+ metavar='<endpoint-group>',
+ help=_('Endpoint Group to modify (name or ID)'),
+ )
+ parser.add_argument(
+ '--name',
+ metavar='<name>',
+ help=_('New enpoint group name'),
+ )
+ parser.add_argument(
+ '--filters',
+ metavar='<filename>',
+ help=_('Filename that contains a new set of filters'),
+ )
+ parser.add_argument(
+ '--description',
+ metavar='<description>',
+ default='',
+ help=_('New endpoint group description'),
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ identity_client = self.app.client_manager.identity
+ endpointgroup = utils.find_resource(identity_client.endpoint_groups,
+ parsed_args.endpointgroup)
+
+ filters = None
+ if parsed_args.filters:
+ filters = self._read_filters(parsed_args.filters)
+
+ identity_client.endpoint_groups.update(
+ endpointgroup.id,
+ name=parsed_args.name,
+ filters=filters,
+ description=parsed_args.description
+ )
+
+
+class ShowEndpointGroup(command.ShowOne):
+ _description = _("Display endpoint group details")
+
+ def get_parser(self, prog_name):
+ parser = super(ShowEndpointGroup, self).get_parser(prog_name)
+ parser.add_argument(
+ 'endpointgroup',
+ metavar='<endpointgroup>',
+ help=_('Endpoint group (name or ID)'),
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ identity_client = self.app.client_manager.identity
+ endpoint_group = utils.find_resource(identity_client.endpoint_groups,
+ parsed_args.endpointgroup)
+
+ info = {}
+ endpoint_group._info.pop('links')
+ info.update(endpoint_group._info)
+ return zip(*sorted(six.iteritems(info)))
diff --git a/openstackclient/identity/v3/group.py b/openstackclient/identity/v3/group.py
index 39c8547c..02eeadd6 100644
--- a/openstackclient/identity/v3/group.py
+++ b/openstackclient/identity/v3/group.py
@@ -16,7 +16,6 @@
"""Group action implementations"""
import logging
-import sys
from keystoneauth1 import exceptions as ks_exc
from osc_lib.command import command
@@ -122,7 +121,7 @@ class CheckUserInGroup(command.Command):
'user': parsed_args.user,
'group': parsed_args.group,
}
- sys.stderr.write(msg)
+ self.app.stderr.write(msg)
else:
raise e
else:
@@ -130,7 +129,7 @@ class CheckUserInGroup(command.Command):
'user': parsed_args.user,
'group': parsed_args.group,
}
- sys.stdout.write(msg)
+ self.app.stdout.write(msg)
class CreateGroup(command.ShowOne):
diff --git a/openstackclient/identity/v3/implied_role.py b/openstackclient/identity/v3/implied_role.py
index c7623389..4e3df88a 100644
--- a/openstackclient/identity/v3/implied_role.py
+++ b/openstackclient/identity/v3/implied_role.py
@@ -71,7 +71,7 @@ class CreateImpliedRole(command.ShowOne):
identity_client = self.app.client_manager.identity
(prior_role_id, implied_role_id) = _get_role_ids(
identity_client, parsed_args)
- response = identity_client.roles.create_implied(
+ response = identity_client.inference_rules.create(
prior_role_id, implied_role_id)
response._info.pop('links', None)
return zip(*sorted([(k, v['id'])
@@ -101,7 +101,7 @@ class DeleteImpliedRole(command.Command):
identity_client = self.app.client_manager.identity
(prior_role_id, implied_role_id) = _get_role_ids(
identity_client, parsed_args)
- identity_client.roles.delete_implied(
+ identity_client.inference_rules.delete(
prior_role_id, implied_role_id)
@@ -125,5 +125,5 @@ class ListImpliedRole(command.Lister):
implies['name'])
identity_client = self.app.client_manager.identity
- response = identity_client.roles.list_inference_roles()
+ response = identity_client.inference_rules.list_inference_roles()
return (self._COLUMNS, _list_implied(response))
diff --git a/openstackclient/identity/v3/project.py b/openstackclient/identity/v3/project.py
index 60efbac4..e819a0a8 100644
--- a/openstackclient/identity/v3/project.py
+++ b/openstackclient/identity/v3/project.py
@@ -26,7 +26,7 @@ import six
from openstackclient.i18n import _
from openstackclient.identity import common
-
+from openstackclient.identity.v3 import tag
LOG = logging.getLogger(__name__)
@@ -79,6 +79,7 @@ class CreateProject(command.ShowOne):
action='store_true',
help=_('Return existing project'),
)
+ tag.add_tag_option_to_parser_for_create(parser, _('project'))
return parser
def take_action(self, parsed_args):
@@ -102,6 +103,7 @@ class CreateProject(command.ShowOne):
kwargs = {}
if parsed_args.property:
kwargs = parsed_args.property.copy()
+ kwargs['tags'] = list(set(parsed_args.tags))
try:
project = identity_client.projects.create(
@@ -207,6 +209,7 @@ class ListProject(command.Lister):
'(default: asc), repeat this option to specify multiple '
'keys and directions.'),
)
+ tag.add_tag_filtering_option_to_parser(parser, _('projects'))
return parser
def take_action(self, parsed_args):
@@ -234,6 +237,8 @@ class ListProject(command.Lister):
kwargs['user'] = user_id
+ tag.get_tag_filtering_args(parsed_args, kwargs)
+
if parsed_args.my_projects:
# NOTE(adriant): my-projects supersedes all the other filters.
kwargs = {'user': self.app.client_manager.auth_ref.user_id}
@@ -303,6 +308,7 @@ class SetProject(command.Command):
help=_('Set a property on <project> '
'(repeat option to set multiple properties)'),
)
+ tag.add_tag_option_to_parser_for_set(parser, _('project'))
return parser
def take_action(self, parsed_args):
@@ -323,6 +329,7 @@ class SetProject(command.Command):
kwargs['enabled'] = False
if parsed_args.property:
kwargs.update(parsed_args.property)
+ tag.update_tags_in_args(parsed_args, project, kwargs)
identity_client.projects.update(project.id, **kwargs)
diff --git a/openstackclient/identity/v3/role.py b/openstackclient/identity/v3/role.py
index 1bbf5f07..58a76f8a 100644
--- a/openstackclient/identity/v3/role.py
+++ b/openstackclient/identity/v3/role.py
@@ -31,13 +31,18 @@ LOG = logging.getLogger(__name__)
def _add_identity_and_resource_options_to_parser(parser):
- domain_or_project = parser.add_mutually_exclusive_group()
- domain_or_project.add_argument(
+ system_or_domain_or_project = parser.add_mutually_exclusive_group()
+ system_or_domain_or_project.add_argument(
+ '--system',
+ metavar='<system>',
+ help=_('Include <system> (all)'),
+ )
+ system_or_domain_or_project.add_argument(
'--domain',
metavar='<domain>',
help=_('Include <domain> (name or ID)'),
)
- domain_or_project.add_argument(
+ system_or_domain_or_project.add_argument(
'--project',
metavar='<project>',
help=_('Include <project> (name or ID)'),
@@ -62,7 +67,14 @@ def _add_identity_and_resource_options_to_parser(parser):
def _process_identity_and_resource_options(parsed_args,
identity_client_manager):
kwargs = {}
- if parsed_args.user and parsed_args.domain:
+ if parsed_args.user and parsed_args.system:
+ kwargs['user'] = common.find_user(
+ identity_client_manager,
+ parsed_args.user,
+ parsed_args.user_domain,
+ ).id
+ kwargs['system'] = parsed_args.system
+ elif parsed_args.user and parsed_args.domain:
kwargs['user'] = common.find_user(
identity_client_manager,
parsed_args.user,
@@ -83,6 +95,13 @@ def _process_identity_and_resource_options(parsed_args,
parsed_args.project,
parsed_args.project_domain,
).id
+ elif parsed_args.group and parsed_args.system:
+ kwargs['group'] = common.find_group(
+ identity_client_manager,
+ parsed_args.group,
+ parsed_args.group_domain,
+ ).id
+ kwargs['system'] = parsed_args.system
elif parsed_args.group and parsed_args.domain:
kwargs['group'] = common.find_group(
identity_client_manager,
@@ -109,8 +128,8 @@ def _process_identity_and_resource_options(parsed_args,
class AddRole(command.Command):
- _description = _("Adds a role assignment to a user or group on a domain "
- "or project")
+ _description = _("Adds a role assignment to a user or group on the "
+ "system, a domain, or a project")
def get_parser(self, prog_name):
parser = super(AddRole, self).get_parser(prog_name)
@@ -126,8 +145,8 @@ class AddRole(command.Command):
def take_action(self, parsed_args):
identity_client = self.app.client_manager.identity
- if (not parsed_args.user and not parsed_args.domain
- and not parsed_args.group and not parsed_args.project):
+ if (not parsed_args.user and not parsed_args.domain and
+ not parsed_args.group and not parsed_args.project):
msg = _("Role not added, incorrect set of arguments "
"provided. See openstack --help for more details")
raise exceptions.CommandError(msg)
@@ -381,7 +400,7 @@ class ListRole(command.Lister):
class RemoveRole(command.Command):
- _description = _("Removes a role assignment from domain/project : "
+ _description = _("Removes a role assignment from system/domain/project : "
"user/group")
def get_parser(self, prog_name):
@@ -399,8 +418,8 @@ class RemoveRole(command.Command):
def take_action(self, parsed_args):
identity_client = self.app.client_manager.identity
- if (not parsed_args.user and not parsed_args.domain
- and not parsed_args.group and not parsed_args.project):
+ if (not parsed_args.user and not parsed_args.domain and
+ not parsed_args.group and not parsed_args.project):
msg = _("Incorrect set of arguments provided. "
"See openstack --help for more details")
raise exceptions.CommandError(msg)
diff --git a/openstackclient/identity/v3/role_assignment.py b/openstackclient/identity/v3/role_assignment.py
index a362adb0..9c2f3d24 100644
--- a/openstackclient/identity/v3/role_assignment.py
+++ b/openstackclient/identity/v3/role_assignment.py
@@ -55,17 +55,22 @@ class ListRoleAssignment(command.Lister):
help=_('Group to filter (name or ID)'),
)
common.add_group_domain_option_to_parser(parser)
- domain_or_project = parser.add_mutually_exclusive_group()
- domain_or_project.add_argument(
+ system_or_domain_or_project = parser.add_mutually_exclusive_group()
+ system_or_domain_or_project.add_argument(
'--domain',
metavar='<domain>',
help=_('Domain to filter (name or ID)'),
)
- domain_or_project.add_argument(
+ system_or_domain_or_project.add_argument(
'--project',
metavar='<project>',
help=_('Project to filter (name or ID)'),
)
+ system_or_domain_or_project.add_argument(
+ '--system',
+ metavar='<system>',
+ help=_('Filter based on system role assignments'),
+ )
common.add_project_domain_option_to_parser(parser)
common.add_inherited_option_to_parser(parser)
parser.add_argument(
@@ -85,7 +90,8 @@ class ListRoleAssignment(command.Lister):
def _as_tuple(self, assignment):
return (assignment.role, assignment.user, assignment.group,
- assignment.project, assignment.domain, assignment.inherited)
+ assignment.project, assignment.domain, assignment.system,
+ assignment.inherited)
def take_action(self, parsed_args):
identity_client = self.app.client_manager.identity
@@ -117,6 +123,10 @@ class ListRoleAssignment(command.Lister):
auth_ref.user_id
)
+ system = None
+ if parsed_args.system:
+ system = parsed_args.system
+
domain = None
if parsed_args.domain:
domain = common.find_domain(
@@ -149,7 +159,9 @@ class ListRoleAssignment(command.Lister):
include_names = True if parsed_args.names else False
effective = True if parsed_args.effective else False
- columns = ('Role', 'User', 'Group', 'Project', 'Domain', 'Inherited')
+ columns = (
+ 'Role', 'User', 'Group', 'Project', 'Domain', 'System', 'Inherited'
+ )
inherited_to = 'projects' if parsed_args.inherited else None
data = identity_client.role_assignments.list(
@@ -157,6 +169,7 @@ class ListRoleAssignment(command.Lister):
user=user,
group=group,
project=project,
+ system=system,
role=role,
effective=effective,
os_inherit_extension_inherited_to=inherited_to,
@@ -174,14 +187,24 @@ class ListRoleAssignment(command.Lister):
else:
setattr(assignment, 'project', scope['project']['id'])
assignment.domain = ''
+ assignment.system = ''
elif 'domain' in scope:
if include_names:
setattr(assignment, 'domain', scope['domain']['name'])
else:
setattr(assignment, 'domain', scope['domain']['id'])
assignment.project = ''
-
+ assignment.system = ''
+ elif 'system' in scope:
+ # NOTE(lbragstad): If, or when, keystone supports role
+ # assignments on subsets of a system, this will have to evolve
+ # to handle that case instead of hardcoding to the entire
+ # system.
+ setattr(assignment, 'system', 'all')
+ assignment.domain = ''
+ assignment.project = ''
else:
+ assignment.system = ''
assignment.domain = ''
assignment.project = ''
diff --git a/openstackclient/identity/v3/tag.py b/openstackclient/identity/v3/tag.py
new file mode 100644
index 00000000..abf022d4
--- /dev/null
+++ b/openstackclient/identity/v3/tag.py
@@ -0,0 +1,116 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import argparse
+
+from openstackclient.i18n import _
+
+
+class _CommaListAction(argparse.Action):
+
+ def __call__(self, parser, namespace, values, option_string=None):
+ setattr(namespace, self.dest, values.split(','))
+
+
+def add_tag_filtering_option_to_parser(parser, collection_name):
+ parser.add_argument(
+ '--tags',
+ metavar='<tag>[,<tag>,...]',
+ action=_CommaListAction,
+ help=_('List %s which have all given tag(s) '
+ '(Comma-separated list of tags)') % collection_name
+ )
+ parser.add_argument(
+ '--tags-any',
+ metavar='<tag>[,<tag>,...]',
+ action=_CommaListAction,
+ help=_('List %s which have any given tag(s) '
+ '(Comma-separated list of tags)') % collection_name
+ )
+ parser.add_argument(
+ '--not-tags',
+ metavar='<tag>[,<tag>,...]',
+ action=_CommaListAction,
+ help=_('Exclude %s which have all given tag(s) '
+ '(Comma-separated list of tags)') % collection_name
+ )
+ parser.add_argument(
+ '--not-tags-any',
+ metavar='<tag>[,<tag>,...]',
+ action=_CommaListAction,
+ help=_('Exclude %s which have any given tag(s) '
+ '(Comma-separated list of tags)') % collection_name
+ )
+
+
+def get_tag_filtering_args(parsed_args, args):
+ if parsed_args.tags:
+ args['tags'] = ','.join(parsed_args.tags)
+ if parsed_args.tags_any:
+ args['tags-any'] = ','.join(parsed_args.tags_any)
+ if parsed_args.not_tags:
+ args['not-tags'] = ','.join(parsed_args.not_tags)
+ if parsed_args.not_tags_any:
+ args['not-tags-any'] = ','.join(parsed_args.not_tags_any)
+
+
+def add_tag_option_to_parser_for_create(parser, resource_name):
+ tag_group = parser.add_mutually_exclusive_group()
+ tag_group.add_argument(
+ '--tag',
+ action='append',
+ dest='tags',
+ metavar='<tag>',
+ default=[],
+ help=_('Tag to be added to the %s '
+ '(repeat option to set multiple tags)') % resource_name
+ )
+
+
+def add_tag_option_to_parser_for_set(parser, resource_name):
+ parser.add_argument(
+ '--tag',
+ action='append',
+ dest='tags',
+ metavar='<tag>',
+ default=[],
+ help=_('Tag to be added to the %s '
+ '(repeat option to set multiple tags)') % resource_name
+ )
+ parser.add_argument(
+ '--clear-tags',
+ action='store_true',
+ help=_('Clear tags associated with the %s. Specify '
+ 'both --tag and --clear-tags to overwrite '
+ 'current tags') % resource_name
+ )
+ parser.add_argument(
+ '--remove-tag',
+ metavar='<tag>',
+ default=[],
+ help=_('Tag to be deleted from the %s '
+ '(repeat option to delete multiple tags)') % resource_name
+ )
+
+
+def update_tags_in_args(parsed_args, obj, args):
+ if parsed_args.clear_tags:
+ args['tags'] = []
+ obj.tags = []
+ if parsed_args.remove_tag:
+ if parsed_args.remove_tag in obj.tags:
+ obj.tags.remove(parsed_args.remove_tag)
+ args['tags'] = list(set(obj.tags))
+ return
+ if parsed_args.tags:
+ args['tags'] = list(set(obj.tags).union(
+ set(parsed_args.tags)))
diff --git a/openstackclient/identity/v3/token.py b/openstackclient/identity/v3/token.py
index effb9e35..1933ecad 100644
--- a/openstackclient/identity/v3/token.py
+++ b/openstackclient/identity/v3/token.py
@@ -192,6 +192,12 @@ class IssueToken(command.ShowOne):
data['user_id'] = auth_ref.user_id
if auth_ref.domain_id:
data['domain_id'] = auth_ref.domain_id
+ if auth_ref.system_scoped:
+ # NOTE(lbragstad): This could change in the future when, or if,
+ # keystone supports the ability to scope to a subset of the entire
+ # deployment system. When that happens, this will have to relay
+ # scope information and IDs like we do for projects and domains.
+ data['system'] = 'all'
return zip(*sorted(six.iteritems(data)))
diff --git a/openstackclient/image/v1/image.py b/openstackclient/image/v1/image.py
index 7a8e67bf..7ecaa3ef 100644
--- a/openstackclient/image/v1/image.py
+++ b/openstackclient/image/v1/image.py
@@ -21,11 +21,6 @@ import logging
import os
import sys
-if os.name == "nt":
- import msvcrt
-else:
- msvcrt = None
-
from glanceclient.common import utils as gc_utils
from osc_lib.cli import parseractions
from osc_lib.command import command
@@ -35,6 +30,11 @@ import six
from openstackclient.api import utils as api_utils
from openstackclient.i18n import _
+if os.name == "nt":
+ import msvcrt
+else:
+ msvcrt = None
+
CONTAINER_CHOICES = ["ami", "ari", "aki", "bare", "docker", "ova", "ovf"]
DEFAULT_CONTAINER_FORMAT = 'bare'
diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py
index 7e6a7aa1..4a51062f 100644
--- a/openstackclient/image/v2/image.py
+++ b/openstackclient/image/v2/image.py
@@ -17,7 +17,6 @@
import argparse
import logging
-import sys
from glanceclient.common import utils as gc_utils
from osc_lib.cli import parseractions
@@ -592,6 +591,39 @@ class ListImage(command.Lister):
)
+class ListImageProjects(command.Lister):
+ _description = _("List projects associated with image")
+
+ def get_parser(self, prog_name):
+ parser = super(ListImageProjects, self).get_parser(prog_name)
+ parser.add_argument(
+ "image",
+ metavar="<image>",
+ help=_("Image (name or ID)"),
+ )
+ common.add_project_domain_option_to_parser(parser)
+ return parser
+
+ def take_action(self, parsed_args):
+ image_client = self.app.client_manager.image
+ columns = (
+ "Image ID",
+ "Member ID",
+ "Status"
+ )
+
+ image_id = utils.find_resource(
+ image_client.images,
+ parsed_args.image).id
+
+ data = image_client.image_members.list(image_id)
+
+ return (columns,
+ (utils.get_item_properties(
+ s, columns,
+ ) for s in data))
+
+
class RemoveProjectImage(command.Command):
_description = _("Disassociate project with image")
@@ -653,7 +685,7 @@ class SaveImage(command.Command):
if data.wrapped is None:
msg = _('Image %s has no data.') % image.id
LOG.error(msg)
- sys.stdout.write(msg + '\n')
+ self.app.stdout.write(msg + '\n')
raise SystemExit
gc_utils.save_image(data, parsed_args.file)
diff --git a/openstackclient/locale/tr_TR/LC_MESSAGES/openstackclient.po b/openstackclient/locale/tr_TR/LC_MESSAGES/openstackclient.po
index f3470f18..b44baf88 100644
--- a/openstackclient/locale/tr_TR/LC_MESSAGES/openstackclient.po
+++ b/openstackclient/locale/tr_TR/LC_MESSAGES/openstackclient.po
@@ -2,18 +2,18 @@
# işbaran akçayır <isbaran@gmail.com>, 2017. #zanata
msgid ""
msgstr ""
-"Project-Id-Version: python-openstackclient 3.12.1.dev21\n"
+"Project-Id-Version: python-openstackclient VERSION\n"
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
-"POT-Creation-Date: 2017-08-14 16:23+0000\n"
+"POT-Creation-Date: 2018-02-25 01:10+0000\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"PO-Revision-Date: 2017-08-15 12:09+0000\n"
"Last-Translator: Andreas Jaeger <jaegerandi@gmail.com>\n"
"Language-Team: Turkish (Turkey)\n"
-"Language: tr-TR\n"
+"Language: tr_TR\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
-"X-Generator: Zanata 3.9.6\n"
+"X-Generator: Zanata 4.3.3\n"
"X-POOTLE-MTIME: 1502656444.000000\n"
#, python-format
@@ -308,12 +308,6 @@ msgstr ""
msgid "Activate the image"
msgstr "İmajı aktifleştir"
-msgid ""
-"Add a policy to <name> ('affinity' or 'anti-affinity', default to 'affinity')"
-msgstr ""
-"<name>'e bir politika ekle ('affinity' veya 'anti-affinity', varsayılan "
-"'affinity' için)"
-
msgid "Add a port to a router"
msgstr "Yönlendiriciye bir bağlantı noktası ekle"
@@ -2552,9 +2546,6 @@ msgstr "Servise göre filtrele (tür, isim veya ID)"
msgid "Filter credentials by <user> (name or ID)"
msgstr "Kimlik bilgilerini <user>'ya göre filtrele (isim veya ID)"
-msgid "Filter credentials by type: cert, ec2"
-msgstr "Kimlik bilgilerinin türe göre filtrele: cert, ec2"
-
msgid "Filter group list by <domain> (name or ID)"
msgstr "Grup listesini <domain>'e göre filtrele (isim veya ID)"
@@ -2817,15 +2808,6 @@ msgstr "Sunucuya eklenecek IP adresi (sadece isim)"
msgid "IP address to remove from server (name only)"
msgstr "Sunucudan kaldırılacak IP adresi (sadece isim)"
-msgid ""
-"IP protocol (ah, dccp, egp, esp, gre, icmp, igmp, ipv6-encap, ipv6-frag, "
-"ipv6-icmp, ipv6-nonxt, ipv6-opts, ipv6-route, ospf, pgm, rsvp, sctp, tcp, "
-"udp, udplite, vrrp and integer representations [0-255]; default: tcp)"
-msgstr ""
-"IP protokolü (ah, dccp, egp, esp, gre, icmp, igmp, ipv6-encap, ipv6-frag, "
-"ipv6-icmp, ipv6-nonxt, ipv6-opts, ipv6-route, ospf, pgm, rsvp, sctp, tcp, "
-"udp, udplite, vrrp ve tam sayı gösterimi [0-255]; varsayılan: tcp)"
-
msgid "IP protocol (icmp, tcp, udp; default: tcp)"
msgstr "IP protokolü (icmp, tcp, udp; varsayılan: tcp)"
@@ -4029,9 +4011,6 @@ msgstr "Yeni kap ismi"
msgid "New credential data"
msgstr "Yeni kimlik bilgileri verisi"
-msgid "New credential type: cert, ec2"
-msgstr "Yeni kimlik bilgisi türü: cert, ec2"
-
msgid "New domain description"
msgstr "Yeni alan tanımı"
@@ -4599,13 +4578,6 @@ msgstr "RX/TX faktörü (varsayılan 1.0)"
msgid "Read image data from standard input"
msgstr "Standart girdiden imaj verilerini oku"
-msgid ""
-"Reason for disabling the service (in quotas). Should be used with --disable "
-"option."
-msgstr ""
-"Servisi devre dışı bırakma sebebi (kota cinsinden). --disable seçeneği ile "
-"birlikte kullanılmalı."
-
msgid "Reason for disabling the service (should be used with --disable option)"
msgstr ""
"Servisi devredışı bırakmak için sebep (--disable seçeneği ile kullanılmalı)"
@@ -6046,9 +6018,6 @@ msgstr "Silinecek altaÄŸ(lar) (isim veya ID)"
msgid "Suspend server(s)"
msgstr "Sunucu(yu/ları) durdur"
-msgid "Swap space size in MB (default 0M)"
-msgstr "MB cinsinden takas alanı boyutu (varsayılan 0M)"
-
#, python-format
msgid "Tag to be added to the %s (repeat option to set multiple tags)"
msgstr ""
@@ -6342,17 +6311,6 @@ msgstr ""
"Proje özelliğini kaldır (birden fazla özellik kaldırmak için seçeneği "
"tekrarla)"
-msgid ""
-"Unset a property on this image (repeat option to set multiple properties)"
-msgstr ""
-"Bu imajdaki bir özelliği kaldır (birden fazla özelliği ayarlamak için "
-"seçeneği tekrarla)"
-
-msgid "Unset a tag on this image (repeat option to set multiple tags)"
-msgstr ""
-"Bu imajdan etiketi kaldır (birden fazla etiket ayarlamak için seçeneği "
-"tekrarla)"
-
msgid "Unset account properties"
msgstr "Hesap özelliklerinin ayarını kaldır"
diff --git a/openstackclient/network/common.py b/openstackclient/network/common.py
index eca0de3c..37bf1406 100644
--- a/openstackclient/network/common.py
+++ b/openstackclient/network/common.py
@@ -48,7 +48,10 @@ class NetworkAndComputeCommand(command.Command):
parser = super(NetworkAndComputeCommand, self).get_parser(prog_name)
parser = self.update_parser_common(parser)
LOG.debug('common parser: %s', parser)
- if self.app.client_manager.is_network_endpoint_enabled():
+ if (
+ self.app is None or
+ self.app.client_manager.is_network_endpoint_enabled()
+ ):
return self.update_parser_network(parser)
else:
return self.update_parser_compute(parser)
diff --git a/openstackclient/network/v2/floating_ip.py b/openstackclient/network/v2/floating_ip.py
index a6f03404..9019e01d 100644
--- a/openstackclient/network/v2/floating_ip.py
+++ b/openstackclient/network/v2/floating_ip.py
@@ -13,8 +13,6 @@
"""IP Floating action implementations"""
-import logging
-
from osc_lib.command import command
from osc_lib import utils
@@ -22,6 +20,7 @@ from openstackclient.i18n import _
from openstackclient.identity import common as identity_common
from openstackclient.network import common
from openstackclient.network import sdk_utils
+from openstackclient.network.v2 import _tag
def _get_network_columns(item):
@@ -139,11 +138,14 @@ class CreateFloatingIP(common.NetworkAndComputeShowOne):
help=_("Owner's project (name or ID)")
)
identity_common.add_project_domain_option_to_parser(parser)
+ _tag.add_tag_option_to_parser_for_create(parser, _('floating IP'))
return parser
def take_action_network(self, client, parsed_args):
attrs = _get_attrs(self.app.client_manager, parsed_args)
obj = client.create_ip(**attrs)
+ # tags cannot be set when created, so tags need to be set later.
+ _tag.update_tags_for_set(client, obj, parsed_args)
display_columns, columns = _get_network_columns(obj)
data = utils.get_item_properties(obj, columns)
return (display_columns, data)
@@ -155,30 +157,6 @@ class CreateFloatingIP(common.NetworkAndComputeShowOne):
return (columns, data)
-class CreateIPFloating(CreateFloatingIP):
- _description = _("Create floating IP")
-
- # TODO(tangchen): Remove this class and ``ip floating create`` command
- # two cycles after Mitaka.
-
- # This notifies cliff to not display the help for this command
- deprecated = True
-
- log = logging.getLogger('deprecated')
-
- def take_action_network(self, client, parsed_args):
- self.log.warning(_('This command has been deprecated. '
- 'Please use "floating ip create" instead.'))
- return super(CreateIPFloating, self).take_action_network(
- client, parsed_args)
-
- def take_action_compute(self, client, parsed_args):
- self.log.warning(_('This command has been deprecated. '
- 'Please use "floating ip create" instead.'))
- return super(CreateIPFloating, self).take_action_compute(
- client, parsed_args)
-
-
class DeleteFloatingIP(common.NetworkAndComputeDelete):
_description = _("Delete floating IP(s)")
@@ -206,30 +184,6 @@ class DeleteFloatingIP(common.NetworkAndComputeDelete):
client.api.floating_ip_delete(self.r)
-class DeleteIPFloating(DeleteFloatingIP):
- _description = _("Delete floating IP(s)")
-
- # TODO(tangchen): Remove this class and ``ip floating delete`` command
- # two cycles after Mitaka.
-
- # This notifies cliff to not display the help for this command
- deprecated = True
-
- log = logging.getLogger('deprecated')
-
- def take_action_network(self, client, parsed_args):
- self.log.warning(_('This command has been deprecated. '
- 'Please use "floating ip delete" instead.'))
- return super(DeleteIPFloating, self).take_action_network(
- client, parsed_args)
-
- def take_action_compute(self, client, parsed_args):
- self.log.warning(_('This command has been deprecated. '
- 'Please use "floating ip delete" instead.'))
- return super(DeleteIPFloating, self).take_action_compute(
- client, parsed_args)
-
-
class ListFloatingIP(common.NetworkAndComputeLister):
# TODO(songminglong): Use SDK resource mapped attribute names once
# the OSC minimum requirements include SDK 1.0
@@ -280,6 +234,7 @@ class ListFloatingIP(common.NetworkAndComputeLister):
help=_("List floating IP(s) according to "
"given router (name or ID)")
)
+ _tag.add_tag_filtering_option_to_parser(parser, _('floating IP'))
return parser
@@ -308,11 +263,13 @@ class ListFloatingIP(common.NetworkAndComputeLister):
'router_id',
'status',
'description',
+ 'tags',
)
headers = headers + (
'Router',
'Status',
'Description',
+ 'Tags',
)
query = {}
@@ -342,6 +299,8 @@ class ListFloatingIP(common.NetworkAndComputeLister):
ignore_missing=False)
query['router_id'] = router.id
+ _tag.get_tag_filtering_args(parsed_args, query)
+
data = client.ips(**query)
return (headers,
@@ -375,30 +334,6 @@ class ListFloatingIP(common.NetworkAndComputeLister):
) for s in data))
-class ListIPFloating(ListFloatingIP):
- _description = _("List floating IP(s)")
-
- # TODO(tangchen): Remove this class and ``ip floating list`` command
- # two cycles after Mitaka.
-
- # This notifies cliff to not display the help for this command
- deprecated = True
-
- log = logging.getLogger('deprecated')
-
- def take_action_network(self, client, parsed_args):
- self.log.warning(_('This command has been deprecated. '
- 'Please use "floating ip list" instead.'))
- return super(ListIPFloating, self).take_action_network(
- client, parsed_args)
-
- def take_action_compute(self, client, parsed_args):
- self.log.warning(_('This command has been deprecated. '
- 'Please use "floating ip list" instead.'))
- return super(ListIPFloating, self).take_action_compute(
- client, parsed_args)
-
-
class SetFloatingIP(command.Command):
_description = _("Set floating IP Properties")
@@ -412,7 +347,7 @@ class SetFloatingIP(command.Command):
'--port',
metavar='<port>',
required=True,
- help=_("Assocaite the floating IP with port (name or ID)")),
+ help=_("Associate the floating IP with port (name or ID)")),
parser.add_argument(
'--fixed-ip-address',
metavar='<ip-address>',
@@ -431,6 +366,9 @@ class SetFloatingIP(command.Command):
action='store_true',
help=_("Remove the QoS policy attached to the floating IP")
)
+
+ _tag.add_tag_option_to_parser_for_set(parser, _('floating IP'))
+
return parser
def take_action(self, parsed_args):
@@ -453,7 +391,11 @@ class SetFloatingIP(command.Command):
if 'no_qos_policy' in parsed_args and parsed_args.no_qos_policy:
attrs['qos_policy_id'] = None
- client.update_ip(obj, **attrs)
+ if attrs:
+ client.update_ip(obj, **attrs)
+
+ # tags is a subresource and it needs to be updated separately.
+ _tag.update_tags_for_set(client, obj, parsed_args)
class ShowFloatingIP(common.NetworkAndComputeShowOne):
@@ -483,30 +425,6 @@ class ShowFloatingIP(common.NetworkAndComputeShowOne):
return (columns, data)
-class ShowIPFloating(ShowFloatingIP):
- _description = _("Display floating IP details")
-
- # TODO(tangchen): Remove this class and ``ip floating show`` command
- # two cycles after Mitaka.
-
- # This notifies cliff to not display the help for this command
- deprecated = True
-
- log = logging.getLogger('deprecated')
-
- def take_action_network(self, client, parsed_args):
- self.log.warning(_('This command has been deprecated. '
- 'Please use "floating ip show" instead.'))
- return super(ShowIPFloating, self).take_action_network(
- client, parsed_args)
-
- def take_action_compute(self, client, parsed_args):
- self.log.warning(_('This command has been deprecated. '
- 'Please use "floating ip show" instead.'))
- return super(ShowIPFloating, self).take_action_compute(
- client, parsed_args)
-
-
class UnsetFloatingIP(command.Command):
_description = _("Unset floating IP Properties")
@@ -528,6 +446,8 @@ class UnsetFloatingIP(command.Command):
default=False,
help=_("Remove the QoS policy attached to the floating IP")
)
+ _tag.add_tag_option_to_parser_for_unset(parser, _('floating IP'))
+
return parser
def take_action(self, parsed_args):
@@ -544,3 +464,6 @@ class UnsetFloatingIP(command.Command):
if attrs:
client.update_ip(obj, **attrs)
+
+ # tags is a subresource and it needs to be updated separately.
+ _tag.update_tags_for_unset(client, obj, parsed_args)
diff --git a/openstackclient/network/v2/floating_ip_pool.py b/openstackclient/network/v2/floating_ip_pool.py
index ebb15da8..32852004 100644
--- a/openstackclient/network/v2/floating_ip_pool.py
+++ b/openstackclient/network/v2/floating_ip_pool.py
@@ -13,7 +13,6 @@
"""Floating IP Pool action implementations"""
-import logging
from osc_lib import exceptions
from osc_lib import utils
@@ -40,27 +39,3 @@ class ListFloatingIPPool(common.NetworkAndComputeLister):
(utils.get_dict_properties(
s, columns,
) for s in data))
-
-
-class ListIPFloatingPool(ListFloatingIPPool):
- _description = _("List pools of floating IP addresses")
-
- # TODO(tangchen): Remove this class and ``ip floating pool list`` command
- # two cycles after Mitaka.
-
- # This notifies cliff to not display the help for this command
- deprecated = True
-
- log = logging.getLogger('deprecated')
-
- def take_action_network(self, client, parsed_args):
- self.log.warning(_('This command has been deprecated. '
- 'Please use "floating ip pool list" instead.'))
- return super(ListIPFloatingPool, self).take_action_network(
- client, parsed_args)
-
- def take_action_compute(self, client, parsed_args):
- self.log.warning(_('This command has been deprecated. '
- 'Please use "floating ip pool list" instead.'))
- return super(ListIPFloatingPool, self).take_action_compute(
- client, parsed_args)
diff --git a/openstackclient/network/v2/network.py b/openstackclient/network/v2/network.py
index 4c1725c5..d1c7f005 100644
--- a/openstackclient/network/v2/network.py
+++ b/openstackclient/network/v2/network.py
@@ -107,6 +107,10 @@ def _get_attrs_network(client_manager, parsed_args):
if parsed_args.description:
attrs['description'] = parsed_args.description
+ # set mtu
+ if parsed_args.mtu:
+ attrs['mtu'] = parsed_args.mtu
+
# update_external_network_options
if parsed_args.internal:
attrs['router:external'] = False
@@ -217,6 +221,11 @@ class CreateNetwork(common.NetworkAndComputeShowOne):
metavar='<description>',
help=_("Set network description")
)
+ parser.add_argument(
+ '--mtu',
+ metavar='<mtu>',
+ help=_("Set network mtu")
+ )
identity_common.add_project_domain_option_to_parser(parser)
parser.add_argument(
'--availability-zone-hint',
@@ -619,6 +628,11 @@ class SetNetwork(command.Command):
metavar="<description",
help=_("Set network description")
)
+ parser.add_argument(
+ '--mtu',
+ metavar="<mtu",
+ help=_("Set network mtu")
+ )
port_security_group = parser.add_mutually_exclusive_group()
port_security_group.add_argument(
'--enable-port-security',
diff --git a/openstackclient/network/v2/network_qos_rule.py b/openstackclient/network/v2/network_qos_rule.py
index f50e58b3..9c4275a8 100644
--- a/openstackclient/network/v2/network_qos_rule.py
+++ b/openstackclient/network/v2/network_qos_rule.py
@@ -29,11 +29,11 @@ RULE_TYPE_MINIMUM_BANDWIDTH = 'minimum-bandwidth'
MANDATORY_PARAMETERS = {
RULE_TYPE_MINIMUM_BANDWIDTH: {'min_kbps', 'direction'},
RULE_TYPE_DSCP_MARKING: {'dscp_mark'},
- RULE_TYPE_BANDWIDTH_LIMIT: {'max_kbps', 'max_burst_kbps'}}
+ RULE_TYPE_BANDWIDTH_LIMIT: {'max_kbps'}}
OPTIONAL_PARAMETERS = {
RULE_TYPE_MINIMUM_BANDWIDTH: set(),
RULE_TYPE_DSCP_MARKING: set(),
- RULE_TYPE_BANDWIDTH_LIMIT: {'direction'}}
+ RULE_TYPE_BANDWIDTH_LIMIT: {'direction', 'max_burst_kbps'}}
DIRECTION_EGRESS = 'egress'
DIRECTION_INGRESS = 'ingress'
DSCP_VALID_MARKS = [0, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32,
@@ -62,11 +62,11 @@ def _check_type_parameters(attrs, type, is_create):
notreq_params -= type_params
if is_create and None in map(attrs.get, req_params):
msg = (_('"Create" rule command for type "%(rule_type)s" requires '
- 'arguments %(args)s') %
+ 'arguments: %(args)s') %
{'rule_type': type, 'args': ", ".join(sorted(req_params))})
raise exceptions.CommandError(msg)
if set(attrs.keys()) & notreq_params:
- msg = (_('Rule type "%(rule_type)s" only requires arguments %(args)s')
+ msg = (_('Rule type "%(rule_type)s" only requires arguments: %(args)s')
% {'rule_type': type, 'args': ", ".join(sorted(type_params))})
raise exceptions.CommandError(msg)
diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py
index 032e1787..f13ee7b9 100644
--- a/openstackclient/network/v2/port.py
+++ b/openstackclient/network/v2/port.py
@@ -127,6 +127,8 @@ def _get_attrs(client_manager, parsed_args):
if parsed_args.mac_address is not None:
attrs['mac_address'] = parsed_args.mac_address
+ if parsed_args.dns_domain is not None:
+ attrs['dns_domain'] = parsed_args.dns_domain
if parsed_args.dns_name is not None:
attrs['dns_name'] = parsed_args.dns_name
# It is possible that name is not updated during 'port set'
@@ -269,6 +271,12 @@ def _add_updatable_args(parser):
help=argparse.SUPPRESS,
)
parser.add_argument(
+ '--dns-domain',
+ metavar='dns-domain',
+ help=_("Set DNS domain to this port "
+ "(requires dns_domain extension for ports)")
+ )
+ parser.add_argument(
'--dns-name',
metavar='dns-name',
help=_("Set DNS name to this port "
diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py
index caf3236a..f0a51967 100644
--- a/openstackclient/network/v2/router.py
+++ b/openstackclient/network/v2/router.py
@@ -86,8 +86,8 @@ def _get_attrs(client_manager, parsed_args):
attrs['distributed'] = False
if parsed_args.distributed:
attrs['distributed'] = True
- if ('availability_zone_hints' in parsed_args
- and parsed_args.availability_zone_hints is not None):
+ if ('availability_zone_hints' in parsed_args and
+ parsed_args.availability_zone_hints is not None):
attrs['availability_zone_hints'] = parsed_args.availability_zone_hints
if parsed_args.description is not None:
attrs['description'] = parsed_args.description
diff --git a/openstackclient/network/v2/subnet.py b/openstackclient/network/v2/subnet.py
index 2c71e1e0..9c56186f 100644
--- a/openstackclient/network/v2/subnet.py
+++ b/openstackclient/network/v2/subnet.py
@@ -167,6 +167,7 @@ def convert_entries_to_gateway(entries):
def _get_attrs(client_manager, parsed_args, is_create=True):
attrs = {}
+ client = client_manager.network
if 'name' in parsed_args and parsed_args.name is not None:
attrs['name'] = str(parsed_args.name)
@@ -179,7 +180,6 @@ def _get_attrs(client_manager, parsed_args, is_create=True):
parsed_args.project_domain,
).id
attrs['tenant_id'] = project_id
- client = client_manager.network
attrs['network_id'] = client.find_network(parsed_args.network,
ignore_missing=False).id
if parsed_args.subnet_pool is not None:
@@ -200,10 +200,10 @@ def _get_attrs(client_manager, parsed_args, is_create=True):
attrs['ipv6_ra_mode'] = parsed_args.ipv6_ra_mode
if parsed_args.ipv6_address_mode is not None:
attrs['ipv6_address_mode'] = parsed_args.ipv6_address_mode
- if parsed_args.network_segment is not None:
- attrs['segment_id'] = client.find_segment(
- parsed_args.network_segment, ignore_missing=False).id
+ if parsed_args.network_segment is not None:
+ attrs['segment_id'] = client.find_segment(
+ parsed_args.network_segment, ignore_missing=False).id
if 'gateway' in parsed_args and parsed_args.gateway is not None:
gateway = parsed_args.gateway.lower()
@@ -559,6 +559,14 @@ class SetSubnet(command.Command):
"e.g.: --gateway 192.168.9.1, --gateway none.")
)
parser.add_argument(
+ '--network-segment',
+ metavar='<network-segment>',
+ help=_("Network segment to associate with this subnet (name or "
+ "ID). It is only allowed to set the segment if the current "
+ "value is `None`, the network must also have only one "
+ "segment and only one subnet can exist on the network.")
+ )
+ parser.add_argument(
'--description',
metavar='<description>',
help=_("Set subnet description")
@@ -581,7 +589,7 @@ class SetSubnet(command.Command):
if not parsed_args.no_host_route:
attrs['host_routes'] += obj.host_routes
elif parsed_args.no_host_route:
- attrs['host_routes'] = ''
+ attrs['host_routes'] = []
if 'allocation_pools' in attrs:
if not parsed_args.no_allocation_pool:
attrs['allocation_pools'] += obj.allocation_pools
diff --git a/openstackclient/tests/functional/common/test_help.py b/openstackclient/tests/functional/common/test_help.py
index 7f274099..3a9aef9e 100644
--- a/openstackclient/tests/functional/common/test_help.py
+++ b/openstackclient/tests/functional/common/test_help.py
@@ -60,8 +60,12 @@ class HelpTests(base.TestCase):
"""Check server commands in main help message."""
raw_output = self.openstack('help')
for command, description in self.SERVER_COMMANDS:
- self.assertIn(command, raw_output)
- self.assertIn(description, raw_output)
+ msg = 'Command: %s not found in help output:\n%s' % (
+ command, raw_output)
+ self.assertIn(command, raw_output, msg)
+ msg = 'Description: %s not found in help output:\n%s' % (
+ description, raw_output)
+ self.assertIn(description, raw_output, msg)
def test_server_only_help(self):
"""Check list of server-related commands only."""
diff --git a/openstackclient/tests/functional/common/test_module.py b/openstackclient/tests/functional/common/test_module.py
index d589f19c..41aabb7f 100644
--- a/openstackclient/tests/functional/common/test_module.py
+++ b/openstackclient/tests/functional/common/test_module.py
@@ -46,7 +46,7 @@ class ModuleTest(base.TestCase):
class CommandTest(base.TestCase):
"""Functional tests for openstackclient command list."""
GROUPS = [
- 'openstack.volume.v2',
+ 'openstack.volume.v3',
'openstack.network.v2',
'openstack.image.v2',
'openstack.identity.v3',
diff --git a/openstackclient/tests/functional/identity/v3/common.py b/openstackclient/tests/functional/identity/v3/common.py
index 33cb5d86..54132be5 100644
--- a/openstackclient/tests/functional/identity/v3/common.py
+++ b/openstackclient/tests/functional/identity/v3/common.py
@@ -52,6 +52,8 @@ class IdentityTests(base.TestCase):
'id', 'relay_state_prefix', 'sp_url']
SERVICE_PROVIDER_LIST_HEADERS = ['ID', 'Enabled', 'Description',
'Auth URL']
+ IMPLIED_ROLE_LIST_HEADERS = ['Prior Role ID', 'Prior Role Name',
+ 'Implied Role ID', 'Implied Role Name']
@classmethod
def setUpClass(cls):
@@ -149,6 +151,17 @@ class IdentityTests(base.TestCase):
self.assertEqual(role_name, role['name'])
return role_name
+ def _create_dummy_implied_role(self, add_clean_up=True):
+ role_name = self._create_dummy_role(add_clean_up)
+ implied_role_name = self._create_dummy_role(add_clean_up)
+ self.openstack(
+ 'implied role create '
+ '--implied-role %(implied_role)s '
+ '%(role)s' % {'implied_role': implied_role_name,
+ 'role': role_name})
+
+ return implied_role_name, role_name
+
def _create_dummy_group(self, add_clean_up=True):
group_name = data_utils.rand_name('TestGroup')
description = data_utils.rand_name('description')
diff --git a/openstackclient/tests/functional/identity/v3/test_application_credential.py b/openstackclient/tests/functional/identity/v3/test_application_credential.py
new file mode 100644
index 00000000..daf64607
--- /dev/null
+++ b/openstackclient/tests/functional/identity/v3/test_application_credential.py
@@ -0,0 +1,143 @@
+# Copyright 2018 SUSE Linux GmbH
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import datetime
+
+from tempest.lib.common.utils import data_utils
+
+from openstackclient.tests.functional.identity.v3 import common
+
+
+class ApplicationCredentialTests(common.IdentityTests):
+
+ APPLICATION_CREDENTIAL_FIELDS = ['id', 'name', 'project_id',
+ 'description', 'roles', 'expires_at',
+ 'unrestricted']
+ APPLICATION_CREDENTIAL_LIST_HEADERS = ['ID', 'Name', 'Project ID',
+ 'Description', 'Expires At']
+
+ def test_application_credential_create(self):
+ name = data_utils.rand_name('name')
+ raw_output = self.openstack('application credential create %(name)s'
+ % {'name': name})
+ self.addCleanup(
+ self.openstack,
+ 'application credential delete %(name)s' % {'name': name})
+ items = self.parse_show(raw_output)
+ self.assert_show_fields(items, self.APPLICATION_CREDENTIAL_FIELDS)
+
+ def _create_role_assignments(self):
+ try:
+ user = self.openstack('configuration show -f value'
+ ' -c auth.username')
+ except Exception:
+ user = self.openstack('configuration show -f value'
+ ' -c auth.user_id')
+ try:
+ user_domain = self.openstack('configuration show -f value'
+ ' -c auth.user_domain_name')
+ except Exception:
+ user_domain = self.openstack('configuration show -f value'
+ ' -c auth.user_domain_id')
+ try:
+ project = self.openstack('configuration show -f value'
+ ' -c auth.project_name')
+ except Exception:
+ project = self.openstack('configuration show -f value'
+ ' -c auth.project_id')
+ try:
+ project_domain = self.openstack('configuration show -f value'
+ ' -c auth.project_domain_name')
+ except Exception:
+ project_domain = self.openstack('configuration show -f value'
+ ' -c auth.project_domain_id')
+ role1 = self._create_dummy_role()
+ role2 = self._create_dummy_role()
+ for role in role1, role2:
+ self.openstack('role add'
+ ' --user %(user)s'
+ ' --user-domain %(user_domain)s'
+ ' --project %(project)s'
+ ' --project-domain %(project_domain)s'
+ ' %(role)s'
+ % {'user': user,
+ 'user_domain': user_domain,
+ 'project': project,
+ 'project_domain': project_domain,
+ 'role': role})
+ self.addCleanup(self.openstack,
+ 'role remove'
+ ' --user %(user)s'
+ ' --user-domain %(user_domain)s'
+ ' --project %(project)s'
+ ' --project-domain %(project_domain)s'
+ ' %(role)s'
+ % {'user': user,
+ 'user_domain': user_domain,
+ 'project': project,
+ 'project_domain': project_domain,
+ 'role': role})
+ return role1, role2
+
+ def test_application_credential_create_with_options(self):
+ name = data_utils.rand_name('name')
+ secret = data_utils.rand_name('secret')
+ description = data_utils.rand_name('description')
+ tomorrow = (datetime.datetime.utcnow() +
+ datetime.timedelta(days=1)).strftime('%Y-%m-%dT%H:%M:%S%z')
+ role1, role2 = self._create_role_assignments()
+ raw_output = self.openstack('application credential create %(name)s'
+ ' --secret %(secret)s'
+ ' --description %(description)s'
+ ' --expiration %(tomorrow)s'
+ ' --role %(role1)s'
+ ' --role %(role2)s'
+ ' --unrestricted'
+ % {'name': name,
+ 'secret': secret,
+ 'description': description,
+ 'tomorrow': tomorrow,
+ 'role1': role1,
+ 'role2': role2})
+ self.addCleanup(
+ self.openstack,
+ 'application credential delete %(name)s' % {'name': name})
+ items = self.parse_show(raw_output)
+ self.assert_show_fields(items, self.APPLICATION_CREDENTIAL_FIELDS)
+
+ def test_application_credential_delete(self):
+ name = data_utils.rand_name('name')
+ self.openstack('application credential create %(name)s'
+ % {'name': name})
+ raw_output = self.openstack('application credential delete '
+ '%(name)s' % {'name': name})
+ self.assertEqual(0, len(raw_output))
+
+ def test_application_credential_list(self):
+ raw_output = self.openstack('application credential list')
+ items = self.parse_listing(raw_output)
+ self.assert_table_structure(
+ items, self.APPLICATION_CREDENTIAL_LIST_HEADERS)
+
+ def test_application_credential_show(self):
+ name = data_utils.rand_name('name')
+ raw_output = self.openstack('application credential create %(name)s'
+ % {'name': name})
+ self.addCleanup(
+ self.openstack,
+ 'application credential delete %(name)s' % {'name': name})
+ raw_output = self.openstack('application credential show '
+ '%(name)s' % {'name': name})
+ items = self.parse_show(raw_output)
+ self.assert_show_fields(items, self.APPLICATION_CREDENTIAL_FIELDS)
diff --git a/openstackclient/tests/functional/identity/v3/test_role.py b/openstackclient/tests/functional/identity/v3/test_role.py
index ab8af9c0..fb9e0614 100644
--- a/openstackclient/tests/functional/identity/v3/test_role.py
+++ b/openstackclient/tests/functional/identity/v3/test_role.py
@@ -143,3 +143,28 @@ class RoleTests(common.IdentityTests):
'role': role_name})
self.assertEqual(0, len(add_raw_output))
self.assertEqual(0, len(remove_raw_output))
+
+ def test_implied_role_list(self):
+ self._create_dummy_implied_role()
+ raw_output = self.openstack('implied role list')
+ items = self.parse_listing(raw_output)
+ self.assert_table_structure(items, self.IMPLIED_ROLE_LIST_HEADERS)
+ self.assertEqual(3, len(items))
+
+ def test_implied_role_create(self):
+ role_name = self._create_dummy_role()
+ implied_role_name = self._create_dummy_role()
+ self.openstack(
+ 'implied role create '
+ '--implied-role %(implied_role)s '
+ '%(role)s' % {'implied_role': implied_role_name,
+ 'role': role_name})
+
+ def test_implied_role_delete(self):
+ implied_role_name, role_name = self._create_dummy_implied_role()
+ raw_output = self.openstack(
+ 'implied role delete '
+ '--implied-role %(implied_role)s '
+ '%(role)s' % {'implied_role': implied_role_name,
+ 'role': role_name})
+ self.assertEqual(0, len(raw_output))
diff --git a/openstackclient/tests/functional/network/v2/test_network.py b/openstackclient/tests/functional/network/v2/test_network.py
index 40fb382a..9cef135f 100644
--- a/openstackclient/tests/functional/network/v2/test_network.py
+++ b/openstackclient/tests/functional/network/v2/test_network.py
@@ -209,7 +209,7 @@ class NetworkTests(common.NetworkTagTests):
cmd_output["description"],
)
- del_output = self.openstack('network delete ' + name1 + ' ' + name2)
+ del_output = self.openstack('network delete %s %s' % (name1, name2))
self.assertOutput('', del_output)
def test_network_list(self):
@@ -224,7 +224,7 @@ class NetworkTests(common.NetworkTagTests):
network_options +
name1
))
- self.addCleanup(self.openstack, 'network delete ' + name1)
+ self.addCleanup(self.openstack, 'network delete %s' % name1)
self.assertIsNotNone(cmd_output["id"])
if self.haz_network:
self.assertEqual(
@@ -264,10 +264,8 @@ class NetworkTests(common.NetworkTagTests):
else:
network_options = '--subnet 4.5.6.7/28 '
cmd_output = json.loads(self.openstack(
- 'network create -f json ' +
- '--share ' +
- network_options +
- name2
+ 'network create -f json --share %s%s' %
+ (network_options, name2)
))
self.addCleanup(self.openstack, 'network delete ' + name2)
self.assertIsNotNone(cmd_output["id"])
@@ -313,8 +311,7 @@ class NetworkTests(common.NetworkTagTests):
# Test list --long
if self.haz_network:
cmd_output = json.loads(self.openstack(
- "network list -f json " +
- "--long"
+ "network list -f json --long"
))
col_name = [x["Name"] for x in cmd_output]
self.assertIn(name1, col_name)
@@ -323,9 +320,7 @@ class NetworkTests(common.NetworkTagTests):
# Test list --long --enable
if self.haz_network:
cmd_output = json.loads(self.openstack(
- "network list -f json " +
- "--enable " +
- "--long"
+ "network list -f json --enable --long"
))
col_name = [x["Name"] for x in cmd_output]
self.assertIn(name1, col_name)
@@ -334,9 +329,7 @@ class NetworkTests(common.NetworkTagTests):
# Test list --long --disable
if self.haz_network:
cmd_output = json.loads(self.openstack(
- "network list -f json " +
- "--disable " +
- "--long"
+ "network list -f json --disable --long"
))
col_name = [x["Name"] for x in cmd_output]
self.assertNotIn(name1, col_name)
@@ -345,8 +338,7 @@ class NetworkTests(common.NetworkTagTests):
# Test list --share
if self.haz_network:
cmd_output = json.loads(self.openstack(
- "network list -f json " +
- "--share "
+ "network list -f json --share "
))
col_name = [x["Name"] for x in cmd_output]
self.assertNotIn(name1, col_name)
@@ -355,8 +347,7 @@ class NetworkTests(common.NetworkTagTests):
# Test list --no-share
if self.haz_network:
cmd_output = json.loads(self.openstack(
- "network list -f json " +
- "--no-share "
+ "network list -f json --no-share "
))
col_name = [x["Name"] for x in cmd_output]
self.assertIn(name1, col_name)
@@ -368,12 +359,10 @@ class NetworkTests(common.NetworkTagTests):
name1 = uuid.uuid4().hex
cmd_output1 = json.loads(self.openstack(
- 'network create -f json ' +
- '--description aaaa ' +
- name1
+ 'network create -f json --description aaaa %s' % name1
))
- self.addCleanup(self.openstack, 'network delete ' + name1)
+ self.addCleanup(self.openstack, 'network delete %s' % name1)
# Get network ID
network_id = cmd_output1['id']
@@ -386,20 +375,19 @@ class NetworkTests(common.NetworkTagTests):
# Add Agent to Network
self.openstack(
- 'network agent add network --dhcp '
- + agent_id + ' ' + network_id
+ 'network agent add network --dhcp %s %s' % (agent_id, network_id)
)
# Test network list --agent
cmd_output3 = json.loads(self.openstack(
- 'network list -f json --agent ' + agent_id
+ 'network list -f json --agent %s' % agent_id
))
# Cleanup
# Remove Agent from Network
self.openstack(
- 'network agent remove network --dhcp '
- + agent_id + ' ' + network_id
+ 'network agent remove network --dhcp %s %s' %
+ (agent_id, network_id)
)
# Assert
@@ -415,16 +403,16 @@ class NetworkTests(common.NetworkTagTests):
name = uuid.uuid4().hex
cmd_output = json.loads(self.openstack(
- 'network create -f json ' +
- '--description aaaa ' +
- '--enable ' +
- '--no-share ' +
- '--internal ' +
- '--no-default ' +
- '--enable-port-security ' +
+ 'network create -f json '
+ '--description aaaa '
+ '--enable '
+ '--no-share '
+ '--internal '
+ '--no-default '
+ '--enable-port-security %s' %
name
))
- self.addCleanup(self.openstack, 'network delete ' + name)
+ self.addCleanup(self.openstack, 'network delete %s' % name)
self.assertIsNotNone(cmd_output["id"])
self.assertEqual(
'aaaa',
@@ -453,12 +441,12 @@ class NetworkTests(common.NetworkTagTests):
)
raw_output = self.openstack(
- 'network set ' +
- '--description cccc ' +
- '--disable ' +
- '--share ' +
- '--external ' +
- '--disable-port-security ' +
+ 'network set '
+ '--description cccc '
+ '--disable '
+ '--share '
+ '--external '
+ '--disable-port-security %s' %
name
)
self.assertOutput('', raw_output)
diff --git a/openstackclient/tests/functional/network/v2/test_network_agent.py b/openstackclient/tests/functional/network/v2/test_network_agent.py
index 0c74ea1d..86769e0c 100644
--- a/openstackclient/tests/functional/network/v2/test_network_agent.py
+++ b/openstackclient/tests/functional/network/v2/test_network_agent.py
@@ -42,8 +42,7 @@ class NetworkAgentTests(common.NetworkTests):
# agent show
cmd_output = json.loads(self.openstack(
- 'network agent show -f json ' +
- agent_ids[0]
+ 'network agent show -f json %s' % agent_ids[0]
))
self.assertEqual(
agent_ids[0],
@@ -52,15 +51,12 @@ class NetworkAgentTests(common.NetworkTests):
# agent set
raw_output = self.openstack(
- 'network agent set ' +
- '--disable ' +
- agent_ids[0]
+ 'network agent set --disable %s' % agent_ids[0]
)
self.assertOutput('', raw_output)
cmd_output = json.loads(self.openstack(
- 'network agent show -f json ' +
- agent_ids[0]
+ 'network agent show -f json %s' % agent_ids[0]
))
self.assertEqual(
"DOWN",
@@ -68,15 +64,12 @@ class NetworkAgentTests(common.NetworkTests):
)
raw_output = self.openstack(
- 'network agent set ' +
- '--enable ' +
- agent_ids[0]
+ 'network agent set --enable %s' % agent_ids[0]
)
self.assertOutput('', raw_output)
cmd_output = json.loads(self.openstack(
- 'network agent show -f json ' +
- agent_ids[0]
+ 'network agent show -f json %s' % agent_ids[0]
))
self.assertEqual(
"UP",
@@ -98,12 +91,10 @@ class NetworkAgentListTests(common.NetworkTests):
name1 = uuid.uuid4().hex
cmd_output1 = json.loads(self.openstack(
- 'network create -f json ' +
- '--description aaaa ' +
- name1
+ 'network create -f json --description aaaa %s' % name1
))
- self.addCleanup(self.openstack, 'network delete ' + name1)
+ self.addCleanup(self.openstack, 'network delete %s' % name1)
# Get network ID
network_id = cmd_output1['id']
@@ -116,20 +107,20 @@ class NetworkAgentListTests(common.NetworkTests):
# Add Agent to Network
self.openstack(
- 'network agent add network --dhcp '
- + agent_id + ' ' + network_id
+ 'network agent add network --dhcp %s %s' %
+ (agent_id, network_id)
)
# Test network agent list --network
cmd_output3 = json.loads(self.openstack(
- 'network agent list -f json --network ' + network_id
+ 'network agent list -f json --network %s' % network_id
))
# Cleanup
# Remove Agent from Network
self.openstack(
- 'network agent remove network --dhcp '
- + agent_id + ' ' + network_id
+ 'network agent remove network --dhcp %s %s' %
+ (agent_id, network_id)
)
# Assert
@@ -142,9 +133,9 @@ class NetworkAgentListTests(common.NetworkTests):
"""Add agent to router, list agents on router, delete."""
name = uuid.uuid4().hex
cmd_output = json.loads(self.openstack(
- 'router create -f json ' + name))
+ 'router create -f json %s' % name))
- self.addCleanup(self.openstack, 'router delete ' + name)
+ self.addCleanup(self.openstack, 'router delete %s' % name)
# Get router ID
router_id = cmd_output['id']
# Get l3 agent id
@@ -157,19 +148,19 @@ class NetworkAgentListTests(common.NetworkTests):
# Add router to agent
self.openstack(
- 'network agent add router --l3 ' + agent_id + ' ' + router_id)
+ 'network agent add router --l3 %s %s' % (agent_id, router_id))
# Test router list --agent
cmd_output = json.loads(self.openstack(
- 'network agent list -f json --router ' + router_id))
+ 'network agent list -f json --router %s' % router_id))
agent_ids = [x['ID'] for x in cmd_output]
self.assertIn(agent_id, agent_ids)
# Remove router from agent
self.openstack(
- 'network agent remove router --l3 ' + agent_id + ' ' + router_id)
+ 'network agent remove router --l3 %s %s' % (agent_id, router_id))
cmd_output = json.loads(self.openstack(
- 'network agent list -f json --router ' + router_id))
+ 'network agent list -f json --router %s' % router_id))
agent_ids = [x['ID'] for x in cmd_output]
self.assertNotIn(agent_id, agent_ids)
diff --git a/openstackclient/tests/functional/network/v2/test_network_flavor.py b/openstackclient/tests/functional/network/v2/test_network_flavor.py
index 47e7b440..ba3de2cd 100644
--- a/openstackclient/tests/functional/network/v2/test_network_flavor.py
+++ b/openstackclient/tests/functional/network/v2/test_network_flavor.py
@@ -39,13 +39,13 @@ class NetworkFlavorTests(common.NetworkTests):
# Create Service Flavor
cmd_output2 = json.loads(self.openstack(
'network flavor profile create -f json --description '
- + 'fakedescription' + ' --enable --metainfo ' + 'Extrainfo'
+ 'fakedescription --enable --metainfo Extrainfo'
))
service_profile_id = cmd_output2.get('id')
- self.addCleanup(self.openstack, 'network flavor delete ' +
+ self.addCleanup(self.openstack, 'network flavor delete %s' %
flavor_id)
- self.addCleanup(self.openstack, 'network flavor profile delete ' +
+ self.addCleanup(self.openstack, 'network flavor profile delete %s' %
service_profile_id)
# Add flavor to service profile
self.openstack(
diff --git a/openstackclient/tests/functional/network/v2/test_network_qos_rule.py b/openstackclient/tests/functional/network/v2/test_network_qos_rule.py
index 770abe94..98e588e8 100644
--- a/openstackclient/tests/functional/network/v2/test_network_qos_rule.py
+++ b/openstackclient/tests/functional/network/v2/test_network_qos_rule.py
@@ -28,62 +28,60 @@ class NetworkQosRuleTestsMinimumBandwidth(common.NetworkTests):
if not self.haz_network:
self.skipTest("No Network service present")
- self.QOS_POLICY_NAME = 'qos_policy_' + uuid.uuid4().hex
+ self.QOS_POLICY_NAME = 'qos_policy_%s' % uuid.uuid4().hex
self.openstack(
- 'network qos policy create ' +
- self.QOS_POLICY_NAME
+ 'network qos policy create %s' % self.QOS_POLICY_NAME
)
self.addCleanup(self.openstack,
- 'network qos policy delete ' + self.QOS_POLICY_NAME)
+ 'network qos policy delete %s' % self.QOS_POLICY_NAME)
cmd_output = json.loads(self.openstack(
- 'network qos rule create -f json ' +
- '--type minimum-bandwidth ' +
- '--min-kbps 2800 ' +
- '--egress ' +
+ 'network qos rule create -f json '
+ '--type minimum-bandwidth '
+ '--min-kbps 2800 '
+ '--egress %s' %
self.QOS_POLICY_NAME
))
self.RULE_ID = cmd_output['id']
self.addCleanup(self.openstack,
- 'network qos rule delete ' +
- self.QOS_POLICY_NAME + ' ' +
- self.RULE_ID)
+ 'network qos rule delete %s %s' %
+ (self.QOS_POLICY_NAME, self.RULE_ID))
self.assertTrue(self.RULE_ID)
def test_qos_rule_create_delete(self):
# This is to check the output of qos rule delete
policy_name = uuid.uuid4().hex
- self.openstack('network qos policy create -f json ' + policy_name)
+ self.openstack('network qos policy create -f json %s' % policy_name)
self.addCleanup(self.openstack,
- 'network qos policy delete ' + policy_name)
+ 'network qos policy delete %s' % policy_name)
rule = json.loads(self.openstack(
- 'network qos rule create -f json ' +
- '--type minimum-bandwidth ' +
- '--min-kbps 2800 ' +
- '--egress ' + policy_name
+ 'network qos rule create -f json '
+ '--type minimum-bandwidth '
+ '--min-kbps 2800 '
+ '--egress %s' % policy_name
))
raw_output = self.openstack(
- 'network qos rule delete ' +
- policy_name + ' ' + rule['id'])
+ 'network qos rule delete %s %s' %
+ (policy_name, rule['id']))
self.assertEqual('', raw_output)
def test_qos_rule_list(self):
cmd_output = json.loads(self.openstack(
- 'network qos rule list -f json ' + self.QOS_POLICY_NAME))
+ 'network qos rule list -f json %s' % self.QOS_POLICY_NAME))
self.assertIn(self.RULE_ID, [rule['ID'] for rule in cmd_output])
def test_qos_rule_show(self):
cmd_output = json.loads(self.openstack(
- 'network qos rule show -f json ' +
- self.QOS_POLICY_NAME + ' ' + self.RULE_ID))
+ 'network qos rule show -f json %s %s' %
+ (self.QOS_POLICY_NAME, self.RULE_ID)))
self.assertEqual(self.RULE_ID, cmd_output['id'])
def test_qos_rule_set(self):
- self.openstack('network qos rule set --min-kbps 7500 ' +
- self.QOS_POLICY_NAME + ' ' + self.RULE_ID)
+ self.openstack('network qos rule set --min-kbps 7500 %s %s' %
+ (self.QOS_POLICY_NAME, self.RULE_ID))
cmd_output = json.loads(self.openstack(
- 'network qos rule show -f json ' +
- self.QOS_POLICY_NAME + ' ' + self.RULE_ID))
+ 'network qos rule show -f json %s %s' %
+ (self.QOS_POLICY_NAME, self.RULE_ID)))
self.assertEqual(7500, cmd_output['min_kbps'])
@@ -96,58 +94,57 @@ class NetworkQosRuleTestsDSCPMarking(common.NetworkTests):
if not self.haz_network:
self.skipTest("No Network service present")
- self.QOS_POLICY_NAME = 'qos_policy_' + uuid.uuid4().hex
+ self.QOS_POLICY_NAME = 'qos_policy_%s' % uuid.uuid4().hex
self.openstack(
- 'network qos policy create ' +
- self.QOS_POLICY_NAME
+ 'network qos policy create %s' % self.QOS_POLICY_NAME
)
self.addCleanup(self.openstack,
- 'network qos policy delete ' + self.QOS_POLICY_NAME)
+ 'network qos policy delete %s' % self.QOS_POLICY_NAME)
cmd_output = json.loads(self.openstack(
- 'network qos rule create -f json ' +
- '--type dscp-marking ' +
- '--dscp-mark 8 ' +
+ 'network qos rule create -f json '
+ '--type dscp-marking '
+ '--dscp-mark 8 %s' %
self.QOS_POLICY_NAME
))
self.RULE_ID = cmd_output['id']
self.addCleanup(self.openstack,
- 'network qos rule delete ' +
- self.QOS_POLICY_NAME + ' ' + self.RULE_ID)
+ 'network qos rule delete %s %s' %
+ (self.QOS_POLICY_NAME, self.RULE_ID))
self.assertTrue(self.RULE_ID)
def test_qos_rule_create_delete(self):
# This is to check the output of qos rule delete
policy_name = uuid.uuid4().hex
- self.openstack('network qos policy create -f json ' + policy_name)
+ self.openstack('network qos policy create -f json %s' % policy_name)
self.addCleanup(self.openstack,
- 'network qos policy delete ' + policy_name)
+ 'network qos policy delete %s' % policy_name)
rule = json.loads(self.openstack(
- 'network qos rule create -f json ' +
- '--type dscp-marking ' +
- '--dscp-mark 8 ' + policy_name
+ 'network qos rule create -f json '
+ '--type dscp-marking '
+ '--dscp-mark 8 %s' % policy_name
))
raw_output = self.openstack(
- 'network qos rule delete ' +
- policy_name + ' ' + rule['id'])
+ 'network qos rule delete %s %s' %
+ (policy_name, rule['id']))
self.assertEqual('', raw_output)
def test_qos_rule_list(self):
cmd_output = json.loads(self.openstack(
- 'network qos rule list -f json ' + self.QOS_POLICY_NAME))
+ 'network qos rule list -f json %s' % self.QOS_POLICY_NAME))
self.assertIn(self.RULE_ID, [rule['ID'] for rule in cmd_output])
def test_qos_rule_show(self):
cmd_output = json.loads(self.openstack(
- 'network qos rule show -f json ' +
- self.QOS_POLICY_NAME + ' ' + self.RULE_ID))
+ 'network qos rule show -f json %s %s' %
+ (self.QOS_POLICY_NAME, self.RULE_ID)))
self.assertEqual(self.RULE_ID, cmd_output['id'])
def test_qos_rule_set(self):
- self.openstack('network qos rule set --dscp-mark 32 ' +
- self.QOS_POLICY_NAME + ' ' + self.RULE_ID)
+ self.openstack('network qos rule set --dscp-mark 32 %s %s' %
+ (self.QOS_POLICY_NAME, self.RULE_ID))
cmd_output = json.loads(self.openstack(
- 'network qos rule show -f json ' +
- self.QOS_POLICY_NAME + ' ' + self.RULE_ID))
+ 'network qos rule show -f json %s %s' %
+ (self.QOS_POLICY_NAME, self.RULE_ID)))
self.assertEqual(32, cmd_output['dscp_mark'])
@@ -160,65 +157,63 @@ class NetworkQosRuleTestsBandwidthLimit(common.NetworkTests):
if not self.haz_network:
self.skipTest("No Network service present")
- self.QOS_POLICY_NAME = 'qos_policy_' + uuid.uuid4().hex
+ self.QOS_POLICY_NAME = 'qos_policy_%s' % uuid.uuid4().hex
self.openstack(
- 'network qos policy create ' +
- self.QOS_POLICY_NAME
+ 'network qos policy create %s' % self.QOS_POLICY_NAME
)
self.addCleanup(self.openstack,
- 'network qos policy delete ' + self.QOS_POLICY_NAME)
+ 'network qos policy delete %s' % self.QOS_POLICY_NAME)
cmd_output = json.loads(self.openstack(
- 'network qos rule create -f json ' +
- '--type bandwidth-limit ' +
- '--max-kbps 10000 ' +
- '--max-burst-kbits 1400 ' +
- '--egress ' +
+ 'network qos rule create -f json '
+ '--type bandwidth-limit '
+ '--max-kbps 10000 '
+ '--egress %s' %
self.QOS_POLICY_NAME
))
self.RULE_ID = cmd_output['id']
self.addCleanup(self.openstack,
- 'network qos rule delete ' +
- self.QOS_POLICY_NAME + ' ' + self.RULE_ID)
+ 'network qos rule delete %s %s' %
+ (self.QOS_POLICY_NAME, self.RULE_ID))
self.assertTrue(self.RULE_ID)
def test_qos_rule_create_delete(self):
# This is to check the output of qos rule delete
policy_name = uuid.uuid4().hex
- self.openstack('network qos policy create -f json ' + policy_name)
+ self.openstack('network qos policy create -f json %s' % policy_name)
self.addCleanup(self.openstack,
- 'network qos policy delete ' + policy_name)
+ 'network qos policy delete %s' % policy_name)
rule = json.loads(self.openstack(
- 'network qos rule create -f json ' +
- '--type bandwidth-limit ' +
- '--max-kbps 10000 ' +
- '--max-burst-kbits 1400 ' +
- '--egress ' + policy_name
+ 'network qos rule create -f json '
+ '--type bandwidth-limit '
+ '--max-kbps 10000 '
+ '--max-burst-kbits 1400 '
+ '--egress %s' % policy_name
))
raw_output = self.openstack(
- 'network qos rule delete ' +
- policy_name + ' ' + rule['id'])
+ 'network qos rule delete %s %s' %
+ (policy_name, rule['id']))
self.assertEqual('', raw_output)
def test_qos_rule_list(self):
cmd_output = json.loads(self.openstack(
- 'network qos rule list -f json '
- + self.QOS_POLICY_NAME))
+ 'network qos rule list -f json %s' %
+ self.QOS_POLICY_NAME))
self.assertIn(self.RULE_ID, [rule['ID'] for rule in cmd_output])
def test_qos_rule_show(self):
cmd_output = json.loads(self.openstack(
- 'network qos rule show -f json ' +
- self.QOS_POLICY_NAME + ' ' + self.RULE_ID))
+ 'network qos rule show -f json %s %s' %
+ (self.QOS_POLICY_NAME, self.RULE_ID)))
self.assertEqual(self.RULE_ID, cmd_output['id'])
def test_qos_rule_set(self):
- self.openstack('network qos rule set --max-kbps 15000 ' +
- '--max-burst-kbits 1800 ' +
- '--ingress ' +
- self.QOS_POLICY_NAME + ' ' + self.RULE_ID)
+ self.openstack('network qos rule set --max-kbps 15000 '
+ '--max-burst-kbits 1800 '
+ '--ingress %s %s' %
+ (self.QOS_POLICY_NAME, self.RULE_ID))
cmd_output = json.loads(self.openstack(
- 'network qos rule show -f json ' +
- self.QOS_POLICY_NAME + ' ' + self.RULE_ID))
+ 'network qos rule show -f json %s %s' %
+ (self.QOS_POLICY_NAME, self.RULE_ID)))
self.assertEqual(15000, cmd_output['max_kbps'])
self.assertEqual(1800, cmd_output['max_burst_kbps'])
self.assertEqual('ingress', cmd_output['direction'])
diff --git a/openstackclient/tests/functional/network/v2/test_network_segment.py b/openstackclient/tests/functional/network/v2/test_network_segment.py
index 8940273f..6ffb11cf 100644
--- a/openstackclient/tests/functional/network/v2/test_network_segment.py
+++ b/openstackclient/tests/functional/network/v2/test_network_segment.py
@@ -113,9 +113,20 @@ class NetworkSegmentTests(common.NetworkTests):
self.openstack,
'network segment delete ' + name
)
- self.assertIsNone(
- json_output["description"],
- )
+
+ extension_output = json.loads(self.openstack(
+ "extension list -f json "
+ ))
+ ext_alias = [x["Alias"] for x in extension_output]
+ if "standard-attr-segment" in ext_alias:
+ self.assertEqual(
+ '',
+ json_output["description"],
+ )
+ else:
+ self.assertIsNone(
+ json_output["description"],
+ )
new_description = 'new_description'
cmd_output = self.openstack(
diff --git a/openstackclient/tests/functional/network/v2/test_port.py b/openstackclient/tests/functional/network/v2/test_port.py
index 7357c0ed..e3067d90 100644
--- a/openstackclient/tests/functional/network/v2/test_port.py
+++ b/openstackclient/tests/functional/network/v2/test_port.py
@@ -33,7 +33,7 @@ class PortTests(common.NetworkTagTests):
# Create a network for the port tests
cls.openstack(
- 'network create ' + cls.NETWORK_NAME
+ 'network create %s' % cls.NETWORK_NAME
)
@classmethod
@@ -41,7 +41,7 @@ class PortTests(common.NetworkTagTests):
try:
if cls.haz_network:
raw_output = cls.openstack(
- 'network delete ' + cls.NETWORK_NAME
+ 'network delete %s' % cls.NETWORK_NAME
)
cls.assertOutput('', raw_output)
finally:
@@ -56,8 +56,8 @@ class PortTests(common.NetworkTagTests):
def test_port_delete(self):
"""Test create, delete multiple"""
json_output = json.loads(self.openstack(
- 'port create -f json --network ' +
- self.NETWORK_NAME + ' ' + self.NAME
+ 'port create -f json --network %s %s' %
+ (self.NETWORK_NAME, self.NAME)
))
id1 = json_output.get('id')
self.assertIsNotNone(id1)
@@ -65,8 +65,8 @@ class PortTests(common.NetworkTagTests):
self.assertEqual(self.NAME, json_output.get('name'))
json_output = json.loads(self.openstack(
- 'port create -f json --network ' + self.NETWORK_NAME + ' ' +
- self.NAME + 'x'
+ 'port create -f json --network %s %sx' %
+ (self.NETWORK_NAME, self.NAME)
))
id2 = json_output.get('id')
self.assertIsNotNone(id2)
@@ -74,31 +74,31 @@ class PortTests(common.NetworkTagTests):
self.assertEqual(self.NAME + 'x', json_output.get('name'))
# Clean up after ourselves
- raw_output = self.openstack('port delete ' + id1 + ' ' + id2)
+ raw_output = self.openstack('port delete %s %s' % (id1, id2))
self.assertOutput('', raw_output)
def test_port_list(self):
"""Test create defaults, list, delete"""
json_output = json.loads(self.openstack(
- 'port create -f json --network ' + self.NETWORK_NAME + ' ' +
- self.NAME
+ 'port create -f json --network %s %s' %
+ (self.NETWORK_NAME, self.NAME)
))
id1 = json_output.get('id')
self.assertIsNotNone(id1)
mac1 = json_output.get('mac_address')
self.assertIsNotNone(mac1)
- self.addCleanup(self.openstack, 'port delete ' + id1)
+ self.addCleanup(self.openstack, 'port delete %s' % id1)
self.assertEqual(self.NAME, json_output.get('name'))
json_output = json.loads(self.openstack(
- 'port create -f json --network ' + self.NETWORK_NAME + ' ' +
- self.NAME + 'x'
+ 'port create -f json --network %s %sx' %
+ (self.NETWORK_NAME, self.NAME)
))
id2 = json_output.get('id')
self.assertIsNotNone(id2)
mac2 = json_output.get('mac_address')
self.assertIsNotNone(mac2)
- self.addCleanup(self.openstack, 'port delete ' + id2)
+ self.addCleanup(self.openstack, 'port delete %s' % id2)
self.assertEqual(self.NAME + 'x', json_output.get('name'))
# Test list
@@ -122,7 +122,7 @@ class PortTests(common.NetworkTagTests):
# Test list --mac-address
json_output = json.loads(self.openstack(
- 'port list -f json --mac-address ' + mac2
+ 'port list -f json --mac-address %s' % mac2
))
item_map = {item.get('ID'): item.get('MAC Address') for item in
json_output}
@@ -145,26 +145,26 @@ class PortTests(common.NetworkTagTests):
"""Test create, set, show, delete"""
name = uuid.uuid4().hex
json_output = json.loads(self.openstack(
- 'port create -f json ' +
- '--network ' + self.NETWORK_NAME + ' ' +
- '--description xyzpdq ' +
- '--disable ' +
- name
+ 'port create -f json '
+ '--network %s '
+ '--description xyzpdq '
+ '--disable %s' %
+ (self.NETWORK_NAME, name)
))
id1 = json_output.get('id')
- self.addCleanup(self.openstack, 'port delete ' + id1)
+ self.addCleanup(self.openstack, 'port delete %s' % id1)
self.assertEqual(name, json_output.get('name'))
self.assertEqual('xyzpdq', json_output.get('description'))
self.assertEqual('DOWN', json_output.get('admin_state_up'))
raw_output = self.openstack(
- 'port set ' + '--enable ' +
+ 'port set --enable %s' %
name
)
self.assertOutput('', raw_output)
json_output = json.loads(self.openstack(
- 'port show -f json ' + name
+ 'port show -f json %s' % name
))
sg_id = json_output.get('security_group_ids')
@@ -174,30 +174,30 @@ class PortTests(common.NetworkTagTests):
self.assertIsNotNone(json_output.get('mac_address'))
raw_output = self.openstack(
- 'port unset --security-group ' + sg_id + ' ' + id1)
+ 'port unset --security-group %s %s' % (sg_id, id1))
self.assertOutput('', raw_output)
json_output = json.loads(self.openstack(
- 'port show -f json ' + name
+ 'port show -f json %s' % name
))
self.assertEqual('', json_output.get('security_group_ids'))
def test_port_admin_set(self):
"""Test create, set (as admin), show, delete"""
json_output = json.loads(self.openstack(
- 'port create -f json ' +
- '--network ' + self.NETWORK_NAME + ' ' + self.NAME
+ 'port create -f json '
+ '--network %s %s' % (self.NETWORK_NAME, self.NAME)
))
id_ = json_output.get('id')
- self.addCleanup(self.openstack, 'port delete ' + id_)
+ self.addCleanup(self.openstack, 'port delete %s' % id_)
raw_output = self.openstack(
'--os-username admin '
- + 'port set --mac-address 11:22:33:44:55:66 '
- + self.NAME)
+ 'port set --mac-address 11:22:33:44:55:66 %s' %
+ self.NAME)
self.assertOutput('', raw_output)
json_output = json.loads(self.openstack(
- 'port show -f json ' + self.NAME
+ 'port show -f json %s' % self.NAME
))
self.assertEqual(json_output.get('mac_address'), '11:22:33:44:55:66')
@@ -205,41 +205,41 @@ class PortTests(common.NetworkTagTests):
"""Test create, set, show, delete"""
sg_name1 = uuid.uuid4().hex
json_output = json.loads(self.openstack(
- 'security group create -f json ' +
+ 'security group create -f json %s' %
sg_name1
))
sg_id1 = json_output.get('id')
- self.addCleanup(self.openstack, 'security group delete ' + sg_id1)
+ self.addCleanup(self.openstack, 'security group delete %s' % sg_id1)
sg_name2 = uuid.uuid4().hex
json_output = json.loads(self.openstack(
- 'security group create -f json ' +
+ 'security group create -f json %s' %
sg_name2
))
sg_id2 = json_output.get('id')
- self.addCleanup(self.openstack, 'security group delete ' + sg_id2)
+ self.addCleanup(self.openstack, 'security group delete %s' % sg_id2)
name = uuid.uuid4().hex
json_output = json.loads(self.openstack(
- 'port create -f json ' +
- '--network ' + self.NETWORK_NAME + ' ' +
- '--security-group ' + sg_name1 + ' ' +
- name
+ 'port create -f json '
+ '--network %s '
+ '--security-group %s %s' %
+ (self.NETWORK_NAME, sg_name1, name)
))
id1 = json_output.get('id')
- self.addCleanup(self.openstack, 'port delete ' + id1)
+ self.addCleanup(self.openstack, 'port delete %s' % id1)
self.assertEqual(name, json_output.get('name'))
self.assertEqual(sg_id1, json_output.get('security_group_ids'))
raw_output = self.openstack(
- 'port set ' +
- '--security-group ' + sg_name2 + ' ' +
- name
+ 'port set '
+ '--security-group %s %s' %
+ (sg_name2, name)
)
self.assertOutput('', raw_output)
json_output = json.loads(self.openstack(
- 'port show -f json ' + name
+ 'port show -f json %s' % name
))
self.assertEqual(name, json_output.get('name'))
self.assertIn(
@@ -254,11 +254,11 @@ class PortTests(common.NetworkTagTests):
)
raw_output = self.openstack(
- 'port unset --security-group ' + sg_id1 + ' ' + id1)
+ 'port unset --security-group %s %s' % (sg_id1, id1))
self.assertOutput('', raw_output)
json_output = json.loads(self.openstack(
- 'port show -f json ' + name
+ 'port show -f json %s' % name
))
self.assertEqual(
# TODO(dtroyer): output formatters should do this on JSON!
diff --git a/openstackclient/tests/functional/object/v1/test_object.py b/openstackclient/tests/functional/object/v1/test_object.py
index d1a73c54..226ef8ad 100644
--- a/openstackclient/tests/functional/object/v1/test_object.py
+++ b/openstackclient/tests/functional/object/v1/test_object.py
@@ -53,39 +53,39 @@ class ObjectTests(common.ObjectStoreTests):
self.openstack('container save ' + self.CONTAINER_NAME)
# TODO(stevemar): Assert returned fields
- raw_output = self.openstack('object create ' + self.CONTAINER_NAME
- + ' ' + object_file)
+ raw_output = self.openstack('object create %s %s' %
+ (self.CONTAINER_NAME, object_file))
items = self.parse_listing(raw_output)
self.assert_show_fields(items, OBJECT_FIELDS)
- raw_output = self.openstack('object list ' + self.CONTAINER_NAME)
+ raw_output = self.openstack('object list %s' % self.CONTAINER_NAME)
items = self.parse_listing(raw_output)
self.assert_table_structure(items, BASIC_LIST_HEADERS)
- self.openstack('object save ' + self.CONTAINER_NAME
- + ' ' + object_file)
+ self.openstack('object save %s %s' %
+ (self.CONTAINER_NAME, object_file))
# TODO(stevemar): Assert returned fields
tmp_file = 'tmp.txt'
self.addCleanup(os.remove, tmp_file)
- self.openstack('object save ' + self.CONTAINER_NAME
- + ' ' + object_file + ' --file ' + tmp_file)
+ self.openstack('object save %s %s --file %s' %
+ (self.CONTAINER_NAME, object_file, tmp_file))
# TODO(stevemar): Assert returned fields
- raw_output = self.openstack('object save ' + self.CONTAINER_NAME
- + ' ' + object_file + ' --file -')
+ raw_output = self.openstack('object save %s %s --file -' %
+ (self.CONTAINER_NAME, object_file))
self.assertEqual(raw_output, 'test content')
- self.openstack('object show ' + self.CONTAINER_NAME
- + ' ' + object_file)
+ self.openstack('object show %s %s' %
+ (self.CONTAINER_NAME, object_file))
# TODO(stevemar): Assert returned fields
- raw_output = self.openstack('object delete ' + self.CONTAINER_NAME
- + ' ' + object_file)
+ raw_output = self.openstack('object delete %s %s' %
+ (self.CONTAINER_NAME, object_file))
self.assertEqual(0, len(raw_output))
- self.openstack('object create ' + self.CONTAINER_NAME
- + ' ' + object_file)
- raw_output = self.openstack('container delete -r ' +
+ self.openstack('object create %s %s' %
+ (self.CONTAINER_NAME, object_file))
+ raw_output = self.openstack('container delete -r %s' %
self.CONTAINER_NAME)
self.assertEqual(0, len(raw_output))
diff --git a/openstackclient/tests/functional/volume/v1/test_volume_type.py b/openstackclient/tests/functional/volume/v1/test_volume_type.py
index 74e14070..eb9d7f64 100644
--- a/openstackclient/tests/functional/volume/v1/test_volume_type.py
+++ b/openstackclient/tests/functional/volume/v1/test_volume_type.py
@@ -20,70 +20,100 @@ from openstackclient.tests.functional.volume.v1 import common
class VolumeTypeTests(common.BaseVolumeTests):
"""Functional tests for volume type. """
- NAME = uuid.uuid4().hex
-
- @classmethod
- def setUpClass(cls):
- super(VolumeTypeTests, cls).setUpClass()
- cmd_output = json.loads(cls.openstack(
- 'volume type create -f json ' + cls.NAME))
- cls.assertOutput(cls.NAME, cmd_output['name'])
-
- @classmethod
- def tearDownClass(cls):
- try:
- raw_output = cls.openstack('volume type delete ' + cls.NAME)
- cls.assertOutput('', raw_output)
- finally:
- super(VolumeTypeTests, cls).tearDownClass()
-
- def test_volume_type_list(self):
+ def test_volume_type_create_list(self):
+ name = uuid.uuid4().hex
+ cmd_output = json.loads(self.openstack(
+ 'volume type create -f json --private ' +
+ name,
+ ))
+ self.addCleanup(
+ self.openstack,
+ 'volume type delete ' +
+ name,
+ )
+ self.assertEqual(name, cmd_output['name'])
+
+ cmd_output = json.loads(self.openstack(
+ 'volume type show -f json %s' % name
+ ))
+ self.assertEqual(self.NAME, cmd_output['name'])
+
cmd_output = json.loads(self.openstack('volume type list -f json'))
self.assertIn(self.NAME, [t['Name'] for t in cmd_output])
- def test_volume_type_show(self):
cmd_output = json.loads(self.openstack(
- 'volume type show -f json ' + self.NAME))
- self.assertEqual(self.NAME, cmd_output['name'])
+ 'volume type list -f json --default'
+ ))
+ self.assertEqual(1, len(cmd_output))
+ self.assertEqual('lvmdriver-1', cmd_output[0]['Name'])
def test_volume_type_set_unset_properties(self):
+ name = uuid.uuid4().hex
+ cmd_output = json.loads(self.openstack(
+ 'volume type create -f json --private ' +
+ name,
+ ))
+ self.addCleanup(
+ self.openstack,
+ 'volume type delete ' + name
+ )
+ self.assertEqual(name, cmd_output['name'])
+
raw_output = self.openstack(
- 'volume type set --property a=b --property c=d ' + self.NAME)
+ 'volume type set --property a=b --property c=d %s' % name
+ )
self.assertEqual("", raw_output)
-
cmd_output = json.loads(self.openstack(
- 'volume type show -f json ' + self.NAME))
+ 'volume type show -f json %s' % name
+ ))
+ # TODO(amotoki): properties output should be machine-readable
self.assertEqual("a='b', c='d'", cmd_output['properties'])
- raw_output = self.openstack('volume type unset --property a '
- + self.NAME)
+ raw_output = self.openstack(
+ 'volume type unset --property a %s' % name
+ )
self.assertEqual("", raw_output)
cmd_output = json.loads(self.openstack(
- 'volume type show -f json ' + self.NAME))
+ 'volume type show -f json %s' % name
+ ))
self.assertEqual("c='d'", cmd_output['properties'])
def test_volume_type_set_unset_multiple_properties(self):
+ name = uuid.uuid4().hex
+ cmd_output = json.loads(self.openstack(
+ 'volume type create -f json --private ' +
+ name,
+ ))
+ self.addCleanup(
+ self.openstack,
+ 'volume type delete ' + name
+ )
+ self.assertEqual(name, cmd_output['name'])
+
raw_output = self.openstack(
- 'volume type set --property a=b --property c=d ' + self.NAME)
+ 'volume type set --property a=b --property c=d %s' % name
+ )
self.assertEqual("", raw_output)
-
cmd_output = json.loads(self.openstack(
- 'volume type show -f json ' + self.NAME))
+ 'volume type show -f json %s' % name
+ ))
self.assertEqual("a='b', c='d'", cmd_output['properties'])
raw_output = self.openstack(
- 'volume type unset --property a --property c ' + self.NAME)
+ 'volume type unset --property a --property c %s' % name
+ )
self.assertEqual("", raw_output)
cmd_output = json.loads(self.openstack(
- 'volume type show -f json ' + self.NAME))
+ 'volume type show -f json %s' % name
+ ))
self.assertEqual("", cmd_output['properties'])
def test_multi_delete(self):
vol_type1 = uuid.uuid4().hex
vol_type2 = uuid.uuid4().hex
- self.openstack('volume type create ' + vol_type1)
+ self.openstack('volume type create %s' % vol_type1)
time.sleep(5)
- self.openstack('volume type create ' + vol_type2)
+ self.openstack('volume type create %s' % vol_type2)
time.sleep(5)
cmd = 'volume type delete %s %s' % (vol_type1, vol_type2)
raw_output = self.openstack(cmd)
@@ -140,8 +170,21 @@ class VolumeTypeTests(common.BaseVolumeTests):
'--encryption-control-location front-end ' +
self.NAME)
self.assertEqual('', raw_output)
+
+ name = uuid.uuid4().hex
+ cmd_output = json.loads(self.openstack(
+ 'volume type create -f json --private ' +
+ name,
+ ))
+ self.addCleanup(
+ self.openstack,
+ 'volume type delete ' + name,
+ )
+ self.assertEqual(name, cmd_output['name'])
+
cmd_output = json.loads(self.openstack(
- 'volume type show -f json --encryption-type ' + self.NAME))
+ 'volume type show -f json --encryption-type ' + name
+ ))
expected = ["provider='LuksEncryptor'",
"cipher='aes-xts-plain64'",
"key_size='128'",
@@ -150,10 +193,12 @@ class VolumeTypeTests(common.BaseVolumeTests):
self.assertIn(attr, cmd_output['encryption'])
# test unset encryption type
raw_output = self.openstack(
- 'volume type unset --encryption-type ' + self.NAME)
+ 'volume type unset --encryption-type ' + name
+ )
self.assertEqual('', raw_output)
cmd_output = json.loads(self.openstack(
- 'volume type show -f json --encryption-type ' + self.NAME))
+ 'volume type show -f json --encryption-type ' + name
+ ))
self.assertEqual('', cmd_output['encryption'])
# test delete encryption type
raw_output = self.openstack('volume type delete ' + encryption_type)
diff --git a/openstackclient/tests/functional/volume/v2/test_qos.py b/openstackclient/tests/functional/volume/v2/test_qos.py
index 888f12b1..646becc1 100644
--- a/openstackclient/tests/functional/volume/v2/test_qos.py
+++ b/openstackclient/tests/functional/volume/v2/test_qos.py
@@ -125,7 +125,6 @@ class QosTests(common.BaseVolumeTests):
def test_volume_qos_asso_disasso(self):
"""Tests associate and disassociate qos with volume type"""
vol_type1 = uuid.uuid4().hex
- vol_type2 = uuid.uuid4().hex
cmd_output = json.loads(self.openstack(
'volume type create -f json ' +
vol_type1
@@ -134,6 +133,9 @@ class QosTests(common.BaseVolumeTests):
vol_type1,
cmd_output['name']
)
+ self.addCleanup(self.openstack, 'volume type delete ' + vol_type1)
+
+ vol_type2 = uuid.uuid4().hex
cmd_output = json.loads(self.openstack(
'volume type create -f json ' +
vol_type2
@@ -142,7 +144,6 @@ class QosTests(common.BaseVolumeTests):
vol_type2,
cmd_output['name']
)
- self.addCleanup(self.openstack, 'volume type delete ' + vol_type1)
self.addCleanup(self.openstack, 'volume type delete ' + vol_type2)
name = uuid.uuid4().hex
diff --git a/openstackclient/tests/functional/volume/v2/test_volume_type.py b/openstackclient/tests/functional/volume/v2/test_volume_type.py
index 99630e6b..d8dd5bd6 100644
--- a/openstackclient/tests/functional/volume/v2/test_volume_type.py
+++ b/openstackclient/tests/functional/volume/v2/test_volume_type.py
@@ -20,84 +20,121 @@ from openstackclient.tests.functional.volume.v2 import common
class VolumeTypeTests(common.BaseVolumeTests):
"""Functional tests for volume type. """
- NAME = uuid.uuid4().hex
-
- @classmethod
- def setUpClass(cls):
- super(VolumeTypeTests, cls).setUpClass()
- cmd_output = json.loads(cls.openstack(
- 'volume type create -f json --private ' + cls.NAME))
- cls.assertOutput(cls.NAME, cmd_output['name'])
-
- @classmethod
- def tearDownClass(cls):
- try:
- raw_output = cls.openstack('volume type delete ' + cls.NAME)
- cls.assertOutput('', raw_output)
- finally:
- super(VolumeTypeTests, cls).tearDownClass()
-
- def test_volume_type_list(self):
+ def test_volume_type_create_list(self):
+ name = uuid.uuid4().hex
+ cmd_output = json.loads(self.openstack(
+ 'volume type create -f json --private ' +
+ name,
+ ))
+ self.addCleanup(
+ self.openstack,
+ 'volume type delete ' + name,
+ )
+ self.assertEqual(name, cmd_output['name'])
+
+ cmd_output = json.loads(self.openstack(
+ 'volume type show -f json %s' % name
+ ))
+ self.assertEqual(name, cmd_output['name'])
+
cmd_output = json.loads(self.openstack('volume type list -f json'))
- self.assertIn(self.NAME, [t['Name'] for t in cmd_output])
+ self.assertIn(name, [t['Name'] for t in cmd_output])
- def test_volume_type_list_default(self):
cmd_output = json.loads(self.openstack(
- 'volume type list -f json --default'))
+ 'volume type list -f json --default'
+ ))
self.assertEqual(1, len(cmd_output))
self.assertEqual('lvmdriver-1', cmd_output[0]['Name'])
- def test_volume_type_show(self):
+ def test_volume_type_set_unset_properties(self):
+ name = uuid.uuid4().hex
cmd_output = json.loads(self.openstack(
- 'volume type show -f json ' + self.NAME))
- self.assertEqual(self.NAME, cmd_output['name'])
+ 'volume type create -f json --private ' +
+ name,
+ ))
+ self.addCleanup(
+ self.openstack,
+ 'volume type delete ' + name
+ )
+ self.assertEqual(name, cmd_output['name'])
- def test_volume_type_set_unset_properties(self):
raw_output = self.openstack(
- 'volume type set --property a=b --property c=d ' + self.NAME)
+ 'volume type set --property a=b --property c=d %s' % name
+ )
self.assertEqual("", raw_output)
cmd_output = json.loads(self.openstack(
- 'volume type show -f json ' + self.NAME))
+ 'volume type show -f json %s' % name
+ ))
# TODO(amotoki): properties output should be machine-readable
self.assertEqual("a='b', c='d'", cmd_output['properties'])
- raw_output = self.openstack('volume type unset --property a '
- + self.NAME)
+ raw_output = self.openstack(
+ 'volume type unset --property a %s' % name
+ )
self.assertEqual("", raw_output)
cmd_output = json.loads(self.openstack(
- 'volume type show -f json ' + self.NAME))
+ 'volume type show -f json %s' % name
+ ))
self.assertEqual("c='d'", cmd_output['properties'])
def test_volume_type_set_unset_multiple_properties(self):
+ name = uuid.uuid4().hex
+ cmd_output = json.loads(self.openstack(
+ 'volume type create -f json --private ' +
+ name,
+ ))
+ self.addCleanup(
+ self.openstack,
+ 'volume type delete ' + name
+ )
+ self.assertEqual(name, cmd_output['name'])
+
raw_output = self.openstack(
- 'volume type set --property a=b --property c=d ' + self.NAME)
+ 'volume type set --property a=b --property c=d %s' % name
+ )
self.assertEqual("", raw_output)
cmd_output = json.loads(self.openstack(
- 'volume type show -f json ' + self.NAME))
+ 'volume type show -f json %s' % name
+ ))
self.assertEqual("a='b', c='d'", cmd_output['properties'])
raw_output = self.openstack(
- 'volume type unset --property a --property c ' + self.NAME)
+ 'volume type unset --property a --property c %s' % name
+ )
self.assertEqual("", raw_output)
cmd_output = json.loads(self.openstack(
- 'volume type show -f json ' + self.NAME))
+ 'volume type show -f json %s' % name
+ ))
self.assertEqual("", cmd_output['properties'])
def test_volume_type_set_unset_project(self):
+ name = uuid.uuid4().hex
+ cmd_output = json.loads(self.openstack(
+ 'volume type create -f json --private ' +
+ name,
+ ))
+ self.addCleanup(
+ self.openstack,
+ 'volume type delete ' + name
+ )
+ self.assertEqual(name, cmd_output['name'])
+
raw_output = self.openstack(
- 'volume type set --project admin ' + self.NAME)
+ 'volume type set --project admin %s' % name
+ )
self.assertEqual("", raw_output)
raw_output = self.openstack(
- 'volume type unset --project admin ' + self.NAME)
+ 'volume type unset --project admin %s' % name
+ )
self.assertEqual("", raw_output)
def test_multi_delete(self):
vol_type1 = uuid.uuid4().hex
vol_type2 = uuid.uuid4().hex
- self.openstack('volume type create ' + vol_type1)
+ self.openstack('volume type create %s' % vol_type1)
time.sleep(5)
- self.openstack('volume type create ' + vol_type2)
+ self.openstack('volume type create %s' % vol_type2)
time.sleep(5)
cmd = 'volume type delete %s %s' % (vol_type1, vol_type2)
raw_output = self.openstack(cmd)
@@ -108,6 +145,7 @@ class VolumeTypeTests(common.BaseVolumeTests):
# these to new test format when beef up all tests for
# volume tye commands.
def test_encryption_type(self):
+ name = uuid.uuid4().hex
encryption_type = uuid.uuid4().hex
# test create new encryption type
cmd_output = json.loads(self.openstack(
@@ -162,16 +200,28 @@ class VolumeTypeTests(common.BaseVolumeTests):
for attr in expected:
self.assertIn(attr, cmd_output['encryption'])
# test set new encryption type
+ cmd_output = json.loads(self.openstack(
+ 'volume type create -f json --private ' +
+ name,
+ ))
+ self.addCleanup(
+ self.openstack,
+ 'volume type delete ' + name,
+ )
+ self.assertEqual(name, cmd_output['name'])
+
raw_output = self.openstack(
'volume type set '
'--encryption-provider LuksEncryptor '
'--encryption-cipher aes-xts-plain64 '
'--encryption-key-size 128 '
'--encryption-control-location front-end ' +
- self.NAME)
+ name)
self.assertEqual('', raw_output)
+
cmd_output = json.loads(self.openstack(
- 'volume type show -f json --encryption-type ' + self.NAME))
+ 'volume type show -f json --encryption-type ' + name
+ ))
expected = ["provider='LuksEncryptor'",
"cipher='aes-xts-plain64'",
"key_size='128'",
@@ -180,10 +230,12 @@ class VolumeTypeTests(common.BaseVolumeTests):
self.assertIn(attr, cmd_output['encryption'])
# test unset encryption type
raw_output = self.openstack(
- 'volume type unset --encryption-type ' + self.NAME)
+ 'volume type unset --encryption-type ' + name
+ )
self.assertEqual('', raw_output)
cmd_output = json.loads(self.openstack(
- 'volume type show -f json --encryption-type ' + self.NAME))
+ 'volume type show -f json --encryption-type ' + name
+ ))
self.assertEqual('', cmd_output['encryption'])
# test delete encryption type
raw_output = self.openstack('volume type delete ' + encryption_type)
diff --git a/openstackclient/tests/unit/api/test_compute_v2.py b/openstackclient/tests/unit/api/test_compute_v2.py
index 4f3b8031..edf5258f 100644
--- a/openstackclient/tests/unit/api/test_compute_v2.py
+++ b/openstackclient/tests/unit/api/test_compute_v2.py
@@ -55,6 +55,43 @@ class TestFloatingIP(TestComputeAPIv2):
FAKE_FLOATING_IP_RESP_2,
]
+ FAKE_SERVER_RESP_1 = {
+ 'id': 1,
+ 'name': 'server1',
+ }
+
+ def test_floating_ip_add_id(self):
+ self.requests_mock.register_uri(
+ 'POST',
+ FAKE_URL + '/servers/1/action',
+ json={'server': {}},
+ status_code=200,
+ )
+ self.requests_mock.register_uri(
+ 'GET',
+ FAKE_URL + '/servers/1',
+ json={'server': self.FAKE_SERVER_RESP_1},
+ status_code=200,
+ )
+ ret = self.api.floating_ip_add('1', '1.0.1.0')
+ self.assertEqual(200, ret.status_code)
+
+ def test_floating_ip_add_name(self):
+ self.requests_mock.register_uri(
+ 'POST',
+ FAKE_URL + '/servers/1/action',
+ json={'server': {}},
+ status_code=200,
+ )
+ self.requests_mock.register_uri(
+ 'GET',
+ FAKE_URL + '/servers/server1',
+ json={'server': self.FAKE_SERVER_RESP_1},
+ status_code=200,
+ )
+ ret = self.api.floating_ip_add('server1', '1.0.1.0')
+ self.assertEqual(200, ret.status_code)
+
def test_floating_ip_create(self):
self.requests_mock.register_uri(
'POST',
@@ -144,6 +181,36 @@ class TestFloatingIP(TestComputeAPIv2):
ret = self.api.floating_ip_list()
self.assertEqual(self.LIST_FLOATING_IP_RESP, ret)
+ def test_floating_ip_remove_id(self):
+ self.requests_mock.register_uri(
+ 'POST',
+ FAKE_URL + '/servers/1/action',
+ status_code=200,
+ )
+ self.requests_mock.register_uri(
+ 'GET',
+ FAKE_URL + '/servers/1',
+ json={'server': self.FAKE_SERVER_RESP_1},
+ status_code=200,
+ )
+ ret = self.api.floating_ip_remove('1', '1.0.1.0')
+ self.assertEqual(200, ret.status_code)
+
+ def test_floating_ip_remove_name(self):
+ self.requests_mock.register_uri(
+ 'POST',
+ FAKE_URL + '/servers/1/action',
+ status_code=200,
+ )
+ self.requests_mock.register_uri(
+ 'GET',
+ FAKE_URL + '/servers/server1',
+ json={'server': self.FAKE_SERVER_RESP_1},
+ status_code=200,
+ )
+ ret = self.api.floating_ip_remove('server1', '1.0.1.0')
+ self.assertEqual(200, ret.status_code)
+
class TestFloatingIPPool(TestComputeAPIv2):
@@ -163,6 +230,115 @@ class TestFloatingIPPool(TestComputeAPIv2):
self.assertEqual(self.LIST_FLOATING_IP_POOL_RESP, ret)
+class TestHost(TestComputeAPIv2):
+
+ FAKE_HOST_RESP_1 = {
+ "zone": "internal",
+ "host_name": "myhost",
+ "service": "conductor",
+ }
+
+ FAKE_HOST_RESP_2 = {
+ "zone": "internal",
+ "host_name": "myhost",
+ "service": "scheduler",
+ }
+
+ FAKE_HOST_RESP_3 = {
+ "zone": "nova",
+ "host_name": "myhost",
+ "service": "compute",
+ }
+
+ LIST_HOST_RESP = [
+ FAKE_HOST_RESP_1,
+ FAKE_HOST_RESP_2,
+ FAKE_HOST_RESP_3,
+ ]
+
+ def test_host_list_no_options(self):
+ self.requests_mock.register_uri(
+ 'GET',
+ FAKE_URL + '/os-hosts',
+ json={'hosts': self.LIST_HOST_RESP},
+ status_code=200,
+ )
+ ret = self.api.host_list()
+ self.assertEqual(self.LIST_HOST_RESP, ret)
+
+ def test_host_list_zone(self):
+ self.requests_mock.register_uri(
+ 'GET',
+ FAKE_URL + '/os-hosts?zone=nova',
+ json={'hosts': [self.FAKE_HOST_RESP_3]},
+ status_code=200,
+ )
+ self.requests_mock.register_uri(
+ 'GET',
+ FAKE_URL + '/os-hosts',
+ json={'hosts': [self.FAKE_HOST_RESP_3]},
+ status_code=200,
+ )
+ ret = self.api.host_list(zone='nova')
+ self.assertEqual([self.FAKE_HOST_RESP_3], ret)
+
+ def test_host_set_none(self):
+ ret = self.api.host_set(host='myhost')
+ self.assertIsNone(ret)
+
+ def test_host_set(self):
+ self.requests_mock.register_uri(
+ 'PUT',
+ FAKE_URL + '/os-hosts/myhost',
+ json={},
+ status_code=200,
+ )
+ ret = self.api.host_set(host='myhost', status='enabled')
+ self.assertEqual({}, ret)
+
+ def test_host_show(self):
+ FAKE_RESOURCE_1 = {
+ "cpu": 2,
+ "disk_gb": 1028,
+ "host": "c1a7de0ac9d94e4baceae031d05caae3",
+ "memory_mb": 8192,
+ "project": "(total)",
+ }
+ FAKE_RESOURCE_2 = {
+ "cpu": 0,
+ "disk_gb": 0,
+ "host": "c1a7de0ac9d94e4baceae031d05caae3",
+ "memory_mb": 512,
+ "project": "(used_now)",
+ }
+ FAKE_RESOURCE_3 = {
+ "cpu": 0,
+ "disk_gb": 0,
+ "host": "c1a7de0ac9d94e4baceae031d05caae3",
+ "memory_mb": 0,
+ "project": "(used_max)",
+ }
+ FAKE_HOST_RESP = [
+ {'resource': FAKE_RESOURCE_1},
+ {'resource': FAKE_RESOURCE_2},
+ {'resource': FAKE_RESOURCE_3},
+ ]
+ FAKE_HOST_LIST = [
+ FAKE_RESOURCE_1,
+ FAKE_RESOURCE_2,
+ FAKE_RESOURCE_3,
+ ]
+
+ self.requests_mock.register_uri(
+ 'GET',
+ FAKE_URL + '/os-hosts/myhost',
+ json={'host': FAKE_HOST_RESP},
+ status_code=200,
+ )
+ ret = self.api.host_show(host='myhost')
+ self.assertEqual(FAKE_HOST_LIST, ret)
+
+
class TestNetwork(TestComputeAPIv2):
FAKE_NETWORK_RESP = {
diff --git a/openstackclient/tests/unit/common/test_limits.py b/openstackclient/tests/unit/common/test_limits.py
new file mode 100644
index 00000000..d73db2cb
--- /dev/null
+++ b/openstackclient/tests/unit/common/test_limits.py
@@ -0,0 +1,125 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+
+from openstackclient.common import limits
+from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes
+from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes
+
+
+class TestComputeLimits(compute_fakes.TestComputev2):
+
+ absolute_columns = [
+ 'Name',
+ 'Value',
+ ]
+
+ rate_columns = [
+ "Verb",
+ "URI",
+ "Value",
+ "Remain",
+ "Unit",
+ "Next Available"
+ ]
+
+ def setUp(self):
+ super(TestComputeLimits, self).setUp()
+ self.app.client_manager.volume_endpoint_enabled = False
+ self.compute = self.app.client_manager.compute
+
+ self.fake_limits = compute_fakes.FakeLimits()
+ self.compute.limits.get.return_value = self.fake_limits
+
+ def test_compute_show_absolute(self):
+ arglist = ['--absolute']
+ verifylist = [('is_absolute', True)]
+ cmd = limits.ShowLimits(self.app, None)
+ parsed_args = self.check_parser(cmd, arglist, verifylist)
+
+ columns, data = cmd.take_action(parsed_args)
+
+ ret_limits = list(data)
+ compute_reference_limits = self.fake_limits.absolute_limits()
+
+ self.assertEqual(self.absolute_columns, columns)
+ self.assertEqual(compute_reference_limits, ret_limits)
+ self.assertEqual(19, len(ret_limits))
+
+ def test_compute_show_rate(self):
+ arglist = ['--rate']
+ verifylist = [('is_rate', True)]
+ cmd = limits.ShowLimits(self.app, None)
+ parsed_args = self.check_parser(cmd, arglist, verifylist)
+
+ columns, data = cmd.take_action(parsed_args)
+
+ ret_limits = list(data)
+ compute_reference_limits = self.fake_limits.rate_limits()
+
+ self.assertEqual(self.rate_columns, columns)
+ self.assertEqual(compute_reference_limits, ret_limits)
+ self.assertEqual(3, len(ret_limits))
+
+
+class TestVolumeLimits(volume_fakes.TestVolume):
+ absolute_columns = [
+ 'Name',
+ 'Value',
+ ]
+
+ rate_columns = [
+ "Verb",
+ "URI",
+ "Value",
+ "Remain",
+ "Unit",
+ "Next Available"
+ ]
+
+ def setUp(self):
+ super(TestVolumeLimits, self).setUp()
+ self.app.client_manager.compute_endpoint_enabled = False
+ self.volume = self.app.client_manager.volume
+
+ self.fake_limits = volume_fakes.FakeLimits()
+ self.volume.limits.get.return_value = self.fake_limits
+
+ def test_volume_show_absolute(self):
+ arglist = ['--absolute']
+ verifylist = [('is_absolute', True)]
+ cmd = limits.ShowLimits(self.app, None)
+ parsed_args = self.check_parser(cmd, arglist, verifylist)
+
+ columns, data = cmd.take_action(parsed_args)
+
+ ret_limits = list(data)
+ compute_reference_limits = self.fake_limits.absolute_limits()
+
+ self.assertEqual(self.absolute_columns, columns)
+ self.assertEqual(compute_reference_limits, ret_limits)
+ self.assertEqual(10, len(ret_limits))
+
+ def test_volume_show_rate(self):
+ arglist = ['--rate']
+ verifylist = [('is_rate', True)]
+ cmd = limits.ShowLimits(self.app, None)
+ parsed_args = self.check_parser(cmd, arglist, verifylist)
+
+ columns, data = cmd.take_action(parsed_args)
+
+ ret_limits = list(data)
+ compute_reference_limits = self.fake_limits.rate_limits()
+
+ self.assertEqual(self.rate_columns, columns)
+ self.assertEqual(compute_reference_limits, ret_limits)
+ self.assertEqual(3, len(ret_limits))
diff --git a/openstackclient/tests/unit/compute/v2/fakes.py b/openstackclient/tests/unit/compute/v2/fakes.py
index 0fae19af..46fa5992 100644
--- a/openstackclient/tests/unit/compute/v2/fakes.py
+++ b/openstackclient/tests/unit/compute/v2/fakes.py
@@ -149,6 +149,9 @@ class FakeComputev2Client(object):
self.images = mock.Mock()
self.images.resource_class = fakes.FakeResource(None, {})
+ self.limits = mock.Mock()
+ self.limits.resource_class = fakes.FakeResource(None, {})
+
self.servers = mock.Mock()
self.servers.resource_class = fakes.FakeResource(None, {})
@@ -1230,10 +1233,7 @@ class FakeHost(object):
'project': 'project-' + uuid.uuid4().hex,
}
host_info.update(attrs)
- host = fakes.FakeResource(
- info=copy.deepcopy(host_info),
- loaded=True)
- return host
+ return host_info
class FakeServerGroup(object):
@@ -1395,3 +1395,110 @@ class FakeQuota(object):
quota.project_id = quota_attrs['id']
return quota
+
+
+class FakeLimits(object):
+ """Fake limits"""
+
+ def __init__(self, absolute_attrs=None, rate_attrs=None):
+ self.absolute_limits_attrs = {
+ 'maxServerMeta': 128,
+ 'maxTotalInstances': 10,
+ 'maxPersonality': 5,
+ 'totalServerGroupsUsed': 0,
+ 'maxImageMeta': 128,
+ 'maxPersonalitySize': 10240,
+ 'maxTotalRAMSize': 51200,
+ 'maxServerGroups': 10,
+ 'maxSecurityGroupRules': 20,
+ 'maxTotalKeypairs': 100,
+ 'totalCoresUsed': 0,
+ 'totalRAMUsed': 0,
+ 'maxSecurityGroups': 10,
+ 'totalFloatingIpsUsed': 0,
+ 'totalInstancesUsed': 0,
+ 'maxServerGroupMembers': 10,
+ 'maxTotalFloatingIps': 10,
+ 'totalSecurityGroupsUsed': 0,
+ 'maxTotalCores': 20,
+ }
+ absolute_attrs = absolute_attrs or {}
+ self.absolute_limits_attrs.update(absolute_attrs)
+
+ self.rate_limits_attrs = [{
+ "uri": "*",
+ "limit": [
+ {
+ "value": 10,
+ "verb": "POST",
+ "remaining": 2,
+ "unit": "MINUTE",
+ "next-available": "2011-12-15T22:42:45Z"
+ },
+ {
+ "value": 10,
+ "verb": "PUT",
+ "remaining": 2,
+ "unit": "MINUTE",
+ "next-available": "2011-12-15T22:42:45Z"
+ },
+ {
+ "value": 100,
+ "verb": "DELETE",
+ "remaining": 100,
+ "unit": "MINUTE",
+ "next-available": "2011-12-15T22:42:45Z"
+ }
+ ]
+ }]
+
+ @property
+ def absolute(self):
+ for (name, value) in self.absolute_limits_attrs.items():
+ yield FakeAbsoluteLimit(name, value)
+
+ def absolute_limits(self):
+ reference_data = []
+ for (name, value) in self.absolute_limits_attrs.items():
+ reference_data.append((name, value))
+ return reference_data
+
+ @property
+ def rate(self):
+ for group in self.rate_limits_attrs:
+ uri = group['uri']
+ for rate in group['limit']:
+ yield FakeRateLimit(rate['verb'], uri, rate['value'],
+ rate['remaining'], rate['unit'],
+ rate['next-available'])
+
+ def rate_limits(self):
+ reference_data = []
+ for group in self.rate_limits_attrs:
+ uri = group['uri']
+ for rate in group['limit']:
+ reference_data.append((rate['verb'], uri, rate['value'],
+ rate['remaining'], rate['unit'],
+ rate['next-available']))
+ return reference_data
+
+
+class FakeAbsoluteLimit(object):
+ """Data model that represents an absolute limit"""
+
+ def __init__(self, name, value):
+ self.name = name
+ self.value = value
+
+
+class FakeRateLimit(object):
+ """Data model that represents a flattened view of a single rate limit"""
+
+ def __init__(self, verb, uri, value, remain,
+ unit, next_available):
+ self.verb = verb
+ self.uri = uri
+ self.value = value
+ self.remain = remain
+ self.unit = unit
+ self.next_available = next_available
diff --git a/openstackclient/tests/unit/compute/v2/test_host.py b/openstackclient/tests/unit/compute/v2/test_host.py
index a388172f..329095de 100644
--- a/openstackclient/tests/unit/compute/v2/test_host.py
+++ b/openstackclient/tests/unit/compute/v2/test_host.py
@@ -13,6 +13,8 @@
# under the License.
#
+import mock
+
from openstackclient.compute.v2 import host
from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes
from openstackclient.tests.unit import utils as tests_utils
@@ -23,11 +25,13 @@ class TestHost(compute_fakes.TestComputev2):
def setUp(self):
super(TestHost, self).setUp()
- # Get a shortcut to the FlavorManager Mock
- self.host_mock = self.app.client_manager.compute.hosts
- self.host_mock.reset_mock()
+ # Get a shortcut to the compute client
+ self.compute = self.app.client_manager.compute
+@mock.patch(
+ 'openstackclient.api.compute_v2.APIv2.host_list'
+)
class TestHostList(TestHost):
host = compute_fakes.FakeHost.create_one_host()
@@ -39,19 +43,18 @@ class TestHostList(TestHost):
)
data = [(
- host.host_name,
- host.service,
- host.zone,
+ host['host_name'],
+ host['service'],
+ host['zone'],
)]
def setUp(self):
super(TestHostList, self).setUp()
- self.host_mock.list_all.return_value = [self.host]
-
self.cmd = host.ListHost(self.app, None)
- def test_host_list_no_option(self):
+ def test_host_list_no_option(self, h_mock):
+ h_mock.return_value = [self.host]
arglist = []
verifylist = []
@@ -59,44 +62,48 @@ class TestHostList(TestHost):
columns, data = self.cmd.take_action(parsed_args)
- self.host_mock.list_all.assert_called_with(None)
+ h_mock.assert_called_with(None)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
- def test_host_list_with_option(self):
+ def test_host_list_with_option(self, h_mock):
+ h_mock.return_value = [self.host]
arglist = [
- '--zone', self.host.zone,
+ '--zone', self.host['zone'],
]
verifylist = [
- ('zone', self.host.zone),
+ ('zone', self.host['zone']),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- self.host_mock.list_all.assert_called_with(self.host.zone)
+ h_mock.assert_called_with(self.host['zone'])
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
+@mock.patch(
+ 'openstackclient.api.compute_v2.APIv2.host_set'
+)
class TestHostSet(TestHost):
def setUp(self):
super(TestHostSet, self).setUp()
self.host = compute_fakes.FakeHost.create_one_host()
- self.host_mock.get.return_value = self.host
- self.host_mock.update.return_value = None
self.cmd = host.SetHost(self.app, None)
- def test_host_set_no_option(self):
+ def test_host_set_no_option(self, h_mock):
+ h_mock.return_value = self.host
+ h_mock.update.return_value = None
arglist = [
- self.host.host
+ self.host['host'],
]
verifylist = [
- ('host', self.host.host)
+ ('host', self.host['host']),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -105,18 +112,20 @@ class TestHostSet(TestHost):
self.assertIsNone(result)
body = {}
- self.host_mock.update.assert_called_with(self.host.host, body)
+ h_mock.assert_called_with(self.host['host'], body)
- def test_host_set(self):
+ def test_host_set(self, h_mock):
+ h_mock.return_value = self.host
+ h_mock.update.return_value = None
arglist = [
'--enable',
'--disable-maintenance',
- self.host.host
+ self.host['host'],
]
verifylist = [
('enable', True),
('enable_maintenance', False),
- ('host', self.host.host)
+ ('host', self.host['host']),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -125,9 +134,12 @@ class TestHostSet(TestHost):
self.assertIsNone(result)
body = {'status': 'enable', 'maintenance_mode': 'disable'}
- self.host_mock.update.assert_called_with(self.host.host, body)
+ h_mock.assert_called_with(self.host['host'], body)
+@mock.patch(
+ 'openstackclient.api.compute_v2.APIv2.host_show'
+)
class TestHostShow(TestHost):
host = compute_fakes.FakeHost.create_one_host()
@@ -139,22 +151,22 @@ class TestHostShow(TestHost):
'Memory MB',
'Disk GB',
)
+
data = [(
- host.host,
- host.project,
- host.cpu,
- host.memory_mb,
- host.disk_gb,
+ host['host'],
+ host['project'],
+ host['cpu'],
+ host['memory_mb'],
+ host['disk_gb'],
)]
def setUp(self):
super(TestHostShow, self).setUp()
- self.host_mock.get.return_value = [self.host]
-
self.cmd = host.ShowHost(self.app, None)
- def test_host_show_no_option(self):
+ def test_host_show_no_option(self, h_mock):
+ h_mock.host_show.return_value = [self.host]
arglist = []
verifylist = []
@@ -162,18 +174,19 @@ class TestHostShow(TestHost):
self.assertRaises(tests_utils.ParserException, self.check_parser,
self.cmd, arglist, verifylist)
- def test_host_show_with_option(self):
+ def test_host_show_with_option(self, h_mock):
+ h_mock.return_value = [self.host]
arglist = [
- self.host.host_name,
+ self.host['host_name'],
]
verifylist = [
- ('host', self.host.host_name),
+ ('host', self.host['host_name']),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- self.host_mock.get.assert_called_with(self.host.host_name)
+ h_mock.assert_called_with(self.host['host_name'])
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py
index c4e125ba..a53c6c81 100644
--- a/openstackclient/tests/unit/compute/v2/test_server.py
+++ b/openstackclient/tests/unit/compute/v2/test_server.py
@@ -141,48 +141,148 @@ class TestServerAddFixedIP(TestServer):
self._test_server_add_fixed_ip(extralist, '5.6.7.8')
-class TestServerAddFloatingIP(TestServer):
+@mock.patch(
+ 'openstackclient.api.compute_v2.APIv2.floating_ip_add'
+)
+class TestServerAddFloatingIPCompute(compute_fakes.TestComputev2):
def setUp(self):
- super(TestServerAddFloatingIP, self).setUp()
+ super(TestServerAddFloatingIPCompute, self).setUp()
+
+ self.app.client_manager.network_endpoint_enabled = False
# Get the command object to test
self.cmd = server.AddFloatingIP(self.app, None)
- # Set add_floating_ip method to be tested.
- self.methods = {
- 'add_floating_ip': None,
- }
+ def test_server_add_floating_ip_default(self, fip_mock):
+ _floating_ip = compute_fakes.FakeFloatingIP.create_one_floating_ip()
+ arglist = [
+ 'server1',
+ _floating_ip['ip'],
+ ]
+ verifylist = [
+ ('server', 'server1'),
+ ('ip_address', _floating_ip['ip']),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- self.find_port = mock.Mock()
- self.app.client_manager.network.find_port = self.find_port
+ self.cmd.take_action(parsed_args)
- def _test_server_add_floating_ip(self, extralist, fixed_ip_address):
- servers = self.setup_servers_mock(count=1)
+ fip_mock.assert_called_once_with(
+ 'server1',
+ _floating_ip['ip'],
+ fixed_address=None,
+ )
+ def test_server_add_floating_ip_fixed(self, fip_mock):
+ _floating_ip = compute_fakes.FakeFloatingIP.create_one_floating_ip()
arglist = [
- servers[0].id,
- '1.2.3.4',
- ] + extralist
+ '--fixed-ip-address', _floating_ip['fixed_ip'],
+ 'server1',
+ _floating_ip['ip'],
+ ]
verifylist = [
- ('server', servers[0].id),
- ('ip_address', '1.2.3.4'),
- ('fixed_ip_address', fixed_ip_address),
+ ('fixed_ip_address', _floating_ip['fixed_ip']),
+ ('server', 'server1'),
+ ('ip_address', _floating_ip['ip']),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- result = self.cmd.take_action(parsed_args)
+ self.cmd.take_action(parsed_args)
- servers[0].add_floating_ip.assert_called_once_with('1.2.3.4',
- fixed_ip_address)
- self.assertIsNone(result)
+ fip_mock.assert_called_once_with(
+ 'server1',
+ _floating_ip['ip'],
+ fixed_address=_floating_ip['fixed_ip'],
+ )
- def test_server_add_floating_ip(self):
- self._test_server_add_floating_ip([], None)
- def test_server_add_floating_ip_to_fixed_ip(self):
- extralist = ['--fixed-ip-address', '5.6.7.8']
- self._test_server_add_floating_ip(extralist, '5.6.7.8')
+class TestServerAddFloatingIPNetwork(
+ TestServer,
+ network_fakes.TestNetworkV2,
+):
+
+ def setUp(self):
+ super(TestServerAddFloatingIPNetwork, self).setUp()
+
+ self.app.client_manager.network = mock.Mock()
+ self.network = self.app.client_manager.network
+ self.network.update_ip = mock.Mock(return_value=None)
+
+ # Get the command object to test
+ self.cmd = server.AddFloatingIP(self.app, self.namespace)
+
+ def test_server_add_floating_ip_default(self):
+ _server = compute_fakes.FakeServer.create_one_server()
+ self.servers_mock.get.return_value = _server
+ _port = network_fakes.FakePort.create_one_port()
+ _floating_ip = network_fakes.FakeFloatingIP.create_one_floating_ip()
+ self.network.find_ip = mock.Mock(return_value=_floating_ip)
+ self.network.ports = mock.Mock(return_value=[_port])
+ arglist = [
+ _server.id,
+ _floating_ip['ip'],
+ ]
+ verifylist = [
+ ('server', _server.id),
+ ('ip_address', _floating_ip['ip']),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ self.cmd.take_action(parsed_args)
+
+ attrs = {
+ 'port_id': _port.id,
+ }
+
+ self.network.find_ip.assert_called_once_with(
+ _floating_ip['ip'],
+ ignore_missing=False,
+ )
+ self.network.ports.assert_called_once_with(
+ device_id=_server.id,
+ )
+ self.network.update_ip.assert_called_once_with(
+ _floating_ip,
+ **attrs
+ )
+
+ def test_server_add_floating_ip_fixed(self):
+ _server = compute_fakes.FakeServer.create_one_server()
+ self.servers_mock.get.return_value = _server
+ _port = network_fakes.FakePort.create_one_port()
+ _floating_ip = network_fakes.FakeFloatingIP.create_one_floating_ip()
+ self.network.find_ip = mock.Mock(return_value=_floating_ip)
+ self.network.ports = mock.Mock(return_value=[_port])
+ arglist = [
+ '--fixed-ip-address', _floating_ip['fixed_ip'],
+ _server.id,
+ _floating_ip['ip'],
+ ]
+ verifylist = [
+ ('fixed_ip_address', _floating_ip['fixed_ip']),
+ ('server', _server.id),
+ ('ip_address', _floating_ip['ip']),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ self.cmd.take_action(parsed_args)
+
+ attrs = {
+ 'port_id': _port.id,
+ }
+
+ self.network.find_ip.assert_called_once_with(
+ _floating_ip['ip'],
+ ignore_missing=False,
+ )
+ self.network.ports.assert_called_once_with(
+ device_id=_server.id,
+ )
+ self.network.update_ip.assert_called_once_with(
+ _floating_ip,
+ **attrs
+ )
class TestServerAddPort(TestServer):
@@ -925,7 +1025,7 @@ class TestServerCreate(TestServer):
mock_wait_for_status.assert_called_once_with(
self.servers_mock.get,
self.new_server.id,
- callback=server._show_progress,
+ callback=mock.ANY,
)
kwargs = dict(
@@ -975,7 +1075,7 @@ class TestServerCreate(TestServer):
mock_wait_for_status.assert_called_once_with(
self.servers_mock.get,
self.new_server.id,
- callback=server._show_progress,
+ callback=mock.ANY,
)
kwargs = dict(
@@ -1428,6 +1528,164 @@ class TestServerCreate(TestServer):
self.cmd.take_action,
parsed_args)
+ def test_server_create_image_property(self):
+ arglist = [
+ '--image-property', 'hypervisor_type=qemu',
+ '--flavor', 'flavor1',
+ '--nic', 'none',
+ self.new_server.name,
+ ]
+ verifylist = [
+ ('image_property', {'hypervisor_type': 'qemu'}),
+ ('flavor', 'flavor1'),
+ ('nic', ['none']),
+ ('config_drive', False),
+ ('server_name', self.new_server.name),
+ ]
+ _image = image_fakes.FakeImage.create_one_image()
+ # create a image_info as the side_effect of the fake image_list()
+ image_info = {
+ 'id': _image.id,
+ 'name': _image.name,
+ 'owner': _image.owner,
+ 'hypervisor_type': 'qemu',
+ }
+ self.api_mock = mock.Mock()
+ self.api_mock.image_list.side_effect = [
+ [image_info], [],
+ ]
+ self.app.client_manager.image.api = self.api_mock
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = dict(
+ files={},
+ reservation_id=None,
+ min_count=1,
+ max_count=1,
+ security_groups=[],
+ userdata=None,
+ key_name=None,
+ availability_zone=None,
+ block_device_mapping_v2=[],
+ nics='none',
+ meta=None,
+ scheduler_hints={},
+ config_drive=None,
+ )
+ # ServerManager.create(name, image, flavor, **kwargs)
+ self.servers_mock.create.assert_called_with(
+ self.new_server.name,
+ image_info,
+ self.flavor,
+ **kwargs
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist(), data)
+
+ def test_server_create_image_property_multi(self):
+ arglist = [
+ '--image-property', 'hypervisor_type=qemu',
+ '--image-property', 'hw_disk_bus=ide',
+ '--flavor', 'flavor1',
+ '--nic', 'none',
+ self.new_server.name,
+ ]
+ verifylist = [
+ ('image_property', {'hypervisor_type': 'qemu',
+ 'hw_disk_bus': 'ide'}),
+ ('flavor', 'flavor1'),
+ ('nic', ['none']),
+ ('config_drive', False),
+ ('server_name', self.new_server.name),
+ ]
+ _image = image_fakes.FakeImage.create_one_image()
+ # create a image_info as the side_effect of the fake image_list()
+ image_info = {
+ 'id': _image.id,
+ 'name': _image.name,
+ 'owner': _image.owner,
+ 'hypervisor_type': 'qemu',
+ 'hw_disk_bus': 'ide',
+ }
+ self.api_mock = mock.Mock()
+ self.api_mock.image_list.side_effect = [
+ [image_info], [],
+ ]
+ self.app.client_manager.image.api = self.api_mock
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = dict(
+ files={},
+ reservation_id=None,
+ min_count=1,
+ max_count=1,
+ security_groups=[],
+ userdata=None,
+ key_name=None,
+ availability_zone=None,
+ block_device_mapping_v2=[],
+ nics='none',
+ meta=None,
+ scheduler_hints={},
+ config_drive=None,
+ )
+ # ServerManager.create(name, image, flavor, **kwargs)
+ self.servers_mock.create.assert_called_with(
+ self.new_server.name,
+ image_info,
+ self.flavor,
+ **kwargs
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist(), data)
+
+ def test_server_create_image_property_missed(self):
+ arglist = [
+ '--image-property', 'hypervisor_type=qemu',
+ '--image-property', 'hw_disk_bus=virtio',
+ '--flavor', 'flavor1',
+ '--nic', 'none',
+ self.new_server.name,
+ ]
+ verifylist = [
+ ('image_property', {'hypervisor_type': 'qemu',
+ 'hw_disk_bus': 'virtio'}),
+ ('flavor', 'flavor1'),
+ ('nic', ['none']),
+ ('config_drive', False),
+ ('server_name', self.new_server.name),
+ ]
+ _image = image_fakes.FakeImage.create_one_image()
+ # create a image_info as the side_effect of the fake image_list()
+ image_info = {
+ 'id': _image.id,
+ 'name': _image.name,
+ 'owner': _image.owner,
+ 'hypervisor_type': 'qemu',
+ 'hw_disk_bus': 'ide',
+ }
+ self.api_mock = mock.Mock()
+ self.api_mock.image_list.side_effect = [
+ [image_info], [],
+ ]
+ self.app.client_manager.image.api = self.api_mock
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ self.assertRaises(exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args)
+
class TestServerDelete(TestServer):
@@ -1494,7 +1752,7 @@ class TestServerDelete(TestServer):
mock_wait_for_delete.assert_called_once_with(
self.servers_mock,
servers[0].id,
- callback=server._show_progress
+ callback=mock.ANY,
)
self.assertIsNone(result)
@@ -1516,7 +1774,7 @@ class TestServerDelete(TestServer):
mock_wait_for_delete.assert_called_once_with(
self.servers_mock,
servers[0].id,
- callback=server._show_progress
+ callback=mock.ANY,
)
@@ -2073,17 +2331,17 @@ class TestServerRebuild(TestServer):
self.images_mock.get.return_value = self.image
# Fake the rebuilt new server.
- new_server = compute_fakes.FakeServer.create_one_server()
-
- # Fake the server to be rebuilt. The IDs of them should be the same.
attrs = {
- 'id': new_server.id,
'image': {
'id': self.image.id
},
'networks': {},
'adminPass': 'passw0rd',
}
+ new_server = compute_fakes.FakeServer.create_one_server(attrs=attrs)
+
+ # Fake the server to be rebuilt. The IDs of them should be the same.
+ attrs['id'] = new_server.id
methods = {
'rebuild': new_server,
}
@@ -2152,7 +2410,7 @@ class TestServerRebuild(TestServer):
mock_wait_for_status.assert_called_once_with(
self.servers_mock.get,
self.server.id,
- callback=server._show_progress,
+ callback=mock.ANY,
# **kwargs
)
@@ -2177,7 +2435,7 @@ class TestServerRebuild(TestServer):
mock_wait_for_status.assert_called_once_with(
self.servers_mock.get,
self.server.id,
- callback=server._show_progress
+ callback=mock.ANY,
)
self.servers_mock.get.assert_called_with(self.server.id)
@@ -2302,36 +2560,80 @@ class TestServerRescue(TestServer):
self.server.rescue.assert_called_with(image=None, password=password)
-class TestServerRemoveFloatingIP(TestServer):
+@mock.patch(
+ 'openstackclient.api.compute_v2.APIv2.floating_ip_remove'
+)
+class TestServerRemoveFloatingIPCompute(compute_fakes.TestComputev2):
def setUp(self):
- super(TestServerRemoveFloatingIP, self).setUp()
+ super(TestServerRemoveFloatingIPCompute, self).setUp()
+
+ self.app.client_manager.network_endpoint_enabled = False
# Get the command object to test
self.cmd = server.RemoveFloatingIP(self.app, None)
- # Set unshelve method to be tested.
- self.methods = {
- 'remove_floating_ip': None,
- }
+ def test_server_remove_floating_ip(self, fip_mock):
+ _floating_ip = compute_fakes.FakeFloatingIP.create_one_floating_ip()
- def test_server_remove_floating_ip(self):
- servers = self.setup_servers_mock(count=1)
+ arglist = [
+ 'server1',
+ _floating_ip['ip'],
+ ]
+ verifylist = [
+ ('server', 'server1'),
+ ('ip_address', _floating_ip['ip']),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ self.cmd.take_action(parsed_args)
+
+ fip_mock.assert_called_once_with(
+ 'server1',
+ _floating_ip['ip'],
+ )
+
+
+class TestServerRemoveFloatingIPNetwork(network_fakes.TestNetworkV2):
+
+ def setUp(self):
+ super(TestServerRemoveFloatingIPNetwork, self).setUp()
+ self.app.client_manager.network = mock.Mock()
+ self.network = self.app.client_manager.network
+ self.network.update_ip = mock.Mock(return_value=None)
+
+ # Get the command object to test
+ self.cmd = server.RemoveFloatingIP(self.app, self.namespace)
+
+ def test_server_remove_floating_ip_default(self):
+ _server = compute_fakes.FakeServer.create_one_server()
+ _floating_ip = network_fakes.FakeFloatingIP.create_one_floating_ip()
+ self.network.find_ip = mock.Mock(return_value=_floating_ip)
arglist = [
- servers[0].id,
- '1.2.3.4',
+ _server.id,
+ _floating_ip['ip'],
]
verifylist = [
- ('server', servers[0].id),
- ('ip_address', '1.2.3.4'),
+ ('server', _server.id),
+ ('ip_address', _floating_ip['ip']),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- result = self.cmd.take_action(parsed_args)
+ self.cmd.take_action(parsed_args)
- servers[0].remove_floating_ip.assert_called_once_with('1.2.3.4')
- self.assertIsNone(result)
+ attrs = {
+ 'port_id': None,
+ }
+
+ self.network.find_ip.assert_called_once_with(
+ _floating_ip['ip'],
+ ignore_missing=False,
+ )
+ self.network.update_ip.assert_called_once_with(
+ _floating_ip,
+ **attrs
+ )
class TestServerRemovePort(TestServer):
@@ -2619,7 +2921,7 @@ class TestServerResize(TestServer):
mock_wait_for_status.assert_called_once_with(
self.servers_mock.get,
self.server.id,
- callback=server._show_progress,
+ callback=mock.ANY,
**kwargs
)
@@ -2659,7 +2961,7 @@ class TestServerResize(TestServer):
mock_wait_for_status.assert_called_once_with(
self.servers_mock.get,
self.server.id,
- callback=server._show_progress,
+ callback=mock.ANY,
**kwargs
)
@@ -2915,6 +3217,33 @@ class TestServerShow(TestServer):
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
+ def test_show_embedded_flavor(self):
+ # Tests using --os-compute-api-version >= 2.47 where the flavor
+ # details are embedded in the server response body excluding the id.
+ arglist = [
+ self.server.name,
+ ]
+ verifylist = [
+ ('diagnostics', False),
+ ('server', self.server.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ self.server.info['flavor'] = {
+ 'ephemeral': 0,
+ 'ram': 512,
+ 'original_name': 'm1.tiny',
+ 'vcpus': 1,
+ 'extra_specs': {},
+ 'swap': 0,
+ 'disk': 1
+ }
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.assertEqual(self.columns, columns)
+ # Since the flavor details are in a dict we can't be sure of the
+ # ordering so just assert that one of the keys is in the output.
+ self.assertIn('original_name', data[2])
+
def test_show_diagnostics(self):
arglist = [
'--diagnostics',
diff --git a/openstackclient/tests/unit/compute/v2/test_service.py b/openstackclient/tests/unit/compute/v2/test_service.py
index 8403efc9..bd299123 100644
--- a/openstackclient/tests/unit/compute/v2/test_service.py
+++ b/openstackclient/tests/unit/compute/v2/test_service.py
@@ -15,7 +15,7 @@
import mock
from mock import call
-
+from novaclient import api_versions
from osc_lib import exceptions
from openstackclient.compute.v2 import service
@@ -340,6 +340,8 @@ class TestServiceSet(TestService):
('service', self.service.binary),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.11')
result = self.cmd.take_action(parsed_args)
self.service_mock.force_down.assert_called_once_with(
self.service.host, self.service.binary, force_down=False)
@@ -359,6 +361,8 @@ class TestServiceSet(TestService):
('service', self.service.binary),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.11')
result = self.cmd.take_action(parsed_args)
self.service_mock.force_down.assert_called_once_with(
self.service.host, self.service.binary, force_down=True)
@@ -380,6 +384,8 @@ class TestServiceSet(TestService):
('service', self.service.binary),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.11')
result = self.cmd.take_action(parsed_args)
self.service_mock.enable.assert_called_once_with(
self.service.host, self.service.binary)
@@ -402,6 +408,8 @@ class TestServiceSet(TestService):
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ self.app.client_manager.compute.api_version = api_versions.APIVersion(
+ '2.11')
with mock.patch.object(self.service_mock, 'enable',
side_effect=Exception()):
self.assertRaises(exceptions.CommandError,
diff --git a/openstackclient/tests/unit/fakes.py b/openstackclient/tests/unit/fakes.py
index 65c76b3e..954973ef 100644
--- a/openstackclient/tests/unit/fakes.py
+++ b/openstackclient/tests/unit/fakes.py
@@ -140,6 +140,8 @@ class FakeClientManager(object):
self.auth_ref = None
self.auth_plugin_name = None
self.network_endpoint_enabled = True
+ self.compute_endpoint_enabled = True
+ self.volume_endpoint_enabled = True
def get_configuration(self):
return {
@@ -155,6 +157,12 @@ class FakeClientManager(object):
def is_network_endpoint_enabled(self):
return self.network_endpoint_enabled
+ def is_compute_endpoint_enabled(self):
+ return self.compute_endpoint_enabled
+
+ def is_volume_endpoint_enabled(self, client):
+ return self.volume_endpoint_enabled
+
class FakeModule(object):
diff --git a/openstackclient/tests/unit/identity/v3/fakes.py b/openstackclient/tests/unit/identity/v3/fakes.py
index 3e2caf01..7aa9cd7c 100644
--- a/openstackclient/tests/unit/identity/v3/fakes.py
+++ b/openstackclient/tests/unit/identity/v3/fakes.py
@@ -14,6 +14,7 @@
#
import copy
+import datetime
import uuid
from keystoneauth1 import access
@@ -34,6 +35,7 @@ DOMAIN = {
'name': domain_name,
'description': domain_description,
'enabled': True,
+ 'tags': [],
'links': base_url + 'domains/' + domain_id,
}
@@ -115,6 +117,7 @@ PROJECT = {
'description': project_description,
'enabled': True,
'domain_id': domain_id,
+ 'tags': [],
'links': base_url + 'projects/' + project_id,
}
@@ -124,6 +127,7 @@ PROJECT_2 = {
'description': project_description + 'plus four more',
'enabled': True,
'domain_id': domain_id,
+ 'tags': [],
'links': base_url + 'projects/' + project_id,
}
@@ -145,6 +149,7 @@ PROJECT_WITH_PARENT = {
'enabled': True,
'domain_id': domain_id,
'parent_id': project_id,
+ 'tags': [],
'links': base_url + 'projects/' + (project_id + '-with-parent'),
}
@@ -155,6 +160,7 @@ PROJECT_WITH_GRANDPARENT = {
'enabled': True,
'domain_id': domain_id,
'parent_id': PROJECT_WITH_PARENT['id'],
+ 'tags': [],
'links': base_url + 'projects/' + (project_id + '-with-grandparent'),
}
@@ -223,6 +229,20 @@ ENDPOINT = {
'links': base_url + 'endpoints/' + endpoint_id,
}
+endpoint_group_id = 'eg-123'
+endpoint_group_description = 'eg 123 description'
+endpoint_group_filters = {
+ 'service_id': service_id,
+ 'region_id': endpoint_region,
+}
+
+ENDPOINT_GROUP = {
+ 'id': endpoint_group_id,
+ 'filters': endpoint_group_filters,
+ 'description': endpoint_group_description,
+ 'links': base_url + 'endpoint_groups/' + endpoint_group_id,
+}
+
user_id = 'bbbbbbb-aaaa-aaaa-aaaa-bbbbbbbaaaa'
user_name = 'paul'
user_description = 'Sir Paul'
@@ -438,6 +458,34 @@ OAUTH_VERIFIER = {
'oauth_verifier': oauth_verifier_pin
}
+app_cred_id = 'app-cred-id'
+app_cred_name = 'testing_app_cred'
+app_cred_role = {"id": role_id, "name": role_name, "domain": None},
+app_cred_description = 'app credential for testing'
+app_cred_expires = datetime.datetime(2022, 1, 1, 0, 0)
+app_cred_expires_str = app_cred_expires.strftime('%Y-%m-%dT%H:%M:%S%z')
+app_cred_secret = 'moresecuresecret'
+APP_CRED_BASIC = {
+ 'id': app_cred_id,
+ 'name': app_cred_name,
+ 'project_id': project_id,
+ 'roles': app_cred_role,
+ 'description': None,
+ 'expires_at': None,
+ 'unrestricted': False,
+ 'secret': app_cred_secret
+}
+APP_CRED_OPTIONS = {
+ 'id': app_cred_id,
+ 'name': app_cred_name,
+ 'project_id': project_id,
+ 'roles': app_cred_role,
+ 'description': app_cred_description,
+ 'expires_at': app_cred_expires_str,
+ 'unrestricted': False,
+ 'secret': app_cred_secret
+}
+
def fake_auth_ref(fake_token, fake_service=None):
"""Create an auth_ref using keystoneauth's fixtures"""
@@ -495,6 +543,8 @@ class FakeIdentityv3Client(object):
self.endpoints.resource_class = fakes.FakeResource(None, {})
self.endpoint_filter = mock.Mock()
self.endpoint_filter.resource_class = fakes.FakeResource(None, {})
+ self.endpoint_groups = mock.Mock()
+ self.endpoint_groups.resource_class = fakes.FakeResource(None, {})
self.groups = mock.Mock()
self.groups.resource_class = fakes.FakeResource(None, {})
self.oauth1 = mock.Mock()
@@ -523,6 +573,11 @@ class FakeIdentityv3Client(object):
self.auth = FakeAuth()
self.auth.client = mock.Mock()
self.auth.client.resource_class = fakes.FakeResource(None, {})
+ self.application_credentials = mock.Mock()
+ self.application_credentials.resource_class = fakes.FakeResource(None,
+ {})
+ self.inference_rules = mock.Mock()
+ self.inference_rules.resource_class = fakes.FakeResource(None, {})
class FakeFederationManager(object):
@@ -619,6 +674,7 @@ class FakeProject(object):
'is_domain': False,
'domain_id': 'domain-id-' + uuid.uuid4().hex,
'parent_id': 'parent-id-' + uuid.uuid4().hex,
+ 'tags': [],
'links': 'links-' + uuid.uuid4().hex,
}
project_info.update(attrs)
@@ -666,6 +722,7 @@ class FakeDomain(object):
'name': 'domain-name-' + uuid.uuid4().hex,
'description': 'domain-description-' + uuid.uuid4().hex,
'enabled': True,
+ 'tags': [],
'links': 'links-' + uuid.uuid4().hex,
}
domain_info.update(attrs)
diff --git a/openstackclient/tests/unit/identity/v3/test_application_credential.py b/openstackclient/tests/unit/identity/v3/test_application_credential.py
new file mode 100644
index 00000000..e7c8ede8
--- /dev/null
+++ b/openstackclient/tests/unit/identity/v3/test_application_credential.py
@@ -0,0 +1,309 @@
+# Copyright 2018 SUSE Linux GmbH
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+
+import copy
+
+import mock
+from osc_lib import exceptions
+from osc_lib import utils
+
+from openstackclient.identity.v3 import application_credential
+from openstackclient.tests.unit import fakes
+from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes
+
+
+class TestApplicationCredential(identity_fakes.TestIdentityv3):
+
+ def setUp(self):
+ super(TestApplicationCredential, self).setUp()
+
+ identity_manager = self.app.client_manager.identity
+ self.app_creds_mock = identity_manager.application_credentials
+ self.app_creds_mock.reset_mock()
+ self.roles_mock = identity_manager.roles
+ self.roles_mock.reset_mock()
+
+
+class TestApplicationCredentialCreate(TestApplicationCredential):
+
+ def setUp(self):
+ super(TestApplicationCredentialCreate, self).setUp()
+
+ self.roles_mock.get.return_value = fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.ROLE),
+ loaded=True,
+ )
+
+ # Get the command object to test
+ self.cmd = application_credential.CreateApplicationCredential(
+ self.app, None)
+
+ def test_application_credential_create_basic(self):
+ self.app_creds_mock.create.return_value = fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.APP_CRED_BASIC),
+ loaded=True,
+ )
+
+ name = identity_fakes.app_cred_name
+ arglist = [
+ name
+ ]
+ verifylist = [
+ ('name', identity_fakes.app_cred_name)
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class ShowOne in cliff, abstract method take_action()
+ # returns a two-part tuple with a tuple of column names and a tuple of
+ # data to be shown.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'secret': None,
+ 'roles': [],
+ 'expires_at': None,
+ 'description': None,
+ 'unrestricted': False,
+ }
+ self.app_creds_mock.create.assert_called_with(
+ name,
+ **kwargs
+ )
+
+ collist = ('description', 'expires_at', 'id', 'name', 'project_id',
+ 'roles', 'secret', 'unrestricted')
+ self.assertEqual(collist, columns)
+ datalist = (
+ None,
+ None,
+ identity_fakes.app_cred_id,
+ identity_fakes.app_cred_name,
+ identity_fakes.project_id,
+ identity_fakes.role_name,
+ identity_fakes.app_cred_secret,
+ False,
+ )
+ self.assertEqual(datalist, data)
+
+ def test_application_credential_create_with_options(self):
+ name = identity_fakes.app_cred_name
+ self.app_creds_mock.create.return_value = fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.APP_CRED_OPTIONS),
+ loaded=True,
+ )
+
+ arglist = [
+ name,
+ '--secret', 'moresecuresecret',
+ '--role', identity_fakes.role_id,
+ '--expiration', identity_fakes.app_cred_expires_str,
+ '--description', 'credential for testing'
+ ]
+ verifylist = [
+ ('name', identity_fakes.app_cred_name),
+ ('secret', 'moresecuresecret'),
+ ('role', [identity_fakes.role_id]),
+ ('expiration', identity_fakes.app_cred_expires_str),
+ ('description', 'credential for testing')
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class ShowOne in cliff, abstract method take_action()
+ # returns a two-part tuple with a tuple of column names and a tuple of
+ # data to be shown.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'secret': 'moresecuresecret',
+ 'roles': [identity_fakes.role_id],
+ 'expires_at': identity_fakes.app_cred_expires,
+ 'description': 'credential for testing',
+ 'unrestricted': False
+ }
+ self.app_creds_mock.create.assert_called_with(
+ name,
+ **kwargs
+ )
+
+ collist = ('description', 'expires_at', 'id', 'name', 'project_id',
+ 'roles', 'secret', 'unrestricted')
+ self.assertEqual(collist, columns)
+ datalist = (
+ identity_fakes.app_cred_description,
+ identity_fakes.app_cred_expires_str,
+ identity_fakes.app_cred_id,
+ identity_fakes.app_cred_name,
+ identity_fakes.project_id,
+ identity_fakes.role_name,
+ identity_fakes.app_cred_secret,
+ False,
+ )
+ self.assertEqual(datalist, data)
+
+
+class TestApplicationCredentialDelete(TestApplicationCredential):
+
+ def setUp(self):
+ super(TestApplicationCredentialDelete, self).setUp()
+
+ # This is the return value for utils.find_resource()
+ self.app_creds_mock.get.return_value = fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.APP_CRED_BASIC),
+ loaded=True,
+ )
+ self.app_creds_mock.delete.return_value = None
+
+ # Get the command object to test
+ self.cmd = application_credential.DeleteApplicationCredential(
+ self.app, None)
+
+ def test_application_credential_delete(self):
+ arglist = [
+ identity_fakes.app_cred_id,
+ ]
+ verifylist = [
+ ('application_credential', [identity_fakes.app_cred_id])
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ self.app_creds_mock.delete.assert_called_with(
+ identity_fakes.app_cred_id,
+ )
+ self.assertIsNone(result)
+
+ @mock.patch.object(utils, 'find_resource')
+ def test_delete_multi_app_creds_with_exception(self, find_mock):
+ find_mock.side_effect = [self.app_creds_mock.get.return_value,
+ exceptions.CommandError]
+ arglist = [
+ identity_fakes.app_cred_id,
+ 'nonexistent_app_cred',
+ ]
+ verifylist = [
+ ('application_credential', arglist),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ try:
+ self.cmd.take_action(parsed_args)
+ self.fail('CommandError should be raised.')
+ except exceptions.CommandError as e:
+ self.assertEqual('1 of 2 application credentials failed to'
+ ' delete.', str(e))
+
+ find_mock.assert_any_call(self.app_creds_mock,
+ identity_fakes.app_cred_id)
+ find_mock.assert_any_call(self.app_creds_mock,
+ 'nonexistent_app_cred')
+
+ self.assertEqual(2, find_mock.call_count)
+ self.app_creds_mock.delete.assert_called_once_with(
+ identity_fakes.app_cred_id)
+
+
+class TestApplicationCredentialList(TestApplicationCredential):
+
+ def setUp(self):
+ super(TestApplicationCredentialList, self).setUp()
+
+ self.app_creds_mock.list.return_value = [
+ fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.APP_CRED_BASIC),
+ loaded=True,
+ ),
+ ]
+
+ # Get the command object to test
+ self.cmd = application_credential.ListApplicationCredential(self.app,
+ None)
+
+ def test_application_credential_list(self):
+ 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)
+
+ self.app_creds_mock.list.assert_called_with(user=None)
+
+ collist = ('ID', 'Name', 'Project ID', 'Description', 'Expires At')
+ self.assertEqual(collist, columns)
+ datalist = ((
+ identity_fakes.app_cred_id,
+ identity_fakes.app_cred_name,
+ identity_fakes.project_id,
+ None,
+ None
+ ), )
+ self.assertEqual(datalist, tuple(data))
+
+
+class TestApplicationCredentialShow(TestApplicationCredential):
+
+ def setUp(self):
+ super(TestApplicationCredentialShow, self).setUp()
+
+ self.app_creds_mock.get.return_value = fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.APP_CRED_BASIC),
+ loaded=True,
+ )
+
+ # Get the command object to test
+ self.cmd = application_credential.ShowApplicationCredential(self.app,
+ None)
+
+ def test_application_credential_show(self):
+ arglist = [
+ identity_fakes.app_cred_id,
+ ]
+ verifylist = [
+ ('application_credential', identity_fakes.app_cred_id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class ShowOne in cliff, abstract method take_action()
+ # returns a two-part tuple with a tuple of column names and a tuple of
+ # data to be shown.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.app_creds_mock.get.assert_called_with(identity_fakes.app_cred_id)
+
+ collist = ('description', 'expires_at', 'id', 'name', 'project_id',
+ 'roles', 'secret', 'unrestricted')
+ self.assertEqual(collist, columns)
+ datalist = (
+ None,
+ None,
+ identity_fakes.app_cred_id,
+ identity_fakes.app_cred_name,
+ identity_fakes.project_id,
+ identity_fakes.role_name,
+ identity_fakes.app_cred_secret,
+ False,
+ )
+ self.assertEqual(datalist, data)
diff --git a/openstackclient/tests/unit/identity/v3/test_domain.py b/openstackclient/tests/unit/identity/v3/test_domain.py
index 36f13d33..014986e5 100644
--- a/openstackclient/tests/unit/identity/v3/test_domain.py
+++ b/openstackclient/tests/unit/identity/v3/test_domain.py
@@ -31,6 +31,7 @@ class TestDomainCreate(TestDomain):
'enabled',
'id',
'name',
+ 'tags'
)
def setUp(self):
@@ -43,6 +44,7 @@ class TestDomainCreate(TestDomain):
True,
self.domain.id,
self.domain.name,
+ self.domain.tags
)
# Get the command object to test
@@ -390,12 +392,13 @@ class TestDomainShow(TestDomain):
self.domain.id,
)
- collist = ('description', 'enabled', 'id', 'name')
+ collist = ('description', 'enabled', 'id', 'name', 'tags')
self.assertEqual(collist, columns)
datalist = (
self.domain.description,
True,
self.domain.id,
self.domain.name,
+ self.domain.tags
)
self.assertEqual(datalist, data)
diff --git a/openstackclient/tests/unit/identity/v3/test_implied_role.py b/openstackclient/tests/unit/identity/v3/test_implied_role.py
index 08273f73..74968129 100644
--- a/openstackclient/tests/unit/identity/v3/test_implied_role.py
+++ b/openstackclient/tests/unit/identity/v3/test_implied_role.py
@@ -25,26 +25,32 @@ class TestRole(identity_fakes.TestIdentityv3):
def setUp(self):
super(TestRole, self).setUp()
+ identity_client = self.app.client_manager.identity
+
# Get a shortcut to the UserManager Mock
- self.users_mock = self.app.client_manager.identity.users
+ self.users_mock = identity_client.users
self.users_mock.reset_mock()
# Get a shortcut to the UserManager Mock
- self.groups_mock = self.app.client_manager.identity.groups
+ self.groups_mock = identity_client.groups
self.groups_mock.reset_mock()
# Get a shortcut to the DomainManager Mock
- self.domains_mock = self.app.client_manager.identity.domains
+ self.domains_mock = identity_client.domains
self.domains_mock.reset_mock()
# Get a shortcut to the ProjectManager Mock
- self.projects_mock = self.app.client_manager.identity.projects
+ self.projects_mock = identity_client.projects
self.projects_mock.reset_mock()
# Get a shortcut to the RoleManager Mock
- self.roles_mock = self.app.client_manager.identity.roles
+ self.roles_mock = identity_client.roles
self.roles_mock.reset_mock()
+ # Get a shortcut to the InferenceRuleManager Mock
+ self.inference_rules_mock = identity_client.inference_rules
+ self.inference_rules_mock.reset_mock()
+
def _is_inheritance_testcase(self):
return False
@@ -67,12 +73,13 @@ class TestImpliedRoleCreate(TestRole):
),
]
- self.roles_mock.create_implied.return_value = fakes.FakeResource(
+ fake_resource = fakes.FakeResource(
None,
{'prior_role': copy.deepcopy(identity_fakes.ROLES[0]),
'implied': copy.deepcopy(identity_fakes.ROLES[1]), },
loaded=True,
)
+ self.inference_rules_mock.create.return_value = fake_resource
self.cmd = implied_role.CreateImpliedRole(self.app, None)
@@ -93,8 +100,8 @@ class TestImpliedRoleCreate(TestRole):
# data to be shown.
columns, data = self.cmd.take_action(parsed_args)
- # RoleManager.create_implied(prior, implied)
- self.roles_mock.create_implied.assert_called_with(
+ # InferenceRuleManager.create(prior, implied)
+ self.inference_rules_mock.create.assert_called_with(
identity_fakes.ROLES[0]['id'],
identity_fakes.ROLES[1]['id']
)
@@ -126,12 +133,13 @@ class TestImpliedRoleDelete(TestRole):
),
]
- self.roles_mock.delete_implied.return_value = fakes.FakeResource(
+ fake_resource = fakes.FakeResource(
None,
{'prior-role': copy.deepcopy(identity_fakes.ROLES[0]),
'implied': copy.deepcopy(identity_fakes.ROLES[1]), },
loaded=True,
)
+ self.inference_rules_mock.delete.return_value = fake_resource
self.cmd = implied_role.DeleteImpliedRole(self.app, None)
@@ -147,7 +155,7 @@ class TestImpliedRoleDelete(TestRole):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
- self.roles_mock.delete_implied.assert_called_with(
+ self.inference_rules_mock.delete.assert_called_with(
identity_fakes.ROLES[0]['id'],
identity_fakes.ROLES[1]['id']
)
@@ -158,7 +166,7 @@ class TestImpliedRoleList(TestRole):
def setUp(self):
super(TestImpliedRoleList, self).setUp()
- self.roles_mock.list_inference_roles.return_value = (
+ self.inference_rules_mock.list_inference_roles.return_value = (
identity_fakes.FakeImpliedRoleResponse.create_list())
self.cmd = implied_role.ListImpliedRole(self.app, None)
@@ -168,7 +176,7 @@ class TestImpliedRoleList(TestRole):
verifylist = []
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- self.roles_mock.list_inference_roles.assert_called_with()
+ self.inference_rules_mock.list_inference_roles.assert_called_with()
collist = ['Prior Role ID', 'Prior Role Name',
'Implied Role ID', 'Implied Role Name']
diff --git a/openstackclient/tests/unit/identity/v3/test_project.py b/openstackclient/tests/unit/identity/v3/test_project.py
index 16ac3116..266da227 100644
--- a/openstackclient/tests/unit/identity/v3/test_project.py
+++ b/openstackclient/tests/unit/identity/v3/test_project.py
@@ -50,6 +50,7 @@ class TestProjectCreate(TestProject):
'is_domain',
'name',
'parent_id',
+ 'tags'
)
def setUp(self):
@@ -67,6 +68,7 @@ class TestProjectCreate(TestProject):
False,
self.project.name,
self.project.parent_id,
+ self.project.tags
)
# Get the command object to test
self.cmd = project.CreateProject(self.app, None)
@@ -80,6 +82,7 @@ class TestProjectCreate(TestProject):
('enable', False),
('disable', False),
('name', self.project.name),
+ ('tags', [])
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -95,6 +98,7 @@ class TestProjectCreate(TestProject):
'description': None,
'enabled': True,
'parent': None,
+ 'tags': []
}
# ProjectManager.create(name=, domain=, description=,
# enabled=, **kwargs)
@@ -110,6 +114,7 @@ class TestProjectCreate(TestProject):
'is_domain',
'name',
'parent_id',
+ 'tags'
)
self.assertEqual(collist, columns)
datalist = (
@@ -120,6 +125,7 @@ class TestProjectCreate(TestProject):
False,
self.project.name,
self.project.parent_id,
+ self.project.tags
)
self.assertEqual(datalist, data)
@@ -134,6 +140,7 @@ class TestProjectCreate(TestProject):
('disable', False),
('name', self.project.name),
('parent', None),
+ ('tags', [])
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -149,6 +156,7 @@ class TestProjectCreate(TestProject):
'description': 'new desc',
'enabled': True,
'parent': None,
+ 'tags': []
}
# ProjectManager.create(name=, domain=, description=,
# enabled=, **kwargs)
@@ -170,6 +178,7 @@ class TestProjectCreate(TestProject):
('disable', False),
('name', self.project.name),
('parent', None),
+ ('tags', [])
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -185,6 +194,7 @@ class TestProjectCreate(TestProject):
'description': None,
'enabled': True,
'parent': None,
+ 'tags': []
}
# ProjectManager.create(name=, domain=, description=,
# enabled=, **kwargs)
@@ -206,6 +216,7 @@ class TestProjectCreate(TestProject):
('disable', False),
('name', self.project.name),
('parent', None),
+ ('tags', [])
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
mocker = mock.Mock()
@@ -221,6 +232,7 @@ class TestProjectCreate(TestProject):
'description': None,
'enabled': True,
'parent': None,
+ 'tags': []
}
self.projects_mock.create.assert_called_with(
**kwargs
@@ -238,6 +250,7 @@ class TestProjectCreate(TestProject):
('disable', False),
('name', self.project.name),
('parent', None),
+ ('tags', [])
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -253,6 +266,7 @@ class TestProjectCreate(TestProject):
'description': None,
'enabled': True,
'parent': None,
+ 'tags': []
}
# ProjectManager.create(name=, domain=, description=,
# enabled=, **kwargs)
@@ -288,6 +302,7 @@ class TestProjectCreate(TestProject):
'description': None,
'enabled': False,
'parent': None,
+ 'tags': []
}
# ProjectManager.create(name=, domain=,
# description=, enabled=, **kwargs)
@@ -324,6 +339,7 @@ class TestProjectCreate(TestProject):
'parent': None,
'fee': 'fi',
'fo': 'fum',
+ 'tags': []
}
# ProjectManager.create(name=, domain=, description=,
# enabled=, **kwargs)
@@ -352,6 +368,7 @@ class TestProjectCreate(TestProject):
('enable', False),
('disable', False),
('name', self.project.name),
+ ('tags', [])
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -363,6 +380,7 @@ class TestProjectCreate(TestProject):
'parent': self.parent.id,
'description': None,
'enabled': True,
+ 'tags': []
}
self.projects_mock.create.assert_called_with(
@@ -377,6 +395,7 @@ class TestProjectCreate(TestProject):
'is_domain',
'name',
'parent_id',
+ 'tags'
)
self.assertEqual(columns, collist)
datalist = (
@@ -387,6 +406,7 @@ class TestProjectCreate(TestProject):
self.project.is_domain,
self.project.name,
self.parent.id,
+ self.project.tags
)
self.assertEqual(data, datalist)
@@ -417,6 +437,43 @@ class TestProjectCreate(TestProject):
parsed_args,
)
+ def test_project_create_with_tags(self):
+ arglist = [
+ '--domain', self.project.domain_id,
+ '--tag', 'foo',
+ self.project.name,
+ ]
+ verifylist = [
+ ('domain', self.project.domain_id),
+ ('enable', False),
+ ('disable', False),
+ ('name', self.project.name),
+ ('parent', None),
+ ('tags', ['foo'])
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class ShowOne in cliff, abstract method take_action()
+ # returns a two-part tuple with a tuple of column names and a tuple of
+ # data to be shown.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'name': self.project.name,
+ 'domain': self.project.domain_id,
+ 'description': None,
+ 'enabled': True,
+ 'parent': None,
+ 'tags': ['foo']
+ }
+ self.projects_mock.create.assert_called_with(
+ **kwargs
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist, data)
+
class TestProjectDelete(TestProject):
@@ -816,6 +873,38 @@ class TestProjectSet(TestProject):
)
self.assertIsNone(result)
+ def test_project_set_tags(self):
+ arglist = [
+ '--name', 'qwerty',
+ '--domain', self.project.domain_id,
+ '--tag', 'foo',
+ self.project.name,
+ ]
+ verifylist = [
+ ('name', 'qwerty'),
+ ('domain', self.project.domain_id),
+ ('enable', False),
+ ('disable', False),
+ ('project', self.project.name),
+ ('tags', ['foo'])
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'name': 'qwerty',
+ 'tags': ['foo']
+ }
+ # ProjectManager.update(project, name=, domain=, description=,
+ # enabled=, **kwargs)
+ self.projects_mock.update.assert_called_with(
+ self.project.id,
+ **kwargs
+ )
+ self.assertIsNone(result)
+
class TestProjectShow(TestProject):
@@ -867,6 +956,7 @@ class TestProjectShow(TestProject):
'is_domain',
'name',
'parent_id',
+ 'tags'
)
self.assertEqual(collist, columns)
datalist = (
@@ -877,6 +967,7 @@ class TestProjectShow(TestProject):
False,
self.project.name,
self.project.parent_id,
+ self.project.tags
)
self.assertEqual(datalist, data)
@@ -926,6 +1017,7 @@ class TestProjectShow(TestProject):
'name',
'parent_id',
'parents',
+ 'tags'
)
self.assertEqual(columns, collist)
datalist = (
@@ -936,7 +1028,8 @@ class TestProjectShow(TestProject):
self.project.is_domain,
self.project.name,
self.project.parent_id,
- [{'project': {'id': self.project.parent_id}}]
+ [{'project': {'id': self.project.parent_id}}],
+ self.project.tags
)
self.assertEqual(data, datalist)
@@ -985,6 +1078,7 @@ class TestProjectShow(TestProject):
'name',
'parent_id',
'subtree',
+ 'tags'
)
self.assertEqual(columns, collist)
datalist = (
@@ -995,7 +1089,8 @@ class TestProjectShow(TestProject):
self.project.is_domain,
self.project.name,
self.project.parent_id,
- [{'project': {'id': 'children-id'}}]
+ [{'project': {'id': 'children-id'}}],
+ self.project.tags
)
self.assertEqual(data, datalist)
@@ -1047,6 +1142,7 @@ class TestProjectShow(TestProject):
'parent_id',
'parents',
'subtree',
+ 'tags'
)
self.assertEqual(columns, collist)
datalist = (
@@ -1058,7 +1154,8 @@ class TestProjectShow(TestProject):
self.project.name,
self.project.parent_id,
[{'project': {'id': self.project.parent_id}}],
- [{'project': {'id': 'children-id'}}]
+ [{'project': {'id': 'children-id'}}],
+ self.project.tags
)
self.assertEqual(data, datalist)
diff --git a/openstackclient/tests/unit/identity/v3/test_role_assignment.py b/openstackclient/tests/unit/identity/v3/test_role_assignment.py
index 835837e6..bff6c56d 100644
--- a/openstackclient/tests/unit/identity/v3/test_role_assignment.py
+++ b/openstackclient/tests/unit/identity/v3/test_role_assignment.py
@@ -34,6 +34,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
'Group',
'Project',
'Domain',
+ 'System',
'Inherited',
)
@@ -95,6 +96,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
self.role_assignments_mock.list.assert_called_with(
domain=None,
+ system=None,
group=None,
effective=False,
role=None,
@@ -110,12 +112,14 @@ class TestRoleAssignmentList(TestRoleAssignment):
'',
identity_fakes.project_id,
'',
+ '',
False
), (identity_fakes.role_id,
'',
identity_fakes.group_id,
identity_fakes.project_id,
'',
+ '',
False
),)
self.assertEqual(datalist, tuple(data))
@@ -143,6 +147,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
verifylist = [
('user', identity_fakes.user_name),
('group', None),
+ ('system', None),
('domain', None),
('project', None),
('role', None),
@@ -159,6 +164,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
self.role_assignments_mock.list.assert_called_with(
domain=None,
+ system=None,
user=self.users_mock.get(),
group=None,
project=None,
@@ -174,12 +180,14 @@ class TestRoleAssignmentList(TestRoleAssignment):
'',
'',
identity_fakes.domain_id,
+ '',
False
), (identity_fakes.role_id,
identity_fakes.user_id,
'',
identity_fakes.project_id,
'',
+ '',
False
),)
self.assertEqual(datalist, tuple(data))
@@ -207,6 +215,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
verifylist = [
('user', None),
('group', identity_fakes.group_name),
+ ('system', None),
('domain', None),
('project', None),
('role', None),
@@ -223,6 +232,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
self.role_assignments_mock.list.assert_called_with(
domain=None,
+ system=None,
group=self.groups_mock.get(),
effective=False,
project=None,
@@ -238,12 +248,14 @@ class TestRoleAssignmentList(TestRoleAssignment):
identity_fakes.group_id,
'',
identity_fakes.domain_id,
+ '',
False
), (identity_fakes.role_id,
'',
identity_fakes.group_id,
identity_fakes.project_id,
'',
+ '',
False
),)
self.assertEqual(datalist, tuple(data))
@@ -271,6 +283,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
verifylist = [
('user', None),
('group', None),
+ ('system', None),
('domain', identity_fakes.domain_name),
('project', None),
('role', None),
@@ -287,6 +300,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
self.role_assignments_mock.list.assert_called_with(
domain=self.domains_mock.get(),
+ system=None,
group=None,
effective=False,
project=None,
@@ -302,12 +316,14 @@ class TestRoleAssignmentList(TestRoleAssignment):
'',
'',
identity_fakes.domain_id,
+ '',
False
), (identity_fakes.role_id,
'',
identity_fakes.group_id,
'',
identity_fakes.domain_id,
+ '',
False
),)
self.assertEqual(datalist, tuple(data))
@@ -335,6 +351,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
verifylist = [
('user', None),
('group', None),
+ ('system', None),
('domain', None),
('project', identity_fakes.project_name),
('role', None),
@@ -351,6 +368,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
self.role_assignments_mock.list.assert_called_with(
domain=None,
+ system=None,
group=None,
effective=False,
project=self.projects_mock.get(),
@@ -366,12 +384,14 @@ class TestRoleAssignmentList(TestRoleAssignment):
'',
identity_fakes.project_id,
'',
+ '',
False
), (identity_fakes.role_id,
'',
identity_fakes.group_id,
identity_fakes.project_id,
'',
+ '',
False
),)
self.assertEqual(datalist, tuple(data))
@@ -398,6 +418,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
verifylist = [
('user', None),
('group', None),
+ ('system', None),
('domain', None),
('project', None),
('role', None),
@@ -416,6 +437,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
self.role_assignments_mock.list.assert_called_with(
domain=None,
+ system=None,
user=self.users_mock.get(),
group=None,
project=self.projects_mock.get(),
@@ -431,6 +453,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
'',
identity_fakes.project_id,
'',
+ '',
False
),)
self.assertEqual(datalist, tuple(data))
@@ -456,6 +479,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
verifylist = [
('user', None),
('group', None),
+ ('system', None),
('domain', None),
('project', None),
('role', None),
@@ -472,6 +496,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
self.role_assignments_mock.list.assert_called_with(
domain=None,
+ system=None,
group=None,
effective=True,
project=None,
@@ -487,12 +512,14 @@ class TestRoleAssignmentList(TestRoleAssignment):
'',
identity_fakes.project_id,
'',
+ '',
False
), (identity_fakes.role_id,
identity_fakes.user_id,
'',
'',
identity_fakes.domain_id,
+ '',
False
),)
self.assertEqual(tuple(data), datalist)
@@ -520,6 +547,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
verifylist = [
('user', None),
('group', None),
+ ('system', None),
('domain', None),
('project', None),
('role', None),
@@ -536,6 +564,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
self.role_assignments_mock.list.assert_called_with(
domain=None,
+ system=None,
group=None,
effective=False,
project=None,
@@ -551,12 +580,14 @@ class TestRoleAssignmentList(TestRoleAssignment):
'',
identity_fakes.project_id,
'',
+ '',
True
), (identity_fakes.role_id,
identity_fakes.user_id,
'',
'',
identity_fakes.domain_id,
+ '',
True
),)
self.assertEqual(datalist, tuple(data))
@@ -584,6 +615,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
verifylist = [
('user', None),
('group', None),
+ ('system', None),
('domain', None),
('project', None),
('role', None),
@@ -602,6 +634,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
self.role_assignments_mock.list.assert_called_with(
domain=None,
+ system=None,
group=None,
effective=False,
project=None,
@@ -610,7 +643,9 @@ class TestRoleAssignmentList(TestRoleAssignment):
os_inherit_extension_inherited_to=None,
include_names=True)
- collist = ('Role', 'User', 'Group', 'Project', 'Domain', 'Inherited')
+ collist = (
+ 'Role', 'User', 'Group', 'Project', 'Domain', 'System', 'Inherited'
+ )
self.assertEqual(columns, collist)
datalist1 = ((
@@ -620,12 +655,14 @@ class TestRoleAssignmentList(TestRoleAssignment):
'@'.join([identity_fakes.project_name,
identity_fakes.domain_name]),
'',
+ '',
False
), (identity_fakes.role_name,
'@'.join([identity_fakes.user_name, identity_fakes.domain_name]),
'',
'',
identity_fakes.domain_name,
+ '',
False
),)
self.assertEqual(tuple(data), datalist1)
@@ -648,6 +685,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
verifylist = [
('user', None),
('group', None),
+ ('system', None),
('domain', None),
('project', None),
('role', identity_fakes.ROLE_2['name']),
@@ -664,6 +702,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
self.role_assignments_mock.list.assert_called_with(
domain=None,
+ system=None,
user=None,
group=None,
project=None,
@@ -679,6 +718,7 @@ class TestRoleAssignmentList(TestRoleAssignment):
'',
'',
identity_fakes.domain_id,
+ '',
False
),)
self.assertEqual(datalist, tuple(data))
diff --git a/openstackclient/tests/unit/image/v2/test_image.py b/openstackclient/tests/unit/image/v2/test_image.py
index e1a79d13..301cd037 100644
--- a/openstackclient/tests/unit/image/v2/test_image.py
+++ b/openstackclient/tests/unit/image/v2/test_image.py
@@ -780,6 +780,52 @@ class TestImageList(TestImage):
)
+class TestListImageProjects(TestImage):
+
+ project = identity_fakes.FakeProject.create_one_project()
+ _image = image_fakes.FakeImage.create_one_image()
+ member = image_fakes.FakeImage.create_one_image_member(
+ attrs={'image_id': _image.id,
+ 'member_id': project.id}
+ )
+
+ columns = (
+ "Image ID",
+ "Member ID",
+ "Status"
+ )
+
+ datalist = ((
+ _image.id,
+ member.member_id,
+ member.status,
+ ))
+
+ def setUp(self):
+ super(TestListImageProjects, self).setUp()
+
+ self.images_mock.get.return_value = self._image
+ self.image_members_mock.list.return_value = self.datalist
+
+ self.cmd = image.ListImageProjects(self.app, None)
+
+ def test_image_member_list(self):
+ arglist = [
+ self._image.id
+ ]
+ verifylist = [
+ ('image', self._image.id)
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.image_members_mock.list.assert_called_with(self._image.id)
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(len(self.datalist), len(tuple(data)))
+
+
class TestRemoveProjectImage(TestImage):
project = identity_fakes.FakeProject.create_one_project()
diff --git a/openstackclient/tests/unit/network/v2/fakes.py b/openstackclient/tests/unit/network/v2/fakes.py
index e69df88a..45392125 100644
--- a/openstackclient/tests/unit/network/v2/fakes.py
+++ b/openstackclient/tests/unit/network/v2/fakes.py
@@ -335,6 +335,7 @@ class FakeNetwork(object):
'name': 'network-name-' + uuid.uuid4().hex,
'status': 'ACTIVE',
'description': 'network-description-' + uuid.uuid4().hex,
+ 'mtu': '1350',
'tenant_id': 'project-id-' + uuid.uuid4().hex,
'admin_state_up': True,
'shared': False,
@@ -347,7 +348,6 @@ class FakeNetwork(object):
'availability_zone_hints': [],
'is_default': False,
'port_security_enabled': True,
- 'tags': ['test'],
'qos_policy_id': 'qos-policy-id-' + uuid.uuid4().hex,
'ipv4_address_scope': 'ipv4' + uuid.uuid4().hex,
'ipv6_address_scope': 'ipv6' + uuid.uuid4().hex,
@@ -565,6 +565,7 @@ class FakePort(object):
'device_id': 'device-id-' + uuid.uuid4().hex,
'device_owner': 'compute:nova',
'dns_assignment': [{}],
+ 'dns_domain': 'dns-domain-' + uuid.uuid4().hex,
'dns_name': 'dns-name-' + uuid.uuid4().hex,
'extra_dhcp_opts': [{}],
'fixed_ips': [{'ip_address': '10.0.0.3',
@@ -1380,6 +1381,7 @@ class FakeFloatingIP(object):
'tenant_id': 'project-id-' + uuid.uuid4().hex,
'description': 'floating-ip-description-' + uuid.uuid4().hex,
'qos_policy_id': 'qos-policy-id-' + uuid.uuid4().hex,
+ 'tags': [],
}
# Overwrite default attributes.
diff --git a/openstackclient/tests/unit/network/v2/test_floating_ip_network.py b/openstackclient/tests/unit/network/v2/test_floating_ip_network.py
index f19849c4..65d87377 100644
--- a/openstackclient/tests/unit/network/v2/test_floating_ip_network.py
+++ b/openstackclient/tests/unit/network/v2/test_floating_ip_network.py
@@ -44,7 +44,7 @@ class TestCreateFloatingIPNetwork(TestFloatingIPNetwork):
subnet = network_fakes.FakeSubnet.create_one_subnet()
port = network_fakes.FakePort.create_one_port()
- # The floating ip to be deleted.
+ # The floating ip created.
floating_ip = network_fakes.FakeFloatingIP.create_one_floating_ip(
attrs={
'floating_network_id': floating_network.id,
@@ -65,6 +65,7 @@ class TestCreateFloatingIPNetwork(TestFloatingIPNetwork):
'qos_policy_id',
'router_id',
'status',
+ 'tags',
)
data = (
@@ -80,12 +81,14 @@ class TestCreateFloatingIPNetwork(TestFloatingIPNetwork):
floating_ip.qos_policy_id,
floating_ip.router_id,
floating_ip.status,
+ floating_ip.tags,
)
def setUp(self):
super(TestCreateFloatingIPNetwork, self).setUp()
self.network.create_ip = mock.Mock(return_value=self.floating_ip)
+ self.network.set_tags = mock.Mock(return_value=None)
self.network.find_network = mock.Mock(
return_value=self.floating_network)
@@ -221,6 +224,42 @@ class TestCreateFloatingIPNetwork(TestFloatingIPNetwork):
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
+ def _test_create_with_tag(self, add_tags=True):
+ arglist = [self.floating_ip.floating_network_id]
+ if add_tags:
+ arglist += ['--tag', 'red', '--tag', 'blue']
+ else:
+ arglist += ['--no-tag']
+
+ verifylist = [
+ ('network', self.floating_ip.floating_network_id),
+ ]
+ if add_tags:
+ verifylist.append(('tags', ['red', 'blue']))
+ else:
+ verifylist.append(('no_tag', True))
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = (self.cmd.take_action(parsed_args))
+
+ self.network.create_ip.assert_called_once_with(**{
+ 'floating_network_id': self.floating_ip.floating_network_id,
+ })
+ if add_tags:
+ self.network.set_tags.assert_called_once_with(
+ self.floating_ip,
+ tests_utils.CompareBySet(['red', 'blue']))
+ else:
+ self.assertFalse(self.network.set_tags.called)
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
+ def test_create_with_tags(self):
+ self._test_create_with_tag(add_tags=True)
+
+ def test_create_with_no_tag(self):
+ self._test_create_with_tag(add_tags=False)
+
class TestDeleteFloatingIPNetwork(TestFloatingIPNetwork):
@@ -353,6 +392,7 @@ class TestListFloatingIPNetwork(TestFloatingIPNetwork):
'Router',
'Status',
'Description',
+ 'Tags',
)
data = []
@@ -376,6 +416,7 @@ class TestListFloatingIPNetwork(TestFloatingIPNetwork):
ip.router_id,
ip.status,
ip.description,
+ ip.tags,
))
def setUp(self):
@@ -539,6 +580,31 @@ class TestListFloatingIPNetwork(TestFloatingIPNetwork):
self.assertEqual(self.columns_long, columns)
self.assertEqual(self.data_long, list(data))
+ def test_list_with_tag_options(self):
+ arglist = [
+ '--tags', 'red,blue',
+ '--any-tags', 'red,green',
+ '--not-tags', 'orange,yellow',
+ '--not-any-tags', 'black,white',
+ ]
+ verifylist = [
+ ('tags', ['red', 'blue']),
+ ('any_tags', ['red', 'green']),
+ ('not_tags', ['orange', 'yellow']),
+ ('not_any_tags', ['black', 'white']),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.ips.assert_called_once_with(
+ **{'tags': 'red,blue',
+ 'any_tags': 'red,green',
+ 'not_tags': 'orange,yellow',
+ 'not_any_tags': 'black,white'}
+ )
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, list(data))
+
class TestShowFloatingIPNetwork(TestFloatingIPNetwork):
@@ -558,6 +624,7 @@ class TestShowFloatingIPNetwork(TestFloatingIPNetwork):
'qos_policy_id',
'router_id',
'status',
+ 'tags',
)
data = (
@@ -573,6 +640,7 @@ class TestShowFloatingIPNetwork(TestFloatingIPNetwork):
floating_ip.qos_policy_id,
floating_ip.router_id,
floating_ip.status,
+ floating_ip.tags,
)
def setUp(self):
@@ -609,11 +677,12 @@ class TestSetFloatingIP(TestFloatingIPNetwork):
subnet = network_fakes.FakeSubnet.create_one_subnet()
port = network_fakes.FakePort.create_one_port()
- # The floating ip to be deleted.
+ # The floating ip to be set.
floating_ip = network_fakes.FakeFloatingIP.create_one_floating_ip(
attrs={
'floating_network_id': floating_network.id,
'port_id': port.id,
+ 'tags': ['green', 'red'],
}
)
@@ -622,6 +691,7 @@ class TestSetFloatingIP(TestFloatingIPNetwork):
self.network.find_ip = mock.Mock(return_value=self.floating_ip)
self.network.find_port = mock.Mock(return_value=self.port)
self.network.update_ip = mock.Mock(return_value=None)
+ self.network.set_tags = mock.Mock(return_value=None)
# Get the command object to test
self.cmd = fip.SetFloatingIP(self.app, self.namespace)
@@ -731,6 +801,36 @@ class TestSetFloatingIP(TestFloatingIPNetwork):
self.network.update_ip.assert_called_once_with(
self.floating_ip, **attrs)
+ def _test_set_tags(self, with_tags=True):
+ if with_tags:
+ arglist = ['--tag', 'red', '--tag', 'blue']
+ verifylist = [('tags', ['red', 'blue'])]
+ expected_args = ['red', 'blue', 'green']
+ else:
+ arglist = ['--no-tag']
+ verifylist = [('no_tag', True)]
+ expected_args = []
+ arglist.extend(['--port', self.floating_ip.port_id,
+ self.floating_ip.id])
+ verifylist.extend([
+ ('port', self.floating_ip.port_id),
+ ('floating_ip', self.floating_ip.id)])
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+
+ self.assertTrue(self.network.update_ip.called)
+ self.network.set_tags.assert_called_once_with(
+ self.floating_ip,
+ tests_utils.CompareBySet(expected_args))
+ self.assertIsNone(result)
+
+ def test_set_with_tags(self):
+ self._test_set_tags(with_tags=True)
+
+ def test_set_with_no_tag(self):
+ self._test_set_tags(with_tags=False)
+
class TestUnsetFloatingIP(TestFloatingIPNetwork):
@@ -738,11 +838,12 @@ class TestUnsetFloatingIP(TestFloatingIPNetwork):
subnet = network_fakes.FakeSubnet.create_one_subnet()
port = network_fakes.FakePort.create_one_port()
- # The floating ip to be deleted.
+ # The floating ip to be unset.
floating_ip = network_fakes.FakeFloatingIP.create_one_floating_ip(
attrs={
'floating_network_id': floating_network.id,
'port_id': port.id,
+ 'tags': ['green', 'red'],
}
)
@@ -750,6 +851,7 @@ class TestUnsetFloatingIP(TestFloatingIPNetwork):
super(TestUnsetFloatingIP, self).setUp()
self.network.find_ip = mock.Mock(return_value=self.floating_ip)
self.network.update_ip = mock.Mock(return_value=None)
+ self.network.set_tags = mock.Mock(return_value=None)
# Get the command object to test
self.cmd = fip.UnsetFloatingIP(self.app, self.namespace)
@@ -803,3 +905,31 @@ class TestUnsetFloatingIP(TestFloatingIPNetwork):
self.floating_ip, **attrs)
self.assertIsNone(result)
+
+ def _test_unset_tags(self, with_tags=True):
+ if with_tags:
+ arglist = ['--tag', 'red', '--tag', 'blue']
+ verifylist = [('tags', ['red', 'blue'])]
+ expected_args = ['green']
+ else:
+ arglist = ['--all-tag']
+ verifylist = [('all_tag', True)]
+ expected_args = []
+ arglist.append(self.floating_ip.id)
+ verifylist.append(
+ ('floating_ip', self.floating_ip.id))
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+
+ self.assertFalse(self.network.update_ip.called)
+ self.network.set_tags.assert_called_once_with(
+ self.floating_ip,
+ tests_utils.CompareBySet(expected_args))
+ self.assertIsNone(result)
+
+ def test_unset_with_tags(self):
+ self._test_unset_tags(with_tags=True)
+
+ def test_unset_with_all_tag(self):
+ self._test_unset_tags(with_tags=False)
diff --git a/openstackclient/tests/unit/network/v2/test_network.py b/openstackclient/tests/unit/network/v2/test_network.py
index 357088f3..9f4a6acc 100644
--- a/openstackclient/tests/unit/network/v2/test_network.py
+++ b/openstackclient/tests/unit/network/v2/test_network.py
@@ -64,6 +64,7 @@ class TestCreateNetworkIdentityV3(TestNetwork):
'ipv4_address_scope',
'ipv6_address_scope',
'is_default',
+ 'mtu',
'name',
'port_security_enabled',
'project_id',
@@ -87,6 +88,7 @@ class TestCreateNetworkIdentityV3(TestNetwork):
_network.ipv4_address_scope_id,
_network.ipv6_address_scope_id,
_network.is_default,
+ _network.mtu,
_network.name,
_network.is_port_security_enabled,
_network.project_id,
@@ -149,6 +151,7 @@ class TestCreateNetworkIdentityV3(TestNetwork):
"--disable",
"--share",
"--description", self._network.description,
+ "--mtu", self._network.mtu,
"--project", self.project.name,
"--project-domain", self.domain.name,
"--availability-zone-hint", "nova",
@@ -165,6 +168,7 @@ class TestCreateNetworkIdentityV3(TestNetwork):
('disable', True),
('share', True),
('description', self._network.description),
+ ('mtu', self._network.mtu),
('project', self.project.name),
('project_domain', self.domain.name),
('availability_zone_hints', ["nova"]),
@@ -188,6 +192,7 @@ class TestCreateNetworkIdentityV3(TestNetwork):
'name': self._network.name,
'shared': True,
'description': self._network.description,
+ 'mtu': self._network.mtu,
# TODO(dtroyer): Remove tenant_id when we clean up the SDK refactor
'tenant_id': self.project.id,
'project_id': self.project.id,
@@ -286,6 +291,7 @@ class TestCreateNetworkIdentityV2(TestNetwork):
'ipv4_address_scope',
'ipv6_address_scope',
'is_default',
+ 'mtu',
'name',
'port_security_enabled',
'project_id',
@@ -309,6 +315,7 @@ class TestCreateNetworkIdentityV2(TestNetwork):
_network.ipv4_address_scope_id,
_network.ipv6_address_scope_id,
_network.is_default,
+ _network.mtu,
_network.name,
_network.is_port_security_enabled,
_network.project_id,
@@ -1023,6 +1030,7 @@ class TestShowNetwork(TestNetwork):
'ipv4_address_scope',
'ipv6_address_scope',
'is_default',
+ 'mtu',
'name',
'port_security_enabled',
'project_id',
@@ -1046,6 +1054,7 @@ class TestShowNetwork(TestNetwork):
_network.ipv4_address_scope_id,
_network.ipv6_address_scope_id,
_network.is_default,
+ _network.mtu,
_network.name,
_network.is_port_security_enabled,
_network.project_id,
diff --git a/openstackclient/tests/unit/network/v2/test_network_qos_rule.py b/openstackclient/tests/unit/network/v2/test_network_qos_rule.py
index 176bc86d..5b54d318 100644
--- a/openstackclient/tests/unit/network/v2/test_network_qos_rule.py
+++ b/openstackclient/tests/unit/network/v2/test_network_qos_rule.py
@@ -127,7 +127,7 @@ class TestCreateNetworkQosRuleMinimumBandwidth(TestNetworkQosRule):
self.cmd.take_action(parsed_args)
except exceptions.CommandError as e:
msg = ('"Create" rule command for type "minimum-bandwidth" '
- 'requires arguments direction, min_kbps')
+ 'requires arguments: direction, min_kbps')
self.assertEqual(msg, str(e))
@@ -213,7 +213,7 @@ class TestCreateNetworkQosRuleDSCPMarking(TestNetworkQosRule):
self.cmd.take_action(parsed_args)
except exceptions.CommandError as e:
msg = ('"Create" rule command for type "dscp-marking" '
- 'requires arguments dscp_mark')
+ 'requires arguments: dscp_mark')
self.assertEqual(msg, str(e))
@@ -266,6 +266,49 @@ class TestCreateNetworkQosRuleBandwidtLimit(TestNetworkQosRule):
arglist = [
'--type', RULE_TYPE_BANDWIDTH_LIMIT,
'--max-kbps', str(self.new_rule.max_kbps),
+ '--egress',
+ self.new_rule.qos_policy_id,
+ ]
+
+ verifylist = [
+ ('type', RULE_TYPE_BANDWIDTH_LIMIT),
+ ('max_kbps', self.new_rule.max_kbps),
+ ('egress', True),
+ ('qos_policy', self.new_rule.qos_policy_id),
+ ]
+
+ rule = network_fakes.FakeNetworkQosRule.create_one_qos_rule(
+ {'qos_policy_id': self.qos_policy.id,
+ 'type': RULE_TYPE_BANDWIDTH_LIMIT})
+ rule.max_burst_kbits = 0
+ expected_data = (
+ rule.direction,
+ rule.id,
+ rule.max_burst_kbits,
+ rule.max_kbps,
+ rule.project_id,
+ rule.qos_policy_id,
+ rule.type,
+ )
+
+ with mock.patch.object(
+ self.network, "create_qos_bandwidth_limit_rule",
+ return_value=rule) as create_qos_bandwidth_limit_rule:
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = (self.cmd.take_action(parsed_args))
+
+ create_qos_bandwidth_limit_rule.assert_called_once_with(
+ self.qos_policy.id,
+ **{'max_kbps': self.new_rule.max_kbps,
+ 'direction': self.new_rule.direction}
+ )
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(expected_data, data)
+
+ def test_create_all_options(self):
+ arglist = [
+ '--type', RULE_TYPE_BANDWIDTH_LIMIT,
+ '--max-kbps', str(self.new_rule.max_kbps),
'--max-burst-kbits', str(self.new_rule.max_burst_kbits),
'--egress',
self.new_rule.qos_policy_id,
@@ -309,7 +352,7 @@ class TestCreateNetworkQosRuleBandwidtLimit(TestNetworkQosRule):
self.cmd.take_action(parsed_args)
except exceptions.CommandError as e:
msg = ('"Create" rule command for type "bandwidth-limit" '
- 'requires arguments max_burst_kbps, max_kbps')
+ 'requires arguments: max_kbps')
self.assertEqual(msg, str(e))
@@ -579,7 +622,7 @@ class TestSetNetworkQosRuleMinimumBandwidth(TestNetworkQosRule):
self.cmd.take_action(parsed_args)
except exceptions.CommandError as e:
msg = ('Failed to set Network QoS rule ID "%(rule)s": Rule type '
- '"minimum-bandwidth" only requires arguments direction, '
+ '"minimum-bandwidth" only requires arguments: direction, '
'min_kbps' % {'rule': self.new_rule.id})
self.assertEqual(msg, str(e))
@@ -673,7 +716,7 @@ class TestSetNetworkQosRuleDSCPMarking(TestNetworkQosRule):
self.cmd.take_action(parsed_args)
except exceptions.CommandError as e:
msg = ('Failed to set Network QoS rule ID "%(rule)s": Rule type '
- '"dscp-marking" only requires arguments dscp_mark' %
+ '"dscp-marking" only requires arguments: dscp_mark' %
{'rule': self.new_rule.id})
self.assertEqual(msg, str(e))
@@ -837,7 +880,7 @@ class TestSetNetworkQosRuleBandwidthLimit(TestNetworkQosRule):
self.cmd.take_action(parsed_args)
except exceptions.CommandError as e:
msg = ('Failed to set Network QoS rule ID "%(rule)s": Rule type '
- '"bandwidth-limit" only requires arguments direction, '
+ '"bandwidth-limit" only requires arguments: direction, '
'max_burst_kbps, max_kbps' % {'rule': self.new_rule.id})
self.assertEqual(msg, str(e))
diff --git a/openstackclient/tests/unit/network/v2/test_port.py b/openstackclient/tests/unit/network/v2/test_port.py
index 908177ce..03e1d841 100644
--- a/openstackclient/tests/unit/network/v2/test_port.py
+++ b/openstackclient/tests/unit/network/v2/test_port.py
@@ -50,6 +50,7 @@ class TestPort(network_fakes.TestNetworkV2):
'device_id',
'device_owner',
'dns_assignment',
+ 'dns_domain',
'dns_name',
'extra_dhcp_opts',
'fixed_ips',
@@ -78,6 +79,7 @@ class TestPort(network_fakes.TestNetworkV2):
fake_port.device_id,
fake_port.device_owner,
utils.format_list_of_dicts(fake_port.dns_assignment),
+ fake_port.dns_domain,
fake_port.dns_name,
utils.format_list_of_dicts(fake_port.extra_dhcp_opts),
utils.format_list_of_dicts(fake_port.fixed_ips),
@@ -152,6 +154,7 @@ class TestCreatePort(TestPort):
'--binding-profile', 'foo=bar',
'--binding-profile', 'foo2=bar2',
'--network', self._port.network_id,
+ '--dns-domain', 'example.org',
'--dns-name', '8.8.8.8',
'test-port',
@@ -169,6 +172,7 @@ class TestCreatePort(TestPort):
('vnic_type', 'macvtap'),
('binding_profile', {'foo': 'bar', 'foo2': 'bar2'}),
('network', self._port.network_id),
+ ('dns_domain', 'example.org'),
('dns_name', '8.8.8.8'),
('name', 'test-port'),
]
@@ -187,6 +191,7 @@ class TestCreatePort(TestPort):
'binding:vnic_type': 'macvtap',
'binding:profile': {'foo': 'bar', 'foo2': 'bar2'},
'network_id': self._port.network_id,
+ 'dns_domain': 'example.org',
'dns_name': '8.8.8.8',
'name': 'test-port',
})
diff --git a/openstackclient/tests/unit/network/v2/test_subnet.py b/openstackclient/tests/unit/network/v2/test_subnet.py
index b7f741cd..39cb4f53 100644
--- a/openstackclient/tests/unit/network/v2/test_subnet.py
+++ b/openstackclient/tests/unit/network/v2/test_subnet.py
@@ -1046,6 +1046,36 @@ class TestSetSubnet(TestSubnet):
_testsubnet, **attrs)
self.assertIsNone(result)
+ def test_clear_options(self):
+ _testsubnet = network_fakes.FakeSubnet.create_one_subnet(
+ {'host_routes': [{'destination': '10.20.20.0/24',
+ 'nexthop': '10.20.20.1'}],
+ 'allocation_pools': [{'start': '8.8.8.200',
+ 'end': '8.8.8.250'}],
+ 'dns_nameservers': ['10.0.0.1'], })
+ self.network.find_subnet = mock.Mock(return_value=_testsubnet)
+ arglist = [
+ '--no-host-route',
+ '--no-allocation-pool',
+ '--no-dns-nameservers',
+ _testsubnet.name,
+ ]
+ verifylist = [
+ ('no_dns_nameservers', True),
+ ('no_host_route', True),
+ ('no_allocation_pool', True),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+ attrs = {
+ 'host_routes': [],
+ 'allocation_pools': [],
+ 'dns_nameservers': [],
+ }
+ self.network.update_subnet.assert_called_once_with(
+ _testsubnet, **attrs)
+ self.assertIsNone(result)
+
def _test_set_tags(self, with_tags=True):
if with_tags:
arglist = ['--tag', 'red', '--tag', 'blue']
@@ -1074,6 +1104,29 @@ class TestSetSubnet(TestSubnet):
def test_set_with_no_tag(self):
self._test_set_tags(with_tags=False)
+ def test_set_segment(self):
+ _net = network_fakes.FakeNetwork.create_one_network()
+ _segment = network_fakes.FakeNetworkSegment.create_one_network_segment(
+ attrs={'network_id': _net.id})
+ _subnet = network_fakes.FakeSubnet.create_one_subnet(
+ {'host_routes': [{'destination': '10.20.20.0/24',
+ 'nexthop': '10.20.20.1'}],
+ 'allocation_pools': [{'start': '8.8.8.200',
+ 'end': '8.8.8.250'}],
+ 'dns_nameservers': ["10.0.0.1"],
+ 'network_id': _net.id,
+ 'segment_id': None})
+ self.network.find_subnet = mock.Mock(return_value=_subnet)
+ self.network.find_segment = mock.Mock(return_value=_segment)
+ arglist = ['--network-segment', _segment.id, _subnet.name]
+ verifylist = [('network_segment', _segment.id)]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+ attrs = {'segment_id': _segment.id}
+ self.network.update_subnet.assert_called_once_with(_subnet, **attrs)
+ self.network.update_subnet.assert_called_with(_subnet, **attrs)
+ self.assertIsNone(result)
+
class TestShowSubnet(TestSubnet):
# The subnets to be shown
diff --git a/openstackclient/tests/unit/volume/v2/fakes.py b/openstackclient/tests/unit/volume/v2/fakes.py
index 27f37bd8..481509f3 100644
--- a/openstackclient/tests/unit/volume/v2/fakes.py
+++ b/openstackclient/tests/unit/volume/v2/fakes.py
@@ -200,6 +200,8 @@ class FakeVolumeClient(object):
self.volumes.resource_class = fakes.FakeResource(None, {})
self.extensions = mock.Mock()
self.extensions.resource_class = fakes.FakeResource(None, {})
+ self.limits = mock.Mock()
+ self.limits.resource_class = fakes.FakeResource(None, {})
self.volume_snapshots = mock.Mock()
self.volume_snapshots.resource_class = fakes.FakeResource(None, {})
self.backups = mock.Mock()
@@ -1004,3 +1006,101 @@ class FakeQuota(object):
quota.project_id = quota_attrs['id']
return quota
+
+
+class FakeLimits(object):
+ """Fake limits"""
+
+ def __init__(self, absolute_attrs=None):
+ self.absolute_limits_attrs = {
+ 'totalSnapshotsUsed': 1,
+ 'maxTotalBackups': 10,
+ 'maxTotalVolumeGigabytes': 1000,
+ 'maxTotalSnapshots': 10,
+ 'maxTotalBackupGigabytes': 1000,
+ 'totalBackupGigabytesUsed': 0,
+ 'maxTotalVolumes': 10,
+ 'totalVolumesUsed': 4,
+ 'totalBackupsUsed': 0,
+ 'totalGigabytesUsed': 35
+ }
+ absolute_attrs = absolute_attrs or {}
+ self.absolute_limits_attrs.update(absolute_attrs)
+
+ self.rate_limits_attrs = [{
+ "uri": "*",
+ "limit": [
+ {
+ "value": 10,
+ "verb": "POST",
+ "remaining": 2,
+ "unit": "MINUTE",
+ "next-available": "2011-12-15T22:42:45Z"
+ },
+ {
+ "value": 10,
+ "verb": "PUT",
+ "remaining": 2,
+ "unit": "MINUTE",
+ "next-available": "2011-12-15T22:42:45Z"
+ },
+ {
+ "value": 100,
+ "verb": "DELETE",
+ "remaining": 100,
+ "unit": "MINUTE",
+ "next-available": "2011-12-15T22:42:45Z"
+ }
+ ]
+ }]
+
+ @property
+ def absolute(self):
+ for (name, value) in self.absolute_limits_attrs.items():
+ yield FakeAbsoluteLimit(name, value)
+
+ def absolute_limits(self):
+ reference_data = []
+ for (name, value) in self.absolute_limits_attrs.items():
+ reference_data.append((name, value))
+ return reference_data
+
+ @property
+ def rate(self):
+ for group in self.rate_limits_attrs:
+ uri = group['uri']
+ for rate in group['limit']:
+ yield FakeRateLimit(rate['verb'], uri, rate['value'],
+ rate['remaining'], rate['unit'],
+ rate['next-available'])
+
+ def rate_limits(self):
+ reference_data = []
+ for group in self.rate_limits_attrs:
+ uri = group['uri']
+ for rate in group['limit']:
+ reference_data.append((rate['verb'], uri, rate['value'],
+ rate['remaining'], rate['unit'],
+ rate['next-available']))
+ return reference_data
+
+
+class FakeAbsoluteLimit(object):
+ """Data model that represents an absolute limit."""
+
+ def __init__(self, name, value):
+ self.name = name
+ self.value = value
+
+
+class FakeRateLimit(object):
+ """Data model that represents a flattened view of a single rate limit."""
+
+ def __init__(self, verb, uri, value, remain,
+ unit, next_available):
+ self.verb = verb
+ self.uri = uri
+ self.value = value
+ self.remain = remain
+ self.unit = unit
+ self.next_available = next_available
diff --git a/openstackclient/volume/v1/volume_type.py b/openstackclient/volume/v1/volume_type.py
index f9baa5be..b4d8eaca 100644
--- a/openstackclient/volume/v1/volume_type.py
+++ b/openstackclient/volume/v1/volume_type.py
@@ -70,8 +70,8 @@ class CreateVolumeType(command.ShowOne):
parser.add_argument(
'--encryption-provider',
metavar='<provider>',
- help=_('Set the class that provides encryption support for '
- 'this volume type (e.g "LuksEncryptor") (admin only) '
+ help=_('Set the encryption provider format for '
+ 'this volume type (e.g "luks" or "plain") (admin only) '
'(This option is required when setting encryption type '
'of a volume. Consider using other encryption options '
'such as: "--encryption-cipher", "--encryption-key-size" '
@@ -254,8 +254,8 @@ class SetVolumeType(command.Command):
parser.add_argument(
'--encryption-provider',
metavar='<provider>',
- help=_('Set the class that provides encryption support for '
- 'this volume type (e.g "LuksEncryptor") (admin only) '
+ help=_('Set the encryption provider format for '
+ 'this volume type (e.g "luks" or "plain") (admin only) '
'(This option is required when setting encryption type '
'of a volume. Consider using other encryption options '
'such as: "--encryption-cipher", "--encryption-key-size" '
diff --git a/openstackclient/volume/v2/volume_type.py b/openstackclient/volume/v2/volume_type.py
index 64c4d652..71e94a2b 100644
--- a/openstackclient/volume/v2/volume_type.py
+++ b/openstackclient/volume/v2/volume_type.py
@@ -112,8 +112,8 @@ class CreateVolumeType(command.ShowOne):
parser.add_argument(
'--encryption-provider',
metavar='<provider>',
- help=_('Set the class that provides encryption support for '
- 'this volume type (e.g "LuksEncryptor") (admin only) '
+ help=_('Set the encryption provider format for '
+ 'this volume type (e.g "luks" or "plain") (admin only) '
'(This option is required when setting encryption type '
'of a volume. Consider using other encryption options '
'such as: "--encryption-cipher", "--encryption-key-size" '
@@ -371,8 +371,8 @@ class SetVolumeType(command.Command):
parser.add_argument(
'--encryption-provider',
metavar='<provider>',
- help=_('Set the class that provides encryption support for '
- 'this volume type (e.g "LuksEncryptor") (admin only) '
+ help=_('Set the encryption provider format for '
+ 'this volume type (e.g "luks" or "plain") (admin only) '
'(This option is required when setting encryption type '
'of a volume for the first time. Consider using other '
'encryption options such as: "--encryption-cipher", '