Support for volume service list
OSC does not support to list volume services. This patch will provide support for adding volume service related support. Closes-bug:#1550999 Implements: bp cinder-command-support Change-Id: I50ac14aeb96c4b8ddbf7b33e519feea0d126f752
This commit is contained in:
parent
9e7f0cf1a5
commit
4072554608
31
doc/source/command-objects/volume-service.rst
Normal file
31
doc/source/command-objects/volume-service.rst
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
==============
|
||||||
|
volume service
|
||||||
|
==============
|
||||||
|
|
||||||
|
Volume v1, v2
|
||||||
|
|
||||||
|
volume service list
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
List volume service
|
||||||
|
|
||||||
|
.. program:: volume service list
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
os volume service list
|
||||||
|
[--host <host>]
|
||||||
|
[--service <service>]
|
||||||
|
[--long]
|
||||||
|
|
||||||
|
.. _volume-service-list:
|
||||||
|
.. option:: --host <host>
|
||||||
|
|
||||||
|
List services on specified host (name only)
|
||||||
|
|
||||||
|
.. option:: --service <service>
|
||||||
|
|
||||||
|
List only specified service (name only)
|
||||||
|
|
||||||
|
.. option:: --long
|
||||||
|
|
||||||
|
List additional fields in output
|
@ -127,6 +127,7 @@ referring to both Compute and Volume quotas.
|
|||||||
* ``user role``: (**Identity**) roles assigned to a user
|
* ``user role``: (**Identity**) roles assigned to a user
|
||||||
* ``volume``: (**Volume**) block volumes
|
* ``volume``: (**Volume**) block volumes
|
||||||
* ``volume type``: (**Volume**) deployment-specific types of volumes available
|
* ``volume type``: (**Volume**) deployment-specific types of volumes available
|
||||||
|
* ``volume service``: (**Volume**) services to manage block storage operations
|
||||||
|
|
||||||
|
|
||||||
Plugin Objects
|
Plugin Objects
|
||||||
|
@ -129,6 +129,97 @@ QOS_WITH_ASSOCIATIONS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class FakeServiceClient(object):
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
self.services = mock.Mock()
|
||||||
|
self.services.resource_class = fakes.FakeResource(None, {})
|
||||||
|
|
||||||
|
|
||||||
|
class TestService(utils.TestCommand):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestService, self).setUp()
|
||||||
|
|
||||||
|
self.app.client_manager.volume = FakeServiceClient(
|
||||||
|
endpoint=fakes.AUTH_URL,
|
||||||
|
token=fakes.AUTH_TOKEN
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class FakeService(object):
|
||||||
|
"""Fake one or more Services."""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create_one_service(attrs=None):
|
||||||
|
"""Create a fake service.
|
||||||
|
|
||||||
|
:param Dictionary attrs:
|
||||||
|
A dictionary with all attributes of service
|
||||||
|
:retrun:
|
||||||
|
A FakeResource object with host, status, etc.
|
||||||
|
"""
|
||||||
|
# Set default attribute
|
||||||
|
service_info = {
|
||||||
|
'host': 'host_test',
|
||||||
|
'binary': 'cinder_test',
|
||||||
|
'status': 'enabled',
|
||||||
|
'disabled_reason': 'LongHoliday-GoldenWeek',
|
||||||
|
'zone': 'fake_zone',
|
||||||
|
'updated_at': 'fake_date',
|
||||||
|
'state': 'fake_state',
|
||||||
|
}
|
||||||
|
|
||||||
|
# Overwrite default attributes if there are some attributes set
|
||||||
|
if attrs is None:
|
||||||
|
attrs = {}
|
||||||
|
service_info.update(attrs)
|
||||||
|
|
||||||
|
service = fakes.FakeResource(
|
||||||
|
None,
|
||||||
|
service_info,
|
||||||
|
loaded=True)
|
||||||
|
|
||||||
|
return service
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create_services(attrs=None, count=2):
|
||||||
|
"""Create multiple fake services.
|
||||||
|
|
||||||
|
:param Dictionary attrs:
|
||||||
|
A dictionary with all attributes of service
|
||||||
|
:param Integer count:
|
||||||
|
The number of services to be faked
|
||||||
|
:return:
|
||||||
|
A list of FakeResource objects
|
||||||
|
"""
|
||||||
|
services = []
|
||||||
|
for n in range(0, count):
|
||||||
|
services.append(FakeService.create_one_service(attrs))
|
||||||
|
|
||||||
|
return services
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_services(services=None, count=2):
|
||||||
|
"""Get an iterable MagicMock object with a list of faked services.
|
||||||
|
|
||||||
|
If services list is provided, then initialize the Mock object with the
|
||||||
|
list. Otherwise create one.
|
||||||
|
|
||||||
|
:param List services:
|
||||||
|
A list of FakeResource objects faking services
|
||||||
|
:param Integer count:
|
||||||
|
The number of services to be faked
|
||||||
|
:return
|
||||||
|
An iterable Mock object with side_effect set to a list of faked
|
||||||
|
services
|
||||||
|
"""
|
||||||
|
if services is None:
|
||||||
|
services = FakeService.create_services(count)
|
||||||
|
|
||||||
|
return mock.MagicMock(side_effect=services)
|
||||||
|
|
||||||
|
|
||||||
class FakeImagev1Client(object):
|
class FakeImagev1Client(object):
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
|
141
openstackclient/tests/volume/v1/test_service.py
Normal file
141
openstackclient/tests/volume/v1/test_service.py
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
#
|
||||||
|
# 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.volume.v1 import fakes as service_fakes
|
||||||
|
from openstackclient.volume.v1 import service
|
||||||
|
|
||||||
|
|
||||||
|
class TestService(service_fakes.TestService):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestService, self).setUp()
|
||||||
|
|
||||||
|
# Get a shortcut to the ServiceManager Mock
|
||||||
|
self.service_mock = self.app.client_manager.volume.services
|
||||||
|
self.service_mock.reset_mock()
|
||||||
|
|
||||||
|
|
||||||
|
class TestServiceList(TestService):
|
||||||
|
|
||||||
|
# The service to be listed
|
||||||
|
services = service_fakes.FakeService.create_one_service()
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestServiceList, self).setUp()
|
||||||
|
|
||||||
|
self.service_mock.list.return_value = [self.services]
|
||||||
|
|
||||||
|
# Get the command object to test
|
||||||
|
self.cmd = service.ListService(self.app, None)
|
||||||
|
|
||||||
|
def test_service_list(self):
|
||||||
|
arglist = [
|
||||||
|
'--host', self.services.host,
|
||||||
|
'--service', self.services.binary,
|
||||||
|
]
|
||||||
|
verifylist = [
|
||||||
|
('host', self.services.host),
|
||||||
|
('service', self.services.binary),
|
||||||
|
]
|
||||||
|
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)
|
||||||
|
|
||||||
|
expected_columns = [
|
||||||
|
'Binary',
|
||||||
|
'Host',
|
||||||
|
'Zone',
|
||||||
|
'Status',
|
||||||
|
'State',
|
||||||
|
'Updated At',
|
||||||
|
]
|
||||||
|
|
||||||
|
# confirming if all expected columns are present in the result.
|
||||||
|
self.assertEqual(expected_columns, columns)
|
||||||
|
|
||||||
|
datalist = ((
|
||||||
|
self.services.binary,
|
||||||
|
self.services.host,
|
||||||
|
self.services.zone,
|
||||||
|
self.services.status,
|
||||||
|
self.services.state,
|
||||||
|
self.services.updated_at,
|
||||||
|
), )
|
||||||
|
|
||||||
|
# confirming if all expected values are present in the result.
|
||||||
|
self.assertEqual(datalist, tuple(data))
|
||||||
|
|
||||||
|
# checking if proper call was made to list services
|
||||||
|
self.service_mock.list.assert_called_with(
|
||||||
|
self.services.host,
|
||||||
|
self.services.binary,
|
||||||
|
)
|
||||||
|
|
||||||
|
# checking if prohibited columns are present in output
|
||||||
|
self.assertNotIn("Disabled Reason", columns)
|
||||||
|
self.assertNotIn(self.services.disabled_reason,
|
||||||
|
tuple(data))
|
||||||
|
|
||||||
|
def test_service_list_with_long_option(self):
|
||||||
|
arglist = [
|
||||||
|
'--host', self.services.host,
|
||||||
|
'--service', self.services.binary,
|
||||||
|
'--long'
|
||||||
|
]
|
||||||
|
verifylist = [
|
||||||
|
('host', self.services.host),
|
||||||
|
('service', self.services.binary),
|
||||||
|
('long', True)
|
||||||
|
]
|
||||||
|
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)
|
||||||
|
|
||||||
|
expected_columns = [
|
||||||
|
'Binary',
|
||||||
|
'Host',
|
||||||
|
'Zone',
|
||||||
|
'Status',
|
||||||
|
'State',
|
||||||
|
'Updated At',
|
||||||
|
'Disabled Reason'
|
||||||
|
]
|
||||||
|
|
||||||
|
# confirming if all expected columns are present in the result.
|
||||||
|
self.assertEqual(expected_columns, columns)
|
||||||
|
|
||||||
|
datalist = ((
|
||||||
|
self.services.binary,
|
||||||
|
self.services.host,
|
||||||
|
self.services.zone,
|
||||||
|
self.services.status,
|
||||||
|
self.services.state,
|
||||||
|
self.services.updated_at,
|
||||||
|
self.services.disabled_reason,
|
||||||
|
), )
|
||||||
|
|
||||||
|
# confirming if all expected values are present in the result.
|
||||||
|
self.assertEqual(datalist, tuple(data))
|
||||||
|
|
||||||
|
self.service_mock.list.assert_called_with(
|
||||||
|
self.services.host,
|
||||||
|
self.services.binary,
|
||||||
|
)
|
@ -232,6 +232,97 @@ EXTENSION = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class FakeServiceClient(object):
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
self.services = mock.Mock()
|
||||||
|
self.services.resource_class = fakes.FakeResource(None, {})
|
||||||
|
|
||||||
|
|
||||||
|
class TestService(utils.TestCommand):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestService, self).setUp()
|
||||||
|
|
||||||
|
self.app.client_manager.volume = FakeServiceClient(
|
||||||
|
endpoint=fakes.AUTH_URL,
|
||||||
|
token=fakes.AUTH_TOKEN
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class FakeService(object):
|
||||||
|
"""Fake one or more Services."""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create_one_service(attrs=None):
|
||||||
|
"""Create a fake service.
|
||||||
|
|
||||||
|
:param Dictionary attrs:
|
||||||
|
A dictionary with all attributes of service
|
||||||
|
:retrun:
|
||||||
|
A FakeResource object with host, status, etc.
|
||||||
|
"""
|
||||||
|
# Set default attribute
|
||||||
|
service_info = {
|
||||||
|
'host': 'host_test',
|
||||||
|
'binary': 'cinder_test',
|
||||||
|
'status': 'enabled',
|
||||||
|
'disabled_reason': 'LongHoliday-GoldenWeek',
|
||||||
|
'zone': 'fake_zone',
|
||||||
|
'updated_at': 'fake_date',
|
||||||
|
'state': 'fake_state',
|
||||||
|
}
|
||||||
|
|
||||||
|
# Overwrite default attributes if there are some attributes set
|
||||||
|
if attrs is None:
|
||||||
|
attrs = {}
|
||||||
|
service_info.update(attrs)
|
||||||
|
|
||||||
|
service = fakes.FakeResource(
|
||||||
|
None,
|
||||||
|
service_info,
|
||||||
|
loaded=True)
|
||||||
|
|
||||||
|
return service
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create_services(attrs=None, count=2):
|
||||||
|
"""Create multiple fake services.
|
||||||
|
|
||||||
|
:param Dictionary attrs:
|
||||||
|
A dictionary with all attributes of service
|
||||||
|
:param Integer count:
|
||||||
|
The number of services to be faked
|
||||||
|
:return:
|
||||||
|
A list of FakeResource objects
|
||||||
|
"""
|
||||||
|
services = []
|
||||||
|
for n in range(0, count):
|
||||||
|
services.append(FakeService.create_one_service(attrs))
|
||||||
|
|
||||||
|
return services
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_services(services=None, count=2):
|
||||||
|
"""Get an iterable MagicMock object with a list of faked services.
|
||||||
|
|
||||||
|
If services list is provided, then initialize the Mock object with the
|
||||||
|
list. Otherwise create one.
|
||||||
|
|
||||||
|
:param List services:
|
||||||
|
A list of FakeResource objects faking services
|
||||||
|
:param Integer count:
|
||||||
|
The number of services to be faked
|
||||||
|
:return
|
||||||
|
An iterable Mock object with side_effect set to a list of faked
|
||||||
|
services
|
||||||
|
"""
|
||||||
|
if services is None:
|
||||||
|
services = FakeService.create_services(count)
|
||||||
|
|
||||||
|
return mock.MagicMock(side_effect=services)
|
||||||
|
|
||||||
|
|
||||||
class FakeVolumeClient(object):
|
class FakeVolumeClient(object):
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
@ -243,6 +334,8 @@ class FakeVolumeClient(object):
|
|||||||
self.backups.resource_class = fakes.FakeResource(None, {})
|
self.backups.resource_class = fakes.FakeResource(None, {})
|
||||||
self.volume_types = mock.Mock()
|
self.volume_types = mock.Mock()
|
||||||
self.volume_types.resource_class = fakes.FakeResource(None, {})
|
self.volume_types.resource_class = fakes.FakeResource(None, {})
|
||||||
|
self.volume_type_access = mock.Mock()
|
||||||
|
self.volume_type_access.resource_class = fakes.FakeResource(None, {})
|
||||||
self.restores = mock.Mock()
|
self.restores = mock.Mock()
|
||||||
self.restores.resource_class = fakes.FakeResource(None, {})
|
self.restores.resource_class = fakes.FakeResource(None, {})
|
||||||
self.qos_specs = mock.Mock()
|
self.qos_specs = mock.Mock()
|
||||||
@ -279,7 +372,7 @@ class FakeVolume(object):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_one_volume(attrs={}):
|
def create_one_volume(attrs=None):
|
||||||
"""Create a fake volume.
|
"""Create a fake volume.
|
||||||
|
|
||||||
:param Dictionary attrs:
|
:param Dictionary attrs:
|
||||||
@ -287,6 +380,8 @@ class FakeVolume(object):
|
|||||||
:retrun:
|
:retrun:
|
||||||
A FakeResource object with id, name, status, etc.
|
A FakeResource object with id, name, status, etc.
|
||||||
"""
|
"""
|
||||||
|
attrs = attrs or {}
|
||||||
|
|
||||||
# Set default attribute
|
# Set default attribute
|
||||||
volume_info = {
|
volume_info = {
|
||||||
'id': 'volume-id' + uuid.uuid4().hex,
|
'id': 'volume-id' + uuid.uuid4().hex,
|
||||||
@ -318,7 +413,7 @@ class FakeVolume(object):
|
|||||||
return volume
|
return volume
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_volumes(attrs={}, count=2):
|
def create_volumes(attrs=None, count=2):
|
||||||
"""Create multiple fake volumes.
|
"""Create multiple fake volumes.
|
||||||
|
|
||||||
:param Dictionary attrs:
|
:param Dictionary attrs:
|
||||||
@ -359,16 +454,16 @@ class FakeAvailabilityZone(object):
|
|||||||
"""Fake one or more volume availability zones (AZs)."""
|
"""Fake one or more volume availability zones (AZs)."""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_one_availability_zone(attrs={}, methods={}):
|
def create_one_availability_zone(attrs=None):
|
||||||
"""Create a fake AZ.
|
"""Create a fake AZ.
|
||||||
|
|
||||||
:param Dictionary attrs:
|
:param Dictionary attrs:
|
||||||
A dictionary with all attributes
|
A dictionary with all attributes
|
||||||
:param Dictionary methods:
|
|
||||||
A dictionary with all methods
|
|
||||||
:return:
|
:return:
|
||||||
A FakeResource object with zoneName, zoneState, etc.
|
A FakeResource object with zoneName, zoneState, etc.
|
||||||
"""
|
"""
|
||||||
|
attrs = attrs or {}
|
||||||
|
|
||||||
# Set default attributes.
|
# Set default attributes.
|
||||||
availability_zone = {
|
availability_zone = {
|
||||||
'zoneName': uuid.uuid4().hex,
|
'zoneName': uuid.uuid4().hex,
|
||||||
@ -380,18 +475,15 @@ class FakeAvailabilityZone(object):
|
|||||||
|
|
||||||
availability_zone = fakes.FakeResource(
|
availability_zone = fakes.FakeResource(
|
||||||
info=copy.deepcopy(availability_zone),
|
info=copy.deepcopy(availability_zone),
|
||||||
methods=methods,
|
|
||||||
loaded=True)
|
loaded=True)
|
||||||
return availability_zone
|
return availability_zone
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_availability_zones(attrs={}, methods={}, count=2):
|
def create_availability_zones(attrs=None, count=2):
|
||||||
"""Create multiple fake AZs.
|
"""Create multiple fake AZs.
|
||||||
|
|
||||||
:param Dictionary attrs:
|
:param Dictionary attrs:
|
||||||
A dictionary with all attributes
|
A dictionary with all attributes
|
||||||
:param Dictionary methods:
|
|
||||||
A dictionary with all methods
|
|
||||||
:param int count:
|
:param int count:
|
||||||
The number of AZs to fake
|
The number of AZs to fake
|
||||||
:return:
|
:return:
|
||||||
@ -400,8 +492,7 @@ class FakeAvailabilityZone(object):
|
|||||||
availability_zones = []
|
availability_zones = []
|
||||||
for i in range(0, count):
|
for i in range(0, count):
|
||||||
availability_zone = \
|
availability_zone = \
|
||||||
FakeAvailabilityZone.create_one_availability_zone(
|
FakeAvailabilityZone.create_one_availability_zone(attrs)
|
||||||
attrs, methods)
|
|
||||||
availability_zones.append(availability_zone)
|
availability_zones.append(availability_zone)
|
||||||
|
|
||||||
return availability_zones
|
return availability_zones
|
||||||
|
141
openstackclient/tests/volume/v2/test_service.py
Normal file
141
openstackclient/tests/volume/v2/test_service.py
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
#
|
||||||
|
# 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.volume.v2 import fakes as service_fakes
|
||||||
|
from openstackclient.volume.v2 import service
|
||||||
|
|
||||||
|
|
||||||
|
class TestService(service_fakes.TestService):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestService, self).setUp()
|
||||||
|
|
||||||
|
# Get a shortcut to the ServiceManager Mock
|
||||||
|
self.service_mock = self.app.client_manager.volume.services
|
||||||
|
self.service_mock.reset_mock()
|
||||||
|
|
||||||
|
|
||||||
|
class TestServiceList(TestService):
|
||||||
|
|
||||||
|
# The service to be listed
|
||||||
|
services = service_fakes.FakeService.create_one_service()
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestServiceList, self).setUp()
|
||||||
|
|
||||||
|
self.service_mock.list.return_value = [self.services]
|
||||||
|
|
||||||
|
# Get the command object to test
|
||||||
|
self.cmd = service.ListService(self.app, None)
|
||||||
|
|
||||||
|
def test_service_list(self):
|
||||||
|
arglist = [
|
||||||
|
'--host', self.services.host,
|
||||||
|
'--service', self.services.binary,
|
||||||
|
]
|
||||||
|
verifylist = [
|
||||||
|
('host', self.services.host),
|
||||||
|
('service', self.services.binary),
|
||||||
|
]
|
||||||
|
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)
|
||||||
|
|
||||||
|
expected_columns = [
|
||||||
|
'Binary',
|
||||||
|
'Host',
|
||||||
|
'Zone',
|
||||||
|
'Status',
|
||||||
|
'State',
|
||||||
|
'Updated At',
|
||||||
|
]
|
||||||
|
|
||||||
|
# confirming if all expected columns are present in the result.
|
||||||
|
self.assertEqual(expected_columns, columns)
|
||||||
|
|
||||||
|
datalist = ((
|
||||||
|
self.services.binary,
|
||||||
|
self.services.host,
|
||||||
|
self.services.zone,
|
||||||
|
self.services.status,
|
||||||
|
self.services.state,
|
||||||
|
self.services.updated_at,
|
||||||
|
), )
|
||||||
|
|
||||||
|
# confirming if all expected values are present in the result.
|
||||||
|
self.assertEqual(datalist, tuple(data))
|
||||||
|
|
||||||
|
# checking if proper call was made to list services
|
||||||
|
self.service_mock.list.assert_called_with(
|
||||||
|
self.services.host,
|
||||||
|
self.services.binary,
|
||||||
|
)
|
||||||
|
|
||||||
|
# checking if prohibited columns are present in output
|
||||||
|
self.assertNotIn("Disabled Reason", columns)
|
||||||
|
self.assertNotIn(self.services.disabled_reason,
|
||||||
|
tuple(data))
|
||||||
|
|
||||||
|
def test_service_list_with_long_option(self):
|
||||||
|
arglist = [
|
||||||
|
'--host', self.services.host,
|
||||||
|
'--service', self.services.binary,
|
||||||
|
'--long'
|
||||||
|
]
|
||||||
|
verifylist = [
|
||||||
|
('host', self.services.host),
|
||||||
|
('service', self.services.binary),
|
||||||
|
('long', True)
|
||||||
|
]
|
||||||
|
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)
|
||||||
|
|
||||||
|
expected_columns = [
|
||||||
|
'Binary',
|
||||||
|
'Host',
|
||||||
|
'Zone',
|
||||||
|
'Status',
|
||||||
|
'State',
|
||||||
|
'Updated At',
|
||||||
|
'Disabled Reason'
|
||||||
|
]
|
||||||
|
|
||||||
|
# confirming if all expected columns are present in the result.
|
||||||
|
self.assertEqual(expected_columns, columns)
|
||||||
|
|
||||||
|
datalist = ((
|
||||||
|
self.services.binary,
|
||||||
|
self.services.host,
|
||||||
|
self.services.zone,
|
||||||
|
self.services.status,
|
||||||
|
self.services.state,
|
||||||
|
self.services.updated_at,
|
||||||
|
self.services.disabled_reason,
|
||||||
|
), )
|
||||||
|
|
||||||
|
# confirming if all expected values are present in the result.
|
||||||
|
self.assertEqual(datalist, tuple(data))
|
||||||
|
|
||||||
|
self.service_mock.list.assert_called_with(
|
||||||
|
self.services.host,
|
||||||
|
self.services.binary,
|
||||||
|
)
|
70
openstackclient/volume/v1/service.py
Normal file
70
openstackclient/volume/v1/service.py
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
#
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
|
||||||
|
"""Service action implementations"""
|
||||||
|
|
||||||
|
from openstackclient.common import command
|
||||||
|
from openstackclient.common import utils
|
||||||
|
|
||||||
|
|
||||||
|
class ListService(command.Lister):
|
||||||
|
"""List service command"""
|
||||||
|
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(ListService, self).get_parser(prog_name)
|
||||||
|
parser.add_argument(
|
||||||
|
"--host",
|
||||||
|
metavar="<host>",
|
||||||
|
help="List services on specified host (name only)")
|
||||||
|
parser.add_argument(
|
||||||
|
"--service",
|
||||||
|
metavar="<service>",
|
||||||
|
help="List only specified service (name only)")
|
||||||
|
parser.add_argument(
|
||||||
|
"--long",
|
||||||
|
action="store_true",
|
||||||
|
default=False,
|
||||||
|
help="List additional fields in output"
|
||||||
|
)
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
service_client = self.app.client_manager.volume
|
||||||
|
|
||||||
|
if parsed_args.long:
|
||||||
|
columns = [
|
||||||
|
"Binary",
|
||||||
|
"Host",
|
||||||
|
"Zone",
|
||||||
|
"Status",
|
||||||
|
"State",
|
||||||
|
"Updated At",
|
||||||
|
"Disabled Reason"
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
columns = [
|
||||||
|
"Binary",
|
||||||
|
"Host",
|
||||||
|
"Zone",
|
||||||
|
"Status",
|
||||||
|
"State",
|
||||||
|
"Updated At"
|
||||||
|
]
|
||||||
|
|
||||||
|
data = service_client.services.list(parsed_args.host,
|
||||||
|
parsed_args.service)
|
||||||
|
return (columns,
|
||||||
|
(utils.get_item_properties(
|
||||||
|
s, columns,
|
||||||
|
) for s in data))
|
70
openstackclient/volume/v2/service.py
Normal file
70
openstackclient/volume/v2/service.py
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
#
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
|
||||||
|
"""Service action implementations"""
|
||||||
|
|
||||||
|
from openstackclient.common import command
|
||||||
|
from openstackclient.common import utils
|
||||||
|
|
||||||
|
|
||||||
|
class ListService(command.Lister):
|
||||||
|
"""List service command"""
|
||||||
|
|
||||||
|
def get_parser(self, prog_name):
|
||||||
|
parser = super(ListService, self).get_parser(prog_name)
|
||||||
|
parser.add_argument(
|
||||||
|
"--host",
|
||||||
|
metavar="<host>",
|
||||||
|
help="List services on specified host (name only)")
|
||||||
|
parser.add_argument(
|
||||||
|
"--service",
|
||||||
|
metavar="<service>",
|
||||||
|
help="List only specified service (name only)")
|
||||||
|
parser.add_argument(
|
||||||
|
"--long",
|
||||||
|
action="store_true",
|
||||||
|
default=False,
|
||||||
|
help="List additional fields in output"
|
||||||
|
)
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def take_action(self, parsed_args):
|
||||||
|
service_client = self.app.client_manager.volume
|
||||||
|
|
||||||
|
if parsed_args.long:
|
||||||
|
columns = [
|
||||||
|
"Binary",
|
||||||
|
"Host",
|
||||||
|
"Zone",
|
||||||
|
"Status",
|
||||||
|
"State",
|
||||||
|
"Updated At",
|
||||||
|
"Disabled Reason"
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
columns = [
|
||||||
|
"Binary",
|
||||||
|
"Host",
|
||||||
|
"Zone",
|
||||||
|
"Status",
|
||||||
|
"State",
|
||||||
|
"Updated At"
|
||||||
|
]
|
||||||
|
|
||||||
|
data = service_client.services.list(parsed_args.host,
|
||||||
|
parsed_args.service)
|
||||||
|
return (columns,
|
||||||
|
(utils.get_item_properties(
|
||||||
|
s, columns,
|
||||||
|
) for s in data))
|
9
releasenotes/notes/volume_service-5e352a71dfbc828d.yaml
Normal file
9
releasenotes/notes/volume_service-5e352a71dfbc828d.yaml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Adds support for volume service list.
|
||||||
|
|
||||||
|
An user can list available volume services by using
|
||||||
|
``volume service list``
|
||||||
|
|
||||||
|
[Bug 1550999 'https://bugs.launchpad.net/python-openstackclient/+bug/1550999'_]
|
@ -421,6 +421,8 @@ openstack.volume.v1 =
|
|||||||
volume_qos_show = openstackclient.volume.v1.qos_specs:ShowQos
|
volume_qos_show = openstackclient.volume.v1.qos_specs:ShowQos
|
||||||
volume_qos_unset = openstackclient.volume.v1.qos_specs:UnsetQos
|
volume_qos_unset = openstackclient.volume.v1.qos_specs:UnsetQos
|
||||||
|
|
||||||
|
volume_service_list = openstackclient.volume.v1.service:ListService
|
||||||
|
|
||||||
openstack.volume.v2 =
|
openstack.volume.v2 =
|
||||||
backup_create = openstackclient.volume.v2.backup:CreateBackup
|
backup_create = openstackclient.volume.v2.backup:CreateBackup
|
||||||
backup_delete = openstackclient.volume.v2.backup:DeleteBackup
|
backup_delete = openstackclient.volume.v2.backup:DeleteBackup
|
||||||
@ -458,6 +460,8 @@ openstack.volume.v2 =
|
|||||||
volume_qos_show = openstackclient.volume.v2.qos_specs:ShowQos
|
volume_qos_show = openstackclient.volume.v2.qos_specs:ShowQos
|
||||||
volume_qos_unset = openstackclient.volume.v2.qos_specs:UnsetQos
|
volume_qos_unset = openstackclient.volume.v2.qos_specs:UnsetQos
|
||||||
|
|
||||||
|
volume_service_list = openstackclient.volume.v2.service:ListService
|
||||||
|
|
||||||
[build_sphinx]
|
[build_sphinx]
|
||||||
source-dir = doc/source
|
source-dir = doc/source
|
||||||
build-dir = doc/build
|
build-dir = doc/build
|
||||||
|
Loading…
Reference in New Issue
Block a user