diff --git a/doc/source/cli/osc/v2/index.rst b/doc/source/cli/osc/v2/index.rst index c4c80c34c..a7320f5c2 100644 --- a/doc/source/cli/osc/v2/index.rst +++ b/doc/source/cli/osc/v2/index.rst @@ -95,3 +95,10 @@ share availability zones .. autoprogram-cliff:: openstack.share.v2 :command: share availability zone list + +============== +share services +============== + +.. autoprogram-cliff:: openstack.share.v2 + :command: share service * diff --git a/manilaclient/osc/v2/services.py b/manilaclient/osc/v2/services.py new file mode 100644 index 000000000..de7a6c6d8 --- /dev/null +++ b/manilaclient/osc/v2/services.py @@ -0,0 +1,134 @@ +# 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 osc_lib.command import command +from osc_lib import exceptions +from osc_lib import utils as osc_utils + +from manilaclient.common._i18n import _ + + +class SetShareService(command.Command): + """Enable/disable share service (Admin only).""" + _description = _("Enable/Disable share service (Admin only).") + + def get_parser(self, prog_name): + parser = super(SetShareService, self).get_parser(prog_name) + parser.add_argument( + 'host', + metavar='', + help=_("Host name as 'example_host@example_backend'.") + ) + parser.add_argument( + 'binary', + metavar='', + help=_("Service binary, could be 'manila-share', " + "'manila-scheduler' or 'manila-data'") + ) + enable_group = parser.add_mutually_exclusive_group() + enable_group.add_argument( + '--enable', + action='store_true', + help=_('Enable share service'), + ) + enable_group.add_argument( + '--disable', + action='store_true', + help=_('Disable share service'), + ) + return parser + + def take_action(self, parsed_args): + share_client = self.app.client_manager.share + + if parsed_args.enable: + try: + share_client.services.enable( + parsed_args.host, parsed_args.binary) + except Exception as e: + raise exceptions.CommandError(_( + "Failed to enable service: %s" % e)) + + if parsed_args.disable: + try: + share_client.services.disable( + parsed_args.host, parsed_args.binary) + except Exception as e: + raise exceptions.CommandError(_( + "Failed to disable service: %s" % e)) + + +class ListShareService(command.Lister): + """List share services (Admin only).""" + _description = _("List share services (Admin only).") + + def get_parser(self, prog_name): + parser = super(ListShareService, self).get_parser(prog_name) + parser.add_argument( + "--host", + metavar="", + default=None, + help=_("Filter services by name of the host.") + ) + parser.add_argument( + "--binary", + metavar="", + default=None, + help=_("Filter services by the name of the service.") + ) + parser.add_argument( + "--status", + metavar="", + default=None, + help=_("Filter results by status.") + ) + parser.add_argument( + "--state", + metavar="", + default=None, + choices=['up', 'down'], + help=_("Filter results by state.") + ) + parser.add_argument( + "--zone", + metavar="", + default=None, + help=_("Filter services by their availability zone.") + ) + return parser + + def take_action(self, parsed_args): + share_client = self.app.client_manager.share + + search_opts = { + 'host': parsed_args.host, + 'binary': parsed_args.binary, + 'status': parsed_args.status, + 'state': parsed_args.state, + 'zone': parsed_args.zone, + } + + services = share_client.services.list(search_opts=search_opts) + + columns = [ + 'ID', + 'Binary', + 'Host', + 'Zone', + 'Status', + 'State', + 'Updated At' + ] + + data = (osc_utils.get_dict_properties( + service._info, columns) for service in services) + + return (columns, data) diff --git a/manilaclient/tests/unit/osc/v2/fakes.py b/manilaclient/tests/unit/osc/v2/fakes.py index d9f30692d..6ca3e5ef2 100644 --- a/manilaclient/tests/unit/osc/v2/fakes.py +++ b/manilaclient/tests/unit/osc/v2/fakes.py @@ -43,6 +43,7 @@ class FakeShareClient(object): osc_fakes.FakeResource(None, {})) self.messages = mock.Mock() self.availability_zones = mock.Mock() + self.services = mock.Mock() class ManilaParseException(Exception): @@ -697,3 +698,53 @@ class FakeShareAvailabilityZones(object): availability_zones.append( FakeShareAvailabilityZones.create_one_availability_zone(attrs)) return availability_zones + + +class FakeShareService(object): + """Fake one or more share service""" + + @staticmethod + def create_fake_service(attrs=None): + """Create a fake share service + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object, with project_id, resource and so on + """ + + attrs = attrs or {} + + share_service_info = { + "binary": "manila-share", + "host": "fake_host@fake_backend", + "id": uuid.uuid4().hex, + "status": "enabled", + "state": "up", + "updated_at": 'time-' + uuid.uuid4().hex, + "zone": "fake_zone" + } + + share_service_info.update(attrs) + share_service = osc_fakes.FakeResource(info=copy.deepcopy( + share_service_info), + loaded=True) + return share_service + + @staticmethod + def create_fake_services(attrs=None, count=2): + """Create multiple fake services. + + :param Dictionary attrs: + A dictionary with all attributes + :param Integer count: + The number of share services to be faked + :return: + A list of FakeResource objects + """ + + services = [] + for n in range(count): + services.append( + FakeShareService.create_fake_service(attrs)) + return services diff --git a/manilaclient/tests/unit/osc/v2/test_services.py b/manilaclient/tests/unit/osc/v2/test_services.py new file mode 100644 index 000000000..fb4ffc6f4 --- /dev/null +++ b/manilaclient/tests/unit/osc/v2/test_services.py @@ -0,0 +1,211 @@ +# 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 osc_lib import exceptions +from osc_lib import utils as oscutils + +from manilaclient.osc import utils +from manilaclient.osc.v2 import services as osc_services +from manilaclient.tests.unit.osc.v2 import fakes as manila_fakes + + +class TestShareService(manila_fakes.TestShare): + + def setUp(self): + super(TestShareService, self).setUp() + + self.services_mock = self.app.client_manager.share.services + self.services_mock.reset_mock() + + +class TestShareServiceSet(TestShareService): + + def setUp(self): + super(TestShareServiceSet, self).setUp() + + self.share_service = ( + manila_fakes.FakeShareService.create_fake_service() + ) + self.cmd = osc_services.SetShareService(self.app, None) + + def test_share_service_set_enable(self): + arglist = [ + self.share_service.host, + self.share_service.binary, + '--enable' + ] + verifylist = [ + ('host', self.share_service.host), + ('binary', self.share_service.binary), + ('enable', True) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.services_mock.enable.assert_called_with( + self.share_service.host, + self.share_service.binary) + self.assertIsNone(result) + + def test_share_service_set_enable_exception(self): + arglist = [ + self.share_service.host, + self.share_service.binary, + '--enable' + ] + verifylist = [ + ('host', self.share_service.host), + ('binary', self.share_service.binary), + ('enable', True) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.services_mock.enable.side_effect = Exception() + self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args) + + def test_share_service_set_disable(self): + arglist = [ + self.share_service.host, + self.share_service.binary, + '--disable' + ] + verifylist = [ + ('host', self.share_service.host), + ('binary', self.share_service.binary), + ('disable', True) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.services_mock.disable.assert_called_with( + self.share_service.host, + self.share_service.binary) + self.assertIsNone(result) + + def test_share_service_set_disable_exception(self): + arglist = [ + self.share_service.host, + self.share_service.binary, + '--disable' + ] + verifylist = [ + ('host', self.share_service.host), + ('binary', self.share_service.binary), + ('disable', True) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.services_mock.disable.side_effect = Exception() + self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args) + + +class TestShareServiceList(TestShareService): + + columns = [ + 'id', + 'binary', + 'host', + 'zone', + 'status', + 'state', + 'updated_at' + ] + + column_headers = utils.format_column_headers(columns) + + def setUp(self): + super(TestShareServiceList, self).setUp() + + self.services_list = ( + manila_fakes.FakeShareService.create_fake_services() + ) + self.services_mock.list.return_value = self.services_list + self.values = (oscutils.get_dict_properties( + i._info, self.columns) for i in self.services_list) + + self.cmd = osc_services.ListShareService(self.app, None) + + def test_share_service_list(self): + arglist = [] + verifylist = [] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.services_mock.list.assert_called_with( + search_opts={ + 'host': None, + 'binary': None, + 'status': None, + 'state': None, + 'zone': None + }) + self.assertEqual(self.column_headers, columns) + self.assertEqual(list(self.values), list(data)) + + def test_share_service_list_host_status(self): + arglist = [ + '--host', self.services_list[0].host, + '--status', self.services_list[1].status + ] + verifylist = [ + ('host', self.services_list[0].host), + ('status', self.services_list[1].status) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.services_mock.list.assert_called_with( + search_opts={ + 'host': self.services_list[0].host, + 'binary': None, + 'status': self.services_list[1].status, + 'state': None, + 'zone': None + }) + self.assertEqual(self.column_headers, columns) + self.assertEqual(list(self.values), list(data)) + + def test_share_service_list_binary_state_zone(self): + arglist = [ + '--binary', self.services_list[0].binary, + '--state', self.services_list[1].state, + '--zone', self.services_list[1].zone + ] + verifylist = [ + ('binary', self.services_list[0].binary), + ('state', self.services_list[1].state), + ('zone', self.services_list[1].zone) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.services_mock.list.assert_called_with( + search_opts={ + 'host': None, + 'binary': self.services_list[0].binary, + 'status': None, + 'state': self.services_list[1].state, + 'zone': self.services_list[1].zone + }) + self.assertEqual(self.column_headers, columns) + self.assertEqual(list(self.values), list(data)) diff --git a/setup.cfg b/setup.cfg index 6cf16c152..4d8165f72 100644 --- a/setup.cfg +++ b/setup.cfg @@ -90,6 +90,8 @@ openstack.share.v2 = share_replica_promote = manilaclient.osc.v2.share_replicas:PromoteShareReplica share_replica_resync = manilaclient.osc.v2.share_replicas:ResyncShareReplica share_availability_zone_list = manilaclient.osc.v2.availability_zones:ShareAvailabilityZoneList + share_service_set = manilaclient.osc.v2.services:SetShareService + share_service_list = manilaclient.osc.v2.services:ListShareService [coverage:run] omit = manilaclient/tests/*