From c422c2610ed219f84e4f9cfaa8ffdf5689999e7e Mon Sep 17 00:00:00 2001 From: Stephen Balukoff Date: Sat, 29 Aug 2015 02:52:41 -0700 Subject: [PATCH] LBaaS updates to reflect shared pools feature These updates allow the CLI user to fully take advantage of the shared pools functionality being added to Neutron LBaaS in preparation for L7 switching functionality. The Neutron LBaaS patch is here: https://review.openstack.org/#/c/218560/ Partially-Implements: blueprint lbaas-l7-rules Change-Id: Ib72d743ff0d6ca7f0fde034a8c73029308ca01a1 --- neutronclient/neutron/v2_0/lb/v2/listener.py | 55 +++++++++++------ neutronclient/neutron/v2_0/lb/v2/pool.py | 61 +++++++++++++++---- .../tests/unit/lb/v2/test_cli20_listener.py | 44 ++++++++++++- .../tests/unit/lb/v2/test_cli20_pool.py | 48 +++++++++++++-- ...shared-pools-support-6f79b565afad3e47.yaml | 8 +++ 5 files changed, 179 insertions(+), 37 deletions(-) create mode 100644 releasenotes/notes/add-shared-pools-support-6f79b565afad3e47.yaml diff --git a/neutronclient/neutron/v2_0/lb/v2/listener.py b/neutronclient/neutron/v2_0/lb/v2/listener.py index 4a4171908..b63e2c28f 100644 --- a/neutronclient/neutron/v2_0/lb/v2/listener.py +++ b/neutronclient/neutron/v2_0/lb/v2/listener.py @@ -16,18 +16,27 @@ # from neutronclient._i18n import _ +from neutronclient.common import exceptions from neutronclient.common import utils from neutronclient.neutron import v2_0 as neutronV20 def _get_loadbalancer_id(client, lb_id_or_name): return neutronV20.find_resourceid_by_name_or_id( - client, - 'loadbalancer', - lb_id_or_name, + client, 'loadbalancer', lb_id_or_name, cmd_resource='lbaas_loadbalancer') +def _get_pool(client, pool_id_or_name): + return neutronV20.find_resource_by_name_or_id( + client, 'pool', pool_id_or_name, cmd_resource='lbaas_pool') + + +def _get_pool_id(client, pool_id_or_name): + return neutronV20.find_resourceid_by_name_or_id( + client, 'pool', pool_id_or_name, cmd_resource='lbaas_pool') + + class ListListener(neutronV20.ListCommand): """LBaaS v2 List listeners that belong to a given tenant.""" @@ -64,7 +73,8 @@ class CreateListener(neutronV20.CreateCommand): help=_('Description of the listener.')) parser.add_argument( '--name', - help=_('The name of the listener.')) + help=_('The name of the listener. At least one of --default-pool ' + 'or --loadbalancer must be specified.')) parser.add_argument( '--default-tls-container-ref', dest='default_tls_container_ref', @@ -75,9 +85,11 @@ class CreateListener(neutronV20.CreateCommand): dest='sni_container_refs', nargs='+', help=_('List of TLS container references for SNI.')) + parser.add_argument( + '--default-pool', + help=_('Default pool for the listener.')) parser.add_argument( '--loadbalancer', - required=True, metavar='LOADBALANCER', help=_('ID or name of the load balancer.')) parser.add_argument( @@ -93,22 +105,29 @@ class CreateListener(neutronV20.CreateCommand): help=_('Protocol port for the listener.')) def args2body(self, parsed_args): + resource = { + 'protocol': parsed_args.protocol, + 'protocol_port': parsed_args.protocol_port, + 'admin_state_up': parsed_args.admin_state + } + if not parsed_args.loadbalancer and not parsed_args.default_pool: + message = _('Either --default-pool or --loadbalancer must be ' + 'specified.') + raise exceptions.CommandError(message) if parsed_args.loadbalancer: - parsed_args.loadbalancer = _get_loadbalancer_id( - self.get_client(), - parsed_args.loadbalancer) - body = {'loadbalancer_id': parsed_args.loadbalancer, - 'protocol': parsed_args.protocol, - 'protocol_port': parsed_args.protocol_port, - 'admin_state_up': parsed_args.admin_state} + loadbalancer_id = _get_loadbalancer_id( + self.get_client(), parsed_args.loadbalancer) + resource['loadbalancer_id'] = loadbalancer_id + if parsed_args.default_pool: + default_pool_id = _get_pool_id( + self.get_client(), parsed_args.default_pool) + resource['default_pool_id'] = default_pool_id - neutronV20.update_dict(parsed_args, body, + neutronV20.update_dict(parsed_args, resource, ['connection_limit', 'description', - 'loadbalancer_id', 'name', - 'default_tls_container_ref', - 'sni_container_refs', - 'tenant_id']) - return {self.resource: body} + 'name', 'default_tls_container_ref', + 'sni_container_refs', 'tenant_id']) + return {self.resource: resource} class UpdateListener(neutronV20.UpdateCommand): diff --git a/neutronclient/neutron/v2_0/lb/v2/pool.py b/neutronclient/neutron/v2_0/lb/v2/pool.py index 2745091e4..6cd0b8be5 100644 --- a/neutronclient/neutron/v2_0/lb/v2/pool.py +++ b/neutronclient/neutron/v2_0/lb/v2/pool.py @@ -1,6 +1,7 @@ # Copyright 2013 Mirantis Inc. # Copyright 2014 Blue Box Group, Inc. # Copyright 2015 Hewlett-Packard Development Company, L.P. +# Copyright 2015 Blue Box, an IBM Company # All Rights Reserved # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -17,10 +18,27 @@ # from neutronclient._i18n import _ +from neutronclient.common import exceptions from neutronclient.common import utils from neutronclient.neutron import v2_0 as neutronV20 +def _get_loadbalancer_id(client, lb_id_or_name): + return neutronV20.find_resourceid_by_name_or_id( + client, 'loadbalancer', lb_id_or_name, + cmd_resource='lbaas_loadbalancer') + + +def _get_listener(client, listener_id_or_name): + return neutronV20.find_resource_by_name_or_id( + client, 'listener', listener_id_or_name) + + +def _get_listener_id(client, listener_id_or_name): + return neutronV20.find_resourceid_by_name_or_id( + client, 'listener', listener_id_or_name) + + class ListPool(neutronV20.ListCommand): """LBaaS v2 List pools that belong to a given tenant.""" @@ -70,16 +88,22 @@ class CreatePool(neutronV20.CreateCommand): 'cookie name')) parser.add_argument( '--name', help=_('The name of the pool.')) + parser.add_argument( + '--listener', + help=_('Listener whose default-pool should be set to this pool. ' + 'At least one of --listener or --loadbalancer must be ' + 'specified.')) + parser.add_argument( + '--loadbalancer', + help=_('Loadbalancer with which this pool should be associated. ' + 'At least one of --listener or --loadbalancer must be ' + 'specified.')) parser.add_argument( '--lb-algorithm', required=True, choices=['ROUND_ROBIN', 'LEAST_CONNECTIONS', 'SOURCE_IP'], help=_('The algorithm used to distribute load between the members ' 'of the pool.')) - parser.add_argument( - '--listener', - required=True, - help=_('The listener to associate with the pool')) parser.add_argument( '--protocol', required=True, @@ -88,16 +112,29 @@ class CreatePool(neutronV20.CreateCommand): help=_('Protocol for balancing.')) def args2body(self, parsed_args): - _listener_id = neutronV20.find_resourceid_by_name_or_id( - self.get_client(), 'listener', parsed_args.listener) - body = {'admin_state_up': parsed_args.admin_state, - 'protocol': parsed_args.protocol, - 'lb_algorithm': parsed_args.lb_algorithm, - 'listener_id': _listener_id} - neutronV20.update_dict(parsed_args, body, + resource = { + 'admin_state_up': parsed_args.admin_state, + 'protocol': parsed_args.protocol, + 'lb_algorithm': parsed_args.lb_algorithm + } + if not parsed_args.listener and not parsed_args.loadbalancer: + message = _('At least one of --listener or --loadbalancer must be ' + 'specified.') + raise exceptions.CommandError(message) + if parsed_args.listener: + listener_id = _get_listener_id( + self.get_client(), + parsed_args.listener) + resource['listener_id'] = listener_id + if parsed_args.loadbalancer: + loadbalancer_id = _get_loadbalancer_id( + self.get_client(), + parsed_args.loadbalancer) + resource['loadbalancer_id'] = loadbalancer_id + neutronV20.update_dict(parsed_args, resource, ['description', 'name', 'session_persistence', 'tenant_id']) - return {self.resource: body} + return {self.resource: resource} class UpdatePool(neutronV20.UpdateCommand): diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_listener.py b/neutronclient/tests/unit/lb/v2/test_cli20_listener.py index 77d62e0b0..581e511ea 100644 --- a/neutronclient/tests/unit/lb/v2/test_cli20_listener.py +++ b/neutronclient/tests/unit/lb/v2/test_cli20_listener.py @@ -16,14 +16,15 @@ import sys +from neutronclient.common import exceptions from neutronclient.neutron.v2_0.lb.v2 import listener from neutronclient.tests.unit import test_cli20 class CLITestV20LbListenerJSON(test_cli20.CLITestV20Base): - def test_create_listener_with_mandatory_params(self): - # lbaas-listener-create with mandatory params only. + def test_create_listener_with_loadbalancer(self): + # lbaas-listener-create with --loadbalancer resource = 'listener' cmd_resource = 'lbaas_listener' cmd = listener.CreateListener(test_cli20.MyApp(sys.stdout), None) @@ -40,6 +41,41 @@ class CLITestV20LbListenerJSON(test_cli20.CLITestV20Base): position_names, position_values, cmd_resource=cmd_resource) + def test_create_listener_with_default_pool(self): + # lbaas-listener-create with --default-pool and no --loadbalancer. + resource = 'listener' + cmd_resource = 'lbaas_listener' + cmd = listener.CreateListener(test_cli20.MyApp(sys.stdout), None) + my_id = 'my-id' + default_pool_id = 'default-pool' + protocol = 'TCP' + protocol_port = '80' + args = ['--protocol', protocol, '--protocol-port', protocol_port, + '--default-pool', default_pool_id] + position_names = ['protocol', 'protocol_port', 'default_pool_id'] + position_values = [protocol, protocol_port, default_pool_id, + True] + self._test_create_resource(resource, cmd, '', my_id, args, + position_names, position_values, + cmd_resource=cmd_resource) + + def test_create_listener_with_no_loadbalancer_or_default_pool(self): + # lbaas-listener-create without --default-pool or --loadbalancer. + resource = 'listener' + cmd_resource = 'lbaas_listener' + cmd = listener.CreateListener(test_cli20.MyApp(sys.stdout), None) + my_id = 'my-id' + protocol = 'TCP' + protocol_port = '80' + args = ['--protocol', protocol, '--protocol-port', protocol_port] + position_names = ['protocol', 'protocol_port'] + position_values = [protocol, protocol_port, True] + self._test_create_resource(resource, cmd, '', my_id, args, + position_names, position_values, + cmd_resource=cmd_resource, + no_api_call=True, + expected_exception=exceptions.CommandError) + def test_create_listener_with_all_params(self): # lbaas-listener-create with all params set. resource = 'listener' @@ -47,6 +83,7 @@ class CLITestV20LbListenerJSON(test_cli20.CLITestV20Base): cmd = listener.CreateListener(test_cli20.MyApp(sys.stdout), None) my_id = 'my-id' loadbalancer = 'loadbalancer' + default_pool_id = 'default-pool' protocol = 'TCP' protocol_port = '80' connection_limit = 10 @@ -54,14 +91,17 @@ class CLITestV20LbListenerJSON(test_cli20.CLITestV20Base): args = ['--admin-state-down', '--protocol', protocol, '--protocol-port', protocol_port, '--loadbalancer', loadbalancer, + '--default-pool', default_pool_id, '--default-tls-container-ref', def_tls_cont_ref, '--sni-container-refs', '1111', '2222', '3333', '--connection-limit', '10'] position_names = ['admin_state_up', 'protocol', 'protocol_port', 'loadbalancer_id', + 'default_pool_id', 'default_tls_container_ref', 'sni_container_refs', 'connection_limit'] position_values = [False, protocol, protocol_port, loadbalancer, + default_pool_id, def_tls_cont_ref, ['1111', '2222', '3333'], connection_limit] self._test_create_resource(resource, cmd, '', my_id, args, diff --git a/neutronclient/tests/unit/lb/v2/test_cli20_pool.py b/neutronclient/tests/unit/lb/v2/test_cli20_pool.py index efaa06565..2e5787b0e 100644 --- a/neutronclient/tests/unit/lb/v2/test_cli20_pool.py +++ b/neutronclient/tests/unit/lb/v2/test_cli20_pool.py @@ -16,14 +16,15 @@ import sys +from neutronclient.common import exceptions from neutronclient.neutron.v2_0.lb.v2 import pool from neutronclient.tests.unit import test_cli20 class CLITestV20LbPoolJSON(test_cli20.CLITestV20Base): - def test_create_pool_with_mandatory_params(self): - # lbaas-pool-create with mandatory params only. + def test_create_pool_with_listener(self): + # lbaas-pool-create with listener resource = 'pool' cmd_resource = 'lbaas_pool' cmd = pool.CreatePool(test_cli20.MyApp(sys.stdout), None) @@ -40,6 +41,41 @@ class CLITestV20LbPoolJSON(test_cli20.CLITestV20Base): position_names, position_values, cmd_resource=cmd_resource) + def test_create_pool_with_loadbalancer_no_listener(self): + """lbaas-pool-create with loadbalancer, no listener.""" + resource = 'pool' + cmd_resource = 'lbaas_pool' + cmd = pool.CreatePool(test_cli20.MyApp(sys.stdout), None) + my_id = 'my-id' + lb_algorithm = 'ROUND_ROBIN' + loadbalancer = 'loadbalancer' + protocol = 'TCP' + args = ['--lb-algorithm', lb_algorithm, '--protocol', protocol, + '--loadbalancer', loadbalancer] + position_names = ['admin_state_up', 'lb_algorithm', 'protocol', + 'loadbalancer_id'] + position_values = [True, lb_algorithm, protocol, loadbalancer] + self._test_create_resource(resource, cmd, '', my_id, args, + position_names, position_values, + cmd_resource=cmd_resource) + + def test_create_pool_with_no_listener_or_loadbalancer(self): + """lbaas-pool-create with no listener or loadbalancer.""" + resource = 'pool' + cmd_resource = 'lbaas_pool' + cmd = pool.CreatePool(test_cli20.MyApp(sys.stdout), None) + my_id = 'my-id' + lb_algorithm = 'ROUND_ROBIN' + protocol = 'TCP' + args = ['--lb-algorithm', lb_algorithm, '--protocol', protocol] + position_names = ['admin_state_up', 'lb_algorithm', 'protocol'] + position_values = [True, lb_algorithm, protocol] + self._test_create_resource(resource, cmd, '', my_id, args, + position_names, position_values, + cmd_resource=cmd_resource, + no_api_call=True, + expected_exception=exceptions.CommandError) + def test_create_pool_with_all_params(self): # lbaas-pool-create with all params set. resource = 'pool' @@ -48,6 +84,7 @@ class CLITestV20LbPoolJSON(test_cli20.CLITestV20Base): my_id = 'my-id' lb_algorithm = 'ROUND_ROBIN' listener = 'listener' + loadbalancer = 'loadbalancer' protocol = 'TCP' description = 'description' session_persistence_str = 'type=APP_COOKIE,cookie_name=1234' @@ -57,12 +94,13 @@ class CLITestV20LbPoolJSON(test_cli20.CLITestV20Base): args = ['--lb-algorithm', lb_algorithm, '--protocol', protocol, '--description', description, '--session-persistence', session_persistence_str, '--admin-state-down', '--name', name, - '--listener', listener] + '--listener', listener, '--loadbalancer', loadbalancer] position_names = ['lb_algorithm', 'protocol', 'description', 'session_persistence', 'admin_state_up', 'name', - 'listener_id'] + 'listener_id', 'loadbalancer_id'] position_values = [lb_algorithm, protocol, description, - session_persistence, False, name, listener] + session_persistence, False, name, listener, + loadbalancer] self._test_create_resource(resource, cmd, '', my_id, args, position_names, position_values, cmd_resource=cmd_resource) diff --git a/releasenotes/notes/add-shared-pools-support-6f79b565afad3e47.yaml b/releasenotes/notes/add-shared-pools-support-6f79b565afad3e47.yaml new file mode 100644 index 000000000..1be2e0ed1 --- /dev/null +++ b/releasenotes/notes/add-shared-pools-support-6f79b565afad3e47.yaml @@ -0,0 +1,8 @@ +--- +features: + - | + CLI support for Neutron-LBaaS v2 shared pools added. + + * Pools can be created independently from listeners. + * Listeners can share the same default_pool. + * Makes Layer 7 switching support much more useful.