From e3af2e6dd3ca07bbddec5cdd2768f380af3dc1be Mon Sep 17 00:00:00 2001 From: Alexander Saprykin Date: Fri, 31 Jul 2015 12:46:57 +0300 Subject: [PATCH] Add network templates support for v2 Partial-Bug: #1478577 Change-Id: I76a5dc887ee50c9482fdd847fa55968a9727b02d --- fuelclient/commands/network_template.py | 98 +++++++++++++++++++ fuelclient/objects/environment.py | 12 ++- .../v2/unit/cli/test_network_template.py | 65 ++++++++++++ .../v2/unit/lib/test_network_template.py | 74 ++++++++++++++ fuelclient/v1/environment.py | 23 +++++ setup.cfg | 3 + 6 files changed, 272 insertions(+), 3 deletions(-) create mode 100644 fuelclient/commands/network_template.py create mode 100644 fuelclient/tests/v2/unit/cli/test_network_template.py create mode 100644 fuelclient/tests/v2/unit/lib/test_network_template.py diff --git a/fuelclient/commands/network_template.py b/fuelclient/commands/network_template.py new file mode 100644 index 00000000..30257523 --- /dev/null +++ b/fuelclient/commands/network_template.py @@ -0,0 +1,98 @@ +# Copyright 2015 Mirantis, Inc. +# +# 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 fuelclient.commands import base + + +class NetworkTemplateMixin(object): + + entity_name = 'environment' + + @staticmethod + def add_env_argument(parser): + parser.add_argument( + 'env', + type=int, + help='Upload network template for specified environment' + ) + + @staticmethod + def add_dir_argument(parser): + parser.add_argument( + '-d', '--dir', + type=str, + help='Directory with network data' + ) + + +class NetworkTemplateUpload(NetworkTemplateMixin, base.BaseCommand): + """To upload network configuration for specified environment: + + fuel2 network-template upload --dir path/to/directory 1 + """ + + def get_parser(self, prog_name): + parser = super(NetworkTemplateUpload, self).get_parser(prog_name) + + self.add_dir_argument(parser) + self.add_env_argument(parser) + + return parser + + def take_action(self, parsed_args): + file_path = self.client.upload_network_template( + parsed_args.env, parsed_args.dir) + msg = "Network template {0} has been uploaded.".format(file_path) + self.app.stdout.write(msg) + + +class NetworkTemplateDownload(NetworkTemplateMixin, base.BaseCommand): + """To download network configuration for environment to the specified + directory: + fuel2 network-template download --dir path/to/directory 1 + """ + + def get_parser(self, prog_name): + parser = super(NetworkTemplateDownload, self).get_parser(prog_name) + + self.add_dir_argument(parser) + self.add_env_argument(parser) + + return parser + + def take_action(self, parsed_args): + file_path = self.client.download_network_template( + parsed_args.env, parsed_args.dir) + + msg = ("Network template configuration for environment with id={0}" + " downloaded to {1}\n").format( + parsed_args.env, file_path) + self.app.stdout.write(msg) + + +class NetworkTemplateDelete(NetworkTemplateMixin, base.BaseCommand): + + def get_parser(self, prog_name): + parser = super(NetworkTemplateDelete, self).get_parser(prog_name) + + self.add_env_argument(parser) + + return parser + + def take_action(self, parsed_args): + self.client.delete_network_template(parsed_args.env) + + msg = ("Network template for environment id={0}" + " has been deleted.".format(parsed_args.env)) + self.app.stdout.write(msg) diff --git a/fuelclient/objects/environment.py b/fuelclient/objects/environment.py index e4502e99..82575cc4 100644 --- a/fuelclient/objects/environment.py +++ b/fuelclient/objects/environment.py @@ -147,7 +147,8 @@ class Environment(BaseObject): "vmware_settings_{0}".format(self.id) ) - def get_network_template_data_path(self, directory=os.curdir): + def get_network_template_data_path(self, directory=None): + directory = directory or os.curdir return os.path.join( os.path.abspath(directory), "network_template_{0}".format(self.id) @@ -167,15 +168,17 @@ class Environment(BaseObject): settings_data ) - def write_vmware_settings_data(self, settings_data, directory=os.curdir, + def write_vmware_settings_data(self, settings_data, directory=None, serializer=None): + directory = directory or os.curdir return (serializer or self.serializer).write_to_path( self.get_vmware_settings_data_path(directory), settings_data ) - def write_network_template_data(self, template_data, directory=os.curdir, + def write_network_template_data(self, template_data, directory=None, serializer=None): + directory = directory or os.curdir return (serializer or self.serializer).write_to_path( self.get_network_template_data_path(directory), template_data @@ -275,6 +278,9 @@ class Environment(BaseObject): return self.connection.put_request( self.network_template_url, data) + def delete_network_template_data(self): + return self.connection.delete_request(self.network_template_url) + def _get_fact_dir_name(self, fact_type, directory=os.path.curdir): return os.path.join( os.path.abspath(directory), diff --git a/fuelclient/tests/v2/unit/cli/test_network_template.py b/fuelclient/tests/v2/unit/cli/test_network_template.py new file mode 100644 index 00000000..edfb5a86 --- /dev/null +++ b/fuelclient/tests/v2/unit/cli/test_network_template.py @@ -0,0 +1,65 @@ +# +# Copyright 2015 Mirantis, Inc. +# +# 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 mock + +from fuelclient.tests.v2.unit.cli import test_engine + + +class TestNetworkTemplateCommand(test_engine.BaseCLITest): + + def test_network_template_upload(self): + args = 'network-template upload 1' + self.exec_command(args) + + self.m_get_client.assert_called_once_with('environment', mock.ANY) + self.m_client.upload_network_template.assert_called_once_with( + 1, None) + + def test_network_template_upload_w_dir(self): + args = 'network-template upload --dir /tmp/test-dir 1' + self.exec_command(args) + + self.m_get_client.assert_called_once_with('environment', mock.ANY) + self.m_client.upload_network_template.assert_called_once_with( + 1, '/tmp/test-dir') + + def test_network_template_download(self): + download_mock = self.m_client.download_network_template + download_mock.return_value = '/tmp/test-dir/settings_1' + + args = 'network-template download 1' + self.exec_command(args) + + self.m_get_client.assert_called_once_with('environment', mock.ANY) + download_mock.assert_called_once_with(1, None) + + def test_network_template_download_w_dir(self): + download_mock = self.m_client.download_network_template + download_mock.return_value = '/tmp/test-dir/settings_1' + + args = 'network-template download --dir /tmp/test-dir 1' + self.exec_command(args) + + self.m_get_client.assert_called_once_with('environment', mock.ANY) + download_mock.assert_called_once_with(1, '/tmp/test-dir') + + def test_network_template_delete(self): + args = 'network-template delete 1' + self.exec_command(args) + + self.m_get_client.assert_called_once_with('environment', mock.ANY) + delete_mock = self.m_client.delete_network_template + delete_mock.assert_called_once_with(1) diff --git a/fuelclient/tests/v2/unit/lib/test_network_template.py b/fuelclient/tests/v2/unit/lib/test_network_template.py new file mode 100644 index 00000000..4acad82f --- /dev/null +++ b/fuelclient/tests/v2/unit/lib/test_network_template.py @@ -0,0 +1,74 @@ +# +# Copyright 2015 Mirantis, Inc. +# +# 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 mock +import yaml + +from oslo_serialization import jsonutils as json + +import fuelclient +from fuelclient.tests.common.unit import \ + test_network_template as common_net_template +from fuelclient.tests.v2.unit.lib import test_api + + +class TestNetworkTemplateFacade(test_api.BaseLibTest): + + def setUp(self): + super(TestNetworkTemplateFacade, self).setUp() + + self.version = 'v1' + self.env_id = 42 + self.res_uri = ( + '/api/{version}/clusters/{env_id}' + '/network_configuration/template'.format( + version=self.version, env_id=self.env_id)) + + self.client = fuelclient.get_client('environment', self.version) + + def test_network_template_upload(self): + expected_body = json.loads(common_net_template.JSON_TEMPLATE) + matcher = self.m_request.put( + self.res_uri, json=expected_body) + + m_open = mock.mock_open(read_data=common_net_template.YAML_TEMPLATE) + with mock.patch('fuelclient.cli.serializers.open', + m_open, create=True): + self.client.upload_network_template(self.env_id) + + self.assertTrue(matcher.called) + self.assertEqual(expected_body, matcher.last_request.json()) + + def test_network_template_download(self): + expected_body = json.loads(common_net_template.JSON_TEMPLATE) + matcher = self.m_request.get(self.res_uri, json=expected_body) + + m_open = mock.mock_open() + with mock.patch('fuelclient.cli.serializers.open', + m_open, create=True): + self.client.download_network_template(self.env_id) + + self.assertTrue(matcher.called) + + written_yaml = yaml.safe_load(m_open().write.mock_calls[0][1][0]) + expected_yaml = yaml.safe_load(common_net_template.YAML_TEMPLATE) + self.assertEqual(written_yaml, expected_yaml) + + def test_network_template_delete(self): + matcher = self.m_request.delete(self.res_uri, json={}) + + self.client.delete_network_template(self.env_id) + + self.assertTrue(matcher.called) diff --git a/fuelclient/v1/environment.py b/fuelclient/v1/environment.py index 6bc771d5..a6a45633 100644 --- a/fuelclient/v1/environment.py +++ b/fuelclient/v1/environment.py @@ -92,6 +92,29 @@ class EnvironmentClient(base_v1.BaseV1Client): env = self._entity_wrapper(obj_id=environment_id) return env.spawn_vms() + def upload_network_template(self, environment_id, directory=None): + env = self._entity_wrapper(environment_id) + network_template_data = env.read_network_template_data( + directory=directory) + env.set_network_template_data(network_template_data) + + file_path = env.serializer.prepare_path( + env.get_network_template_data_path(directory=directory)) + return file_path + + def download_network_template(self, environment_id, directory=None): + env = self._entity_wrapper(environment_id) + template_data = env.get_network_template_data() + file_path = env.write_network_template_data( + template_data, + directory=directory) + + return file_path + + def delete_network_template(self, environment_id): + env = self._entity_wrapper(environment_id) + env.delete_network_template_data() + def get_client(): return EnvironmentClient() diff --git a/setup.cfg b/setup.cfg index b6f2feaa..79eba3a2 100644 --- a/setup.cfg +++ b/setup.cfg @@ -48,6 +48,9 @@ fuelclient = task_list=fuelclient.commands.task:TaskList task_show=fuelclient.commands.task:TaskShow fuel-version=fuelclient.commands.fuelversion:FuelVersion + network-template_upload=fuelclient.commands.network_template:NetworkTemplateUpload + network-template_download=fuelclient.commands.network_template:NetworkTemplateDownload + network-template_delete=fuelclient.commands.network_template:NetworkTemplateDelete [global] setup-hooks =