From 2fc590464817adfe2ed642f233118c3ffbcaacc6 Mon Sep 17 00:00:00 2001 From: yushangbin Date: Tue, 4 Jul 2017 00:51:13 +0800 Subject: [PATCH] Add OpenStackClient plugin for checkpoint This patch adds dataprotection support to the python-openstackclient through a plugin for checkpoint. Partially-Implements: blueprint karbor-support-python-openstackclient Change-Id: I5c4c927c63ac39538d5565a4167f5836e7127170 --- karborclient/osc/v1/checkpoints.py | 207 ++++++++++++++++++ .../tests/unit/osc/v1/test_checkpoints.py | 160 ++++++++++++++ setup.cfg | 4 + 3 files changed, 371 insertions(+) create mode 100644 karborclient/osc/v1/checkpoints.py create mode 100644 karborclient/tests/unit/osc/v1/test_checkpoints.py diff --git a/karborclient/osc/v1/checkpoints.py b/karborclient/osc/v1/checkpoints.py new file mode 100644 index 0000000..ab95d72 --- /dev/null +++ b/karborclient/osc/v1/checkpoints.py @@ -0,0 +1,207 @@ +# 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 checkpoint 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.common.apiclient import exceptions +from karborclient.i18n import _ +from karborclient import utils + + +class ListCheckpoints(command.Lister): + _description = _("List checkpoints.") + + log = logging.getLogger(__name__ + ".ListCheckpoints") + + def get_parser(self, prog_name): + parser = super(ListCheckpoints, self).get_parser(prog_name) + parser.add_argument( + 'provider_id', + metavar='', + help=_('ID of provider.'), + ) + parser.add_argument( + '--plan_id', + metavar='', + default=None, + help=_('Filters results by a plan ID. Default=None.'), + ) + parser.add_argument( + '--start_date', + type=str, + metavar='', + default=None, + help=_('Filters results by a start date("Y-m-d"). Default=None.'), + ) + parser.add_argument( + '--end_date', + type=str, + metavar='', + default=None, + help=_('Filters results by a end date("Y-m-d"). Default=None.'), + ) + parser.add_argument( + '--project_id', + metavar='', + default=None, + help=_('Filters results by a project ID. Default=None.'), + ) + parser.add_argument( + '--marker', + metavar='', + help=_('The last checkpoint ID of the previous page.'), + ) + parser.add_argument( + '--limit', + type=int, + metavar='', + help=_('Maximum number of checkpoints 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"), + ) + 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 = { + 'plan_id': parsed_args.plan_id, + 'start_date': parsed_args.start_date, + 'end_date': parsed_args.end_date, + 'project_id': parsed_args.project_id, + } + + data = data_protection_client.checkpoints.list( + provider_id=parsed_args.provider_id, search_opts=search_opts, + marker=parsed_args.marker, limit=parsed_args.limit, + sort=parsed_args.sort) + + column_headers = ['Id', 'Project id', 'Status', 'Protection plan', + 'Metadata', 'Created at'] + + return (column_headers, + (osc_utils.get_item_properties( + s, column_headers + ) for s in data)) + + +class ShowCheckpoint(command.ShowOne): + _description = "Shows checkpoint details" + + def get_parser(self, prog_name): + parser = super(ShowCheckpoint, self).get_parser(prog_name) + parser.add_argument( + 'provider_id', + metavar="", + help=_('Id of provider.') + ) + parser.add_argument( + 'checkpoint_id', + metavar="", + help=_('Id of checkpoint.') + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.data_protection + checkpoint = client.checkpoints.get(parsed_args.provider_id, + parsed_args.checkpoint_id) + checkpoint._info.pop("links", None) + return zip(*sorted(six.iteritems(checkpoint._info))) + + +class CreateCheckpoint(command.ShowOne): + _description = "Creates a checkpoint" + + def get_parser(self, prog_name): + parser = super(CreateCheckpoint, self).get_parser(prog_name) + parser.add_argument( + 'provider_id', + metavar='', + help=_('ID of provider.') + ) + parser.add_argument( + 'plan_id', + metavar='', + help=_('ID of plan.') + ) + parser.add_argument( + '--extra_info', + type=str, + nargs='*', + metavar='', + default=None, + help=_('The extra info of a checkpoint.') + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.data_protection + checkpoint_extra_info = None + if parsed_args.extra_info is not None: + checkpoint_extra_info = utils.extract_extra_info(parsed_args) + checkpoint = client.checkpoints.create(parsed_args.provider_id, + parsed_args.plan_id, + checkpoint_extra_info) + checkpoint._info.pop("links", None) + return zip(*sorted(six.iteritems(checkpoint._info))) + + +class DeleteCheckpoint(command.Command): + _description = "Delete checkpoint" + + log = logging.getLogger(__name__ + ".DeleteCheckpoint") + + def get_parser(self, prog_name): + parser = super(DeleteCheckpoint, self).get_parser(prog_name) + parser.add_argument( + 'provider_id', + metavar='', + help=_('Id of provider.') + ) + parser.add_argument( + 'checkpoint', + metavar='', + nargs="+", + help=_('Id of checkpoint.') + ) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.data_protection + failure_count = 0 + for checkpoint_id in parsed_args.checkpoint: + try: + client.checkpoints.delete(parsed_args.provider_id, + checkpoint_id) + except exceptions.NotFound: + failure_count += 1 + self.log.error( + "Failed to delete '{0}'; checkpoint not found". + format(checkpoint_id)) + if failure_count == len(parsed_args.checkpoint): + raise exceptions.CommandError( + "Unable to find and delete any of the " + "specified checkpoint.") diff --git a/karborclient/tests/unit/osc/v1/test_checkpoints.py b/karborclient/tests/unit/osc/v1/test_checkpoints.py new file mode 100644 index 0000000..26c5260 --- /dev/null +++ b/karborclient/tests/unit/osc/v1/test_checkpoints.py @@ -0,0 +1,160 @@ +# 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 checkpoints as osc_checkpoints +from karborclient.tests.unit.osc.v1 import fakes +from karborclient.v1 import checkpoints + + +CHECKPOINT_INFO = { + "id": "dcb20606-ad71-40a3-80e4-ef0fafdad0c3", + "project_id": "e486a2f49695423ca9c47e589b948108", + "status": "available", + "protection_plan": { + "id": "3523a271-68aa-42f5-b9ba-56e5200a2ebb", + "name": "My application", + "provider_id": "cf56bd3e-97a7-4078-b6d5-f36246333fd9", + "resources": [{ + "id": "99777fdd-8a5b-45ab-ba2c-52420008103f", + "type": "OS::Glance::Image", + "name": "cirros-0.3.4-x86_64-uec"}] + }, + "resource_graph": "[{'0x0': ['OS::Glance::Image', " + "'99777fdd-8a5b-45ab-ba2c-52420008103f', " + "'cirros-0.3.4-x86_64-uec']}, [[['0x0']]]]" +} + + +class TestCheckpoints(fakes.TestDataProtection): + def setUp(self): + super(TestCheckpoints, self).setUp() + cm = self.app.client_manager + self.checkpoints_mock = cm.data_protection.checkpoints + self.checkpoints_mock.reset_mock() + + +class TestListCheckpoints(TestCheckpoints): + def setUp(self): + super(TestListCheckpoints, self).setUp() + self.checkpoints_mock.list.return_value = [checkpoints.Checkpoint( + None, CHECKPOINT_INFO)] + + # Command to test + self.cmd = osc_checkpoints.ListCheckpoints(self.app, None) + + def test_checkpoints_list(self): + arglist = ['cf56bd3e-97a7-4078-b6d5-f36246333fd9'] + verifylist = [('provider_id', 'cf56bd3e-97a7-4078-b6d5-f36246333fd9')] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + # Check that columns are correct + expected_columns = ( + ['Id', 'Project id', 'Status', 'Protection plan', 'Metadata', + 'Created at']) + self.assertEqual(expected_columns, columns) + + # Check that data is correct + expected_data = [( + "dcb20606-ad71-40a3-80e4-ef0fafdad0c3", + "e486a2f49695423ca9c47e589b948108", + "available", + { + "id": "3523a271-68aa-42f5-b9ba-56e5200a2ebb", + "name": "My application", + "provider_id": "cf56bd3e-97a7-4078-b6d5-f36246333fd9", + "resources": [{ + "id": "99777fdd-8a5b-45ab-ba2c-52420008103f", + "type": "OS::Glance::Image", + "name": "cirros-0.3.4-x86_64-uec"}] + }, + '', + '')] + self.assertEqual(expected_data, list(data)) + + +class TestCreateCheckpoint(TestCheckpoints): + def setUp(self): + super(TestCreateCheckpoint, self).setUp() + self.checkpoints_mock.create.return_value = checkpoints.Checkpoint( + None, CHECKPOINT_INFO) + # Command to test + self.cmd = osc_checkpoints.CreateCheckpoint(self.app, None) + + def test_checkpoint_create(self): + arglist = ['cf56bd3e-97a7-4078-b6d5-f36246333fd9', + '3523a271-68aa-42f5-b9ba-56e5200a2ebb'] + verifylist = [('provider_id', 'cf56bd3e-97a7-4078-b6d5-f36246333fd9'), + ('plan_id', '3523a271-68aa-42f5-b9ba-56e5200a2ebb')] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + + # Check that correct arguments were passed + self.checkpoints_mock.create.assert_called_once_with( + 'cf56bd3e-97a7-4078-b6d5-f36246333fd9', + '3523a271-68aa-42f5-b9ba-56e5200a2ebb', + None) + + +class TestShowCheckpoint(TestCheckpoints): + def setUp(self): + super(TestShowCheckpoint, self).setUp() + self.checkpoints_mock.get.return_value = checkpoints.Checkpoint( + None, CHECKPOINT_INFO) + # Command to test + self.cmd = osc_checkpoints.ShowCheckpoint(self.app, None) + + def test_checkpoint_show(self): + arglist = ['cf56bd3e-97a7-4078-b6d5-f36246333fd9', + 'dcb20606-ad71-40a3-80e4-ef0fafdad0c3'] + verifylist = [('provider_id', 'cf56bd3e-97a7-4078-b6d5-f36246333fd9'), + ('checkpoint_id', + 'dcb20606-ad71-40a3-80e4-ef0fafdad0c3')] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + + # Check that correct arguments were passed + self.checkpoints_mock.get.assert_called_once_with( + 'cf56bd3e-97a7-4078-b6d5-f36246333fd9', + 'dcb20606-ad71-40a3-80e4-ef0fafdad0c3') + + +class TestDeleteCheckpoint(TestCheckpoints): + def setUp(self): + super(TestDeleteCheckpoint, self).setUp() + self.checkpoints_mock.get.return_value = checkpoints.Checkpoint( + None, CHECKPOINT_INFO) + # Command to test + self.cmd = osc_checkpoints.DeleteCheckpoint(self.app, None) + + def test_checkpoint_delete(self): + arglist = ['cf56bd3e-97a7-4078-b6d5-f36246333fd9', + 'dcb20606-ad71-40a3-80e4-ef0fafdad0c3'] + verifylist = [('provider_id', 'cf56bd3e-97a7-4078-b6d5-f36246333fd9'), + ('checkpoint', + ['dcb20606-ad71-40a3-80e4-ef0fafdad0c3'])] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + + # Check that correct arguments were passed + self.checkpoints_mock.delete.assert_called_once_with( + 'cf56bd3e-97a7-4078-b6d5-f36246333fd9', + 'dcb20606-ad71-40a3-80e4-ef0fafdad0c3') diff --git a/setup.cfg b/setup.cfg index 4aacfdf..7fb006c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -53,6 +53,10 @@ openstack.data_protection.v1 = data_protection_trigger_create = karborclient.osc.v1.triggers:CreateTrigger data_protection_trigger_update = karborclient.osc.v1.triggers:UpdateTrigger data_protection_trigger_delete = karborclient.osc.v1.triggers:DeleteTrigger + data_protection_checkpoint_list = karborclient.osc.v1.checkpoints:ListCheckpoints + data_protection_checkpoint_show = karborclient.osc.v1.checkpoints:ShowCheckpoint + data_protection_checkpoint_create = karborclient.osc.v1.checkpoints:CreateCheckpoint + data_protection_checkpoint_delete = karborclient.osc.v1.checkpoints:DeleteCheckpoint [compile_catalog] directory = karborclient/locale