From ea96686ffb83d434d7db02b4884e78c7e2982865 Mon Sep 17 00:00:00 2001 From: Pierre Riteau Date: Thu, 30 May 2019 11:29:04 +0100 Subject: [PATCH] Add support for floating IP reservation Change-Id: I7a73a4e7d2026eba2d0a3d6dfe9db42879ff6416 --- blazarclient/shell.py | 7 +- .../v1/shell_commands/test_floatingips.py | 142 ++++++++++++++++++ blazarclient/v1/client.py | 7 + blazarclient/v1/floatingips.py | 47 ++++++ blazarclient/v1/shell_commands/floatingips.py | 76 ++++++++++ blazarclient/v1/shell_commands/leases.py | 7 + 6 files changed, 285 insertions(+), 1 deletion(-) create mode 100644 blazarclient/tests/v1/shell_commands/test_floatingips.py create mode 100644 blazarclient/v1/floatingips.py create mode 100644 blazarclient/v1/shell_commands/floatingips.py diff --git a/blazarclient/shell.py b/blazarclient/shell.py index f26c6b7..a892e91 100644 --- a/blazarclient/shell.py +++ b/blazarclient/shell.py @@ -33,6 +33,7 @@ import six from blazarclient import client as blazar_client from blazarclient import exception from blazarclient import utils +from blazarclient.v1.shell_commands import floatingips from blazarclient.v1.shell_commands import hosts from blazarclient.v1.shell_commands import leases from blazarclient import version as base_version @@ -47,7 +48,11 @@ COMMANDS_V1 = { 'host-show': hosts.ShowHost, 'host-create': hosts.CreateHost, 'host-update': hosts.UpdateHost, - 'host-delete': hosts.DeleteHost + 'host-delete': hosts.DeleteHost, + 'floatingip-list': floatingips.ListFloatingIPs, + 'floatingip-show': floatingips.ShowFloatingIP, + 'floatingip-create': floatingips.CreateFloatingIP, + 'floatingip-delete': floatingips.DeleteFloatingIP, } VERSION = 1 diff --git a/blazarclient/tests/v1/shell_commands/test_floatingips.py b/blazarclient/tests/v1/shell_commands/test_floatingips.py new file mode 100644 index 0000000..5816742 --- /dev/null +++ b/blazarclient/tests/v1/shell_commands/test_floatingips.py @@ -0,0 +1,142 @@ +# Copyright (c) 2019 StackHPC Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import argparse +import mock + +from blazarclient import shell +from blazarclient import tests +from blazarclient.v1.shell_commands import floatingips + + +class CreateFloatingIPTest(tests.TestCase): + + def setUp(self): + super(CreateFloatingIPTest, self).setUp() + self.create_floatingip = floatingips.CreateFloatingIP( + shell.BlazarShell(), mock.Mock()) + + def test_args2body(self): + args = argparse.Namespace( + network_id='1e17587e-a7ed-4b82-a17b-4beb32523e28', + floating_ip_address='172.24.4.101', + ) + + expected = { + 'network_id': '1e17587e-a7ed-4b82-a17b-4beb32523e28', + 'floating_ip_address': '172.24.4.101', + } + + ret = self.create_floatingip.args2body(args) + self.assertDictEqual(ret, expected) + + +class ListFloatingIPsTest(tests.TestCase): + + def create_list_command(self, list_value): + mock_floatingip_manager = mock.Mock() + mock_floatingip_manager.list.return_value = list_value + + mock_client = mock.Mock() + mock_client.floatingip = mock_floatingip_manager + + blazar_shell = shell.BlazarShell() + blazar_shell.client = mock_client + return (floatingips.ListFloatingIPs(blazar_shell, mock.Mock()), + mock_floatingip_manager) + + def test_list_floatingips(self): + list_value = [ + {'id': '84c4d37e-1f8b-45ce-897b-16ad7f49b0e9'}, + {'id': 'f180cf4c-f886-4dd1-8c36-854d17fbefb5'}, + ] + + list_floatingips, floatingip_manager = self.create_list_command( + list_value) + + args = argparse.Namespace(sort_by='id', columns=['id']) + expected = [['id'], [('84c4d37e-1f8b-45ce-897b-16ad7f49b0e9',), + ('f180cf4c-f886-4dd1-8c36-854d17fbefb5',)]] + + ret = list_floatingips.get_data(args) + self.assertEqual(expected[0], ret[0]) + self.assertEqual(expected[1], [x for x in ret[1]]) + + floatingip_manager.list.assert_called_once_with(sort_by='id') + + +class ShowFloatingIPTest(tests.TestCase): + + def create_show_command(self, list_value, get_value): + mock_floatingip_manager = mock.Mock() + mock_floatingip_manager.list.return_value = list_value + mock_floatingip_manager.get.return_value = get_value + + mock_client = mock.Mock() + mock_client.floatingip = mock_floatingip_manager + + blazar_shell = shell.BlazarShell() + blazar_shell.client = mock_client + return (floatingips.ShowFloatingIP(blazar_shell, mock.Mock()), + mock_floatingip_manager) + + def test_show_floatingip(self): + list_value = [ + {'id': '84c4d37e-1f8b-45ce-897b-16ad7f49b0e9'}, + {'id': 'f180cf4c-f886-4dd1-8c36-854d17fbefb5'}, + ] + get_value = { + 'id': '84c4d37e-1f8b-45ce-897b-16ad7f49b0e9'} + + show_floatingip, floatingip_manager = self.create_show_command( + list_value, get_value) + + args = argparse.Namespace(id='84c4d37e-1f8b-45ce-897b-16ad7f49b0e9') + expected = [('id',), ('84c4d37e-1f8b-45ce-897b-16ad7f49b0e9',)] + + ret = show_floatingip.get_data(args) + self.assertEqual(ret, expected) + + floatingip_manager.get.assert_called_once_with( + '84c4d37e-1f8b-45ce-897b-16ad7f49b0e9') + + +class DeleteFloatingIPTest(tests.TestCase): + + def create_delete_command(self, list_value): + mock_floatingip_manager = mock.Mock() + mock_floatingip_manager.list.return_value = list_value + + mock_client = mock.Mock() + mock_client.floatingip = mock_floatingip_manager + + blazar_shell = shell.BlazarShell() + blazar_shell.client = mock_client + return (floatingips.DeleteFloatingIP(blazar_shell, mock.Mock()), + mock_floatingip_manager) + + def test_delete_floatingip(self): + list_value = [ + {'id': '84c4d37e-1f8b-45ce-897b-16ad7f49b0e9'}, + {'id': 'f180cf4c-f886-4dd1-8c36-854d17fbefb5'}, + ] + delete_floatingip, floatingip_manager = self.create_delete_command( + list_value) + + args = argparse.Namespace(id='84c4d37e-1f8b-45ce-897b-16ad7f49b0e9') + delete_floatingip.run(args) + + floatingip_manager.delete.assert_called_once_with( + '84c4d37e-1f8b-45ce-897b-16ad7f49b0e9') diff --git a/blazarclient/v1/client.py b/blazarclient/v1/client.py index d9ffe3d..3a6ea85 100644 --- a/blazarclient/v1/client.py +++ b/blazarclient/v1/client.py @@ -15,6 +15,7 @@ import logging +from blazarclient.v1 import floatingips from blazarclient.v1 import hosts from blazarclient.v1 import leases @@ -55,3 +56,9 @@ class Client(object): session=self.session, version=self.version, **kwargs) + self.floatingip = floatingips.FloatingIPClientManager( + blazar_url=self.blazar_url, + auth_token=self.auth_token, + session=self.session, + version=self.version, + **kwargs) diff --git a/blazarclient/v1/floatingips.py b/blazarclient/v1/floatingips.py new file mode 100644 index 0000000..029f079 --- /dev/null +++ b/blazarclient/v1/floatingips.py @@ -0,0 +1,47 @@ +# Copyright (c) 2019 StackHPC Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from blazarclient import base + + +class FloatingIPClientManager(base.BaseClientManager): + """Manager for floating IP requests.""" + + def create(self, network_id, floating_ip_address, **kwargs): + """Creates a floating IP from values passed.""" + values = {'floating_network_id': network_id, + 'floating_ip_address': floating_ip_address} + values.update(**kwargs) + resp, body = self.request_manager.post('/floatingips', body=values) + return body['floatingip'] + + def get(self, floatingip_id): + """Show floating IP details.""" + resp, body = self.request_manager.get( + '/floatingips/%s' % floatingip_id) + return body['floatingip'] + + def delete(self, floatingip_id): + """Deletes floating IP with specified ID.""" + resp, body = self.request_manager.delete( + '/floatingips/%s' % floatingip_id) + + def list(self, sort_by=None): + """List all floating IPs.""" + resp, body = self.request_manager.get('/floatingips') + floatingips = body['floatingips'] + if sort_by: + floatingips = sorted(floatingips, key=lambda l: l[sort_by]) + return floatingips diff --git a/blazarclient/v1/shell_commands/floatingips.py b/blazarclient/v1/shell_commands/floatingips.py new file mode 100644 index 0000000..b93e91e --- /dev/null +++ b/blazarclient/v1/shell_commands/floatingips.py @@ -0,0 +1,76 @@ +# Copyright (c) 2019 StackHPC Ltd. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging + +from blazarclient import command + + +class ListFloatingIPs(command.ListCommand): + """Print a list of floating IPs.""" + resource = 'floatingip' + log = logging.getLogger(__name__ + '.ListFloatingIPs') + list_columns = ['id', 'floating_ip_address', 'floating_network_id'] + + def get_parser(self, prog_name): + parser = super(ListFloatingIPs, self).get_parser(prog_name) + parser.add_argument( + '--sort-by', metavar="", + help='column name used to sort result', + default='id' + ) + return parser + + +class ShowFloatingIP(command.ShowCommand): + """Show floating IP details.""" + resource = 'floatingip' + allow_names = False + json_indent = 4 + log = logging.getLogger(__name__ + '.ShowFloatingIP') + + +class CreateFloatingIP(command.CreateCommand): + """Create a floating IP.""" + resource = 'floatingip' + json_indent = 4 + log = logging.getLogger(__name__ + '.CreateFloatingIP') + + def get_parser(self, prog_name): + parser = super(CreateFloatingIP, self).get_parser(prog_name) + parser.add_argument( + 'network_id', metavar='NETWORK_ID', + help='External network ID to which the floating IP belongs' + ) + parser.add_argument( + 'floating_ip_address', metavar='FLOATING_IP_ADDRESS', + help='Floating IP address to add to Blazar' + ) + return parser + + def args2body(self, parsed_args): + params = {} + if parsed_args.network_id: + params['network_id'] = parsed_args.network_id + if parsed_args.floating_ip_address: + params['floating_ip_address'] = parsed_args.floating_ip_address + return params + + +class DeleteFloatingIP(command.DeleteCommand): + """Delete a floating IP.""" + resource = 'floatingip' + allow_names = False + log = logging.getLogger(__name__ + '.DeleteFloatingIP') diff --git a/blazarclient/v1/shell_commands/leases.py b/blazarclient/v1/shell_commands/leases.py index ec8342f..8831187 100644 --- a/blazarclient/v1/shell_commands/leases.py +++ b/blazarclient/v1/shell_commands/leases.py @@ -33,6 +33,11 @@ CREATE_RESERVATION_KEYS = { "before_end": None, "resource_type": 'physical:host' }, + "virtual:floatingip": { + "amount": 1, + "required_floatingips": [], + "resource_type": 'virtual:floatingip' + }, "virtual:instance": { "vcpus": "", "memory_mb": "", @@ -275,6 +280,8 @@ class CreateLease(command.CreateCommand): defaults = CREATE_RESERVATION_KEYS['physical:host'] elif "virtual:instance" in res_str: defaults = CREATE_RESERVATION_KEYS['virtual:instance'] + elif "virtual:floatingip" in res_str: + defaults = CREATE_RESERVATION_KEYS['virtual:floatingip'] else: defaults = CREATE_RESERVATION_KEYS['others']