From b0954297834f0a6d8f2687b8036c4b129606f3e9 Mon Sep 17 00:00:00 2001 From: Maari Tamm Date: Sat, 30 Oct 2021 21:05:32 +0000 Subject: [PATCH] [OSC] Implement share network subnet commands This patch adds the following openstack share commands: share network subnet create share network subnet delete share network subnet show Partially-implements: bp openstack-client-support Change-Id: I4e066a982fa40b079f969e7c3ca3276d3a842074 --- doc/source/cli/osc/v2/index.rst | 15 +- manilaclient/osc/v2/share_network_subnets.py | 162 +++++++++++++ manilaclient/tests/unit/osc/v2/fakes.py | 59 +++++ .../unit/osc/v2/test_share_network_subnets.py | 213 ++++++++++++++++++ setup.cfg | 3 + 5 files changed, 451 insertions(+), 1 deletion(-) create mode 100644 manilaclient/osc/v2/share_network_subnets.py create mode 100644 manilaclient/tests/unit/osc/v2/test_share_network_subnets.py diff --git a/doc/source/cli/osc/v2/index.rst b/doc/source/cli/osc/v2/index.rst index 278a03fc1..db80e5654 100644 --- a/doc/source/cli/osc/v2/index.rst +++ b/doc/source/cli/osc/v2/index.rst @@ -66,7 +66,20 @@ share networks ============== .. autoprogram-cliff:: openstack.share.v2 - :command: share network * + :command: share network [!s]* + +.. autoprogram-cliff:: openstack.share.v2 + :command: share network show + +.. autoprogram-cliff:: openstack.share.v2 + :command: share network set + +===================== +share network subnets +===================== + +.. autoprogram-cliff:: openstack.share.v2 + :command: share network subnet * =========== share types diff --git a/manilaclient/osc/v2/share_network_subnets.py b/manilaclient/osc/v2/share_network_subnets.py new file mode 100644 index 000000000..4c372c26b --- /dev/null +++ b/manilaclient/osc/v2/share_network_subnets.py @@ -0,0 +1,162 @@ +# 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 logging +from operator import xor + +from osc_lib.command import command +from osc_lib import exceptions +from osc_lib import utils as oscutils + +from manilaclient.common._i18n import _ + + +LOG = logging.getLogger(__name__) + + +class CreateShareNetworkSubnet(command.ShowOne): + """Create a share network subnet.""" + _description = _("Create a share network subnet") + + def get_parser(self, prog_name): + parser = super(CreateShareNetworkSubnet, self).get_parser(prog_name) + parser.add_argument( + "share_network", + metavar="", + help=_("Share network name or ID.") + ) + parser.add_argument( + "--neutron-net-id", + metavar="", + default=None, + help=_("Neutron network ID. Used to set up network for share " + "servers (optional). Should be defined together with " + "'--neutron-subnet-id'.") + ) + parser.add_argument( + "--neutron-subnet-id", + metavar="", + default=None, + help=_("Neutron subnet ID. Used to set up network for share " + "servers (optional). Should be defined together with " + "'--neutron-net-id' to which this subnet belongs to. ") + ) + parser.add_argument( + "--availability-zone", + metavar="", + default=None, + help=_("Optional availability zone that the subnet is available " + "within (Default=None). If None, the subnet will be " + "considered as being available across all availability " + "zones.") + ) + return parser + + def take_action(self, parsed_args): + share_client = self.app.client_manager.share + + if xor(bool(parsed_args.neutron_net_id), + bool(parsed_args.neutron_subnet_id)): + raise exceptions.CommandError( + "Both neutron_net_id and neutron_subnet_id should be " + "specified. Alternatively, neither of them should be " + "specified.") + + share_network_id = oscutils.find_resource( + share_client.share_networks, + parsed_args.share_network).id + + share_network_subnet = share_client.share_network_subnets.create( + neutron_net_id=parsed_args.neutron_net_id, + neutron_subnet_id=parsed_args.neutron_subnet_id, + availability_zone=parsed_args.availability_zone, + share_network_id=share_network_id + ) + subnet_data = share_network_subnet._info + + return self.dict2columns(subnet_data) + + +class DeleteShareNetworkSubnet(command.Command): + """Create a share network subnet.""" + _description = _("Create a share network subnet") + + def get_parser(self, prog_name): + parser = super(DeleteShareNetworkSubnet, self).get_parser(prog_name) + parser.add_argument( + "share_network", + metavar="", + help=_("Share network name or ID.") + ) + parser.add_argument( + "share_network_subnet", + metavar="", + nargs="+", + help=_("ID(s) of share network subnet(s) to be deleted.") + ) + return parser + + def take_action(self, parsed_args): + share_client = self.app.client_manager.share + result = 0 + + share_network_id = oscutils.find_resource( + share_client.share_networks, + parsed_args.share_network).id + + for subnet in parsed_args.share_network_subnet: + try: + share_client.share_network_subnets.delete( + share_network_id, + subnet) + + except Exception as e: + result += 1 + LOG.error(f"Failed to delete share network subnet with " + f"ID {subnet}: {e}") + if result > 0: + total = len(parsed_args.share_network_subnet) + raise exceptions.CommandError( + f"{result} of {total} share network subnets failed to be " + f"deleted.") + + +class ShowShareNetworkSubnet(command.ShowOne): + """Show share network subnet.""" + _description = _("Show share network subnet") + + def get_parser(self, prog_name): + parser = super(ShowShareNetworkSubnet, self).get_parser(prog_name) + parser.add_argument( + "share_network", + metavar="", + help=_("Share network name or ID.") + ) + parser.add_argument( + "share_network_subnet", + metavar="", + help=_("ID of share network subnet to show.") + ) + return parser + + def take_action(self, parsed_args): + share_client = self.app.client_manager.share + + share_network_id = oscutils.find_resource( + share_client.share_networks, + parsed_args.share_network).id + + share_network_subnet = share_client.share_network_subnets.get( + share_network_id, + parsed_args.share_network_subnet) + + return self.dict2columns(share_network_subnet._info) diff --git a/manilaclient/tests/unit/osc/v2/fakes.py b/manilaclient/tests/unit/osc/v2/fakes.py index 401e155c7..7a845c700 100644 --- a/manilaclient/tests/unit/osc/v2/fakes.py +++ b/manilaclient/tests/unit/osc/v2/fakes.py @@ -41,6 +41,7 @@ class FakeShareClient(object): self.share_replicas = mock.Mock() self.share_replica_export_locations = mock.Mock() self.share_networks = mock.Mock() + self.share_network_subnets = mock.Mock() self.security_services = mock.Mock() self.shares.resource_class = osc_fakes.FakeResource(None, {}) self.share_instance_export_locations = mock.Mock() @@ -1095,6 +1096,64 @@ class FakeShareNetwork(object): return share_networks +class FakeShareNetworkSubnet(object): + """Fake a share network subnet""" + + @staticmethod + def create_one_share_subnet(attrs=None): + """Create a fake share network subnet + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object, with project_id, resource and so on + """ + + attrs = attrs or {} + + share_network_subnet = { + "availability_zone": None, + "cidr": "10.0.0.0/24", + "created_at": datetime.datetime.now().isoformat(), + "gateway": "10.0.0.1", + 'id': str(uuid.uuid4()), + "ip_version": 4, + "mtu": "1500", + "network_type": "vlan", + "neutron_net_id": str(uuid.uuid4()), + "neutron_subnet_id": str(uuid.uuid4()), + "segmentation_id": 1010, + "share_network_id": str(uuid.uuid4()), + "share_network_name": str(uuid.uuid4()), + "updated_at": datetime.datetime.now().isoformat(), + } + + share_network_subnet.update(attrs) + share_network_subnet = osc_fakes.FakeResource(info=copy.deepcopy( + share_network_subnet), + loaded=True) + return share_network_subnet + + @staticmethod + def create_share_network_subnets(attrs=None, count=2): + """Create multiple fake share network subnets. + + :param Dictionary attrs: + A dictionary with all attributes + :param Integer count: + The number of share network subnets to be faked + :return: + A list of FakeResource objects + """ + + share_network_subnets = [] + for n in range(count): + share_network_subnets.append( + FakeShareNetworkSubnet.create_one_share_subnet(attrs)) + + return share_network_subnets + + class FakeShareGroup(object): """Fake a share group""" diff --git a/manilaclient/tests/unit/osc/v2/test_share_network_subnets.py b/manilaclient/tests/unit/osc/v2/test_share_network_subnets.py new file mode 100644 index 000000000..0f354d639 --- /dev/null +++ b/manilaclient/tests/unit/osc/v2/test_share_network_subnets.py @@ -0,0 +1,213 @@ +# 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 uuid + +from osc_lib import exceptions + +from manilaclient.osc.v2 import share_network_subnets as osc_share_subnets +from manilaclient.tests.unit.osc import osc_utils +from manilaclient.tests.unit.osc.v2 import fakes as manila_fakes + + +class TestShareNetworkSubnet(manila_fakes.TestShare): + + def setUp(self): + super(TestShareNetworkSubnet, self).setUp() + + self.share_networks_mock = self.app.client_manager.share.share_networks + self.share_networks_mock.reset_mock() + + self.share_subnets_mock = ( + self.app.client_manager.share.share_network_subnets) + self.share_subnets_mock.reset_mock() + + +class TestShareNetworkSubnetCreate(TestShareNetworkSubnet): + + def setUp(self): + super(TestShareNetworkSubnetCreate, self).setUp() + + self.share_network = ( + manila_fakes.FakeShareNetwork.create_one_share_network()) + self.share_networks_mock.get.return_value = self.share_network + + self.share_network_subnet = ( + manila_fakes.FakeShareNetworkSubnet.create_one_share_subnet()) + self.share_subnets_mock.create.return_value = self.share_network_subnet + + self.cmd = osc_share_subnets.CreateShareNetworkSubnet( + self.app, None) + + self.data = self.share_network_subnet._info.values() + self.columns = self.share_network_subnet._info.keys() + + def test_share_network_subnet_create_missing_args(self): + arglist = [] + verifylist = [] + + self.assertRaises(osc_utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_share_network_subnet_create(self): + fake_neutron_net_id = str(uuid.uuid4()) + fake_neutron_subnet_id = str(uuid.uuid4()) + + arglist = [ + self.share_network.id, + '--neutron-net-id', fake_neutron_net_id, + '--neutron-subnet-id', fake_neutron_subnet_id, + '--availability-zone', 'nova', + ] + verifylist = [ + ('share_network', self.share_network.id), + ('neutron_net_id', fake_neutron_net_id), + ('neutron_subnet_id', fake_neutron_subnet_id), + ('availability_zone', 'nova'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.share_subnets_mock.create.assert_called_once_with( + neutron_net_id=fake_neutron_net_id, + neutron_subnet_id=fake_neutron_subnet_id, + availability_zone='nova', + share_network_id=self.share_network.id + ) + + self.assertCountEqual(self.columns, columns) + self.assertCountEqual(self.data, data) + + def test_share_network_subnet_create_arg_group_exception(self): + fake_neutron_net_id = str(uuid.uuid4()) + + arglist = [ + self.share_network.id, + '--neutron-net-id', fake_neutron_net_id + ] + verifylist = [ + ('share_network', self.share_network.id), + ('neutron_net_id', fake_neutron_net_id) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.assertRaises(exceptions.CommandError, + self.cmd.take_action, + parsed_args) + + +class TestShareNetworkSubnetDelete(TestShareNetworkSubnet): + + def setUp(self): + super(TestShareNetworkSubnetDelete, self).setUp() + + self.share_network = ( + manila_fakes.FakeShareNetwork.create_one_share_network()) + self.share_networks_mock.get.return_value = self.share_network + + self.share_network_subnets = ( + manila_fakes.FakeShareNetworkSubnet.create_share_network_subnets()) + + self.cmd = osc_share_subnets.DeleteShareNetworkSubnet( + self.app, None) + + def test_share_network_subnet_delete_missing_args(self): + arglist = [] + verifylist = [] + + self.assertRaises(osc_utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_share_network_subnets_delete(self): + arglist = [ + self.share_network.id, + self.share_network_subnets[0].id, + self.share_network_subnets[1].id, + ] + verifylist = [ + ('share_network', self.share_network.id), + ('share_network_subnet', [self.share_network_subnets[0].id, + self.share_network_subnets[1].id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.assertEqual(self.share_subnets_mock.delete.call_count, + len(self.share_network_subnets)) + self.assertIsNone(result) + + def test_share_network_subnet_delete_exception(self): + arglist = [ + self.share_network.id, + self.share_network_subnets[0].id, + ] + verifylist = [ + ('share_network', self.share_network.id), + ('share_network_subnet', [self.share_network_subnets[0].id]), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.share_subnets_mock.delete.side_effect = exceptions.CommandError() + self.assertRaises(exceptions.CommandError, + self.cmd.take_action, + parsed_args) + + +class TestShareNetworkSubnetShow(TestShareNetworkSubnet): + + def setUp(self): + super(TestShareNetworkSubnetShow, self).setUp() + + self.share_network = ( + manila_fakes.FakeShareNetwork.create_one_share_network()) + self.share_networks_mock.get.return_value = self.share_network + + self.share_network_subnet = ( + manila_fakes.FakeShareNetworkSubnet.create_one_share_subnet()) + self.share_subnets_mock.get.return_value = self.share_network_subnet + + self.cmd = osc_share_subnets.ShowShareNetworkSubnet( + self.app, None) + + self.data = self.share_network_subnet._info.values() + self.columns = self.share_network_subnet._info.keys() + + def test_share_network_subnet_show_missing_args(self): + arglist = [] + verifylist = [] + + self.assertRaises(osc_utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_share_network_subnet_show(self): + arglist = [ + self.share_network.id, + self.share_network_subnet.id, + ] + verifylist = [ + ('share_network', self.share_network.id), + ('share_network_subnet', self.share_network_subnet.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.share_subnets_mock.get.assert_called_once_with( + self.share_network.id, + self.share_network_subnet.id + ) + + self.assertCountEqual(self.columns, columns) + self.assertCountEqual(self.data, data) diff --git a/setup.cfg b/setup.cfg index 62d0e5f12..1c9a1f3af 100644 --- a/setup.cfg +++ b/setup.cfg @@ -113,6 +113,9 @@ openstack.share.v2 = share_network_delete = manilaclient.osc.v2.share_networks:DeleteShareNetwork share_network_set = manilaclient.osc.v2.share_networks:SetShareNetwork share_network_unset = manilaclient.osc.v2.share_networks:UnsetShareNetwork + share_network_subnet_create = manilaclient.osc.v2.share_network_subnets:CreateShareNetworkSubnet + share_network_subnet_delete = manilaclient.osc.v2.share_network_subnets:DeleteShareNetworkSubnet + share_network_subnet_show = manilaclient.osc.v2.share_network_subnets:ShowShareNetworkSubnet share_group_create = manilaclient.osc.v2.share_groups:CreateShareGroup share_group_delete = manilaclient.osc.v2.share_groups:DeleteShareGroup share_group_list = manilaclient.osc.v2.share_groups:ListShareGroup