diff options
Diffstat (limited to 'openstackclient')
| -rw-r--r-- | openstackclient/compute/v2/floatingip.py | 23 | ||||
| -rw-r--r-- | openstackclient/network/v2/floating_ip.py | 83 | ||||
| -rw-r--r-- | openstackclient/network/v2/subnet.py | 215 | ||||
| -rw-r--r-- | openstackclient/tests/network/v2/test_floating_ip.py | 166 | ||||
| -rw-r--r-- | openstackclient/tests/network/v2/test_subnet.py | 74 |
5 files changed, 458 insertions, 103 deletions
diff --git a/openstackclient/compute/v2/floatingip.py b/openstackclient/compute/v2/floatingip.py index 6212989f..fac4d2e3 100644 --- a/openstackclient/compute/v2/floatingip.py +++ b/openstackclient/compute/v2/floatingip.py @@ -15,8 +15,6 @@ """Floating IP action implementations""" -import six - from openstackclient.common import command from openstackclient.common import utils @@ -47,27 +45,6 @@ class AddFloatingIP(command.Command): server.add_floating_ip(parsed_args.ip_address) -class CreateFloatingIP(command.ShowOne): - """Create new floating IP address""" - - def get_parser(self, prog_name): - parser = super(CreateFloatingIP, self).get_parser(prog_name) - parser.add_argument( - 'pool', - metavar='<pool>', - help='Pool to fetch IP address from (name or ID)', - ) - return parser - - def take_action(self, parsed_args): - compute_client = self.app.client_manager.compute - floating_ip = compute_client.floating_ips.create(parsed_args.pool) - - info = {} - info.update(floating_ip._info) - return zip(*sorted(six.iteritems(info))) - - class RemoveFloatingIP(command.Command): """Remove floating IP address from server""" diff --git a/openstackclient/network/v2/floating_ip.py b/openstackclient/network/v2/floating_ip.py index 16f2b574..b21d6e96 100644 --- a/openstackclient/network/v2/floating_ip.py +++ b/openstackclient/network/v2/floating_ip.py @@ -25,6 +25,89 @@ def _get_columns(item): return tuple(sorted(columns)) +def _get_attrs(client_manager, parsed_args): + attrs = {} + network_client = client_manager.network + + if parsed_args.network is not None: + network = network_client.find_network(parsed_args.network, + ignore_missing=False) + attrs['floating_network_id'] = network.id + + if parsed_args.subnet is not None: + subnet = network_client.find_subnet(parsed_args.subnet, + ignore_missing=False) + attrs['subnet_id'] = subnet.id + + if parsed_args.port is not None: + port = network_client.find_port(parsed_args.port, + ignore_missing=False) + attrs['port_id'] = port.id + + if parsed_args.floating_ip_address is not None: + attrs['floating_ip_address'] = parsed_args.floating_ip_address + + if parsed_args.fixed_ip_address is not None: + attrs['fixed_ip_address'] = parsed_args.fixed_ip_address + + return attrs + + +class CreateFloatingIP(common.NetworkAndComputeShowOne): + """Create floating IP""" + + def update_parser_common(self, parser): + # In Compute v2 network, floating IPs could be allocated from floating + # IP pools, which are actually external networks. So deprecate the + # parameter "pool", and use "network" instead. + parser.add_argument( + 'network', + metavar='<network>', + help='Network to allocate floating IP from (name or ID)', + ) + return parser + + def update_parser_network(self, parser): + parser.add_argument( + '--subnet', + metavar='<subnet>', + help="Subnet on which you want to create the floating IP " + "(name or ID)" + ) + parser.add_argument( + '--port', + metavar='<port>', + help="Port to be associated with the floating IP " + "(name or ID)" + ) + parser.add_argument( + '--floating-ip-address', + metavar='<floating-ip-address>', + dest='floating_ip_address', + help="Floating IP address" + ) + parser.add_argument( + '--fixed-ip-address', + metavar='<fixed-ip-address>', + dest='fixed_ip_address', + help="Fixed IP address mapped to the floating IP" + ) + return parser + + def take_action_network(self, client, parsed_args): + attrs = _get_attrs(self.app.client_manager, parsed_args) + obj = client.create_ip(**attrs) + columns = _get_columns(obj) + data = utils.get_item_properties(obj, columns) + return (columns, data) + + def take_action_compute(self, client, parsed_args): + obj = client.floating_ips.create(parsed_args.network) + columns = _get_columns(obj._info) + data = utils.get_dict_properties(obj._info, columns) + return (columns, data) + + class DeleteFloatingIP(common.NetworkAndComputeCommand): """Delete floating IP""" diff --git a/openstackclient/network/v2/subnet.py b/openstackclient/network/v2/subnet.py index 794c787f..da4f6536 100644 --- a/openstackclient/network/v2/subnet.py +++ b/openstackclient/network/v2/subnet.py @@ -17,6 +17,7 @@ import copy from json.encoder import JSONEncoder from openstackclient.common import command +from openstackclient.common import exceptions from openstackclient.common import parseractions from openstackclient.common import utils from openstackclient.identity import common as identity_common @@ -42,6 +43,39 @@ _formatters = { } +def _get_common_parse_arguments(parser): + parser.add_argument( + '--allocation-pool', + metavar='start=<ip-address>,end=<ip-address>', + dest='allocation_pools', + action=parseractions.MultiKeyValueAction, + required_keys=['start', 'end'], + help='Allocation pool IP addresses for this subnet ' + 'e.g.: start=192.168.199.2,end=192.168.199.254 ' + '(This option can be repeated)', + ) + parser.add_argument( + '--dns-nameserver', + metavar='<dns-nameserver>', + action='append', + dest='dns_nameservers', + help='DNS name server for this subnet ' + '(This option can be repeated)', + ) + parser.add_argument( + '--host-route', + metavar='destination=<subnet>,gateway=<ip-address>', + dest='host_routes', + action=parseractions.MultiKeyValueAction, + required_keys=['destination', 'gateway'], + help='Additional route for this subnet ' + 'e.g.: destination=10.10.0.0/16,gateway=192.168.71.254 ' + 'destination: destination subnet (in CIDR notation) ' + 'gateway: nexthop IP address ' + '(This option can be repeated)', + ) + + def _get_columns(item): columns = list(item.keys()) if 'tenant_id' in columns: @@ -70,57 +104,66 @@ def convert_entries_to_gateway(entries): return changed_entries -def _get_attrs(client_manager, parsed_args): +def _get_attrs(client_manager, parsed_args, is_create=True): attrs = {} - if parsed_args.name is not None: + if 'name' in parsed_args and parsed_args.name is not None: attrs['name'] = str(parsed_args.name) - if 'project' in parsed_args and parsed_args.project is not None: - identity_client = client_manager.identity - project_id = identity_common.find_project( - identity_client, - parsed_args.project, - parsed_args.project_domain, - ).id - attrs['tenant_id'] = project_id - - client = client_manager.network - attrs['network_id'] = client.find_network(parsed_args.network, - ignore_missing=False).id - - if parsed_args.subnet_pool is not None: - subnet_pool = client.find_subnet_pool(parsed_args.subnet_pool, - ignore_missing=False) - attrs['subnetpool_id'] = subnet_pool.id - - if parsed_args.use_default_subnet_pool: - attrs['use_default_subnetpool'] = True - if parsed_args.gateway.lower() != 'auto': - if parsed_args.gateway.lower() == 'none': - attrs['gateway_ip'] = None - else: - attrs['gateway_ip'] = parsed_args.gateway - if parsed_args.prefix_length is not None: - attrs['prefixlen'] = parsed_args.prefix_length - if parsed_args.subnet_range is not None: - attrs['cidr'] = parsed_args.subnet_range - if parsed_args.ip_version is not None: - attrs['ip_version'] = parsed_args.ip_version - if parsed_args.ipv6_ra_mode is not None: - attrs['ipv6_ra_mode'] = parsed_args.ipv6_ra_mode - if parsed_args.ipv6_address_mode is not None: - attrs['ipv6_address_mode'] = parsed_args.ipv6_address_mode - if parsed_args.allocation_pools is not None: + if is_create: + if 'project' in parsed_args and parsed_args.project is not None: + identity_client = client_manager.identity + project_id = identity_common.find_project( + identity_client, + parsed_args.project, + parsed_args.project_domain, + ).id + attrs['tenant_id'] = project_id + client = client_manager.network + attrs['network_id'] = client.find_network(parsed_args.network, + ignore_missing=False).id + if parsed_args.subnet_pool is not None: + subnet_pool = client.find_subnet_pool(parsed_args.subnet_pool, + ignore_missing=False) + attrs['subnetpool_id'] = subnet_pool.id + if parsed_args.use_default_subnet_pool: + attrs['use_default_subnetpool'] = True + if parsed_args.prefix_length is not None: + attrs['prefixlen'] = parsed_args.prefix_length + if parsed_args.subnet_range is not None: + attrs['cidr'] = parsed_args.subnet_range + if parsed_args.ip_version is not None: + attrs['ip_version'] = parsed_args.ip_version + if parsed_args.ipv6_ra_mode is not None: + attrs['ipv6_ra_mode'] = parsed_args.ipv6_ra_mode + if parsed_args.ipv6_address_mode is not None: + attrs['ipv6_address_mode'] = parsed_args.ipv6_address_mode + + if 'gateway' in parsed_args and parsed_args.gateway is not None: + gateway = parsed_args.gateway.lower() + + if not is_create and gateway == 'auto': + raise exceptions.CommandError("Auto option is not available" + " for Subnet Set. Valid options are" + " <ip-address> or none") + elif gateway != 'auto': + if gateway == 'none': + attrs['gateway_ip'] = None + else: + attrs['gateway_ip'] = gateway + if ('allocation_pools' in parsed_args and + parsed_args.allocation_pools is not None): attrs['allocation_pools'] = parsed_args.allocation_pools - if parsed_args.enable_dhcp is not None: - attrs['enable_dhcp'] = parsed_args.enable_dhcp - if parsed_args.dns_nameservers is not None: + if parsed_args.dhcp: + attrs['enable_dhcp'] = True + elif parsed_args.no_dhcp: + attrs['enable_dhcp'] = False + if ('dns_nameservers' in parsed_args and + parsed_args.dns_nameservers is not None): attrs['dns_nameservers'] = parsed_args.dns_nameservers - if parsed_args.host_routes is not None: + if 'host_routes' in parsed_args and parsed_args.host_routes is not None: # Change 'gateway' entry to 'nexthop' to match the API attrs['host_routes'] = convert_entries_to_nexthop( parsed_args.host_routes) - return attrs @@ -163,39 +206,19 @@ class CreateSubnet(command.ShowOne): '(required if --subnet-pool is not specified, ' 'optional otherwise)', ) - parser.add_argument( - '--allocation-pool', - metavar='start=<ip-address>,end=<ip-address>', - dest='allocation_pools', - action=parseractions.MultiKeyValueAction, - required_keys=['start', 'end'], - help='Allocation pool IP addresses for this subnet ' - 'e.g.: start=192.168.199.2,end=192.168.199.254 ' - '(This option can be repeated)', - ) dhcp_enable_group = parser.add_mutually_exclusive_group() dhcp_enable_group.add_argument( '--dhcp', - dest='enable_dhcp', action='store_true', default=True, help='Enable DHCP (default)', ) dhcp_enable_group.add_argument( '--no-dhcp', - dest='enable_dhcp', - action='store_false', + action='store_true', help='Disable DHCP', ) parser.add_argument( - '--dns-nameserver', - metavar='<dns-nameserver>', - action='append', - dest='dns_nameservers', - help='DNS name server for this subnet ' - '(This option can be repeated)', - ) - parser.add_argument( '--gateway', metavar='<gateway>', default='auto', @@ -208,18 +231,6 @@ class CreateSubnet(command.ShowOne): "(default is 'auto')", ) parser.add_argument( - '--host-route', - metavar='destination=<subnet>,gateway=<ip-address>', - dest='host_routes', - action=parseractions.MultiKeyValueAction, - required_keys=['destination', 'gateway'], - help='Additional route for this subnet ' - 'e.g.: destination=10.10.0.0/16,gateway=192.168.71.254 ' - 'destination: destination subnet (in CIDR notation) ' - 'gateway: nexthop IP address ' - '(This option can be repeated)', - ) - parser.add_argument( '--ip-version', type=int, default=4, @@ -246,7 +257,7 @@ class CreateSubnet(command.ShowOne): metavar='<network>', help='Network this subnet belongs to (name or ID)', ) - + _get_common_parse_arguments(parser) return parser def take_action(self, parsed_args): @@ -309,6 +320,56 @@ class ListSubnet(command.Lister): ) for s in data)) +class SetSubnet(command.Command): + """Set subnet properties""" + + def get_parser(self, prog_name): + parser = super(SetSubnet, self).get_parser(prog_name) + parser.add_argument( + 'subnet', + metavar="<subnet>", + help=("Subnet to modify (name or ID)") + ) + parser.add_argument( + '--name', + metavar='<name>', + help='Updated name of the subnet', + ) + dhcp_enable_group = parser.add_mutually_exclusive_group() + dhcp_enable_group.add_argument( + '--dhcp', + action='store_true', + default=None, + help='Enable DHCP', + ) + dhcp_enable_group.add_argument( + '--no-dhcp', + action='store_true', + help='Disable DHCP', + ) + parser.add_argument( + '--gateway', + metavar='<gateway>', + help="Specify a gateway for the subnet. The options are: " + " <ip-address>: Specific IP address to use as the gateway " + " 'none': This subnet will not use a gateway " + "e.g.: --gateway 192.168.9.1, --gateway none" + ) + _get_common_parse_arguments(parser) + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + obj = client.find_subnet(parsed_args.subnet, ignore_missing=False) + attrs = _get_attrs(self.app.client_manager, parsed_args, + is_create=False) + if not attrs: + msg = "Nothing specified to be set" + raise exceptions.CommandError(msg) + client.update_subnet(obj, **attrs) + return + + class ShowSubnet(command.ShowOne): """Show subnet details""" diff --git a/openstackclient/tests/network/v2/test_floating_ip.py b/openstackclient/tests/network/v2/test_floating_ip.py index c9da0fa0..3e261fb5 100644 --- a/openstackclient/tests/network/v2/test_floating_ip.py +++ b/openstackclient/tests/network/v2/test_floating_ip.py @@ -16,6 +16,7 @@ import mock from openstackclient.network.v2 import floating_ip from openstackclient.tests.compute.v2 import fakes as compute_fakes from openstackclient.tests.network.v2 import fakes as network_fakes +from openstackclient.tests import utils as tests_utils # Tests for Neutron network @@ -29,6 +30,115 @@ class TestFloatingIPNetwork(network_fakes.TestNetworkV2): self.network = self.app.client_manager.network +class TestCreateFloatingIPNetwork(TestFloatingIPNetwork): + + # Fake data for option tests. + floating_network = network_fakes.FakeNetwork.create_one_network() + subnet = network_fakes.FakeSubnet.create_one_subnet() + port = network_fakes.FakePort.create_one_port() + + # The floating ip to be deleted. + floating_ip = network_fakes.FakeFloatingIP.create_one_floating_ip( + attrs={ + 'floating_network_id': floating_network.id, + 'port_id': port.id, + } + ) + + columns = ( + 'dns_domain', + 'dns_name', + 'fixed_ip_address', + 'floating_ip_address', + 'floating_network_id', + 'id', + 'port_id', + 'project_id', + 'router_id', + 'status', + ) + + data = ( + floating_ip.dns_domain, + floating_ip.dns_name, + floating_ip.fixed_ip_address, + floating_ip.floating_ip_address, + floating_ip.floating_network_id, + floating_ip.id, + floating_ip.port_id, + floating_ip.project_id, + floating_ip.router_id, + floating_ip.status, + ) + + def setUp(self): + super(TestCreateFloatingIPNetwork, self).setUp() + + self.network.create_ip = mock.Mock(return_value=self.floating_ip) + + self.network.find_network = mock.Mock( + return_value=self.floating_network) + self.network.find_subnet = mock.Mock(return_value=self.subnet) + self.network.find_port = mock.Mock(return_value=self.port) + + # Get the command object to test + self.cmd = floating_ip.CreateFloatingIP(self.app, self.namespace) + + def test_create_no_options(self): + arglist = [] + verifylist = [] + + # Missing required args should bail here + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_create_default_options(self): + arglist = [ + self.floating_ip.floating_network_id, + ] + verifylist = [ + ('network', self.floating_ip.floating_network_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.create_ip.assert_called_once_with(**{ + 'floating_network_id': self.floating_ip.floating_network_id, + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_create_all_options(self): + arglist = [ + '--subnet', self.subnet.id, + '--port', self.floating_ip.port_id, + '--floating-ip-address', self.floating_ip.floating_ip_address, + '--fixed-ip-address', self.floating_ip.fixed_ip_address, + self.floating_ip.floating_network_id, + ] + verifylist = [ + ('subnet', self.subnet.id), + ('port', self.floating_ip.port_id), + ('floating_ip_address', self.floating_ip.floating_ip_address), + ('fixed_ip_address', self.floating_ip.fixed_ip_address), + ('network', self.floating_ip.floating_network_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.create_ip.assert_called_once_with(**{ + 'subnet_id': self.subnet.id, + 'port_id': self.floating_ip.port_id, + 'floating_ip_address': self.floating_ip.floating_ip_address, + 'fixed_ip_address': self.floating_ip.fixed_ip_address, + 'floating_network_id': self.floating_ip.floating_network_id, + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + class TestDeleteFloatingIPNetwork(TestFloatingIPNetwork): # The floating ip to be deleted. @@ -169,6 +279,62 @@ class TestFloatingIPCompute(compute_fakes.TestComputev2): self.compute = self.app.client_manager.compute +class TestCreateFloatingIPCompute(TestFloatingIPCompute): + + # The floating ip to be deleted. + floating_ip = compute_fakes.FakeFloatingIP.create_one_floating_ip() + + columns = ( + 'fixed_ip', + 'id', + 'instance_id', + 'ip', + 'pool', + ) + + data = ( + floating_ip.fixed_ip, + floating_ip.id, + floating_ip.instance_id, + floating_ip.ip, + floating_ip.pool, + ) + + def setUp(self): + super(TestCreateFloatingIPCompute, self).setUp() + + self.app.client_manager.network_endpoint_enabled = False + + self.compute.floating_ips.create.return_value = self.floating_ip + + # Get the command object to test + self.cmd = floating_ip.CreateFloatingIP(self.app, None) + + def test_create_no_options(self): + arglist = [] + verifylist = [] + + # Missing required args should bail here + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_create_default_options(self): + arglist = [ + self.floating_ip.pool, + ] + verifylist = [ + ('network', self.floating_ip.pool), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.compute.floating_ips.create.assert_called_once_with( + self.floating_ip.pool) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + class TestDeleteFloatingIPCompute(TestFloatingIPCompute): # The floating ip to be deleted. diff --git a/openstackclient/tests/network/v2/test_subnet.py b/openstackclient/tests/network/v2/test_subnet.py index de17c789..2535bbe6 100644 --- a/openstackclient/tests/network/v2/test_subnet.py +++ b/openstackclient/tests/network/v2/test_subnet.py @@ -14,6 +14,7 @@ import copy import mock +from openstackclient.common import exceptions from openstackclient.common import utils from openstackclient.network.v2 import subnet as subnet_v2 from openstackclient.tests import fakes @@ -203,9 +204,9 @@ class TestCreateSubnet(TestSubnet): self.network.find_network = mock.Mock(return_value=self._network) arglist = [ - self._subnet.name, "--subnet-range", self._subnet.cidr, "--network", self._subnet.network_id, + self._subnet.name, ] verifylist = [ ('name', self._subnet.name), @@ -266,7 +267,7 @@ class TestCreateSubnet(TestSubnet): ('ip_version', self._subnet_from_pool.ip_version), ('gateway', self._subnet_from_pool.gateway_ip), ('dns_nameservers', self._subnet_from_pool.dns_nameservers), - ('enable_dhcp', self._subnet_from_pool.enable_dhcp), + ('dhcp', self._subnet_from_pool.enable_dhcp), ('host_routes', subnet_v2.convert_entries_to_gateway( self._subnet_from_pool.host_routes)), ('subnet_pool', self._subnet_from_pool.subnetpool_id), @@ -332,7 +333,7 @@ class TestCreateSubnet(TestSubnet): ('ipv6_address_mode', self._subnet_ipv6.ipv6_address_mode), ('gateway', self._subnet_ipv6.gateway_ip), ('dns_nameservers', self._subnet_ipv6.dns_nameservers), - ('enable_dhcp', self._subnet_ipv6.enable_dhcp), + ('dhcp', self._subnet_ipv6.enable_dhcp), ('host_routes', subnet_v2.convert_entries_to_gateway( self._subnet_ipv6.host_routes)), ('allocation_pools', self._subnet_ipv6.allocation_pools), @@ -469,6 +470,73 @@ class TestListSubnet(TestSubnet): self.assertEqual(self.data_long, list(data)) +class TestSetSubnet(TestSubnet): + + _subnet = network_fakes.FakeSubnet.create_one_subnet() + + def setUp(self): + super(TestSetSubnet, self).setUp() + self.network.update_subnet = mock.Mock(return_value=None) + self.network.find_subnet = mock.Mock(return_value=self._subnet) + self.cmd = subnet_v2.SetSubnet(self.app, self.namespace) + + def test_set_this(self): + arglist = [ + "--name", "new_subnet", + "--dhcp", + "--gateway", self._subnet.gateway_ip, + self._subnet.name, + ] + verifylist = [ + ('name', "new_subnet"), + ('dhcp', True), + ('gateway', self._subnet.gateway_ip), + ('subnet', self._subnet.name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + attrs = { + 'enable_dhcp': True, + 'gateway_ip': self._subnet.gateway_ip, + 'name': "new_subnet", + } + self.network.update_subnet.assert_called_with(self._subnet, **attrs) + self.assertIsNone(result) + + def test_set_that(self): + arglist = [ + "--name", "new_subnet", + "--no-dhcp", + "--gateway", "none", + self._subnet.name, + ] + verifylist = [ + ('name', "new_subnet"), + ('no_dhcp', True), + ('gateway', "none"), + ('subnet', self._subnet.name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + attrs = { + 'enable_dhcp': False, + 'gateway_ip': None, + 'name': "new_subnet", + } + self.network.update_subnet.assert_called_with(self._subnet, **attrs) + self.assertIsNone(result) + + def test_set_nothing(self): + arglist = [self._subnet.name, ] + verifylist = [('subnet', self._subnet.name)] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises(exceptions.CommandError, self.cmd.take_action, + parsed_args) + + class TestShowSubnet(TestSubnet): # The subnets to be shown _subnet = network_fakes.FakeSubnet.create_one_subnet() |
