diff --git a/magnumclient/osc/v1/certificates.py b/magnumclient/osc/v1/certificates.py new file mode 100644 index 00000000..0d4d0642 --- /dev/null +++ b/magnumclient/osc/v1/certificates.py @@ -0,0 +1,110 @@ +# Copyright 2015 NEC Corporation. All rights reserved. +# +# 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. + +import os + +from magnumclient.i18n import _ + +from osc_lib.command import command + + +def _show_cert(certificate): + try: + print(certificate.pem) + except AttributeError: + return None + + +def _get_target_uuid(cs, args): + target = None + if args.cluster: + target = cs.clusters.get(args.cluster) + return target.uuid + + +class RotateCa(command.Command): + _description = _("Rotate the CA certificate for cluster to revoke access.") + + def get_parser(self, prog_name): + parser = super(RotateCa, self).get_parser(prog_name) + parser.add_argument('cluster', + metavar='', + help='ID or name of the cluster') + return parser + + def take_action(self, parsed_args): + mag_client = self.app.client_manager.container_infra + cluster = mag_client.clusters.get(parsed_args.cluster) + opts = { + 'cluster_uuid': cluster.uuid + } + + mag_client.certificates.rotate_ca(**opts) + + +class ShowCa(command.Command): + _description = _("Show details about the CA certificate for a cluster.") + + def get_parser(self, prog_name): + parser = super(ShowCa, self).get_parser(prog_name) + # NOTE: All arguments are positional and, if not provided + # with a default, required. + parser.add_argument('cluster', + metavar='', + help='ID or name of the cluster') + return parser + + def take_action(self, parsed_args): + self.log.debug("take_action(%s)", parsed_args) + + mag_client = self.app.client_manager.container_infra + cluster = mag_client.clusters.get(parsed_args.cluster) + cert = mag_client.certificates.get(cluster.uuid) + _show_cert(cert) + + +class SignCa(command.Command): + _description = _("Generate the CA certificate for a cluster.") + + def get_parser(self, prog_name): + parser = super(SignCa, self).get_parser(prog_name) + # NOTE: All arguments are positional and, if not provided + # with a default, required. + parser.add_argument('cluster', + metavar='', + help='ID or name of the cluster') + parser.add_argument('csr', + metavar='', + help='File path of csr file to send to Magnum' + ' to get signed.') + return parser + + def take_action(self, parsed_args): + self.log.debug("take_action(%s)", parsed_args) + + mag_client = self.app.client_manager.container_infra + + opts = { + 'cluster_uuid': _get_target_uuid(mag_client, parsed_args) + } + + if parsed_args.csr is None or not os.path.isfile(parsed_args.csr): + print('A CSR must be provided.') + return + + with open(parsed_args.csr, 'r') as f: + opts['csr'] = f.read() + + cert = mag_client.certificates.create(**opts) + _show_cert(cert) diff --git a/magnumclient/osc/v1/stats.py b/magnumclient/osc/v1/stats.py new file mode 100644 index 00000000..55165c17 --- /dev/null +++ b/magnumclient/osc/v1/stats.py @@ -0,0 +1,41 @@ +# Copyright 2015 NEC Corporation. All rights reserved. +# +# 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 magnumclient.common import cliutils as utils +from magnumclient.i18n import _ + +from osc_lib.command import command + + +class ListStats(command.Command): + _description = _("Show stats for the given project_id") + + def get_parser(self, prog_name): + parser = super(ListStats, self).get_parser(prog_name) + parser.add_argument('project_id', + metavar='', + help='Project ID') + return parser + + def take_action(self, parsed_args): + mag_client = self.app.client_manager.container_infra + opts = { + 'project_id': parsed_args.project_id + } + + stats = mag_client.stats.list(**opts) + try: + utils.print_dict(stats._info) + except AttributeError: + return None diff --git a/magnumclient/tests/osc/unit/v1/fakes.py b/magnumclient/tests/osc/unit/v1/fakes.py index 81ccb343..3f60bb6b 100644 --- a/magnumclient/tests/osc/unit/v1/fakes.py +++ b/magnumclient/tests/osc/unit/v1/fakes.py @@ -42,12 +42,22 @@ class FakeBaseModelManager(object): def update(self, id, patch): pass + def rotate_ca(self, **kwargs): + pass + + +class FakeStatsModelManager(object): + def list(self, **kwargs): + pass + class MagnumFakeContainerInfra(object): def __init__(self): self.cluster_templates = FakeBaseModelManager() self.clusters = FakeBaseModelManager() self.mservices = FakeBaseModelManager() + self.certificates = FakeBaseModelManager() + self.stats = FakeStatsModelManager() class MagnumFakeClientManager(osc_fakes.FakeClientManager): @@ -204,6 +214,7 @@ class FakeCluster(object): 'name': 'fake-cluster', 'master_flavor_id': None, 'flavor_id': 'm1.medium', + 'project_id': None, } # Overwrite default attributes. diff --git a/magnumclient/tests/osc/unit/v1/test_certificates.py b/magnumclient/tests/osc/unit/v1/test_certificates.py new file mode 100644 index 00000000..c5e2644e --- /dev/null +++ b/magnumclient/tests/osc/unit/v1/test_certificates.py @@ -0,0 +1,141 @@ +# Copyright 2015 NEC Corporation. All rights reserved. +# +# 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. + +import mock + +from magnumclient.osc.v1 import certificates as osc_certificates +from magnumclient.tests.osc.unit.v1 import fakes as magnum_fakes + + +class TestCertificate(magnum_fakes.TestMagnumClientOSCV1): + + def setUp(self): + super(TestCertificate, self).setUp() + + self.clusters_mock = self.app.client_manager.container_infra.clusters + + +class TestRotateCa(TestCertificate): + + def setUp(self): + super(TestRotateCa, 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 + + self.cmd = osc_certificates.RotateCa(self.app, None) + + def test_rotate_ca(self): + arglist = ['fake-cluster'] + verifylist = [ + ('cluster', 'fake-cluster') + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + self.clusters_mock.get.assert_called_once_with('fake-cluster') + + def test_rotate_ca_missing_args(self): + arglist = [] + verifylist = [] + self.assertRaises(magnum_fakes.MagnumParseException, + self.check_parser, self.cmd, arglist, verifylist) + + +class TestShowCa(TestCertificate): + + def setUp(self): + super(TestShowCa, 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 + + self.cmd = osc_certificates.ShowCa(self.app, None) + + def test_show_ca(self): + arglist = ['fake-cluster'] + verifylist = [ + ('cluster', 'fake-cluster') + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + self.clusters_mock.get.assert_called_once_with('fake-cluster') + + def test_show_ca_missing_args(self): + arglist = [] + verifylist = [] + self.assertRaises(magnum_fakes.MagnumParseException, + self.check_parser, self.cmd, arglist, verifylist) + + +class TestSignCa(TestCertificate): + + test_csr_path = 'magnumclient/tests/test_csr/test.csr' + + def setUp(self): + super(TestSignCa, 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 + + self.cmd = osc_certificates.SignCa(self.app, None) + + def test_sign_ca(self): + arglist = ['fake-cluster', self.test_csr_path] + verifylist = [ + ('cluster', 'fake-cluster'), + ('csr', self.test_csr_path) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + self.clusters_mock.get.assert_called_once_with('fake-cluster') + + def test_sign_ca_without_csr(self): + arglist = ['fake-cluster'] + verifylist = [ + ('cluster', 'fake-cluster') + ] + self.assertRaises(magnum_fakes.MagnumParseException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_sign_ca_without_cluster(self): + arglist = [self.test_csr_path] + verifylist = [ + ('csr', self.test_csr_path) + ] + self.assertRaises(magnum_fakes.MagnumParseException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_show_ca_missing_args(self): + arglist = [] + verifylist = [] + self.assertRaises(magnum_fakes.MagnumParseException, + self.check_parser, self.cmd, arglist, verifylist) diff --git a/magnumclient/tests/osc/unit/v1/test_stats.py b/magnumclient/tests/osc/unit/v1/test_stats.py new file mode 100644 index 00000000..40fda177 --- /dev/null +++ b/magnumclient/tests/osc/unit/v1/test_stats.py @@ -0,0 +1,72 @@ +# Copyright 2015 NEC Corporation. All rights reserved. +# +# 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. + +import mock + +from magnumclient.osc.v1 import stats as osc_stats +from magnumclient.tests.osc.unit.v1 import fakes as magnum_fakes + + +class TestStats(magnum_fakes.TestMagnumClientOSCV1): + + def setUp(self): + super(TestStats, self).setUp() + + self.clusters_mock = self.app.client_manager.container_infra.stats + + +class TestStatsList(TestStats): + + def setUp(self): + super(TestStatsList, self).setUp() + + attr = dict() + attr['name'] = 'fake-cluster-1' + attr['project_id'] = 'abc' + attr['node_count'] = 2 + attr['master_count'] = 1 + self._cluster = magnum_fakes.FakeCluster.create_one_cluster(attr) + + self.clusters_mock.list = mock.Mock() + self.clusters_mock.list.return_value = self._cluster + + self.cmd = osc_stats.ListStats(self.app, None) + + def test_stats_list(self): + arglist = ['abc'] + verifylist = [ + ('project_id', 'abc') + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + self.clusters_mock.list.assert_called_once_with(project_id='abc') + + def test_stats_list_wrong_projectid(self): + arglist = ['abcd'] + verifylist = [ + ('project_id', 'abcd') + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + self.clusters_mock.list.assert_called_once_with(project_id='abcd') + + def test_stats_list_missing_args(self): + arglist = [] + verifylist = [] + self.assertRaises(magnum_fakes.MagnumParseException, + self.check_parser, self.cmd, arglist, verifylist) diff --git a/magnumclient/tests/test_csr/test.csr b/magnumclient/tests/test_csr/test.csr new file mode 100644 index 00000000..863a5197 --- /dev/null +++ b/magnumclient/tests/test_csr/test.csr @@ -0,0 +1 @@ +'fake-csr' diff --git a/releasenotes/notes/partial_osc_implementation_for_certificate-4597c20b59c152e1.yaml b/releasenotes/notes/partial_osc_implementation_for_certificate-4597c20b59c152e1.yaml new file mode 100644 index 00000000..4ca9f714 --- /dev/null +++ b/releasenotes/notes/partial_osc_implementation_for_certificate-4597c20b59c152e1.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Implemented Openstack command for ca-show, ca-sign, + ca-rotate and stats-list. diff --git a/setup.cfg b/setup.cfg index b72baa4c..36ffd531 100644 --- a/setup.cfg +++ b/setup.cfg @@ -42,6 +42,10 @@ openstack.container_infra.v1 = coe_cluster_show = magnumclient.osc.v1.clusters:ShowCluster coe_cluster_update = magnumclient.osc.v1.clusters:UpdateCluster coe_cluster_config = magnumclient.osc.v1.clusters:ConfigCluster + coe_ca_rotate = magnumclient.osc.v1.certificates:RotateCa + coe_ca_show = magnumclient.osc.v1.certificates:ShowCa + coe_ca_sign = magnumclient.osc.v1.certificates:SignCa + coe_stats_list = magnumclient.osc.v1.stats:ListStats coe_service_list = magnumclient.osc.v1.mservices:ListService