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.