From 07515cd1602841851d72dac30c7b12affee27bbe Mon Sep 17 00:00:00 2001 From: Rodolfo Alonso Hernandez <ralonsoh@redhat.com> Date: Fri, 21 Mar 2025 06:26:36 +0000 Subject: [PATCH] [Neutron] Add "qos-policy" parameter to router creation command This patch adds the parameter "qos-policy" to the router creation command. Closes-Bug: #2103774 Change-Id: I742b3273c5e9d3ec16e8018beddc8cdace8a57c6 --- openstackclient/network/v2/router.py | 12 ++++ .../functional/network/v2/test_router.py | 38 +++++++++++ .../tests/unit/network/v2/test_router.py | 63 +++++++++++++++++++ ...eate-with-qos-policy-b94967a35351cddd.yaml | 7 +++ 4 files changed, 120 insertions(+) create mode 100644 releasenotes/notes/router-create-with-qos-policy-b94967a35351cddd.yaml diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py index 0deae3600d..da35d367eb 100644 --- a/openstackclient/network/v2/router.py +++ b/openstackclient/network/v2/router.py @@ -581,6 +581,11 @@ class CreateRouter(command.ShowOne, common.NeutronCommandWithExtraArgs): help=argparse.SUPPRESS, ) _parser_add_bfd_ecmp_arguments(parser) + parser.add_argument( + '--qos-policy', + metavar='<qos-policy>', + help=_('Attach QoS policy to router gateway IPs'), + ) return parser @@ -603,6 +608,13 @@ class CreateRouter(command.ShowOne, common.NeutronCommandWithExtraArgs): ) raise exceptions.CommandError(msg) + if parsed_args.qos_policy and not parsed_args.external_gateways: + msg = _( + "You must specify '--external-gateway' in order " + "to define a QoS policy" + ) + raise exceptions.CommandError(msg) + if parsed_args.enable_ndp_proxy is not None: attrs['enable_ndp_proxy'] = parsed_args.enable_ndp_proxy diff --git a/openstackclient/tests/functional/network/v2/test_router.py b/openstackclient/tests/functional/network/v2/test_router.py index 89d0652353..90708324b1 100644 --- a/openstackclient/tests/functional/network/v2/test_router.py +++ b/openstackclient/tests/functional/network/v2/test_router.py @@ -44,6 +44,44 @@ class RouterTests(common.NetworkTagTests): del_output = self.openstack('router delete ' + name1 + ' ' + name2) self.assertOutput('', del_output) + def test_router_create_with_external_gateway(self): + network_name = uuid.uuid4().hex + subnet_name = uuid.uuid4().hex + qos_policy = uuid.uuid4().hex + router_name = uuid.uuid4().hex + + cmd_net = self.openstack( + f'network create --external {network_name}', parse_output=True + ) + self.addCleanup(self.openstack, f'network delete {network_name}') + network_id = cmd_net['id'] + + self.openstack( + f'subnet create {subnet_name} ' + f'--network {network_name} --subnet-range 10.0.0.0/24' + ) + + cmd_qos = self.openstack( + f'network qos policy create {qos_policy}', parse_output=True + ) + self.addCleanup( + self.openstack, f'network qos policy delete {qos_policy}' + ) + qos_id = cmd_qos['id'] + + self.openstack( + f'router create --external-gateway {network_name} ' + f'--qos-policy {qos_policy} {router_name}' + ) + self.addCleanup(self.openstack, f'router delete {router_name}') + + cmd_output = self.openstack( + f'router show {router_name}', parse_output=True + ) + gw_info = cmd_output['external_gateway_info'] + self.assertEqual(network_id, gw_info['network_id']) + self.assertEqual(qos_id, gw_info['qos_policy_id']) + def test_router_list(self): """Test create, list filter""" # Get project IDs diff --git a/openstackclient/tests/unit/network/v2/test_router.py b/openstackclient/tests/unit/network/v2/test_router.py index aa6de7d2ef..70993639c2 100644 --- a/openstackclient/tests/unit/network/v2/test_router.py +++ b/openstackclient/tests/unit/network/v2/test_router.py @@ -552,6 +552,69 @@ class TestCreateRouter(TestRouter): parsed_args, ) + def test_create_with_qos_policy(self): + _network = network_fakes.create_one_network() + self.network_client.find_network = mock.Mock(return_value=_network) + _qos_policy = ( + network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() + ) + self.network_client.find_qos_policy = mock.Mock( + return_value=_qos_policy + ) + arglist = [ + self.new_router.name, + '--external-gateway', + _network.id, + '--qos-policy', + _qos_policy.id, + ] + verifylist = [ + ('name', self.new_router.name), + ('enable', True), + ('distributed', False), + ('ha', False), + ('qos_policy', _qos_policy.id), + ('external_gateways', [_network.id]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + gw_info = {'network_id': _network.id, 'qos_policy_id': _qos_policy.id} + self.network_client.create_router.assert_called_once_with( + **{ + 'admin_state_up': True, + 'name': self.new_router.name, + **{'external_gateway_info': gw_info}, + } + ) + self.assertEqual(self.columns, columns) + self.assertCountEqual(self.data, data) + + def test_create_with_qos_policy_no_external_gateway(self): + _qos_policy = ( + network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() + ) + self.network_client.find_qos_policy = mock.Mock( + return_value=_qos_policy + ) + arglist = [ + self.new_router.name, + '--qos-policy', + _qos_policy.id, + ] + verifylist = [ + ('name', self.new_router.name), + ('enable', True), + ('distributed', False), + ('ha', False), + ('qos_policy', _qos_policy.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises( + exceptions.CommandError, + self.cmd.take_action, + parsed_args, + ) + class TestDeleteRouter(TestRouter): # The routers to delete. diff --git a/releasenotes/notes/router-create-with-qos-policy-b94967a35351cddd.yaml b/releasenotes/notes/router-create-with-qos-policy-b94967a35351cddd.yaml new file mode 100644 index 0000000000..67150ece9d --- /dev/null +++ b/releasenotes/notes/router-create-with-qos-policy-b94967a35351cddd.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + The router creation command now has the parameter ``--qos-policy``, that + allows to set a QoS policy for the provided external gateways (one or + many). It is mandatory to define an external gateway if the QoS policy is + set.