From 9981f55920a6f1c1f20396d42e35b075b22f6a8f Mon Sep 17 00:00:00 2001 From: dfilppi Date: Mon, 7 Aug 2017 20:10:53 +0000 Subject: ARIA multivim plugin initial checkin Change-Id: I3a24ab6fc5ba54466bfecaf596a13b8907248ae8 Issue-id: SO-77 Signed-off-by: DeWayne Filppi --- .../openstack_plugin_common/tests/__init__.py | 0 .../tests/openstack_client_tests.py | 849 +++++++++++++++++++++ .../tests/provider-context.json | 78 ++ .../openstack_plugin_common/tests/test.py | 40 + 4 files changed, 967 insertions(+) create mode 100644 aria/multivim-plugin/openstack_plugin_common/tests/__init__.py create mode 100644 aria/multivim-plugin/openstack_plugin_common/tests/openstack_client_tests.py create mode 100644 aria/multivim-plugin/openstack_plugin_common/tests/provider-context.json create mode 100644 aria/multivim-plugin/openstack_plugin_common/tests/test.py (limited to 'aria/multivim-plugin/openstack_plugin_common/tests') diff --git a/aria/multivim-plugin/openstack_plugin_common/tests/__init__.py b/aria/multivim-plugin/openstack_plugin_common/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/aria/multivim-plugin/openstack_plugin_common/tests/openstack_client_tests.py b/aria/multivim-plugin/openstack_plugin_common/tests/openstack_client_tests.py new file mode 100644 index 0000000000..27d443c2e4 --- /dev/null +++ b/aria/multivim-plugin/openstack_plugin_common/tests/openstack_client_tests.py @@ -0,0 +1,849 @@ +######## +# Copyright (c) 2014 GigaSpaces Technologies Ltd. All rights reserved +# +# 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 os +import unittest +import tempfile +import json +import __builtin__ as builtins + +import mock +from cloudify.exceptions import NonRecoverableError + +from cloudify.mocks import MockCloudifyContext +import openstack_plugin_common as common + + +class ConfigTests(unittest.TestCase): + + @mock.patch.dict('os.environ', clear=True) + def test__build_config_from_env_variables_empty(self): + cfg = common.Config._build_config_from_env_variables() + self.assertEqual({}, cfg) + + @mock.patch.dict('os.environ', clear=True, + OS_AUTH_URL='test_url') + def test__build_config_from_env_variables_single(self): + cfg = common.Config._build_config_from_env_variables() + self.assertEqual({'auth_url': 'test_url'}, cfg) + + @mock.patch.dict('os.environ', clear=True, + OS_AUTH_URL='test_url', + OS_PASSWORD='pass', + OS_REGION_NAME='region') + def test__build_config_from_env_variables_multiple(self): + cfg = common.Config._build_config_from_env_variables() + self.assertEqual({ + 'auth_url': 'test_url', + 'password': 'pass', + 'region_name': 'region', + }, cfg) + + @mock.patch.dict('os.environ', clear=True, + OS_INVALID='invalid', + PASSWORD='pass', + os_region_name='region') + def test__build_config_from_env_variables_all_ignored(self): + cfg = common.Config._build_config_from_env_variables() + self.assertEqual({}, cfg) + + @mock.patch.dict('os.environ', clear=True, + OS_AUTH_URL='test_url', + OS_PASSWORD='pass', + OS_REGION_NAME='region', + OS_INVALID='invalid', + PASSWORD='pass', + os_region_name='region') + def test__build_config_from_env_variables_extract_valid(self): + cfg = common.Config._build_config_from_env_variables() + self.assertEqual({ + 'auth_url': 'test_url', + 'password': 'pass', + 'region_name': 'region', + }, cfg) + + def test_update_config_empty_target(self): + target = {} + override = {'k1': 'u1'} + result = override.copy() + + common.Config.update_config(target, override) + self.assertEqual(result, target) + + def test_update_config_empty_override(self): + target = {'k1': 'v1'} + override = {} + result = target.copy() + + common.Config.update_config(target, override) + self.assertEqual(result, target) + + def test_update_config_disjoint_configs(self): + target = {'k1': 'v1'} + override = {'k2': 'u2'} + result = target.copy() + result.update(override) + + common.Config.update_config(target, override) + self.assertEqual(result, target) + + def test_update_config_do_not_remove_empty_from_target(self): + target = {'k1': ''} + override = {} + result = target.copy() + + common.Config.update_config(target, override) + self.assertEqual(result, target) + + def test_update_config_no_empty_in_override(self): + target = {'k1': 'v1', 'k2': 'v2'} + override = {'k1': 'u2'} + result = target.copy() + result.update(override) + + common.Config.update_config(target, override) + self.assertEqual(result, target) + + def test_update_config_all_empty_in_override(self): + target = {'k1': '', 'k2': 'v2'} + override = {'k1': '', 'k3': ''} + result = target.copy() + + common.Config.update_config(target, override) + self.assertEqual(result, target) + + def test_update_config_misc(self): + target = {'k1': 'v1', 'k2': 'v2'} + override = {'k1': '', 'k2': 'u2', 'k3': '', 'k4': 'u4'} + result = {'k1': 'v1', 'k2': 'u2', 'k4': 'u4'} + + common.Config.update_config(target, override) + self.assertEqual(result, target) + + @mock.patch.object(common.Config, 'update_config') + @mock.patch.object(common.Config, '_build_config_from_env_variables', + return_value={}) + @mock.patch.dict('os.environ', clear=True, + values={common.Config.OPENSTACK_CONFIG_PATH_ENV_VAR: + '/this/should/not/exist.json'}) + def test_get_missing_static_config_missing_file(self, from_env, update): + cfg = common.Config.get() + self.assertEqual({}, cfg) + from_env.assert_called_once_with() + update.assert_not_called() + + @mock.patch.object(common.Config, 'update_config') + @mock.patch.object(common.Config, '_build_config_from_env_variables', + return_value={}) + def test_get_empty_static_config_present_file(self, from_env, update): + file_cfg = {'k1': 'v1', 'k2': 'v2'} + env_var = common.Config.OPENSTACK_CONFIG_PATH_ENV_VAR + file = tempfile.NamedTemporaryFile(delete=False) + json.dump(file_cfg, file) + file.close() + + with mock.patch.dict('os.environ', {env_var: file.name}, clear=True): + common.Config.get() + + os.unlink(file.name) + from_env.assert_called_once_with() + update.assert_called_once_with({}, file_cfg) + + @mock.patch.object(common.Config, 'update_config') + @mock.patch.object(common.Config, '_build_config_from_env_variables', + return_value={'k1': 'v1'}) + def test_get_present_static_config_empty_file(self, from_env, update): + file_cfg = {} + env_var = common.Config.OPENSTACK_CONFIG_PATH_ENV_VAR + file = tempfile.NamedTemporaryFile(delete=False) + json.dump(file_cfg, file) + file.close() + + with mock.patch.dict('os.environ', {env_var: file.name}, clear=True): + common.Config.get() + + os.unlink(file.name) + from_env.assert_called_once_with() + update.assert_called_once_with({'k1': 'v1'}, file_cfg) + + @mock.patch.object(common.Config, 'update_config') + @mock.patch.object(common.Config, '_build_config_from_env_variables', + return_value={'k1': 'v1'}) + @mock.patch.dict('os.environ', clear=True, + values={common.Config.OPENSTACK_CONFIG_PATH_ENV_VAR: + '/this/should/not/exist.json'}) + def test_get_present_static_config_missing_file(self, from_env, update): + cfg = common.Config.get() + self.assertEqual({'k1': 'v1'}, cfg) + from_env.assert_called_once_with() + update.assert_not_called() + + @mock.patch.object(common.Config, 'update_config') + @mock.patch.object(common.Config, '_build_config_from_env_variables', + return_value={'k1': 'v1'}) + def test_get_all_present(self, from_env, update): + file_cfg = {'k2': 'u2'} + env_var = common.Config.OPENSTACK_CONFIG_PATH_ENV_VAR + file = tempfile.NamedTemporaryFile(delete=False) + json.dump(file_cfg, file) + file.close() + + with mock.patch.dict('os.environ', {env_var: file.name}, clear=True): + common.Config.get() + + os.unlink(file.name) + from_env.assert_called_once_with() + update.assert_called_once_with({'k1': 'v1'}, file_cfg) + + +class OpenstackClientTests(unittest.TestCase): + + def test__merge_custom_configuration_no_custom_cfg(self): + cfg = {'k1': 'v1'} + new = common.OpenStackClient._merge_custom_configuration(cfg, "dummy") + self.assertEqual(cfg, new) + + def test__merge_custom_configuration_client_present(self): + cfg = { + 'k1': 'v1', + 'k2': 'v2', + 'custom_configuration': { + 'dummy': { + 'k2': 'u2', + 'k3': 'u3' + } + } + } + result = { + 'k1': 'v1', + 'k2': 'u2', + 'k3': 'u3' + } + bak = cfg.copy() + new = common.OpenStackClient._merge_custom_configuration(cfg, "dummy") + self.assertEqual(result, new) + self.assertEqual(cfg, bak) + + def test__merge_custom_configuration_client_missing(self): + cfg = { + 'k1': 'v1', + 'k2': 'v2', + 'custom_configuration': { + 'dummy': { + 'k2': 'u2', + 'k3': 'u3' + } + } + } + result = { + 'k1': 'v1', + 'k2': 'v2' + } + bak = cfg.copy() + new = common.OpenStackClient._merge_custom_configuration(cfg, "baddy") + self.assertEqual(result, new) + self.assertEqual(cfg, bak) + + def test__merge_custom_configuration_multi_client(self): + cfg = { + 'k1': 'v1', + 'k2': 'v2', + 'custom_configuration': { + 'dummy': { + 'k2': 'u2', + 'k3': 'u3' + }, + 'bummy': { + 'k1': 'z1' + } + } + } + result = { + 'k1': 'z1', + 'k2': 'v2', + } + bak = cfg.copy() + new = common.OpenStackClient._merge_custom_configuration(cfg, "bummy") + self.assertEqual(result, new) + self.assertEqual(cfg, bak) + + @mock.patch.object(common, 'ctx') + def test__merge_custom_configuration_nova_url(self, mock_ctx): + cfg = { + 'nova_url': 'gopher://nova', + } + bak = cfg.copy() + + self.assertEqual( + common.OpenStackClient._merge_custom_configuration( + cfg, 'nova_client'), + {'endpoint_override': 'gopher://nova'}, + ) + self.assertEqual( + common.OpenStackClient._merge_custom_configuration( + cfg, 'dummy'), + {}, + ) + self.assertEqual(cfg, bak) + mock_ctx.logger.warn.assert_has_calls([ + mock.call( + "'nova_url' property is deprecated. Use `custom_configuration." + "nova_client.endpoint_override` instead."), + mock.call( + "'nova_url' property is deprecated. Use `custom_configuration." + "nova_client.endpoint_override` instead."), + ]) + + @mock.patch('keystoneauth1.session.Session') + def test___init___multi_region(self, m_session): + mock_client_class = mock.MagicMock() + + cfg = { + 'auth_url': 'test-auth_url/v3', + 'region': 'test-region', + } + + with mock.patch.object( + builtins, 'open', + mock.mock_open( + read_data=""" + { + "region": "region from file", + "other": "this one should get through" + } + """ + ), + create=True, + ): + common.OpenStackClient('fred', mock_client_class, cfg) + + mock_client_class.assert_called_once_with( + region_name='test-region', + other='this one should get through', + session=m_session.return_value, + ) + + def test__validate_auth_params_missing(self): + with self.assertRaises(NonRecoverableError): + common.OpenStackClient._validate_auth_params({}) + + def test__validate_auth_params_too_much(self): + with self.assertRaises(NonRecoverableError): + common.OpenStackClient._validate_auth_params({ + 'auth_url': 'url', + 'password': 'pass', + 'username': 'user', + 'tenant_name': 'tenant', + 'project_id': 'project_test', + }) + + def test__validate_auth_params_v2(self): + common.OpenStackClient._validate_auth_params({ + 'auth_url': 'url', + 'password': 'pass', + 'username': 'user', + 'tenant_name': 'tenant', + }) + + def test__validate_auth_params_v3(self): + common.OpenStackClient._validate_auth_params({ + 'auth_url': 'url', + 'password': 'pass', + 'username': 'user', + 'project_id': 'project_test', + 'user_domain_name': 'user_domain', + }) + + def test__validate_auth_params_v3_mod(self): + common.OpenStackClient._validate_auth_params({ + 'auth_url': 'url', + 'password': 'pass', + 'username': 'user', + 'user_domain_name': 'user_domain', + 'project_name': 'project_test_name', + 'project_domain_name': 'project_domain', + }) + + def test__validate_auth_params_skip_insecure(self): + common.OpenStackClient._validate_auth_params({ + 'auth_url': 'url', + 'password': 'pass', + 'username': 'user', + 'user_domain_name': 'user_domain', + 'project_name': 'project_test_name', + 'project_domain_name': 'project_domain', + 'insecure': True + }) + + def test__split_config(self): + auth = {'auth_url': 'url', 'password': 'pass'} + misc = {'misc1': 'val1', 'misc2': 'val2'} + all = dict(auth) + all.update(misc) + + a, m = common.OpenStackClient._split_config(all) + + self.assertEqual(auth, a) + self.assertEqual(misc, m) + + @mock.patch.object(common, 'loading') + @mock.patch.object(common, 'session') + def test__authenticate_secure(self, mock_session, mock_loading): + auth_params = {'k1': 'v1'} + common.OpenStackClient._authenticate(auth_params) + loader = mock_loading.get_plugin_loader.return_value + loader.load_from_options.assert_called_once_with(k1='v1') + auth = loader.load_from_options.return_value + mock_session.Session.assert_called_once_with(auth=auth, verify=True) + + @mock.patch.object(common, 'loading') + @mock.patch.object(common, 'session') + def test__authenticate_secure_explicit(self, mock_session, mock_loading): + auth_params = {'k1': 'v1', 'insecure': False} + common.OpenStackClient._authenticate(auth_params) + loader = mock_loading.get_plugin_loader.return_value + loader.load_from_options.assert_called_once_with(k1='v1') + auth = loader.load_from_options.return_value + mock_session.Session.assert_called_once_with(auth=auth, verify=True) + + @mock.patch.object(common, 'loading') + @mock.patch.object(common, 'session') + def test__authenticate_insecure(self, mock_session, mock_loading): + auth_params = {'k1': 'v1', 'insecure': True} + common.OpenStackClient._authenticate(auth_params) + loader = mock_loading.get_plugin_loader.return_value + loader.load_from_options.assert_called_once_with(k1='v1') + auth = loader.load_from_options.return_value + mock_session.Session.assert_called_once_with(auth=auth, verify=False) + + @mock.patch.object(common, 'loading') + @mock.patch.object(common, 'session') + def test__authenticate_secure_misc(self, mock_session, mock_loading): + params = {'k1': 'v1'} + tests = ('', 'a', [], {}, set(), 4, 0, -1, 3.14, 0.0, None) + for test in tests: + auth_params = params.copy() + auth_params['insecure'] = test + + common.OpenStackClient._authenticate(auth_params) + loader = mock_loading.get_plugin_loader.return_value + loader.load_from_options.assert_called_with(**params) + auth = loader.load_from_options.return_value + mock_session.Session.assert_called_with(auth=auth, verify=True) + + @mock.patch.object(common, 'cinder_client') + def test_cinder_client_get_name_from_resource(self, cc_mock): + ccws = common.CinderClientWithSugar() + mock_volume = mock.Mock() + + self.assertIs( + mock_volume.name, + ccws.get_name_from_resource(mock_volume)) + + +class ClientsConfigTest(unittest.TestCase): + + def setUp(self): + file = tempfile.NamedTemporaryFile(delete=False) + json.dump(self.get_file_cfg(), file) + file.close() + self.addCleanup(os.unlink, file.name) + + env_cfg = self.get_env_cfg() + env_cfg[common.Config.OPENSTACK_CONFIG_PATH_ENV_VAR] = file.name + mock.patch.dict('os.environ', env_cfg, clear=True).start() + + self.loading = mock.patch.object(common, 'loading').start() + self.session = mock.patch.object(common, 'session').start() + self.nova = mock.patch.object(common, 'nova_client').start() + self.neutron = mock.patch.object(common, 'neutron_client').start() + self.cinder = mock.patch.object(common, 'cinder_client').start() + self.addCleanup(mock.patch.stopall) + + self.loader = self.loading.get_plugin_loader.return_value + self.auth = self.loader.load_from_options.return_value + + +class CustomConfigFromInputs(ClientsConfigTest): + + def get_file_cfg(self): + return { + 'username': 'file-username', + 'password': 'file-password', + 'tenant_name': 'file-tenant-name', + 'custom_configuration': { + 'nova_client': { + 'username': 'custom-username', + 'password': 'custom-password', + 'tenant_name': 'custom-tenant-name' + }, + } + } + + def get_inputs_cfg(self): + return { + 'auth_url': 'envar-auth-url', + 'username': 'inputs-username', + 'custom_configuration': { + 'neutron_client': { + 'password': 'inputs-custom-password' + }, + 'cinder_client': { + 'password': 'inputs-custom-password', + 'auth_url': 'inputs-custom-auth-url', + 'extra_key': 'extra-value' + }, + } + } + + def get_env_cfg(self): + return { + 'OS_USERNAME': 'envar-username', + 'OS_PASSWORD': 'envar-password', + 'OS_TENANT_NAME': 'envar-tenant-name', + 'OS_AUTH_URL': 'envar-auth-url', + common.Config.OPENSTACK_CONFIG_PATH_ENV_VAR: file.name + } + + def test_nova(self): + common.NovaClientWithSugar(config=self.get_inputs_cfg()) + self.loader.load_from_options.assert_called_once_with( + username='inputs-username', + password='file-password', + tenant_name='file-tenant-name', + auth_url='envar-auth-url' + ) + self.session.Session.assert_called_with(auth=self.auth, verify=True) + self.nova.Client.assert_called_once_with( + '2', session=self.session.Session.return_value) + + def test_neutron(self): + common.NeutronClientWithSugar(config=self.get_inputs_cfg()) + self.loader.load_from_options.assert_called_once_with( + username='inputs-username', + password='inputs-custom-password', + tenant_name='file-tenant-name', + auth_url='envar-auth-url' + ) + self.session.Session.assert_called_with(auth=self.auth, verify=True) + self.neutron.Client.assert_called_once_with( + session=self.session.Session.return_value) + + def test_cinder(self): + common.CinderClientWithSugar(config=self.get_inputs_cfg()) + self.loader.load_from_options.assert_called_once_with( + username='inputs-username', + password='inputs-custom-password', + tenant_name='file-tenant-name', + auth_url='inputs-custom-auth-url' + ) + self.session.Session.assert_called_with(auth=self.auth, verify=True) + self.cinder.Client.assert_called_once_with( + '2', session=self.session.Session.return_value, + extra_key='extra-value') + + +class CustomConfigFromFile(ClientsConfigTest): + + def get_file_cfg(self): + return { + 'username': 'file-username', + 'password': 'file-password', + 'tenant_name': 'file-tenant-name', + 'custom_configuration': { + 'nova_client': { + 'username': 'custom-username', + 'password': 'custom-password', + 'tenant_name': 'custom-tenant-name' + }, + } + } + + def get_inputs_cfg(self): + return { + 'auth_url': 'envar-auth-url', + 'username': 'inputs-username', + } + + def get_env_cfg(self): + return { + 'OS_USERNAME': 'envar-username', + 'OS_PASSWORD': 'envar-password', + 'OS_TENANT_NAME': 'envar-tenant-name', + 'OS_AUTH_URL': 'envar-auth-url', + common.Config.OPENSTACK_CONFIG_PATH_ENV_VAR: file.name + } + + def test_nova(self): + common.NovaClientWithSugar(config=self.get_inputs_cfg()) + self.loader.load_from_options.assert_called_once_with( + username='custom-username', + password='custom-password', + tenant_name='custom-tenant-name', + auth_url='envar-auth-url' + ) + self.session.Session.assert_called_with(auth=self.auth, verify=True) + self.nova.Client.assert_called_once_with( + '2', session=self.session.Session.return_value) + + def test_neutron(self): + common.NeutronClientWithSugar(config=self.get_inputs_cfg()) + self.loader.load_from_options.assert_called_once_with( + username='inputs-username', + password='file-password', + tenant_name='file-tenant-name', + auth_url='envar-auth-url' + ) + self.session.Session.assert_called_with(auth=self.auth, verify=True) + self.neutron.Client.assert_called_once_with( + session=self.session.Session.return_value) + + def test_cinder(self): + common.CinderClientWithSugar(config=self.get_inputs_cfg()) + self.loader.load_from_options.assert_called_once_with( + username='inputs-username', + password='file-password', + tenant_name='file-tenant-name', + auth_url='envar-auth-url' + ) + self.session.Session.assert_called_with(auth=self.auth, verify=True) + self.cinder.Client.assert_called_once_with( + '2', session=self.session.Session.return_value) + + +class PutClientInKwTests(unittest.TestCase): + + def test_override_prop_empty_ctx(self): + props = {} + ctx = MockCloudifyContext(node_id='a20846', properties=props) + kwargs = { + 'ctx': ctx, + 'openstack_config': { + 'p1': 'v1' + } + } + expected_cfg = kwargs['openstack_config'] + + client_class = mock.MagicMock() + common._put_client_in_kw('mock_client', client_class, kwargs) + client_class.assert_called_once_with(config=expected_cfg) + + def test_override_prop_nonempty_ctx(self): + props = { + 'openstack_config': { + 'p1': 'u1', + 'p2': 'u2' + } + } + props_copy = props.copy() + ctx = MockCloudifyContext(node_id='a20846', properties=props) + kwargs = { + 'ctx': ctx, + 'openstack_config': { + 'p1': 'v1', + 'p3': 'v3' + } + } + expected_cfg = { + 'p1': 'v1', + 'p2': 'u2', + 'p3': 'v3' + } + + client_class = mock.MagicMock() + common._put_client_in_kw('mock_client', client_class, kwargs) + client_class.assert_called_once_with(config=expected_cfg) + # Making sure that _put_client_in_kw will not modify + # 'openstack_config' property of a node. + self.assertEqual(props_copy, ctx.node.properties) + + def test_override_runtime_prop(self): + props = { + 'openstack_config': { + 'p1': 'u1', + 'p2': 'u2' + } + } + runtime_props = { + 'openstack_config': { + 'p1': 'u3' + } + } + props_copy = props.copy() + runtime_props_copy = runtime_props.copy() + ctx = MockCloudifyContext(node_id='a20847', properties=props, + runtime_properties=runtime_props) + kwargs = { + 'ctx': ctx + } + expected_cfg = { + 'p1': 'u3', + 'p2': 'u2' + } + client_class = mock.MagicMock() + common._put_client_in_kw('mock_client', client_class, kwargs) + client_class.assert_called_once_with(config=expected_cfg) + self.assertEqual(props_copy, ctx.node.properties) + self.assertEqual(runtime_props_copy, ctx.instance.runtime_properties) + + +class ResourceQuotaTests(unittest.TestCase): + + def _test_quota_validation(self, amount, quota, failure_expected): + ctx = MockCloudifyContext(node_id='node_id', properties={}) + client = mock.MagicMock() + + def mock_cosmo_list(_): + return [x for x in range(0, amount)] + client.cosmo_list = mock_cosmo_list + + def mock_get_quota(_): + return quota + client.get_quota = mock_get_quota + + if failure_expected: + self.assertRaisesRegexp( + NonRecoverableError, + 'cannot be created due to quota limitations', + common.validate_resource, + ctx=ctx, sugared_client=client, + openstack_type='openstack_type') + else: + common.validate_resource( + ctx=ctx, sugared_client=client, + openstack_type='openstack_type') + + def test_equals_quotas(self): + self._test_quota_validation(3, 3, True) + + def test_exceeded_quota(self): + self._test_quota_validation(5, 3, True) + + def test_infinite_quota(self): + self._test_quota_validation(5, -1, False) + + +class UseExternalResourceTests(unittest.TestCase): + + def _test_use_external_resource(self, + is_external, + create_if_missing, + exists): + properties = {'create_if_missing': create_if_missing, + 'use_external_resource': is_external, + 'resource_id': 'resource_id'} + client_mock = mock.MagicMock() + os_type = 'test' + + def _raise_error(*_): + raise NonRecoverableError('Error') + + def _return_something(*_): + return mock.MagicMock() + + return_value = _return_something if exists else _raise_error + if exists: + properties.update({'resource_id': 'rid'}) + + node_context = MockCloudifyContext(node_id='a20847', + properties=properties) + with mock.patch( + 'openstack_plugin_common._get_resource_by_name_or_id_from_ctx', + new=return_value): + return common.use_external_resource(node_context, + client_mock, os_type) + + def test_use_existing_resource(self): + self.assertIsNotNone(self._test_use_external_resource(True, True, + True)) + self.assertIsNotNone(self._test_use_external_resource(True, False, + True)) + + def test_create_resource(self): + self.assertIsNone(self._test_use_external_resource(False, True, False)) + self.assertIsNone(self._test_use_external_resource(False, False, + False)) + self.assertIsNone(self._test_use_external_resource(True, True, False)) + + def test_raise_error(self): + # If exists and shouldn't it is checked in resource + # validation so below scenario is not tested here + self.assertRaises(NonRecoverableError, + self._test_use_external_resource, + is_external=True, + create_if_missing=False, + exists=False) + + +class ValidateResourceTests(unittest.TestCase): + + def _test_validate_resource(self, + is_external, + create_if_missing, + exists, + client_mock_provided=None): + properties = {'create_if_missing': create_if_missing, + 'use_external_resource': is_external, + 'resource_id': 'resource_id'} + client_mock = client_mock_provided or mock.MagicMock() + os_type = 'test' + + def _raise_error(*_): + raise NonRecoverableError('Error') + + def _return_something(*_): + return mock.MagicMock() + return_value = _return_something if exists else _raise_error + if exists: + properties.update({'resource_id': 'rid'}) + + node_context = MockCloudifyContext(node_id='a20847', + properties=properties) + with mock.patch( + 'openstack_plugin_common._get_resource_by_name_or_id_from_ctx', + new=return_value): + return common.validate_resource(node_context, client_mock, os_type) + + def test_use_existing_resource(self): + self._test_validate_resource(True, True, True) + self._test_validate_resource(True, False, True) + + def test_create_resource(self): + client_mock = mock.MagicMock() + client_mock.cosmo_list.return_value = ['a', 'b', 'c'] + client_mock.get_quota.return_value = 5 + self._test_validate_resource(False, True, False, client_mock) + self._test_validate_resource(False, False, False, client_mock) + self._test_validate_resource(True, True, False, client_mock) + + def test_raise_error(self): + # If exists and shouldn't it is checked in resource + # validation so below scenario is not tested here + self.assertRaises(NonRecoverableError, + self._test_validate_resource, + is_external=True, + create_if_missing=False, + exists=False) + + def test_raise_quota_error(self): + client_mock = mock.MagicMock() + client_mock.cosmo_list.return_value = ['a', 'b', 'c'] + client_mock.get_quota.return_value = 3 + self.assertRaises(NonRecoverableError, + self._test_validate_resource, + is_external=True, + create_if_missing=True, + exists=False, + client_mock_provided=client_mock) diff --git a/aria/multivim-plugin/openstack_plugin_common/tests/provider-context.json b/aria/multivim-plugin/openstack_plugin_common/tests/provider-context.json new file mode 100644 index 0000000000..f7e20e4ef5 --- /dev/null +++ b/aria/multivim-plugin/openstack_plugin_common/tests/provider-context.json @@ -0,0 +1,78 @@ +{ + "context": { + "resources": { + "management_keypair": { + "name": "p2_cloudify-manager-kp-ilya", + "id": "p2_cloudify-manager-kp-ilya", + "type": "keypair", + "external_resource": true + }, + "router": { + "name": "p2_cloudify-router", + "id": "856f9fb8-6676-4b99-b64d-b76874b30abf", + "type": "router", + "external_resource": true + }, + "subnet": { + "name": "p2_cloudify-admin-network-subnet", + "id": "dd193491-d728-4e3e-8199-27eec0ba18e4", + "type": "subnet", + "external_resource": true + }, + "int_network": { + "name": "p2_cloudify-admin-network", + "id": "27ef2770-5219-4bb1-81d4-14ed450c5181", + "type": "network", + "external_resource": true + }, + "management_server": { + "name": "p2_cfy-mgr-ilya-2014-06-01-11:59", + "id": "be9991da-9c34-4f7c-9c33-5e04ad2d5b3e", + "type": "server", + "external_resource": false + }, + "agents_security_group": { + "name": "p2_cloudify-sg-agents", + "id": "d52280aa-0e79-4697-bd08-baf3f84e2a10", + "type": "neutron security group", + "external_resource": true + }, + "agents_keypair": { + "name": "p2_cloudify-agents-kp-ilya", + "id": "p2_cloudify-agents-kp-ilya", + "type": "keypair", + "external_resource": true + }, + "management_security_group": { + "name": "p2_cloudify-sg-management", + "id": "5862e0d2-8f28-472e-936b-d2da9cb935b3", + "type": "neutron security group", + "external_resource": true + }, + "floating_ip": { + "external_resource": true, + "id": "None", + "type": "floating ip", + "ip": "CENSORED" + }, + "ext_network": { + "name": "Ext-Net", + "id": "7da74520-9d5e-427b-a508-213c84e69616", + "type": "network", + "external_resource": true + } + }, + "cloudify": { + "resources_prefix": "p2_", + "cloudify_agent": { + "user": "ubuntu", + "agent_key_path": "/PATH/CENSORED/p2_cloudify-agents-kp-ilya.pem", + "min_workers": 2, + "max_workers": 5, + "remote_execution_port": 22 + } + } + }, + "name": "cloudify_openstack" +} + diff --git a/aria/multivim-plugin/openstack_plugin_common/tests/test.py b/aria/multivim-plugin/openstack_plugin_common/tests/test.py new file mode 100644 index 0000000000..13099292ca --- /dev/null +++ b/aria/multivim-plugin/openstack_plugin_common/tests/test.py @@ -0,0 +1,40 @@ +import json +import os + +from cloudify.context import BootstrapContext + +from cloudify.mocks import MockCloudifyContext + + +RETRY_AFTER = 1 +# Time during which no retry could possibly happen. +NO_POSSIBLE_RETRY_TIME = RETRY_AFTER / 2.0 + +BOOTSTRAP_CONTEXTS_WITHOUT_PREFIX = ( + { + }, + { + 'resources_prefix': '' + }, + { + 'resources_prefix': None + }, +) + + +def set_mock_provider_context(ctx, provider_context): + + def mock_provider_context(provider_name_unused): + return provider_context + + ctx.get_provider_context = mock_provider_context + + +def create_mock_ctx_with_provider_info(*args, **kw): + cur_dir = os.path.dirname(os.path.realpath(__file__)) + full_file_name = os.path.join(cur_dir, 'provider-context.json') + with open(full_file_name) as f: + provider_context = json.loads(f.read())['context'] + kw['provider_context'] = provider_context + kw['bootstrap_context'] = BootstrapContext(provider_context['cloudify']) + return MockCloudifyContext(*args, **kw) -- cgit 1.2.3-korg