diff --git a/doc/source/command-objects/ip-floating.rst b/doc/source/command-objects/ip-floating.rst index 05e4a13243..869b7af88f 100644 --- a/doc/source/command-objects/ip-floating.rst +++ b/doc/source/command-objects/ip-floating.rst @@ -33,11 +33,35 @@ Create new floating IP address .. code:: bash os ip floating create - + [--subnet ] + [--port ] + [--floating-ip-address ] + [--fixed-ip-address ] + -.. describe:: +.. option:: --subnet - Pool to fetch IP address from (name or ID) + Subnet on which you want to create the floating IP (name or ID) + (Network v2 only) + +.. option:: --port + + Port to be associated with the floating IP (name or ID) + (Network v2 only) + +.. option:: --floating-ip-address + + Floating IP address + (Network v2 only) + +.. option:: --fixed-ip-address + + Fixed IP address mapped to the floating IP + (Network v2 only) + +.. describe:: + + Network to allocate floating IP from (name or ID) ip floating delete ------------------ diff --git a/openstackclient/compute/v2/floatingip.py b/openstackclient/compute/v2/floatingip.py index 6212989f3b..fac4d2e361 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='', - 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 16f2b57472..b21d6e968d 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='', + help='Network to allocate floating IP from (name or ID)', + ) + return parser + + def update_parser_network(self, parser): + parser.add_argument( + '--subnet', + metavar='', + help="Subnet on which you want to create the floating IP " + "(name or ID)" + ) + parser.add_argument( + '--port', + metavar='', + help="Port to be associated with the floating IP " + "(name or ID)" + ) + parser.add_argument( + '--floating-ip-address', + metavar='', + dest='floating_ip_address', + help="Floating IP address" + ) + parser.add_argument( + '--fixed-ip-address', + metavar='', + 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/tests/network/v2/test_floating_ip.py b/openstackclient/tests/network/v2/test_floating_ip.py index c9da0fa07d..3e261fb5ef 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/releasenotes/notes/bug-1519502-d534db6c18adef20.yaml b/releasenotes/notes/bug-1519502-d534db6c18adef20.yaml new file mode 100644 index 0000000000..7f089b9884 --- /dev/null +++ b/releasenotes/notes/bug-1519502-d534db6c18adef20.yaml @@ -0,0 +1,4 @@ +--- +features: + - Command ``ip floating create`` is now available for neutron network. + [Bug `1519502 `_] diff --git a/setup.cfg b/setup.cfg index fcfd4e47bf..0ec5f4c166 100644 --- a/setup.cfg +++ b/setup.cfg @@ -91,7 +91,6 @@ openstack.compute.v2 = ip_fixed_remove = openstackclient.compute.v2.fixedip:RemoveFixedIP ip_floating_add = openstackclient.compute.v2.floatingip:AddFloatingIP - ip_floating_create = openstackclient.compute.v2.floatingip:CreateFloatingIP ip_floating_remove = openstackclient.compute.v2.floatingip:RemoveFloatingIP ip_floating_pool_list = openstackclient.compute.v2.floatingippool:ListFloatingIPPool @@ -322,6 +321,7 @@ openstack.image.v2 = image_set = openstackclient.image.v2.image:SetImage openstack.network.v2 = + ip_floating_create = openstackclient.network.v2.floating_ip:CreateFloatingIP ip_floating_delete = openstackclient.network.v2.floating_ip:DeleteFloatingIP ip_floating_list = openstackclient.network.v2.floating_ip:ListFloatingIP ip_floating_show = openstackclient.network.v2.floating_ip:ShowFloatingIP