diff --git a/magnumclient/osc/v1/clusters.py b/magnumclient/osc/v1/clusters.py index d60c608e..d658ec94 100644 --- a/magnumclient/osc/v1/clusters.py +++ b/magnumclient/osc/v1/clusters.py @@ -12,13 +12,36 @@ # License for the specific language governing permissions and limitations # under the License. - +from magnumclient.common import utils as magnum_utils from magnumclient.i18n import _ from osc_lib.command import command from osc_lib import utils +CLUSTER_ATTRIBUTES = [ + 'status', + 'cluster_template_id', + 'node_addresses', + 'uuid', + 'stack_id', + 'status_reason', + 'created_at', + 'updated_at', + 'coe_version', + 'faults', + 'keypair', + 'api_address', + 'master_addresses', + 'create_timeout', + 'node_count', + 'discovery_url', + 'master_count', + 'container_version', + 'name' +] + + class CreateCluster(command.Command): _description = _("Create a cluster") @@ -89,6 +112,28 @@ class CreateCluster(command.Command): % cluster.uuid) +class DeleteCluster(command.Command): + _description = _("Delete a cluster") + + def get_parser(self, prog_name): + parser = super(DeleteCluster, self).get_parser(prog_name) + parser.add_argument( + 'cluster', + nargs='+', + metavar='', + help='ID or name of the cluster(s) to delete.') + + return parser + + def take_action(self, parsed_args): + self.log.debug("take_action(%s)", parsed_args) + + mag_client = self.app.client_manager.container_infra + for cluster in parsed_args.cluster: + mag_client.clusters.delete(cluster) + print("Request to delete cluster %s has been accepted." % cluster) + + class ListCluster(command.Lister): _description = _("List clusters") @@ -125,3 +170,75 @@ class ListCluster(command.Lister): columns, (utils.get_item_properties(c, columns) for c in clusters) ) + + +class ShowCluster(command.ShowOne): + _description = _("Show a Cluster") + + def get_parser(self, prog_name): + parser = super(ShowCluster, self).get_parser(prog_name) + parser.add_argument( + 'cluster', + metavar='', + help=_('ID or name of the cluster to show.') + ) + return parser + + def take_action(self, parsed_args): + self.log.debug("take_action(%s)", parsed_args) + + columns = CLUSTER_ATTRIBUTES + + mag_client = self.app.client_manager.container_infra + cluster = mag_client.clusters.get(parsed_args.cluster) + + return (columns, utils.get_item_properties(cluster, columns)) + + +class UpdateCluster(command.Command): + _description = _("Update a Cluster") + + def get_parser(self, prog_name): + parser = super(UpdateCluster, self).get_parser(prog_name) + parser.add_argument( + 'cluster', + metavar='', + help=_('The name or UUID of cluster to update')) + + parser.add_argument( + 'op', + metavar='', + choices=['add', 'replace', 'remove'], + help=_("Operations: one of 'add', 'replace' or 'remove'")) + + parser.add_argument( + 'attributes', + metavar='', + nargs='+', + action='append', + default=[], + help=_( + "Attributes to add/replace or remove (only PATH is necessary " + "on remove)")) + + parser.add_argument( + '--rollback', + action='store_true', + dest='rollback', + default=False, + help=_('Rollback cluster on update failure.')) + + return parser + + def take_action(self, parsed_args): + self.log.debug("take_action(%s)", parsed_args) + + mag_client = self.app.client_manager.container_infra + + patch = magnum_utils.args_array_to_patch(parsed_args.op, + parsed_args.attributes[0]) + + mag_client.clusters.update(parsed_args.cluster, + patch) + print("Request to update cluster %s has been accepted." % + parsed_args.cluster) diff --git a/magnumclient/tests/osc/unit/v1/fakes.py b/magnumclient/tests/osc/unit/v1/fakes.py index 7ef977b7..cf8220eb 100644 --- a/magnumclient/tests/osc/unit/v1/fakes.py +++ b/magnumclient/tests/osc/unit/v1/fakes.py @@ -190,6 +190,7 @@ class FakeCluster(object): 'created_at': '2017-03-16T18:40:39+00:00', 'updated_at': '2017-03-16T18:40:45+00:00', 'coe_version': None, + 'faults': None, 'keypair': 'fakekey', 'api_address': None, 'master_addresses': [], diff --git a/magnumclient/tests/osc/unit/v1/test_clusters.py b/magnumclient/tests/osc/unit/v1/test_clusters.py index f7d8d64b..7355bea4 100644 --- a/magnumclient/tests/osc/unit/v1/test_clusters.py +++ b/magnumclient/tests/osc/unit/v1/test_clusters.py @@ -15,6 +15,7 @@ import copy import mock +from mock import call from magnumclient.osc.v1 import clusters as osc_clusters from magnumclient.tests.osc.unit.v1 import fakes as magnum_fakes @@ -60,26 +61,8 @@ class TestClusterCreate(TestCluster): # Get the command object to test self.cmd = osc_clusters.CreateCluster(self.app, None) - self.data = ( - self._cluster.status, - self._cluster.cluster_template_id, - self._cluster.node_addresses, - self._cluster.uuid, - self._cluster.stack_id, - self._cluster.status_reason, - self._cluster.created_at, - self._cluster.updated_at, - self._cluster.coe_version, - self._cluster.keypair, - self._cluster.api_address, - self._cluster.master_addresses, - self._cluster.create_timeout, - self._cluster.node_count, - self._cluster.discovery_url, - self._cluster.master_count, - self._cluster.container_version, - self._cluster.name - ) + self.data = tuple(map(lambda x: getattr(self._cluster, x), + osc_clusters.CLUSTER_ATTRIBUTES)) def test_cluster_create_required_args_pass(self): """Verifies required arguments.""" @@ -107,6 +90,52 @@ class TestClusterCreate(TestCluster): self.check_parser, self.cmd, arglist, verifylist) +class TestClusterDelete(TestCluster): + + def setUp(self): + super(TestClusterDelete, self).setUp() + + self.clusters_mock.delete = mock.Mock() + self.clusters_mock.delete.return_value = None + + # Get the command object to test + self.cmd = osc_clusters.DeleteCluster(self.app, None) + + def test_cluster_delete_one(self): + arglist = ['foo'] + verifylist = [('cluster', ['foo'])] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + self.clusters_mock.delete.assert_called_with('foo') + + def test_cluster_delete_multiple(self): + arglist = ['foo', 'bar'] + verifylist = [('cluster', ['foo', 'bar'])] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + self.clusters_mock.delete.assert_has_calls([call('foo'), call('bar')]) + + def test_cluster_delete_bad_uuid(self): + arglist = ['foo'] + verifylist = [('cluster', ['foo'])] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + returns = self.cmd.take_action(parsed_args) + + self.assertEqual(returns, None) + + def test_cluster_delete_no_uuid(self): + arglist = [] + verifylist = [('cluster', [])] + + self.assertRaises(magnum_fakes.MagnumParseException, + self.check_parser, self.cmd, arglist, verifylist) + + class TestClusterList(TestCluster): attr = dict() attr['name'] = 'fake-cluster-1' @@ -192,3 +221,82 @@ class TestClusterList(TestCluster): self.assertRaises(magnum_fakes.MagnumParseException, self.check_parser, self.cmd, arglist, verifylist) + + +class TestClusterUpdate(TestCluster): + + def setUp(self): + super(TestClusterUpdate, self).setUp() + + self.clusters_mock.update = mock.Mock() + self.clusters_mock.update.return_value = None + + # Get the command object to test + self.cmd = osc_clusters.UpdateCluster(self.app, None) + + def test_cluster_update_pass(self): + arglist = ['foo', 'remove', 'bar'] + verifylist = [ + ('cluster', 'foo'), + ('op', 'remove'), + ('attributes', [['bar']]), + ('rollback', False) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + self.clusters_mock.update.assert_called_with( + 'foo', + [{'op': 'remove', 'path': '/bar'}] + ) + + def test_cluster_update_bad_op(self): + arglist = ['foo', 'bar', 'snafu'] + verifylist = [ + ('cluster', 'foo'), + ('op', 'bar'), + ('attributes', ['snafu']), + ('rollback', False) + ] + + self.assertRaises(magnum_fakes.MagnumParseException, + self.check_parser, self.cmd, arglist, verifylist) + + +class TestClusterShow(TestCluster): + + def setUp(self): + super(TestClusterShow, self).setUp() + + attr = dict() + attr['name'] = 'fake-cluster-1' + self._cluster = magnum_fakes.FakeCluster.create_one_cluster(attr) + + self.clusters_mock.get = mock.Mock() + self.clusters_mock.get.return_value = self._cluster + + # Get the command object to test + self.cmd = osc_clusters.ShowCluster(self.app, None) + + self.data = tuple(map(lambda x: getattr(self._cluster, x), + osc_clusters.CLUSTER_ATTRIBUTES)) + + def test_cluster_show_pass(self): + arglist = ['fake-cluster'] + verifylist = [ + ('cluster', 'fake-cluster') + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + self.clusters_mock.get.assert_called_with('fake-cluster') + self.assertEqual(osc_clusters.CLUSTER_ATTRIBUTES, columns) + self.assertEqual(self.data, data) + + def test_cluster_show_no_cluster_fail(self): + arglist = [] + verifylist = [] + + self.assertRaises(magnum_fakes.MagnumParseException, + self.check_parser, self.cmd, arglist, verifylist) diff --git a/setup.cfg b/setup.cfg index 7c060138..5c5fa8b9 100644 --- a/setup.cfg +++ b/setup.cfg @@ -38,6 +38,9 @@ openstack.container_infra.v1 = coe_cluster_create = magnumclient.osc.v1.clusters:CreateCluster coe_cluster_list = magnumclient.osc.v1.clusters:ListCluster + coe_cluster_delete = magnumclient.osc.v1.clusters:DeleteCluster + coe_cluster_show = magnumclient.osc.v1.clusters:ShowCluster + coe_cluster_update = magnumclient.osc.v1.clusters:UpdateCluster [build_sphinx] source-dir = doc/source