diff --git a/karborclient/osc/v1/protectables.py b/karborclient/osc/v1/protectables.py new file mode 100644 index 0000000..41554ce --- /dev/null +++ b/karborclient/osc/v1/protectables.py @@ -0,0 +1,179 @@ +# 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. + +"""Data protection V1 protectables action implementations""" + +import six + +from osc_lib.command import command +from osc_lib import utils as osc_utils +from oslo_log import log as logging + +from karborclient.i18n import _ +from karborclient import utils + + +class ListProtectables(command.Lister): + _description = _("List protectable types.") + + log = logging.getLogger(__name__ + ".ListProtectables") + + def get_parser(self, prog_name): + parser = super(ListProtectables, self).get_parser(prog_name) + return parser + + def take_action(self, parsed_args): + self.log.debug("take_action(%s)", parsed_args) + data_protection_client = self.app.client_manager.data_protection + + data = data_protection_client.protectables.list() + + column_headers = ['Protectable type'] + + return (column_headers, + (osc_utils.get_item_properties( + s, column_headers + ) for s in data)) + + +class ShowProtectable(command.ShowOne): + _description = "Shows protectable type details" + + def get_parser(self, prog_name): + parser = super(ShowProtectable, self).get_parser(prog_name) + parser.add_argument( + 'protectable_type', + metavar="", + help=_('Protectable type.') + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.data_protection + protectable = osc_utils.find_resource(client.protectables, + parsed_args.protectable_type) + + protectable._info.pop("links", None) + return zip(*sorted(six.iteritems(protectable._info))) + + +class ListProtectableInstances(command.Lister): + _description = _("List protectable instances.") + + log = logging.getLogger(__name__ + ".ListProtectableInstances") + + def get_parser(self, prog_name): + parser = super(ListProtectableInstances, self).get_parser(prog_name) + parser.add_argument( + 'protectable_type', + metavar="", + help=_('Type of protectable.') + ) + parser.add_argument( + '--type', + metavar="", + default=None, + help=_('Filters results by protectable type. Default=None.') + ) + parser.add_argument( + '--marker', + metavar="", + default=None, + help=_('The last protectable instance ID of the previous page.') + ) + parser.add_argument( + '--limit', + metavar="", + default=None, + help=_('Maximum number of protectable instances to display.') + ) + parser.add_argument( + '--sort', + metavar="[:]", + default=None, + help=_("Sort output by selected keys and directions(asc or desc), " + "multiple keys and directions can be " + "specified separated by comma"), + ) + parser.add_argument( + '--parameters', + type=str, + nargs='*', + metavar="", + default=None, + help=_('List instances by parameters key and value pair. ' + 'Default=None.') + ) + return parser + + def take_action(self, parsed_args): + self.log.debug("take_action(%s)", parsed_args) + data_protection_client = self.app.client_manager.data_protection + + search_opts = { + 'type': parsed_args.type, + 'parameters': (utils.extract_instances_parameters(parsed_args) + if parsed_args.parameters else None), + } + + data = data_protection_client.protectables.list_instances( + parsed_args.protectable_type, search_opts=search_opts, + marker=parsed_args.marker, limit=parsed_args.limit, + sort=parsed_args.sort) + + column_headers = ['Id', 'Type', 'Name', 'Dependent resources', + 'Extra info'] + + return (column_headers, data) + + +class ShowProtectableInstance(command.ShowOne): + _description = "Shows protectable instance details" + + def get_parser(self, prog_name): + parser = super(ShowProtectableInstance, self).get_parser(prog_name) + parser.add_argument( + 'protectable_type', + metavar="", + help=_('Protectable type.') + ) + parser.add_argument( + 'protectable_id', + metavar="", + help=_('Protectable instance id.') + ) + parser.add_argument( + '--parameters', + type=str, + nargs='*', + metavar="", + default=None, + help=_('Show a instance by parameters key and value pair. ' + 'Default=None.') + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.data_protection + + search_opts = { + 'parameters': (utils.extract_instances_parameters(parsed_args) + if parsed_args.parameters else None), + } + + instance = client.protectables.get_instance( + parsed_args.protectable_type, + parsed_args.protectable_id, + search_opts=search_opts) + + instance._info.pop("links", None) + return zip(*sorted(six.iteritems(instance._info))) diff --git a/karborclient/tests/unit/osc/v1/test_protectables.py b/karborclient/tests/unit/osc/v1/test_protectables.py new file mode 100644 index 0000000..7d92d9a --- /dev/null +++ b/karborclient/tests/unit/osc/v1/test_protectables.py @@ -0,0 +1,161 @@ +# 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 karborclient.osc.v1 import protectables as osc_protectables +from karborclient.tests.unit.osc.v1 import fakes +from karborclient.v1 import protectables + + +PROTECTABLE_LIST_INFO = { + "protectable_type": [ + "OS::Keystone::Project", + "OS::Cinder::Volume", + "OS::Glance::Image", + "OS::Nova::Server" + ] +} + +PROTECTABLE_SHOW_INFO = { + "name": "OS::Nova::Server", + "dependent_types": [ + "OS::Cinder::Volume", + "OS::Glance::Image" + ] +} + +PROTECTABLE_INSTANCE_LIST_INFO = { + "id": "25336116-f38e-4c22-81ad-e9b7bd71ba51", + "type": "OS::Cinder::Volume", + "name": "System volume", + "extra_info": { + "availability_zone": "az1" + } +} + +PROTECTABLE_INSTANCE_SHOW_INFO = { + "id": "cb4ef2ff-10f5-46c9-bce4-cf7a49c65a01", + "type": "OS::Nova::Server", + "name": "My VM", + "dependent_resources": [{ + "id": "99777fdd-8a5b-45ab-ba2c-52420008103f", + "type": "OS::Glance::Image", + "name": "cirros-0.3.4-x86_64-uec"}] +} + + +class TestProtectables(fakes.TestDataProtection): + def setUp(self): + super(TestProtectables, self).setUp() + cm = self.app.client_manager + self.protectables_mock = cm.data_protection.protectables + self.protectables_mock.reset_mock() + + +class TestListProtectables(TestProtectables): + def setUp(self): + super(TestListProtectables, self).setUp() + self.protectables_mock.list.return_value = [protectables.Protectable( + None, PROTECTABLE_LIST_INFO)] + + # Command to test + self.cmd = osc_protectables.ListProtectables(self.app, None) + + def test_protectables_list(self): + arglist = [] + verifylist = [] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + # Check that columns are correct + expected_columns = ( + ['Protectable type']) + self.assertEqual(expected_columns, columns) + + # Check that data is correct + expected_data = [(['OS::Keystone::Project', + 'OS::Cinder::Volume', + 'OS::Glance::Image', + 'OS::Nova::Server'],)] + self.assertEqual(expected_data, list(data)) + + +class TestShowProtectable(TestProtectables): + def setUp(self): + super(TestShowProtectable, self).setUp() + self.protectables_mock.get.return_value = protectables.Protectable( + None, PROTECTABLE_SHOW_INFO) + # Command to test + self.cmd = osc_protectables.ShowProtectable(self.app, None) + + def test_protectable_show(self): + arglist = ['OS::Nova::Server'] + verifylist = [('protectable_type', 'OS::Nova::Server')] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + + # Check that correct arguments were passed + self.protectables_mock.get.assert_called_once_with( + 'OS::Nova::Server') + + +class TestListProtectableInstances(TestProtectables): + def setUp(self): + super(TestListProtectableInstances, self).setUp() + pm = self.protectables_mock + pm.list_instances.return_value = protectables.Instances( + None, PROTECTABLE_INSTANCE_LIST_INFO) + # Command to test + self.cmd = osc_protectables.ListProtectableInstances(self.app, None) + + def test_protectable_instances_list(self): + arglist = ['OS::Cinder::Volume'] + verifylist = [('protectable_type', 'OS::Cinder::Volume')] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + + # Check that correct arguments were passed + self.protectables_mock.list_instances.assert_called_once_with( + 'OS::Cinder::Volume', limit=None, marker=None, + search_opts={'type': None, 'parameters': None}, + sort=None) + + +class TestShowProtectableInstance(TestProtectables): + def setUp(self): + super(TestShowProtectableInstance, self).setUp() + pm = self.protectables_mock + pm.get_instance.return_value = protectables.Instances( + None, PROTECTABLE_INSTANCE_SHOW_INFO) + # Command to test + self.cmd = osc_protectables.ShowProtectableInstance(self.app, None) + + def test_protectable_instance_show(self): + arglist = ['OS::Nova::Server', 'cb4ef2ff-10f5-46c9-bce4-cf7a49c65a01'] + verifylist = [('protectable_type', 'OS::Nova::Server'), + ('protectable_id', + 'cb4ef2ff-10f5-46c9-bce4-cf7a49c65a01')] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + + # Check that correct arguments were passed + self.protectables_mock.get_instance.assert_called_once_with( + 'OS::Nova::Server', 'cb4ef2ff-10f5-46c9-bce4-cf7a49c65a01', + search_opts={'parameters': None}) diff --git a/setup.cfg b/setup.cfg index b19a128..cca94e1 100644 --- a/setup.cfg +++ b/setup.cfg @@ -44,6 +44,10 @@ openstack.data_protection.v1 = data_protection_restore_create = karborclient.osc.v1.restores:CreateRestore data_protection_provider_list = karborclient.osc.v1.providers:ListProviders data_protection_provider_show = karborclient.osc.v1.providers:ShowProvider + data_protection_protectable_list = karborclient.osc.v1.protectables.ListProtectables + data_protection_protectable_show = karborclient.osc.v1.protectables.ShowProtectable + data_protection_protectable_instance_list = karborclient.osc.v1.protectables.ListProtectableInstances + data_protection_protectable_instance_show = karborclient.osc.v1.protectables.ShowProtectableInstance [compile_catalog] directory = karborclient/locale