summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/source/command-objects/consistency-group.rst6
-rw-r--r--doc/source/command-objects/floating-ip.rst14
-rw-r--r--doc/source/command-objects/port.rst12
-rw-r--r--doc/source/command-objects/router.rst10
-rw-r--r--doc/source/command-objects/security-group.rst14
-rw-r--r--doc/source/command-objects/volume-host.rst30
-rw-r--r--doc/source/command-objects/volume-snapshot.rst16
-rw-r--r--doc/source/commands.rst1
-rw-r--r--openstackclient/identity/v3/unscoped_saml.py22
-rw-r--r--openstackclient/image/v2/image.py15
-rw-r--r--openstackclient/network/v2/floating_ip.py16
-rw-r--r--openstackclient/network/v2/ip_availability.py18
-rw-r--r--openstackclient/network/v2/port.py11
-rw-r--r--openstackclient/network/v2/router.py34
-rw-r--r--openstackclient/network/v2/security_group.py21
-rw-r--r--openstackclient/tests/unit/identity/v3/test_unscoped_saml.py26
-rw-r--r--openstackclient/tests/unit/image/v2/test_image.py21
-rw-r--r--openstackclient/tests/unit/network/v2/fakes.py12
-rw-r--r--openstackclient/tests/unit/network/v2/test_floating_ip.py50
-rw-r--r--openstackclient/tests/unit/network/v2/test_ip_availability.py1
-rw-r--r--openstackclient/tests/unit/network/v2/test_port.py50
-rw-r--r--openstackclient/tests/unit/network/v2/test_router.py41
-rw-r--r--openstackclient/tests/unit/network/v2/test_security_group.py38
-rw-r--r--openstackclient/tests/unit/volume/v1/test_snapshot.py96
-rw-r--r--openstackclient/tests/unit/volume/v2/fakes.py6
-rw-r--r--openstackclient/tests/unit/volume/v2/test_consistency_group.py39
-rw-r--r--openstackclient/tests/unit/volume/v2/test_snapshot.py103
-rw-r--r--openstackclient/tests/unit/volume/v2/test_volume_host.py86
-rw-r--r--openstackclient/volume/v1/volume_snapshot.py34
-rw-r--r--openstackclient/volume/v2/consistency_group.py110
-rw-r--r--openstackclient/volume/v2/volume_host.py50
-rw-r--r--openstackclient/volume/v2/volume_snapshot.py34
-rw-r--r--releasenotes/notes/bp-cinder-command-support-7e3ae1fb4cd90407.yaml5
-rw-r--r--releasenotes/notes/bug-1612136-63aac6377209db38.yaml5
-rw-r--r--releasenotes/notes/bug-1613231-386b2b1373662052.yaml8
-rw-r--r--releasenotes/notes/bug-1645252-219bfd50c8f04846.yaml6
-rw-r--r--setup.cfg2
37 files changed, 918 insertions, 145 deletions
diff --git a/doc/source/command-objects/consistency-group.rst b/doc/source/command-objects/consistency-group.rst
index cd972035..910fbba9 100644
--- a/doc/source/command-objects/consistency-group.rst
+++ b/doc/source/command-objects/consistency-group.rst
@@ -13,7 +13,7 @@ Create new consistency group.
.. code:: bash
os consistency group create
- --volume-type <volume-type> | --consistency-group-source <consistency-group>
+ --volume-type <volume-type> | --consistency-group-source <consistency-group> | --consistency-group-snapshot <consistency-group-snapshot>
[--description <description>]
[--availability-zone <availability-zone>]
[<name>]
@@ -26,6 +26,10 @@ Create new consistency group.
Existing consistency group (name or ID)
+.. option:: --consistency-group-snapshot <consistency-group-snapshot>
+
+ Existing consistency group snapshot (name or ID)
+
.. option:: --description <description>
Description of this consistency group
diff --git a/doc/source/command-objects/floating-ip.rst b/doc/source/command-objects/floating-ip.rst
index b2cc8af0..6a5e38b0 100644
--- a/doc/source/command-objects/floating-ip.rst
+++ b/doc/source/command-objects/floating-ip.rst
@@ -18,6 +18,7 @@ Create floating IP
[--floating-ip-address <floating-ip-address>]
[--fixed-ip-address <fixed-ip-address>]
[--description <description>]
+ [--project <project> [--project-domain <project-domain>]]
<network>
.. option:: --subnet <subnet>
@@ -45,6 +46,19 @@ Create floating IP
Set floating IP description
*Network version 2 only*
+.. option:: --project <project>
+
+ Owner's project (name or ID)
+
+ *Network version 2 only*
+
+.. option:: --project-domain <project-domain>
+
+ Domain the project belongs to (name or ID).
+ This can be used in case collisions between project names exist.
+
+ *Network version 2 only*
+
.. describe:: <network>
Network to allocate floating IP from (name or ID)
diff --git a/doc/source/command-objects/port.rst b/doc/source/command-objects/port.rst
index 3aff2f77..73c53290 100644
--- a/doc/source/command-objects/port.rst
+++ b/doc/source/command-objects/port.rst
@@ -28,6 +28,7 @@ Create new port
[--enable | --disable]
[--mac-address <mac-address>]
[--security-group <security-group> | --no-security-group]
+ [--dns-name <dns-name>]
[--project <project> [--project-domain <project-domain>]]
[--enable-port-security | --disable-port-security]
<name>
@@ -91,6 +92,11 @@ Create new port
Associate no security groups with this port
+.. option:: --dns-name <dns-name>
+
+ Set DNS name to this port
+ (requires DNS integration extension)
+
.. option:: --project <project>
Owner's project (name or ID)
@@ -192,6 +198,7 @@ Set port properties
[--security-group <security-group>]
[--no-security-group]
[--enable-port-security | --disable-port-security]
+ [--dns-name <dns-name>]
<port>
.. option:: --description <description>
@@ -269,6 +276,11 @@ Set port properties
Disable port security for this port
+.. option:: --dns-name <dns-name>
+
+ Set DNS name to this port
+ (requires DNS integration extension)
+
.. _port_set-port:
.. describe:: <port>
diff --git a/doc/source/command-objects/router.rst b/doc/source/command-objects/router.rst
index 56b95ffa..059d1a3f 100644
--- a/doc/source/command-objects/router.rst
+++ b/doc/source/command-objects/router.rst
@@ -137,6 +137,7 @@ List routers
[--name <name>]
[--enable | --disable]
[--long]
+ [--project <project> [--project-domain <project-domain>]]
.. option:: --long
@@ -154,6 +155,15 @@ List routers
List disabled routers
+.. option:: --project <project>
+
+ List routers according to their project (name or ID)
+
+.. option:: --project-domain <project-domain>
+
+ Domain the project belongs to (name or ID).
+ This can be used in case collisions between project names exist.
+
router remove port
------------------
diff --git a/doc/source/command-objects/security-group.rst b/doc/source/command-objects/security-group.rst
index ba054554..5157f530 100644
--- a/doc/source/command-objects/security-group.rst
+++ b/doc/source/command-objects/security-group.rst
@@ -67,6 +67,7 @@ List security groups
os security group list
[--all-projects]
+ [--project <project> [--project-domain <project-domain>]]
.. option:: --all-projects
@@ -75,6 +76,19 @@ List security groups
*Network version 2 ignores this option and will always display information*
*for all projects (admin only).*
+.. option:: --project <project>
+
+ List security groups according to the project (name or ID)
+
+ *Network version 2 only*
+
+.. option:: --project-domain <project-domain>
+
+ Domain the project belongs to (name or ID).
+ This can be used in case collisions between project names exist.
+
+ *Network version 2 only*
+
security group set
------------------
diff --git a/doc/source/command-objects/volume-host.rst b/doc/source/command-objects/volume-host.rst
new file mode 100644
index 00000000..956fce52
--- /dev/null
+++ b/doc/source/command-objects/volume-host.rst
@@ -0,0 +1,30 @@
+===========
+volume host
+===========
+
+Volume v2
+
+volume host set
+---------------
+
+Set volume host properties
+
+.. program:: volume host set
+.. code:: bash
+
+ os volume host set
+ [--enable | --disable]
+ <host-name>
+
+.. option:: --enable
+
+ Thaw and enable the specified volume host
+
+.. option:: --disable
+
+ Freeze and disable the specified volume host
+
+.. _volume-host-set:
+.. describe:: <host-name>
+
+ Name of volume host
diff --git a/doc/source/command-objects/volume-snapshot.rst b/doc/source/command-objects/volume-snapshot.rst
index b84601f4..2d9406b6 100644
--- a/doc/source/command-objects/volume-snapshot.rst
+++ b/doc/source/command-objects/volume-snapshot.rst
@@ -71,6 +71,9 @@ List volume snapshots
[--long]
[--limit <limit>]
[--marker <marker>]
+ [--name <name>]
+ [--status <status>]
+ [--volume <volume>]
.. option:: --all-projects
@@ -80,6 +83,19 @@ List volume snapshots
List additional fields in output
+.. option:: --status <status>
+
+ Filters results by a status.
+ ('available', 'error', 'creating', 'deleting' or 'error-deleting')
+
+.. option:: --name <name>
+
+ Filters results by a name.
+
+.. option:: --volume <volume>
+
+ Filters results by a volume (name or ID).
+
.. option:: --limit <limit>
Maximum number of snapshots to display
diff --git a/doc/source/commands.rst b/doc/source/commands.rst
index f7ef3eaa..795dfd53 100644
--- a/doc/source/commands.rst
+++ b/doc/source/commands.rst
@@ -144,6 +144,7 @@ referring to both Compute and Volume quotas.
* ``user role``: (**Identity**) roles assigned to a user
* ``volume``: (**Volume**) block volumes
* ``volume backup``: (**Volume**) backup for volumes
+* ``volume host``: (**Volume**) the physical computer for volumes
* ``volume qos``: (**Volume**) quality-of-service (QoS) specification for volumes
* ``volume snapshot``: (**Volume**) a point-in-time copy of a volume
* ``volume type``: (**Volume**) deployment-specific types of volumes available
diff --git a/openstackclient/identity/v3/unscoped_saml.py b/openstackclient/identity/v3/unscoped_saml.py
index 5940534a..f7598f17 100644
--- a/openstackclient/identity/v3/unscoped_saml.py
+++ b/openstackclient/identity/v3/unscoped_saml.py
@@ -18,35 +18,14 @@ the user can list domains and projects they are allowed to access, and request
a scoped token."""
from osc_lib.command import command
-from osc_lib import exceptions
from osc_lib import utils
from openstackclient.i18n import _
-UNSCOPED_AUTH_PLUGINS = ['v3unscopedsaml', 'v3unscopedadfs', 'v3oidc']
-
-
-def auth_with_unscoped_saml(func):
- """Check the unscoped federated context"""
-
- def _decorated(self, parsed_args):
- auth_plugin_name = self.app.client_manager.auth_plugin_name
- if auth_plugin_name in UNSCOPED_AUTH_PLUGINS:
- return func(self, parsed_args)
- else:
- msg = (_('This command requires the use of an unscoped SAML '
- 'authentication plugin. Please use argument '
- '--os-auth-type with one of the following '
- 'plugins: %s') % ', '.join(UNSCOPED_AUTH_PLUGINS))
- raise exceptions.CommandError(msg)
- return _decorated
-
-
class ListAccessibleDomains(command.Lister):
_description = _("List accessible domains")
- @auth_with_unscoped_saml
def take_action(self, parsed_args):
columns = ('ID', 'Enabled', 'Name', 'Description')
identity_client = self.app.client_manager.identity
@@ -61,7 +40,6 @@ class ListAccessibleDomains(command.Lister):
class ListAccessibleProjects(command.Lister):
_description = _("List accessible projects")
- @auth_with_unscoped_saml
def take_action(self, parsed_args):
columns = ('ID', 'Domain ID', 'Enabled', 'Name')
identity_client = self.app.client_manager.identity
diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py
index 054d1612..1d167605 100644
--- a/openstackclient/image/v2/image.py
+++ b/openstackclient/image/v2/image.py
@@ -486,7 +486,6 @@ class ListImage(command.Lister):
if parsed_args.marker:
kwargs['marker'] = utils.find_resource(image_client.images,
parsed_args.marker).id
-
if parsed_args.long:
columns = (
'ID',
@@ -519,7 +518,19 @@ class ListImage(command.Lister):
column_headers = columns
# List of image data received
- data = image_client.api.image_list(**kwargs)
+ data = []
+ if 'marker' in kwargs:
+ data = image_client.api.image_list(**kwargs)
+ else:
+ # No pages received yet, so start the page marker at None.
+ marker = None
+ while True:
+ page = image_client.api.image_list(marker=marker, **kwargs)
+ if not page:
+ break
+ data.extend(page)
+ # Set the marker to the id of the last item we received
+ marker = page[-1]['id']
if parsed_args.property:
# NOTE(dtroyer): coerce to a list to subscript it in py3
diff --git a/openstackclient/network/v2/floating_ip.py b/openstackclient/network/v2/floating_ip.py
index c787cd2f..7b8374e2 100644
--- a/openstackclient/network/v2/floating_ip.py
+++ b/openstackclient/network/v2/floating_ip.py
@@ -18,6 +18,7 @@ import logging
from osc_lib import utils
from openstackclient.i18n import _
+from openstackclient.identity import common as identity_common
from openstackclient.network import common
from openstackclient.network import sdk_utils
@@ -66,6 +67,15 @@ def _get_attrs(client_manager, parsed_args):
if parsed_args.description is not None:
attrs['description'] = parsed_args.description
+ if parsed_args.project:
+ identity_client = client_manager.identity
+ project_id = identity_common.find_project(
+ identity_client,
+ parsed_args.project,
+ parsed_args.project_domain,
+ ).id
+ attrs['tenant_id'] = project_id
+
return attrs
@@ -113,6 +123,12 @@ class CreateFloatingIP(common.NetworkAndComputeShowOne):
metavar='<description>',
help=_('Set floating IP description')
)
+ parser.add_argument(
+ '--project',
+ metavar='<project>',
+ help=_("Owner's project (name or ID)")
+ )
+ identity_common.add_project_domain_option_to_parser(parser)
return parser
def take_action_network(self, client, parsed_args):
diff --git a/openstackclient/network/v2/ip_availability.py b/openstackclient/network/v2/ip_availability.py
index 5960e2fa..a80fe1c4 100644
--- a/openstackclient/network/v2/ip_availability.py
+++ b/openstackclient/network/v2/ip_availability.py
@@ -18,7 +18,7 @@ from osc_lib import utils
from openstackclient.i18n import _
from openstackclient.identity import common as identity_common
-
+from openstackclient.network import sdk_utils
_formatters = {
'subnet_ip_availability': utils.format_list_of_dicts,
@@ -26,13 +26,14 @@ _formatters = {
def _get_columns(item):
- columns = list(item.keys())
- if 'tenant_id' in columns:
- columns.remove('tenant_id')
- columns.append('project_id')
- return tuple(sorted(columns))
+ column_map = {
+ 'tenant_id': 'project_id',
+ }
+ return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map)
+# TODO(ankur-gupta-f): Use the SDK resource mapped attribute names once
+# the OSC minimum requirements include SDK 1.0.
class ListIPAvailability(command.Lister):
_description = _("List IP availability for network")
@@ -84,6 +85,7 @@ class ListIPAvailability(command.Lister):
parsed_args.project_domain,
).id
filters['tenant_id'] = project_id
+ filters['project_id'] = project_id
data = client.network_ip_availabilities(**filters)
return (column_headers,
(utils.get_item_properties(
@@ -107,6 +109,6 @@ class ShowIPAvailability(command.ShowOne):
client = self.app.client_manager.network
obj = client.find_network_ip_availability(parsed_args.network,
ignore_missing=False)
- columns = _get_columns(obj)
+ display_columns, columns = _get_columns(obj)
data = utils.get_item_properties(obj, columns, formatters=_formatters)
- return (columns, data)
+ return (display_columns, data)
diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py
index 6cae87ee..bce3e2d3 100644
--- a/openstackclient/network/v2/port.py
+++ b/openstackclient/network/v2/port.py
@@ -50,7 +50,8 @@ def _get_columns(item):
columns = list(item.keys())
if 'tenant_id' in columns:
columns.remove('tenant_id')
- columns.append('project_id')
+ if 'project_id' not in columns:
+ columns.append('project_id')
binding_columns = [
'binding:host_id',
'binding:profile',
@@ -128,6 +129,8 @@ def _get_attrs(client_manager, parsed_args):
if parsed_args.host:
attrs['binding:host_id'] = parsed_args.host
+ 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'
if parsed_args.name is not None:
attrs['name'] = str(parsed_args.name)
@@ -233,6 +236,12 @@ def _add_updatable_args(parser):
metavar='<host-id>',
help=argparse.SUPPRESS,
)
+ parser.add_argument(
+ '--dns-name',
+ metavar='dns-name',
+ help=_("Set DNS name to this port "
+ "(requires DNS integration extension)")
+ )
class CreatePort(command.ShowOne):
diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py
index cbd412b5..cdd634d0 100644
--- a/openstackclient/network/v2/router.py
+++ b/openstackclient/network/v2/router.py
@@ -25,6 +25,7 @@ from osc_lib import utils
from openstackclient.i18n import _
from openstackclient.identity import common as identity_common
+from openstackclient.network import sdk_utils
LOG = logging.getLogger(__name__)
@@ -59,11 +60,10 @@ _formatters = {
def _get_columns(item):
- columns = list(item.keys())
- if 'tenant_id' in columns:
- columns.remove('tenant_id')
- columns.append('project_id')
- return tuple(sorted(columns))
+ column_map = {
+ 'tenant_id': 'project_id',
+ }
+ return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map)
def _get_attrs(client_manager, parsed_args):
@@ -215,10 +215,10 @@ class CreateRouter(command.ShowOne):
attrs['ha'] = parsed_args.ha
obj = client.create_router(**attrs)
- columns = _get_columns(obj)
+ display_columns, columns = _get_columns(obj)
data = utils.get_item_properties(obj, columns, formatters=_formatters)
- return (columns, data)
+ return (display_columns, data)
class DeleteRouter(command.Command):
@@ -282,11 +282,17 @@ class ListRouter(command.Lister):
default=False,
help=_("List additional fields in output")
)
+ parser.add_argument(
+ '--project',
+ metavar='<project>',
+ help=_("List routers according to their project (name or ID)")
+ )
+ identity_common.add_project_domain_option_to_parser(parser)
return parser
def take_action(self, parsed_args):
+ identity_client = self.app.client_manager.identity
client = self.app.client_manager.network
-
columns = (
'id',
'name',
@@ -316,6 +322,13 @@ class ListRouter(command.Lister):
elif parsed_args.disable:
args['admin_state_up'] = False
+ if parsed_args.project:
+ project_id = identity_common.find_project(
+ identity_client,
+ parsed_args.project,
+ parsed_args.project_domain,
+ ).id
+ args['tenant_id'] = project_id
if parsed_args.long:
columns = columns + (
'routes',
@@ -523,9 +536,10 @@ class ShowRouter(command.ShowOne):
def take_action(self, parsed_args):
client = self.app.client_manager.network
obj = client.find_router(parsed_args.router, ignore_missing=False)
- columns = _get_columns(obj)
+ display_columns, columns = _get_columns(obj)
data = utils.get_item_properties(obj, columns, formatters=_formatters)
- return (columns, data)
+
+ return (display_columns, data)
class UnsetRouter(command.Command):
diff --git a/openstackclient/network/v2/security_group.py b/openstackclient/network/v2/security_group.py
index 554dd61d..5420bc8b 100644
--- a/openstackclient/network/v2/security_group.py
+++ b/openstackclient/network/v2/security_group.py
@@ -81,8 +81,9 @@ def _get_columns(item):
columns.remove('security_group_rules')
property_column_mappings.append(('rules', 'security_group_rules'))
if 'tenant_id' in columns:
- columns.append('project_id')
columns.remove('tenant_id')
+ if 'project_id' not in columns:
+ columns.append('project_id')
property_column_mappings.append(('project_id', 'tenant_id'))
display_columns = sorted(columns)
@@ -201,6 +202,13 @@ class ListSecurityGroup(common.NetworkAndComputeLister):
default=False,
help=argparse.SUPPRESS,
)
+ parser.add_argument(
+ '--project',
+ metavar='<project>',
+ help=_("List security groups according to the project "
+ "(name or ID)")
+ )
+ identity_common.add_project_domain_option_to_parser(parser)
return parser
def update_parser_compute(self, parser):
@@ -228,7 +236,16 @@ class ListSecurityGroup(common.NetworkAndComputeLister):
) for s in data))
def take_action_network(self, client, parsed_args):
- return self._get_return_data(client.security_groups())
+ filters = {}
+ if parsed_args.project:
+ identity_client = self.app.client_manager.identity
+ project_id = identity_common.find_project(
+ identity_client,
+ parsed_args.project,
+ parsed_args.project_domain,
+ ).id
+ filters['tenant_id'] = project_id
+ return self._get_return_data(client.security_groups(**filters))
def take_action_compute(self, client, parsed_args):
search = {'all_tenants': parsed_args.all_projects}
diff --git a/openstackclient/tests/unit/identity/v3/test_unscoped_saml.py b/openstackclient/tests/unit/identity/v3/test_unscoped_saml.py
index 9e4e1876..34655263 100644
--- a/openstackclient/tests/unit/identity/v3/test_unscoped_saml.py
+++ b/openstackclient/tests/unit/identity/v3/test_unscoped_saml.py
@@ -12,8 +12,6 @@
import copy
-from osc_lib import exceptions
-
from openstackclient.identity.v3 import unscoped_saml
from openstackclient.tests.unit import fakes
from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes
@@ -48,7 +46,6 @@ class TestDomainList(TestUnscopedSAML):
self.cmd = unscoped_saml.ListAccessibleDomains(self.app, None)
def test_accessible_domains_list(self):
- self.app.client_manager.auth_plugin_name = 'v3unscopedsaml'
arglist = []
verifylist = []
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -70,17 +67,6 @@ class TestDomainList(TestUnscopedSAML):
), )
self.assertEqual(datalist, tuple(data))
- def test_accessible_domains_list_wrong_auth(self):
- auth = identity_fakes.FakeAuth("wrong auth")
- self.app.client_manager.identity.session.auth = auth
- arglist = []
- verifylist = []
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- self.assertRaises(exceptions.CommandError,
- self.cmd.take_action,
- parsed_args)
-
class TestProjectList(TestUnscopedSAML):
@@ -99,7 +85,6 @@ class TestProjectList(TestUnscopedSAML):
self.cmd = unscoped_saml.ListAccessibleProjects(self.app, None)
def test_accessible_projects_list(self):
- self.app.client_manager.auth_plugin_name = 'v3unscopedsaml'
arglist = []
verifylist = []
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -120,14 +105,3 @@ class TestProjectList(TestUnscopedSAML):
identity_fakes.project_name,
), )
self.assertEqual(datalist, tuple(data))
-
- def test_accessible_projects_list_wrong_auth(self):
- auth = identity_fakes.FakeAuth("wrong auth")
- self.app.client_manager.identity.session.auth = auth
- arglist = []
- verifylist = []
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- self.assertRaises(exceptions.CommandError,
- self.cmd.take_action,
- parsed_args)
diff --git a/openstackclient/tests/unit/image/v2/test_image.py b/openstackclient/tests/unit/image/v2/test_image.py
index 2f2212e4..a054e513 100644
--- a/openstackclient/tests/unit/image/v2/test_image.py
+++ b/openstackclient/tests/unit/image/v2/test_image.py
@@ -535,7 +535,9 @@ class TestImageList(TestImage):
# 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.api_mock.image_list.assert_called_with()
+ self.api_mock.image_list.assert_called_with(
+ marker=self._image.id,
+ )
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, tuple(data))
@@ -558,6 +560,7 @@ class TestImageList(TestImage):
columns, data = self.cmd.take_action(parsed_args)
self.api_mock.image_list.assert_called_with(
public=True,
+ marker=self._image.id,
)
self.assertEqual(self.columns, columns)
@@ -581,6 +584,7 @@ class TestImageList(TestImage):
columns, data = self.cmd.take_action(parsed_args)
self.api_mock.image_list.assert_called_with(
private=True,
+ marker=self._image.id,
)
self.assertEqual(self.columns, columns)
@@ -604,6 +608,7 @@ class TestImageList(TestImage):
columns, data = self.cmd.take_action(parsed_args)
self.api_mock.image_list.assert_called_with(
shared=True,
+ marker=self._image.id,
)
self.assertEqual(self.columns, columns)
@@ -622,7 +627,9 @@ class TestImageList(TestImage):
# 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.api_mock.image_list.assert_called_with()
+ self.api_mock.image_list.assert_called_with(
+ marker=self._image.id,
+ )
collist = (
'ID',
@@ -670,7 +677,9 @@ class TestImageList(TestImage):
# 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.api_mock.image_list.assert_called_with()
+ self.api_mock.image_list.assert_called_with(
+ marker=self._image.id,
+ )
sf_mock.assert_called_with(
[self._image],
attr='a',
@@ -693,7 +702,9 @@ class TestImageList(TestImage):
# 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.api_mock.image_list.assert_called_with()
+ self.api_mock.image_list.assert_called_with(
+ marker=self._image.id,
+ )
si_mock.assert_called_with(
[self._image],
'name:asc'
@@ -712,7 +723,7 @@ class TestImageList(TestImage):
columns, data = self.cmd.take_action(parsed_args)
self.api_mock.image_list.assert_called_with(
- limit=1,
+ limit=1, marker=self._image.id
)
self.assertEqual(self.columns, columns)
diff --git a/openstackclient/tests/unit/network/v2/fakes.py b/openstackclient/tests/unit/network/v2/fakes.py
index c18511f7..97d07076 100644
--- a/openstackclient/tests/unit/network/v2/fakes.py
+++ b/openstackclient/tests/unit/network/v2/fakes.py
@@ -194,15 +194,18 @@ class FakeIPAvailability(object):
"""Fake one or more network ip availabilities."""
@staticmethod
- def create_one_ip_availability():
+ def create_one_ip_availability(attrs=None):
"""Create a fake list with ip availability stats of a network.
+ :param Dictionary attrs:
+ A dictionary with all attributes
:return:
A FakeResource object with network_name, network_id, etc.
"""
+ attrs = attrs or {}
# Set default attributes.
- network_ip_availability = {
+ network_ip_attrs = {
'network_id': 'network-id-' + uuid.uuid4().hex,
'network_name': 'network-name-' + uuid.uuid4().hex,
'tenant_id': '',
@@ -210,10 +213,13 @@ class FakeIPAvailability(object):
'total_ips': 254,
'used_ips': 6,
}
+ network_ip_attrs.update(attrs)
network_ip_availability = fakes.FakeResource(
- info=copy.deepcopy(network_ip_availability),
+ info=copy.deepcopy(network_ip_attrs),
loaded=True)
+ network_ip_availability.project_id = network_ip_attrs['tenant_id']
+
return network_ip_availability
@staticmethod
diff --git a/openstackclient/tests/unit/network/v2/test_floating_ip.py b/openstackclient/tests/unit/network/v2/test_floating_ip.py
index 10f3067d..b3d211ba 100644
--- a/openstackclient/tests/unit/network/v2/test_floating_ip.py
+++ b/openstackclient/tests/unit/network/v2/test_floating_ip.py
@@ -18,6 +18,7 @@ from osc_lib import exceptions
from openstackclient.network.v2 import floating_ip
from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes
+from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes_v3
from openstackclient.tests.unit.network.v2 import fakes as network_fakes
from openstackclient.tests.unit import utils as tests_utils
@@ -31,6 +32,7 @@ class TestFloatingIPNetwork(network_fakes.TestNetworkV2):
# Get a shortcut to the network client
self.network = self.app.client_manager.network
+ self.projects_mock = self.app.client_manager.identity.projects
class TestCreateFloatingIPNetwork(TestFloatingIPNetwork):
@@ -145,6 +147,54 @@ class TestCreateFloatingIPNetwork(TestFloatingIPNetwork):
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
+ def test_floating_ip_create_project(self):
+ project = identity_fakes_v3.FakeProject.create_one_project()
+ self.projects_mock.get.return_value = project
+ arglist = [
+ '--project', project.id,
+ self.floating_ip.floating_network_id,
+ ]
+ verifylist = [
+ ('network', self.floating_ip.floating_network_id),
+ ('project', project.id),
+ ]
+ 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,
+ 'tenant_id': project.id,
+ })
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
+ def test_floating_ip_create_project_domain(self):
+ project = identity_fakes_v3.FakeProject.create_one_project()
+ domain = identity_fakes_v3.FakeDomain.create_one_domain()
+ self.projects_mock.get.return_value = project
+ arglist = [
+ "--project", project.name,
+ "--project-domain", domain.name,
+ self.floating_ip.floating_network_id,
+ ]
+ verifylist = [
+ ('network', self.floating_ip.floating_network_id),
+ ('project', project.name),
+ ('project_domain', domain.name),
+ ]
+
+ 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,
+ 'tenant_id': project.id,
+ })
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
class TestDeleteFloatingIPNetwork(TestFloatingIPNetwork):
diff --git a/openstackclient/tests/unit/network/v2/test_ip_availability.py b/openstackclient/tests/unit/network/v2/test_ip_availability.py
index c929ab82..4bdbddc4 100644
--- a/openstackclient/tests/unit/network/v2/test_ip_availability.py
+++ b/openstackclient/tests/unit/network/v2/test_ip_availability.py
@@ -107,6 +107,7 @@ class TestListIPAvailability(TestIPAvailability):
columns, data = self.cmd.take_action(parsed_args)
filters = {'tenant_id': self.project.id,
+ 'project_id': self.project.id,
'ip_version': 4}
self.network.network_ip_availabilities.assert_called_once_with(
diff --git a/openstackclient/tests/unit/network/v2/test_port.py b/openstackclient/tests/unit/network/v2/test_port.py
index 9312a897..aeb9884a 100644
--- a/openstackclient/tests/unit/network/v2/test_port.py
+++ b/openstackclient/tests/unit/network/v2/test_port.py
@@ -140,6 +140,7 @@ class TestCreatePort(TestPort):
'--binding-profile', 'foo=bar',
'--binding-profile', 'foo2=bar2',
'--network', self._port.network_id,
+ '--dns-name', '8.8.8.8',
'test-port',
]
@@ -156,6 +157,7 @@ class TestCreatePort(TestPort):
('vnic_type', 'macvtap'),
('binding_profile', {'foo': 'bar', 'foo2': 'bar2'}),
('network', self._port.network_id),
+ ('dns_name', '8.8.8.8'),
('name', 'test-port'),
]
@@ -174,6 +176,7 @@ class TestCreatePort(TestPort):
'binding:vnic_type': 'macvtap',
'binding:profile': {'foo': 'bar', 'foo2': 'bar2'},
'network_id': self._port.network_id,
+ 'dns_name': '8.8.8.8',
'name': 'test-port',
})
@@ -241,6 +244,7 @@ class TestCreatePort(TestPort):
'--security-group', secgroup.id,
'test-port',
]
+
verifylist = [
('network', self._port.network_id,),
('enable', True),
@@ -262,6 +266,33 @@ class TestCreatePort(TestPort):
self.assertEqual(ref_columns, columns)
self.assertEqual(ref_data, data)
+ def test_create_port_with_dns_name(self):
+ arglist = [
+ '--network', self._port.network_id,
+ '--dns-name', '8.8.8.8',
+ 'test-port',
+ ]
+ verifylist = [
+ ('network', self._port.network_id,),
+ ('enable', True),
+ ('dns_name', '8.8.8.8'),
+ ('name', 'test-port'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = (self.cmd.take_action(parsed_args))
+
+ self.network.create_port.assert_called_once_with(**{
+ 'admin_state_up': True,
+ 'network_id': self._port.network_id,
+ 'dns_name': '8.8.8.8',
+ 'name': 'test-port',
+ })
+
+ ref_columns, ref_data = self._get_common_cols_data(self._port)
+ self.assertEqual(ref_columns, columns)
+ self.assertEqual(ref_data, data)
+
def test_create_with_security_groups(self):
sg_1 = network_fakes.FakeSecurityGroup.create_one_security_group()
sg_2 = network_fakes.FakeSecurityGroup.create_one_security_group()
@@ -676,6 +707,25 @@ class TestSetPort(TestPort):
self.network.update_port.assert_called_once_with(self._port, **attrs)
self.assertIsNone(result)
+ def test_set_dns_name(self):
+ arglist = [
+ '--dns-name', '8.8.8.8',
+ self._port.name,
+ ]
+ verifylist = [
+ ('dns_name', '8.8.8.8'),
+ ('port', self._port.name),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+
+ attrs = {
+ 'dns_name': '8.8.8.8',
+ }
+ self.network.update_port.assert_called_once_with(self._port, **attrs)
+ self.assertIsNone(result)
+
def test_append_fixed_ip(self):
_testport = network_fakes.FakePort.create_one_port(
{'fixed_ips': [{'ip_address': '0.0.0.1'}]})
diff --git a/openstackclient/tests/unit/network/v2/test_router.py b/openstackclient/tests/unit/network/v2/test_router.py
index 24984e47..b0409447 100644
--- a/openstackclient/tests/unit/network/v2/test_router.py
+++ b/openstackclient/tests/unit/network/v2/test_router.py
@@ -18,6 +18,7 @@ from osc_lib import exceptions
from osc_lib import utils as osc_utils
from openstackclient.network.v2 import router
+from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes_v3
from openstackclient.tests.unit.network.v2 import fakes as network_fakes
from openstackclient.tests.unit import utils as tests_utils
@@ -29,6 +30,7 @@ class TestRouter(network_fakes.TestNetworkV2):
# Get a shortcut to the network client
self.network = self.app.client_manager.network
+ self.projects_mock = self.app.client_manager.identity.projects
class TestAddPortToRouter(TestRouter):
@@ -476,6 +478,45 @@ class TestListRouter(TestRouter):
self.network.routers.assert_called_once_with(
**{'admin_state_up': False}
)
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, list(data))
+
+ def test_router_list_project(self):
+ project = identity_fakes_v3.FakeProject.create_one_project()
+ self.projects_mock.get.return_value = project
+ arglist = [
+ '--project', project.id,
+ ]
+ verifylist = [
+ ('project', project.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+ filters = {'tenant_id': project.id}
+
+ self.network.routers.assert_called_once_with(**filters)
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, list(data))
+
+ def test_router_list_project_domain(self):
+ project = identity_fakes_v3.FakeProject.create_one_project()
+ self.projects_mock.get.return_value = project
+ arglist = [
+ '--project', project.id,
+ '--project-domain', project.domain_id,
+ ]
+ verifylist = [
+ ('project', project.id),
+ ('project_domain', project.domain_id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+ filters = {'tenant_id': project.id}
+
+ self.network.routers.assert_called_once_with(**filters)
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
diff --git a/openstackclient/tests/unit/network/v2/test_security_group.py b/openstackclient/tests/unit/network/v2/test_security_group.py
index 2615b77a..43aa07cc 100644
--- a/openstackclient/tests/unit/network/v2/test_security_group.py
+++ b/openstackclient/tests/unit/network/v2/test_security_group.py
@@ -444,6 +444,44 @@ class TestListSecurityGroupNetwork(TestSecurityGroupNetwork):
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
+ def test_security_group_list_project(self):
+ project = identity_fakes.FakeProject.create_one_project()
+ self.projects_mock.get.return_value = project
+ arglist = [
+ '--project', project.id,
+ ]
+ verifylist = [
+ ('project', project.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+ filters = {'tenant_id': project.id}
+
+ self.network.security_groups.assert_called_once_with(**filters)
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, list(data))
+
+ def test_security_group_list_project_domain(self):
+ project = identity_fakes.FakeProject.create_one_project()
+ self.projects_mock.get.return_value = project
+ arglist = [
+ '--project', project.id,
+ '--project-domain', project.domain_id,
+ ]
+ verifylist = [
+ ('project', project.id),
+ ('project_domain', project.domain_id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+ filters = {'tenant_id': project.id}
+
+ self.network.security_groups.assert_called_once_with(**filters)
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, list(data))
+
class TestListSecurityGroupCompute(TestSecurityGroupCompute):
diff --git a/openstackclient/tests/unit/volume/v1/test_snapshot.py b/openstackclient/tests/unit/volume/v1/test_snapshot.py
index 8e30d6a9..fd878f45 100644
--- a/openstackclient/tests/unit/volume/v1/test_snapshot.py
+++ b/openstackclient/tests/unit/volume/v1/test_snapshot.py
@@ -268,6 +268,7 @@ class TestSnapshotList(TestSnapshot):
super(TestSnapshotList, self).setUp()
self.volumes_mock.list.return_value = [self.volume]
+ self.volumes_mock.get.return_value = self.volume
self.snapshots_mock.list.return_value = self.snapshots
# Get the command to test
self.cmd = volume_snapshot.ListVolumeSnapshot(self.app, None)
@@ -283,7 +284,13 @@ class TestSnapshotList(TestSnapshot):
columns, data = self.cmd.take_action(parsed_args)
self.snapshots_mock.list.assert_called_once_with(
- search_opts={'all_tenants': False})
+ search_opts={
+ 'all_tenants': False,
+ 'display_name': None,
+ 'status': None,
+ 'volume_id': None
+ }
+ )
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
@@ -300,11 +307,88 @@ class TestSnapshotList(TestSnapshot):
columns, data = self.cmd.take_action(parsed_args)
self.snapshots_mock.list.assert_called_once_with(
- search_opts={'all_tenants': False}
+ search_opts={
+ 'all_tenants': False,
+ 'display_name': None,
+ 'status': None,
+ 'volume_id': None
+ }
)
self.assertEqual(self.columns_long, columns)
self.assertEqual(self.data_long, list(data))
+ def test_snapshot_list_name_option(self):
+ arglist = [
+ '--name', self.snapshots[0].display_name,
+ ]
+ verifylist = [
+ ('all_projects', False),
+ ('long', False),
+ ('name', self.snapshots[0].display_name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.snapshots_mock.list.assert_called_once_with(
+ search_opts={
+ 'all_tenants': False,
+ 'display_name': self.snapshots[0].display_name,
+ 'status': None,
+ 'volume_id': None
+ }
+ )
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, list(data))
+
+ def test_snapshot_list_status_option(self):
+ arglist = [
+ '--status', self.snapshots[0].status,
+ ]
+ verifylist = [
+ ('all_projects', False),
+ ('long', False),
+ ('status', self.snapshots[0].status),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.snapshots_mock.list.assert_called_once_with(
+ search_opts={
+ 'all_tenants': False,
+ 'display_name': None,
+ 'status': self.snapshots[0].status,
+ 'volume_id': None
+ }
+ )
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, list(data))
+
+ def test_snapshot_list_volumeid_option(self):
+ arglist = [
+ '--volume', self.volume.id,
+ ]
+ verifylist = [
+ ('all_projects', False),
+ ('long', False),
+ ('volume', self.volume.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.snapshots_mock.list.assert_called_once_with(
+ search_opts={
+ 'all_tenants': False,
+ 'display_name': None,
+ 'status': None,
+ 'volume_id': self.volume.id
+ }
+ )
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, list(data))
+
def test_snapshot_list_all_projects(self):
arglist = [
'--all-projects',
@@ -318,7 +402,13 @@ class TestSnapshotList(TestSnapshot):
columns, data = self.cmd.take_action(parsed_args)
self.snapshots_mock.list.assert_called_once_with(
- search_opts={'all_tenants': True})
+ search_opts={
+ 'all_tenants': True,
+ 'display_name': None,
+ 'status': None,
+ 'volume_id': None
+ }
+ )
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
diff --git a/openstackclient/tests/unit/volume/v2/fakes.py b/openstackclient/tests/unit/volume/v2/fakes.py
index 3137bfb0..d5cd72ec 100644
--- a/openstackclient/tests/unit/volume/v2/fakes.py
+++ b/openstackclient/tests/unit/volume/v2/fakes.py
@@ -480,7 +480,7 @@ class FakeBackup(object):
If backups list is provided, then initialize the Mock object with the
list. Otherwise create one.
- :param List volumes:
+ :param List backups:
A list of FakeResource objects faking backups
:param Integer count:
The number of backups to be faked
@@ -764,7 +764,7 @@ class FakeQos(object):
If qoses list is provided, then initialize the Mock object with the
list. Otherwise create one.
- :param List volumes:
+ :param List qoses:
A list of FakeResource objects faking qoses
:param Integer count:
The number of qoses to be faked
@@ -837,7 +837,7 @@ class FakeSnapshot(object):
If snapshots list is provided, then initialize the Mock object with the
list. Otherwise create one.
- :param List volumes:
+ :param List snapshots:
A list of FakeResource objects faking snapshots
:param Integer count:
The number of snapshots to be faked
diff --git a/openstackclient/tests/unit/volume/v2/test_consistency_group.py b/openstackclient/tests/unit/volume/v2/test_consistency_group.py
index 8a997e18..bc99ca8d 100644
--- a/openstackclient/tests/unit/volume/v2/test_consistency_group.py
+++ b/openstackclient/tests/unit/volume/v2/test_consistency_group.py
@@ -32,6 +32,10 @@ class TestConsistencyGroup(volume_fakes.TestVolume):
self.app.client_manager.volume.consistencygroups)
self.consistencygroups_mock.reset_mock()
+ self.cgsnapshots_mock = (
+ self.app.client_manager.volume.cgsnapshots)
+ self.cgsnapshots_mock.reset_mock()
+
self.types_mock = self.app.client_manager.volume.volume_types
self.types_mock.reset_mock()
@@ -41,6 +45,11 @@ class TestConsistencyGroupCreate(TestConsistencyGroup):
volume_type = volume_fakes.FakeType.create_one_type()
new_consistency_group = (
volume_fakes.FakeConsistencyGroup.create_one_consistency_group())
+ consistency_group_snapshot = (
+ volume_fakes.
+ FakeConsistencyGroupSnapshot.
+ create_one_consistency_group_snapshot()
+ )
columns = (
'availability_zone',
@@ -70,6 +79,8 @@ class TestConsistencyGroupCreate(TestConsistencyGroup):
self.consistencygroups_mock.get.return_value = (
self.new_consistency_group)
self.types_mock.get.return_value = self.volume_type
+ self.cgsnapshots_mock.get.return_value = (
+ self.consistency_group_snapshot)
# Get the command object to test
self.cmd = consistency_group.CreateConsistencyGroup(self.app, None)
@@ -164,6 +175,34 @@ class TestConsistencyGroupCreate(TestConsistencyGroup):
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
+ def test_consistency_group_create_from_snapshot(self):
+ arglist = [
+ '--consistency-group-snapshot', self.consistency_group_snapshot.id,
+ '--description', self.new_consistency_group.description,
+ self.new_consistency_group.name,
+ ]
+ verifylist = [
+ ('consistency_group_snapshot', self.consistency_group_snapshot.id),
+ ('description', self.new_consistency_group.description),
+ ('name', self.new_consistency_group.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.types_mock.get.assert_not_called()
+ self.cgsnapshots_mock.get.assert_called_once_with(
+ self.consistency_group_snapshot.id)
+ self.consistencygroups_mock.create_from_src.assert_called_with(
+ self.consistency_group_snapshot.id,
+ None,
+ name=self.new_consistency_group.name,
+ description=self.new_consistency_group.description,
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
class TestConsistencyGroupDelete(TestConsistencyGroup):
diff --git a/openstackclient/tests/unit/volume/v2/test_snapshot.py b/openstackclient/tests/unit/volume/v2/test_snapshot.py
index b67dd6eb..bb238135 100644
--- a/openstackclient/tests/unit/volume/v2/test_snapshot.py
+++ b/openstackclient/tests/unit/volume/v2/test_snapshot.py
@@ -275,6 +275,7 @@ class TestSnapshotList(TestSnapshot):
super(TestSnapshotList, self).setUp()
self.volumes_mock.list.return_value = [self.volume]
+ self.volumes_mock.get.return_value = self.volume
self.snapshots_mock.list.return_value = self.snapshots
# Get the command to test
self.cmd = volume_snapshot.ListVolumeSnapshot(self.app, None)
@@ -283,14 +284,21 @@ class TestSnapshotList(TestSnapshot):
arglist = []
verifylist = [
('all_projects', False),
- ("long", False)
+ ('long', False)
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.snapshots_mock.list.assert_called_once_with(
- limit=None, marker=None, search_opts={'all_tenants': False})
+ limit=None, marker=None,
+ search_opts={
+ 'all_tenants': False,
+ 'name': None,
+ 'status': None,
+ 'volume_id': None
+ }
+ )
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
@@ -313,7 +321,12 @@ class TestSnapshotList(TestSnapshot):
self.snapshots_mock.list.assert_called_once_with(
limit=2,
marker=self.snapshots[0].id,
- search_opts={'all_tenants': False}
+ search_opts={
+ 'all_tenants': False,
+ 'name': None,
+ 'status': None,
+ 'volume_id': None
+ }
)
self.assertEqual(self.columns_long, columns)
self.assertEqual(self.data_long, list(data))
@@ -331,7 +344,89 @@ class TestSnapshotList(TestSnapshot):
columns, data = self.cmd.take_action(parsed_args)
self.snapshots_mock.list.assert_called_once_with(
- limit=None, marker=None, search_opts={'all_tenants': True})
+ limit=None, marker=None,
+ search_opts={
+ 'all_tenants': True,
+ 'name': None,
+ 'status': None,
+ 'volume_id': None
+ }
+ )
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, list(data))
+
+ def test_snapshot_list_name_option(self):
+ arglist = [
+ '--name', self.snapshots[0].name,
+ ]
+ verifylist = [
+ ('all_projects', False),
+ ('long', False),
+ ('name', self.snapshots[0].name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.snapshots_mock.list.assert_called_once_with(
+ limit=None, marker=None,
+ search_opts={
+ 'all_tenants': False,
+ 'name': self.snapshots[0].name,
+ 'status': None,
+ 'volume_id': None
+ }
+ )
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, list(data))
+
+ def test_snapshot_list_status_option(self):
+ arglist = [
+ '--status', self.snapshots[0].status,
+ ]
+ verifylist = [
+ ('all_projects', False),
+ ('long', False),
+ ('status', self.snapshots[0].status),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.snapshots_mock.list.assert_called_once_with(
+ limit=None, marker=None,
+ search_opts={
+ 'all_tenants': False,
+ 'name': None,
+ 'status': self.snapshots[0].status,
+ 'volume_id': None
+ }
+ )
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, list(data))
+
+ def test_snapshot_list_volumeid_option(self):
+ arglist = [
+ '--volume', self.volume.id,
+ ]
+ verifylist = [
+ ('all_projects', False),
+ ('long', False),
+ ('volume', self.volume.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.snapshots_mock.list.assert_called_once_with(
+ limit=None, marker=None,
+ search_opts={
+ 'all_tenants': False,
+ 'name': None,
+ 'status': None,
+ 'volume_id': self.volume.id
+ }
+ )
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
diff --git a/openstackclient/tests/unit/volume/v2/test_volume_host.py b/openstackclient/tests/unit/volume/v2/test_volume_host.py
new file mode 100644
index 00000000..aad7bb0b
--- /dev/null
+++ b/openstackclient/tests/unit/volume/v2/test_volume_host.py
@@ -0,0 +1,86 @@
+#
+# 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.tests.unit.volume.v2 import fakes as host_fakes
+from openstackclient.volume.v2 import volume_host
+
+
+class TestVolumeHost(host_fakes.TestVolume):
+
+ def setUp(self):
+ super(TestVolumeHost, self).setUp()
+
+ self.host_mock = self.app.client_manager.volume.services
+ self.host_mock.reset_mock()
+
+
+class TestVolumeHostSet(TestVolumeHost):
+
+ service = host_fakes.FakeService.create_one_service()
+
+ def setUp(self):
+ super(TestVolumeHostSet, self).setUp()
+
+ self.host_mock.freeze_host.return_value = None
+ self.host_mock.thaw_host.return_value = None
+
+ self.cmd = volume_host.SetVolumeHost(self.app, None)
+
+ def test_volume_host_set_nothing(self):
+ arglist = [
+ self.service.host,
+ ]
+ verifylist = [
+ ('host', self.service.host),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+
+ self.host_mock.freeze_host.assert_not_called()
+ self.host_mock.thaw_host.assert_not_called()
+ self.assertIsNone(result)
+
+ def test_volume_host_set_enable(self):
+ arglist = [
+ '--enable',
+ self.service.host,
+ ]
+ verifylist = [
+ ('enable', True),
+ ('host', self.service.host),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ self.host_mock.thaw_host.assert_called_with(self.service.host)
+ self.host_mock.freeze_host.assert_not_called()
+ self.assertIsNone(result)
+
+ def test_volume_host_set_disable(self):
+ arglist = [
+ '--disable',
+ self.service.host,
+ ]
+ verifylist = [
+ ('disable', True),
+ ('host', self.service.host),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ self.host_mock.freeze_host.assert_called_with(self.service.host)
+ self.host_mock.thaw_host.assert_not_called()
+ self.assertIsNone(result)
diff --git a/openstackclient/volume/v1/volume_snapshot.py b/openstackclient/volume/v1/volume_snapshot.py
index c2ecf75b..77c93ad4 100644
--- a/openstackclient/volume/v1/volume_snapshot.py
+++ b/openstackclient/volume/v1/volume_snapshot.py
@@ -135,9 +135,31 @@ class ListVolumeSnapshot(command.Lister):
default=False,
help=_('List additional fields in output'),
)
+ parser.add_argument(
+ '--name',
+ metavar='<name>',
+ default=None,
+ help=_('Filters results by a name.')
+ )
+ parser.add_argument(
+ '--status',
+ metavar='<status>',
+ choices=['available', 'error', 'creating', 'deleting',
+ 'error-deleting'],
+ help=_("Filters results by a status. "
+ "('available', 'error', 'creating', 'deleting'"
+ " or 'error-deleting')")
+ )
+ parser.add_argument(
+ '--volume',
+ metavar='<volume>',
+ default=None,
+ help=_('Filters results by a volume (name or ID).')
+ )
return parser
def take_action(self, parsed_args):
+ volume_client = self.app.client_manager.volume
def _format_volume_id(volume_id):
"""Return a volume name if available
@@ -169,17 +191,25 @@ class ListVolumeSnapshot(command.Lister):
# Cache the volume list
volume_cache = {}
try:
- for s in self.app.client_manager.volume.volumes.list():
+ for s in volume_client.volumes.list():
volume_cache[s.id] = s
except Exception:
# Just forget it if there's any trouble
pass
+ volume_id = None
+ if parsed_args.volume:
+ volume_id = utils.find_resource(
+ volume_client.volumes, parsed_args.volume).id
+
search_opts = {
'all_tenants': parsed_args.all_projects,
+ 'display_name': parsed_args.name,
+ 'status': parsed_args.status,
+ 'volume_id': volume_id,
}
- data = self.app.client_manager.volume.volume_snapshots.list(
+ data = volume_client.volume_snapshots.list(
search_opts=search_opts)
return (column_headers,
(utils.get_item_properties(
diff --git a/openstackclient/volume/v2/consistency_group.py b/openstackclient/volume/v2/consistency_group.py
index f77da59b..2f4f3c95 100644
--- a/openstackclient/volume/v2/consistency_group.py
+++ b/openstackclient/volume/v2/consistency_group.py
@@ -27,51 +27,6 @@ from openstackclient.i18n import _
LOG = logging.getLogger(__name__)
-class DeleteConsistencyGroup(command.Command):
- _description = _("Delete consistency group(s).")
-
- def get_parser(self, prog_name):
- parser = super(DeleteConsistencyGroup, self).get_parser(prog_name)
- parser.add_argument(
- 'consistency_groups',
- metavar='<consistency-group>',
- nargs="+",
- help=_('Consistency group(s) to delete (name or ID)'),
- )
- parser.add_argument(
- '--force',
- action='store_true',
- default=False,
- help=_("Allow delete in state other than error or available"),
- )
- return parser
-
- def take_action(self, parsed_args):
- volume_client = self.app.client_manager.volume
- result = 0
-
- for i in parsed_args.consistency_groups:
- try:
- consistency_group_id = utils.find_resource(
- volume_client.consistencygroups, i).id
- volume_client.consistencygroups.delete(
- consistency_group_id, parsed_args.force)
- except Exception as e:
- result += 1
- LOG.error(_("Failed to delete consistency group with "
- "name or ID '%(consistency_group)s':%(e)s")
- % {'consistency_group': i, 'e': e})
-
- if result > 0:
- total = len(parsed_args.consistency_groups)
- msg = (_("%(result)s of %(total)s consistency groups failed "
- "to delete.") % {'result': result, 'total': total})
- raise exceptions.CommandError(msg)
-
-
-LOG = logging.getLogger(__name__)
-
-
class CreateConsistencyGroup(command.ShowOne):
_description = _("Create new consistency group.")
@@ -94,6 +49,11 @@ class CreateConsistencyGroup(command.ShowOne):
metavar="<consistency-group>",
help=_("Existing consistency group (name or ID)")
)
+ exclusive_group.add_argument(
+ "--consistency-group-snapshot",
+ metavar="<consistency-group-snapshot>",
+ help=_("Existing consistency group snapshot (name or ID)")
+ )
parser.add_argument(
"--description",
metavar="<description>",
@@ -120,17 +80,23 @@ class CreateConsistencyGroup(command.ShowOne):
description=parsed_args.description,
availability_zone=parsed_args.availability_zone
)
- elif parsed_args.consistency_group_source:
+ else:
if parsed_args.availability_zone:
msg = _("'--availability-zone' option will not work "
"if creating consistency group from source")
LOG.warning(msg)
- consistency_group_id = utils.find_resource(
- volume_client.consistencygroups,
- parsed_args.consistency_group_source).id
+
+ consistency_group_id = None
consistency_group_snapshot = None
- # TODO(Huanxuan Ao): Support for creating from consistency group
- # snapshot after adding "consistency_group_snapshot" resource
+ if parsed_args.consistency_group_source:
+ consistency_group_id = utils.find_resource(
+ volume_client.consistencygroups,
+ parsed_args.consistency_group_source).id
+ elif parsed_args.consistency_group_snapshot:
+ consistency_group_snapshot = utils.find_resource(
+ volume_client.cgsnapshots,
+ parsed_args.consistency_group_snapshot).id
+
consistency_group = (
volume_client.consistencygroups.create_from_src(
consistency_group_snapshot,
@@ -143,6 +109,48 @@ class CreateConsistencyGroup(command.ShowOne):
return zip(*sorted(six.iteritems(consistency_group._info)))
+class DeleteConsistencyGroup(command.Command):
+ _description = _("Delete consistency group(s).")
+
+ def get_parser(self, prog_name):
+ parser = super(DeleteConsistencyGroup, self).get_parser(prog_name)
+ parser.add_argument(
+ 'consistency_groups',
+ metavar='<consistency-group>',
+ nargs="+",
+ help=_('Consistency group(s) to delete (name or ID)'),
+ )
+ parser.add_argument(
+ '--force',
+ action='store_true',
+ default=False,
+ help=_("Allow delete in state other than error or available"),
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ volume_client = self.app.client_manager.volume
+ result = 0
+
+ for i in parsed_args.consistency_groups:
+ try:
+ consistency_group_id = utils.find_resource(
+ volume_client.consistencygroups, i).id
+ volume_client.consistencygroups.delete(
+ consistency_group_id, parsed_args.force)
+ except Exception as e:
+ result += 1
+ LOG.error(_("Failed to delete consistency group with "
+ "name or ID '%(consistency_group)s':%(e)s")
+ % {'consistency_group': i, 'e': e})
+
+ if result > 0:
+ total = len(parsed_args.consistency_groups)
+ msg = (_("%(result)s of %(total)s consistency groups failed "
+ "to delete.") % {'result': result, 'total': total})
+ raise exceptions.CommandError(msg)
+
+
class ListConsistencyGroup(command.Lister):
_description = _("List consistency groups.")
diff --git a/openstackclient/volume/v2/volume_host.py b/openstackclient/volume/v2/volume_host.py
new file mode 100644
index 00000000..376e5024
--- /dev/null
+++ b/openstackclient/volume/v2/volume_host.py
@@ -0,0 +1,50 @@
+#
+# 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.
+#
+
+"""Volume v2 host action implementations"""
+
+from osc_lib.command import command
+
+from openstackclient.i18n import _
+
+
+class SetVolumeHost(command.Command):
+ _description = _("Set volume host properties")
+
+ def get_parser(self, prog_name):
+ parser = super(SetVolumeHost, self).get_parser(prog_name)
+ parser.add_argument(
+ "host",
+ metavar="<host-name>",
+ help=_("Name of volume host")
+ )
+ enabled_group = parser.add_mutually_exclusive_group()
+ enabled_group.add_argument(
+ "--disable",
+ action="store_true",
+ help=_("Freeze and disable the specified volume host.")
+ )
+ enabled_group.add_argument(
+ "--enable",
+ action="store_true",
+ help=_("Thaw and enable the specified volume host.")
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ service_client = self.app.client_manager.volume
+ if parsed_args.enable:
+ service_client.services.thaw_host(parsed_args.host)
+ if parsed_args.disable:
+ service_client.services.freeze_host(parsed_args.host)
diff --git a/openstackclient/volume/v2/volume_snapshot.py b/openstackclient/volume/v2/volume_snapshot.py
index 43f30326..86af6d8c 100644
--- a/openstackclient/volume/v2/volume_snapshot.py
+++ b/openstackclient/volume/v2/volume_snapshot.py
@@ -151,9 +151,31 @@ class ListVolumeSnapshot(command.Lister):
metavar='<limit>',
help=_('Maximum number of snapshots to display'),
)
+ parser.add_argument(
+ '--name',
+ metavar='<name>',
+ default=None,
+ help=_('Filters results by a name.')
+ )
+ parser.add_argument(
+ '--status',
+ metavar='<status>',
+ choices=['available', 'error', 'creating', 'deleting',
+ 'error-deleting'],
+ help=_("Filters results by a status. "
+ "('available', 'error', 'creating', 'deleting'"
+ " or 'error-deleting')")
+ )
+ parser.add_argument(
+ '--volume',
+ metavar='<volume>',
+ default=None,
+ help=_('Filters results by a volume (name or ID).')
+ )
return parser
def take_action(self, parsed_args):
+ volume_client = self.app.client_manager.volume
def _format_volume_id(volume_id):
"""Return a volume name if available
@@ -180,17 +202,25 @@ class ListVolumeSnapshot(command.Lister):
# Cache the volume list
volume_cache = {}
try:
- for s in self.app.client_manager.volume.volumes.list():
+ for s in volume_client.volumes.list():
volume_cache[s.id] = s
except Exception:
# Just forget it if there's any trouble
pass
+ volume_id = None
+ if parsed_args.volume:
+ volume_id = utils.find_resource(
+ volume_client.volumes, parsed_args.volume).id
+
search_opts = {
'all_tenants': parsed_args.all_projects,
+ 'name': parsed_args.name,
+ 'status': parsed_args.status,
+ 'volume_id': volume_id,
}
- data = self.app.client_manager.volume.volume_snapshots.list(
+ data = volume_client.volume_snapshots.list(
search_opts=search_opts,
marker=parsed_args.marker,
limit=parsed_args.limit,
diff --git a/releasenotes/notes/bp-cinder-command-support-7e3ae1fb4cd90407.yaml b/releasenotes/notes/bp-cinder-command-support-7e3ae1fb4cd90407.yaml
new file mode 100644
index 00000000..1b0ca18d
--- /dev/null
+++ b/releasenotes/notes/bp-cinder-command-support-7e3ae1fb4cd90407.yaml
@@ -0,0 +1,5 @@
+---
+features:
+ - Add ``volume host set`` command, it allows a user to enable or disable a volume host.
+ [Blueprint `cinder-command-support <https://blueprints.launchpad.net/python-openstackclient/+spec/cinder-command-support>`_]
+
diff --git a/releasenotes/notes/bug-1612136-63aac6377209db38.yaml b/releasenotes/notes/bug-1612136-63aac6377209db38.yaml
new file mode 100644
index 00000000..51eaa76d
--- /dev/null
+++ b/releasenotes/notes/bug-1612136-63aac6377209db38.yaml
@@ -0,0 +1,5 @@
+---
+features:
+ - |
+ Add ``--dns-name`` option to ``os port create`` and ``os port set`` commands.
+ [Bug `1612136 <https://bugs.launchpad.net/python-openstackclient/+bug/1612136>`_]
diff --git a/releasenotes/notes/bug-1613231-386b2b1373662052.yaml b/releasenotes/notes/bug-1613231-386b2b1373662052.yaml
new file mode 100644
index 00000000..a55af0dd
--- /dev/null
+++ b/releasenotes/notes/bug-1613231-386b2b1373662052.yaml
@@ -0,0 +1,8 @@
+---
+features:
+ - |
+ Add ``--project`` and ``--project-domain`` options to the ``router list``,
+ ``floating ip create`` and ``security group list`` commands.
+ [Bug `1613231 <https://bugs.launchpad.net/bugs/1613231>`_]
+ [Bug `1613629 <https://bugs.launchpad.net/bugs/1613629>`_]
+ [Bug `1610909 <https://bugs.launchpad.net/bugs/1610909>`_]
diff --git a/releasenotes/notes/bug-1645252-219bfd50c8f04846.yaml b/releasenotes/notes/bug-1645252-219bfd50c8f04846.yaml
new file mode 100644
index 00000000..12b1e42d
--- /dev/null
+++ b/releasenotes/notes/bug-1645252-219bfd50c8f04846.yaml
@@ -0,0 +1,6 @@
+---
+features:
+ - |
+ Add ``--name``, ``--status`` and ``--volume`` options
+ to ``volume snapshot list`` command
+ [Bug `1645252 <https://bugs.launchpad.net/bugs/1645252>`_]
diff --git a/setup.cfg b/setup.cfg
index 7f76cd44..8c5c6630 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -548,6 +548,8 @@ openstack.volume.v2 =
volume_backup_set = openstackclient.volume.v2.backup:SetVolumeBackup
volume_backup_show = openstackclient.volume.v2.backup:ShowVolumeBackup
+ volume_host_set = openstackclient.volume.v2.volume_host:SetVolumeHost
+
volume_snapshot_create = openstackclient.volume.v2.volume_snapshot:CreateVolumeSnapshot
volume_snapshot_delete = openstackclient.volume.v2.volume_snapshot:DeleteVolumeSnapshot
volume_snapshot_list = openstackclient.volume.v2.volume_snapshot:ListVolumeSnapshot