diff --git a/CHANGELOG.txt b/CHANGELOG.txt index fc3dadca..5f29aa0f 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,5 @@ +2.14.13: + - Add support for resumable actions. 2.14.12: - expose ipv4 and ipv6 addresses as runtime properties for port. 2.14.11: diff --git a/cinder_plugin/tests/test_volume.py b/cinder_plugin/tests/test_volume.py index 66f58cfb..914c7045 100644 --- a/cinder_plugin/tests/test_volume.py +++ b/cinder_plugin/tests/test_volume.py @@ -154,10 +154,13 @@ def test_delete(self): self.assertTrue(OPENSTACK_NAME_PROPERTY not in ctx_m.instance.runtime_properties) + @mock.patch('openstack_plugin_common' + '._check_valid_resource_id_with_operation', + autospec=True, return_value=True) @mock.patch('openstack_plugin_common.NovaClientWithSugar') @mock.patch('openstack_plugin_common.CinderClientWithSugar') @mock.patch.object(volume, 'wait_until_status', return_value=(None, True)) - def test_attach(self, wait_until_status_m, cinder_m, nova_m): + def test_attach(self, wait_until_status_m, cinder_m, nova_m, *_): volume_id = '00000000-0000-0000-0000-000000000000' server_id = '11111111-1111-1111-1111-111111111111' device_name = '/dev/fake' @@ -273,13 +276,20 @@ def _test_cleanup__after_attach_fails( num_tries=10, timeout=2) - def test_cleanup_after_waituntilstatus_throws_recoverable_error(self): + @mock.patch('openstack_plugin_common' + '._check_valid_resource_id_with_operation', + autospec=True, return_value=True) + def test_cleanup_after_waituntilstatus_throws_recoverable_error(self, *_): err = cfy_exc.RecoverableError('Some recoverable error') with mock.patch.object(volume, 'wait_until_status', side_effect=[err, (None, True)]) as wait_mock: self._test_cleanup__after_attach_fails(type(err), True, wait_mock) - def test_cleanup_after_waituntilstatus_throws_any_not_nonrecov_error(self): + @mock.patch('openstack_plugin_common' + '._check_valid_resource_id_with_operation', + autospec=True, return_value=True) + def test_cleanup_after_waituntilstatus_throws_any_not_nonrecov_error(self, + *_): class ArbitraryNonRecoverableException(Exception): pass err = ArbitraryNonRecoverableException('An exception') @@ -287,21 +297,30 @@ class ArbitraryNonRecoverableException(Exception): side_effect=[err, (None, True)]) as wait_mock: self._test_cleanup__after_attach_fails(type(err), True, wait_mock) - def test_cleanup_after_waituntilstatus_lets_nonrecov_errors_pass(self): + @mock.patch('openstack_plugin_common' + '._check_valid_resource_id_with_operation', + autospec=True, return_value=True) + def test_cleanup_after_waituntilstatus_lets_nonrecov_errors_pass(self, *_): err = cfy_exc.NonRecoverableError('Some non recoverable error') with mock.patch.object(volume, 'wait_until_status', side_effect=[err, (None, True)]) as wait_mock: self._test_cleanup__after_attach_fails(type(err), False, wait_mock) @mock.patch.object(volume, 'wait_until_status', return_value=(None, False)) - def test_cleanup_after_waituntilstatus_times_out(self, wait_mock): + @mock.patch('openstack_plugin_common' + '._check_valid_resource_id_with_operation', + autospec=True, return_value=True) + def test_cleanup_after_waituntilstatus_times_out(self, wait_mock, *_): self._test_cleanup__after_attach_fails(cfy_exc.RecoverableError, True, wait_mock) + @mock.patch('openstack_plugin_common' + '._check_valid_resource_id_with_operation', + autospec=True, return_value=True) @mock.patch('openstack_plugin_common.NovaClientWithSugar') @mock.patch('openstack_plugin_common.CinderClientWithSugar') @mock.patch.object(volume, 'wait_until_status', return_value=(None, True)) - def test_detach(self, wait_until_status_m, cinder_m, nova_m): + def test_detach(self, wait_until_status_m, cinder_m, nova_m, *_): volume_id = '00000000-0000-0000-0000-000000000000' server_id = '11111111-1111-1111-1111-111111111111' attachment_id = '22222222-2222-2222-2222-222222222222' diff --git a/cinder_plugin/volume.py b/cinder_plugin/volume.py index 674a82f3..6d6030ec 100644 --- a/cinder_plugin/volume.py +++ b/cinder_plugin/volume.py @@ -65,7 +65,7 @@ def _set_volume_runtime_properties(volume): ctx.node.properties.get('boot', False) -@operation +@operation(resumable=True) @with_cinder_client def create(cinder_client, args={}, @@ -158,7 +158,7 @@ def _delete_backup(cinder_client, search_opts): retry_after=30) -@operation +@operation(resumable=True) @with_cinder_client def delete(cinder_client, **kwargs): # seach snapshots for volume @@ -204,6 +204,7 @@ def _get_snapshot_name(ctx, kwargs): return "vol-{}-{}".format(get_openstack_id(ctx), kwargs["snapshot_name"]) +@operation(resumable=True) @with_cinder_client def snapshot_create(cinder_client, **kwargs): volume_id = get_openstack_id(ctx) @@ -224,6 +225,7 @@ def snapshot_create(cinder_client, **kwargs): metadata=None) +@operation(resumable=True) @with_cinder_client def snapshot_apply(cinder_client, **kwargs): volume_id = get_openstack_id(ctx) @@ -254,6 +256,7 @@ def snapshot_apply(cinder_client, **kwargs): ctx.logger.error("Apply snapshot is unsuported") +@operation(resumable=True) @with_cinder_client def snapshot_delete(cinder_client, **kwargs): volume_id = get_openstack_id(ctx) @@ -279,14 +282,14 @@ def snapshot_delete(cinder_client, **kwargs): _delete_snapshot(cinder_client, search_opts) -@operation +@operation(resumable=True) @with_cinder_client def creation_validation(cinder_client, **kwargs): validate_resource(ctx, cinder_client, VOLUME_OPENSTACK_TYPE, VOLUME_OPENSTACK_ID_KEY) -@operation +@operation(resumable=True) @with_cinder_client def list_volumes(cinder_client, args, **kwargs): volume_list = cinder_client.volumes.list(**args) diff --git a/glance_plugin/image.py b/glance_plugin/image.py index 303f8782..7d4e9116 100644 --- a/glance_plugin/image.py +++ b/glance_plugin/image.py @@ -39,7 +39,7 @@ REQUIRED_PROPERTIES = ['container_format', 'disk_format'] -@operation +@operation(resumable=True) @with_glance_client def create(glance_client, args, **kwargs): if use_external_resource(ctx, glance_client, IMAGE_OPENSTACK_TYPE): @@ -72,7 +72,7 @@ def _get_image_by_ctx(glance_client, ctx): image_id=get_openstack_id(ctx)) -@operation +@operation(resumable=True) @with_glance_client def start(glance_client, start_retry_interval, **kwargs): img = _get_image_by_ctx(glance_client, ctx) @@ -82,7 +82,7 @@ def start(glance_client, start_retry_interval, **kwargs): retry_after=start_retry_interval) -@operation +@operation(resumable=True) @with_glance_client def delete(glance_client, **kwargs): _remove_protected(glance_client) @@ -107,7 +107,7 @@ def update(glance_client, args, **kwargs): set_openstack_runtime_properties(ctx, image, IMAGE_OPENSTACK_TYPE) -@operation +@operation(resumable=True) @with_glance_client def creation_validation(glance_client, **kwargs): validate_resource(ctx, glance_client, IMAGE_OPENSTACK_TYPE) diff --git a/glance_plugin/tests/test.py b/glance_plugin/tests/test.py index 4a88cba4..cf7e6ec2 100644 --- a/glance_plugin/tests/test.py +++ b/glance_plugin/tests/test.py @@ -123,6 +123,9 @@ class TestStartImage(unittest.TestCase): 'test-image-start.yaml') @mock.patch('glance_plugin.image.create') + @mock.patch('openstack_plugin_common' + '._check_valid_resource_id_with_operation', + autospec=True, return_value=True) @workflow_test(blueprint_path, copy_plugin_yaml=True) def test_image_lifecycle_start(self, cfy_local, *_): test_vars = { diff --git a/keystone_plugin/project.py b/keystone_plugin/project.py index 1d0ffc08..ad88f298 100644 --- a/keystone_plugin/project.py +++ b/keystone_plugin/project.py @@ -46,7 +46,7 @@ ROLES = 'roles' -@operation +@operation(resumable=True) @with_keystone_client def create(keystone_client, args, **kwargs): if use_external_resource(ctx, keystone_client, PROJECT_OPENSTACK_TYPE): @@ -61,7 +61,7 @@ def create(keystone_client, args, **kwargs): set_openstack_runtime_properties(ctx, project, PROJECT_OPENSTACK_TYPE) -@operation +@operation(resumable=True) def start(quota_dict, **kwargs): users = ctx.node.properties[USERS] validate_users(users, **kwargs) @@ -71,7 +71,7 @@ def start(quota_dict, **kwargs): update_project_quota(quota=quota, **kwargs) -@operation +@operation(resumable=True) @with_keystone_client @with_nova_client @with_cinder_client @@ -87,7 +87,6 @@ def delete(keystone_client, nova_client, cinder_client, RUNTIME_PROPERTIES_KEYS) -@operation @with_keystone_client def creation_validation(keystone_client, **kwargs): validate_resource(ctx, keystone_client, PROJECT_OPENSTACK_TYPE) @@ -163,6 +162,7 @@ def delete_quota(project_id, quota, client, what_quota): 'Deleted {0} quota'.format(what_quota)) +@operation(resumable=True) @with_nova_client @with_neutron_client @with_cinder_client @@ -177,12 +177,14 @@ def update_project_quota(nova_client, update_quota(project_id, quota, cinder_client, CINDER) +@operation(resumable=True) @with_keystone_client def list_projects(keystone_client, args, **kwargs): projects_list = keystone_client.projects.list(**args) add_list_to_runtime_properties(ctx, PROJECT_OPENSTACK_TYPE, projects_list) +@operation(resumable=True) @with_nova_client @with_neutron_client @with_cinder_client @@ -198,6 +200,7 @@ def get_project_quota(nova_client, ctx.instance.runtime_properties[QUOTA] = quota +@operation(resumable=True) @with_keystone_client def update_project(keystone_client, args, **kwargs): diff --git a/keystone_plugin/tests/test_project.py b/keystone_plugin/tests/test_project.py index db3eadfe..9ee06f60 100644 --- a/keystone_plugin/tests/test_project.py +++ b/keystone_plugin/tests/test_project.py @@ -85,6 +85,9 @@ def mock_ctx(self, test_vars, test_id, @mock.patch('openstack_plugin_common._handle_kw', autospec=True, return_value=None) + @mock.patch('openstack_plugin_common' + '._check_valid_resource_id_with_operation', + autospec=True, return_value=True) def test_keystone_project_create_and_delete(self, *_): quota = {'nova': {'cpu': 120}, 'neutron': {'networks': 100}} @@ -122,6 +125,9 @@ def test_keystone_project_create_and_delete(self, *_): self.assertNotIn(OPENSTACK_TYPE_PROPERTY, ctx.instance.runtime_properties) + @mock.patch('openstack_plugin_common' + '._check_valid_resource_id_with_operation', + autospec=True, return_value=True) def test_assign_user(self, *_): test_vars = { 'project': {}, @@ -146,6 +152,9 @@ def test_assign_user(self, *_): self.assertEqual({self.test_user: self.test_role}, mock_project._users) + @mock.patch('openstack_plugin_common' + '._check_valid_resource_id_with_operation', + autospec=True, return_value=True) def test_assign_users_not_unique(self, *_): test_vars = { 'project': {}, @@ -171,6 +180,9 @@ def test_assign_users_not_unique(self, *_): cinder_client=mock.MagicMock(), # cinder_client neutron_client=mock.MagicMock()) # neutron_client + @mock.patch('openstack_plugin_common' + '._check_valid_resource_id_with_operation', + autospec=True, return_value=True) def test_assign_user_roles_not_unique(self, *_): test_vars = { 'project': {}, @@ -194,6 +206,9 @@ def test_assign_user_roles_not_unique(self, *_): cinder_client=mock.MagicMock(), # cinder_client neutron_client=mock.MagicMock()) # neutron_client + @mock.patch('openstack_plugin_common' + '._check_valid_resource_id_with_operation', + autospec=True, return_value=True) def test_update_project(self, *_): test_vars = { 'project': {}, @@ -215,6 +230,9 @@ def test_update_project(self, *_): ctx.instance.runtime_properties[ OPENSTACK_NAME_PROPERTY]) + @mock.patch('openstack_plugin_common' + '._check_valid_resource_id_with_operation', + autospec=True, return_value=True) def test_list_projects(self, *_): test_vars = { 'project': {}, @@ -237,6 +255,9 @@ def test_list_projects(self, *_): self.assertEqual(1, len(ctx.instance.runtime_properties[project_list])) + @mock.patch('openstack_plugin_common' + '._check_valid_resource_id_with_operation', + autospec=True, return_value=True) def test_get_quota(self, *_): nova_quota = {'cpu': 120} cinder_quota = {'volumes': 30} diff --git a/keystone_plugin/tests/test_user.py b/keystone_plugin/tests/test_user.py index 4e7d89d2..ddd01077 100644 --- a/keystone_plugin/tests/test_user.py +++ b/keystone_plugin/tests/test_user.py @@ -71,6 +71,9 @@ def mock_ctx(self, test_vars, test_id, @mock.patch('openstack_plugin_common._handle_kw', autospec=True, return_value=None) + @mock.patch('openstack_plugin_common' + '._check_valid_resource_id_with_operation', + autospec=True, return_value=True) def test_keystone_user_create_and_delete(self, *_): test_vars = { 'user': {}, @@ -100,6 +103,9 @@ def test_keystone_user_create_and_delete(self, *_): self.assertNotIn(OPENSTACK_TYPE_PROPERTY, ctx.instance.runtime_properties) + @mock.patch('openstack_plugin_common' + '._check_valid_resource_id_with_operation', + autospec=True, return_value=True) def test_update_user(self, *_): test_vars = { 'user': {}, @@ -117,6 +123,9 @@ def test_update_user(self, *_): ctx.instance.runtime_properties[ OPENSTACK_NAME_PROPERTY]) + @mock.patch('openstack_plugin_common' + '._check_valid_resource_id_with_operation', + autospec=True, return_value=True) def test_list_users(self, *_): test_vars = { 'user': {}, diff --git a/keystone_plugin/user.py b/keystone_plugin/user.py index 0ac3ccd0..74cb18ba 100644 --- a/keystone_plugin/user.py +++ b/keystone_plugin/user.py @@ -30,7 +30,7 @@ RUNTIME_PROPERTIES_KEYS = COMMON_RUNTIME_PROPERTIES_KEYS -@operation +@operation(resumable=True) @with_keystone_client def create(keystone_client, args, **kwargs): if use_external_resource(ctx, keystone_client, USER_OPENSTACK_TYPE): @@ -42,14 +42,14 @@ def create(keystone_client, args, **kwargs): set_openstack_runtime_properties(ctx, user, USER_OPENSTACK_TYPE) -@operation +@operation(resumable=True) @with_keystone_client def delete(keystone_client, **kwargs): delete_resource_and_runtime_properties(ctx, keystone_client, RUNTIME_PROPERTIES_KEYS) -@operation +@operation(resumable=True) @with_keystone_client def update(keystone_client, args, **kwargs): user_dict = create_object_dict(ctx, USER_OPENSTACK_TYPE, args, {}) @@ -58,6 +58,7 @@ def update(keystone_client, args, **kwargs): set_openstack_runtime_properties(ctx, user, USER_OPENSTACK_TYPE) +@operation(resumable=True) @with_keystone_client def list_users(keystone_client, args, **kwargs): users_list = keystone_client.users.list(**args) diff --git a/neutron_plugin/floatingip.py b/neutron_plugin/floatingip.py index d69ea13d..5231a12a 100644 --- a/neutron_plugin/floatingip.py +++ b/neutron_plugin/floatingip.py @@ -43,7 +43,7 @@ FLOATING_NETWORK_ERROR_SUFFIX -@operation +@operation(resumable=True) @with_neutron_client def create(neutron_client, args, **kwargs): @@ -96,12 +96,13 @@ def create(neutron_client, args, **kwargs): ctx.logger.info('Floating IP creation response: {0}'.format(fip)) -@operation +@operation(resumable=True) @with_neutron_client def delete(neutron_client, **kwargs): delete_floatingip(neutron_client) +@operation(resumable=True) @with_neutron_client def list_floatingips(neutron_client, args, **kwargs): fip_list = neutron_client.list_floatingips(**args) @@ -110,13 +111,13 @@ def list_floatingips(neutron_client, args, **kwargs): fip_list.get('floatingips', [])) -@operation +@operation(resumable=True) @with_neutron_client def creation_validation(neutron_client, **kwargs): floatingip_creation_validation(neutron_client, 'floating_ip_address') -@operation +@operation(resumable=True) @with_neutron_client def connect_port(neutron_client, **kwargs): if is_external_relationship_not_conditionally_created(ctx): @@ -129,7 +130,7 @@ def connect_port(neutron_client, **kwargs): floating_ip_id, {FLOATINGIP_OPENSTACK_TYPE: fip}) -@operation +@operation(resumable=True) @with_neutron_client def disconnect_port(neutron_client, **kwargs): if is_external_relationship(ctx): diff --git a/neutron_plugin/network.py b/neutron_plugin/network.py index ad7544d8..9eda3ad7 100644 --- a/neutron_plugin/network.py +++ b/neutron_plugin/network.py @@ -37,7 +37,7 @@ RUNTIME_PROPERTIES_KEYS = COMMON_RUNTIME_PROPERTIES_KEYS -@operation +@operation(resumable=True) @with_neutron_client def create(neutron_client, args, **kwargs): @@ -53,7 +53,7 @@ def create(neutron_client, args, **kwargs): set_neutron_runtime_properties(ctx, net, NETWORK_OPENSTACK_TYPE) -@operation +@operation(resumable=True) @with_neutron_client def start(neutron_client, **kwargs): network_id = get_openstack_id(ctx) @@ -75,7 +75,7 @@ def start(neutron_client, **kwargs): }) -@operation +@operation(resumable=True) @with_neutron_client def stop(neutron_client, **kwargs): if is_external_resource(ctx): @@ -90,13 +90,14 @@ def stop(neutron_client, **kwargs): }) -@operation +@operation(resumable=True) @with_neutron_client def delete(neutron_client, **kwargs): delete_resource_and_runtime_properties(ctx, neutron_client, RUNTIME_PROPERTIES_KEYS) +@operation(resumable=True) @with_neutron_client def list_networks(neutron_client, args, **kwargs): net_list = neutron_client.list_networks(**args) @@ -105,7 +106,7 @@ def list_networks(neutron_client, args, **kwargs): net_list.get('networks', [])) -@operation +@operation(resumable=True) @with_neutron_client def creation_validation(neutron_client, **kwargs): validate_resource(ctx, neutron_client, NETWORK_OPENSTACK_TYPE) diff --git a/neutron_plugin/port.py b/neutron_plugin/port.py index 25735fdf..4aa555f6 100644 --- a/neutron_plugin/port.py +++ b/neutron_plugin/port.py @@ -149,7 +149,7 @@ def _export_ips_to_port_instance(port): ctx.instance.runtime_properties['ipv6_address'] = '' -@operation +@operation(resumable=True) @with_neutron_client def create(neutron_client, args, **kwargs): @@ -188,7 +188,7 @@ def create(neutron_client, args, **kwargs): _export_ips_to_port_instance(p) -@operation +@operation(resumable=True) @with_nova_client @with_neutron_client def attach(nova_client, neutron_client, **kwargs): @@ -249,7 +249,7 @@ def _port_delete(neutron_client, port_id, ext_port): ctx.logger.info("Applied on remove: {}".format(repr(change))) -@operation +@operation(resumable=True) @with_neutron_client def delete(neutron_client, **kwargs): try: @@ -270,7 +270,7 @@ def delete(neutron_client, **kwargs): raise -@operation +@operation(resumable=True) @with_nova_client @with_neutron_client def detach(nova_client, neutron_client, **kwargs): @@ -311,7 +311,7 @@ def detach(nova_client, neutron_client, **kwargs): ctx.logger.info('Successfully detached port {0}'.format(port_id)) -@operation +@operation(resumable=True) @with_neutron_client def connect_security_group(neutron_client, **kwargs): port_id = get_openstack_id(ctx.source) @@ -355,7 +355,7 @@ def connect_security_group(neutron_client, **kwargs): ) -@operation +@operation(resumable=True) @with_neutron_client def disconnect_security_group(neutron_client, **kwargs): port_id = get_openstack_id(ctx.source) @@ -385,6 +385,7 @@ def disconnect_security_group(neutron_client, **kwargs): ) +@operation(resumable=True) @with_neutron_client def list_ports(neutron_client, args, **kwargs): port_list = neutron_client.list_ports(**args) @@ -393,7 +394,7 @@ def list_ports(neutron_client, args, **kwargs): port_list.get('ports', [])) -@operation +@operation(resumable=True) @with_neutron_client def creation_validation(neutron_client, **kwargs): validate_resource(ctx, neutron_client, PORT_OPENSTACK_TYPE) diff --git a/neutron_plugin/rbac_policy.py b/neutron_plugin/rbac_policy.py index 9bc07467..1d26e39f 100644 --- a/neutron_plugin/rbac_policy.py +++ b/neutron_plugin/rbac_policy.py @@ -121,7 +121,7 @@ def create_rbac_policy_object_dict(ctx, args): return rbac_policy -@operation +@operation(resumable=True) @with_neutron_client def create(neutron_client, args, **kwargs): if use_external_resource(ctx, neutron_client, RBAC_POLICY_OPENSTACK_TYPE): @@ -141,7 +141,7 @@ def create(neutron_client, args, **kwargs): set_neutron_runtime_properties(ctx, rp, RBAC_POLICY_OPENSTACK_TYPE) -@operation +@operation(resumable=True) @with_neutron_client def delete(neutron_client, **kwargs): delete_resource_and_runtime_properties( @@ -151,7 +151,7 @@ def delete(neutron_client, **kwargs): ) -@operation +@operation(resumable=True) @with_neutron_client def list_rbac_policies(neutron_client, args, **kwargs): rbac_policies = neutron_client.list_rbac_policies(**args) @@ -162,7 +162,7 @@ def list_rbac_policies(neutron_client, args, **kwargs): ) -@operation +@operation(resumable=True) @with_neutron_client def find_and_delete(neutron_client, args, **kwargs): reference_rbac_policy = create_rbac_policy_object_dict(ctx, args) diff --git a/neutron_plugin/router.py b/neutron_plugin/router.py index 3e0307f6..9ed82387 100644 --- a/neutron_plugin/router.py +++ b/neutron_plugin/router.py @@ -97,7 +97,7 @@ def dict_merge(a, b): return neutron_client.update_router(router_id, new_router) -@operation +@operation(resumable=True) @with_neutron_client def create(neutron_client, args, **kwargs): @@ -133,7 +133,7 @@ def create(neutron_client, args, **kwargs): set_neutron_runtime_properties(ctx, r, ROUTER_OPENSTACK_TYPE) -@operation +@operation(resumable=True) @with_neutron_client def update(neutron_client, args, **kwargs): if not args: @@ -151,7 +151,7 @@ def update(neutron_client, args, **kwargs): return neutron_client.update_router(router_id, args) -@operation +@operation(resumable=True) @with_neutron_client def update_routes(neutron_client, args, **kwargs): routes = args.get(ROUTES_OPENSTACK_TYPE) @@ -179,7 +179,7 @@ def update_routes(neutron_client, args, **kwargs): 'Failed while trying to retrieve router instance') -@operation +@operation(resumable=True) @with_neutron_client def add_routes(neutron_client, args, **kwargs): @@ -216,7 +216,7 @@ def add_routes(neutron_client, args, **kwargs): 'Failed while trying to retrieve router instance') -@operation +@operation(resumable=True) @with_neutron_client def connect_subnet(neutron_client, **kwargs): router_id = get_openstack_id(ctx.target) @@ -236,7 +236,7 @@ def connect_subnet(neutron_client, **kwargs): neutron_client.add_interface_router(router_id, {'subnet_id': subnet_id}) -@operation +@operation(resumable=True) @with_neutron_client def disconnect_subnet(neutron_client, **kwargs): if is_external_relationship(ctx): @@ -256,14 +256,14 @@ def disconnect_subnet(neutron_client, **kwargs): ) -@operation +@operation(resumable=True) @with_neutron_client def delete(neutron_client, **kwargs): delete_resource_and_runtime_properties(ctx, neutron_client, RUNTIME_PROPERTIES_KEYS) -@operation +@operation(resumable=True) @with_neutron_client def delete_routes(neutron_client, **kwargs): @@ -271,6 +271,7 @@ def delete_routes(neutron_client, **kwargs): delete_runtime_properties(ctx, RUNTIME_PROPERTIES_KEYS) +@operation(resumable=True) @with_neutron_client def list_routers(neutron_client, args, **kwargs): router_list = neutron_client.list_routers(**args) @@ -279,7 +280,7 @@ def list_routers(neutron_client, args, **kwargs): router_list.get('routers', [])) -@operation +@operation(resumable=True) @with_neutron_client def creation_validation(neutron_client, **kwargs): validate_resource(ctx, neutron_client, ROUTER_OPENSTACK_TYPE) diff --git a/neutron_plugin/security_group.py b/neutron_plugin/security_group.py index 51849c87..b42abd06 100644 --- a/neutron_plugin/security_group.py +++ b/neutron_plugin/security_group.py @@ -49,7 +49,7 @@ SG_OPENSTACK_TYPE = 'security_group' -@operation +@operation(resumable=True) @with_neutron_client def create( neutron_client, args, @@ -111,12 +111,13 @@ def create( raise -@operation +@operation(resumable=True) @with_neutron_client def delete(neutron_client, **kwargs): delete_sg(neutron_client) +@operation(resumable=True) @with_neutron_client def list_security_groups(neutron_client, args, **kwargs): sg_list = neutron_client.list_security_groups(**args) @@ -125,7 +126,7 @@ def list_security_groups(neutron_client, args, **kwargs): sg_list.get('security_groups', [])) -@operation +@operation(resumable=True) @with_neutron_client def creation_validation(neutron_client, **kwargs): sg_creation_validation(neutron_client, 'remote_ip_prefix') diff --git a/neutron_plugin/subnet.py b/neutron_plugin/subnet.py index 356916d4..ec81feff 100644 --- a/neutron_plugin/subnet.py +++ b/neutron_plugin/subnet.py @@ -41,7 +41,7 @@ RUNTIME_PROPERTIES_KEYS = COMMON_RUNTIME_PROPERTIES_KEYS -@operation +@operation(resumable=True) @with_neutron_client def create(neutron_client, args, **kwargs): @@ -77,13 +77,14 @@ def create(neutron_client, args, **kwargs): set_neutron_runtime_properties(ctx, s, SUBNET_OPENSTACK_TYPE) -@operation +@operation(resumable=True) @with_neutron_client def delete(neutron_client, **kwargs): delete_resource_and_runtime_properties(ctx, neutron_client, RUNTIME_PROPERTIES_KEYS) +@operation(resumable=True) @with_neutron_client def list_subnets(neutron_client, args, **kwargs): subnet_list = neutron_client.list_subnets(**args) @@ -92,7 +93,7 @@ def list_subnets(neutron_client, args, **kwargs): subnet_list.get('subnets', [])) -@operation +@operation(resumable=True) @with_neutron_client def creation_validation(neutron_client, args, **kwargs): validate_resource(ctx, neutron_client, SUBNET_OPENSTACK_TYPE) diff --git a/neutron_plugin/tests/test_port.py b/neutron_plugin/tests/test_port.py index 00b64993..623c82a3 100644 --- a/neutron_plugin/tests/test_port.py +++ b/neutron_plugin/tests/test_port.py @@ -57,6 +57,9 @@ def test_port_delete(self): mock_neutron.body) @mock.patch('openstack_plugin_common._handle_kw') + @mock.patch('openstack_plugin_common' + '._check_valid_resource_id_with_operation', + autospec=True, return_value=True) def test_delete(self, *_): node_props = { 'fixed_ip': '', @@ -116,6 +119,9 @@ def test_port_update(self): {}, port) @mock.patch('openstack_plugin_common._handle_kw') + @mock.patch('openstack_plugin_common' + '._check_valid_resource_id_with_operation', + autospec=True, return_value=True) def test_create(self, *_): node_props = { 'fixed_ip': '', @@ -263,6 +269,9 @@ def show_subnet(self, subnet_id=None): class TestPortSG(unittest.TestCase): @mock.patch('openstack_plugin_common._handle_kw') + @mock.patch('openstack_plugin_common' + '._check_valid_resource_id_with_operation', + autospec=True, return_value=True) def test_connect_sg_to_port(self, *_): mock_neutron = MockNeutronClient(update=True) ctx = MockCloudifyContext( @@ -280,6 +289,9 @@ def test_connect_sg_to_port(self, *_): self.assertIsNone(ctx.operation._operation_retry) @mock.patch('openstack_plugin_common._handle_kw') + @mock.patch('openstack_plugin_common' + '._check_valid_resource_id_with_operation', + autospec=True, return_value=True) def test_connect_sg_to_port_race_condition(self, *_): mock_neutron = MockNeutronClient(update=False) @@ -298,6 +310,9 @@ def test_connect_sg_to_port_race_condition(self, *_): OperationRetry) @mock.patch('openstack_plugin_common._handle_kw') + @mock.patch('openstack_plugin_common' + '._check_valid_resource_id_with_operation', + autospec=True, return_value=True) def test_disconnect_sg_to_port(self, *_): mock_neutron = MockNeutronClient(update=True) ctx = MockCloudifyContext( diff --git a/neutron_plugin/tests/test_rbac_policy.py b/neutron_plugin/tests/test_rbac_policy.py index 3a9687bc..264d12bd 100644 --- a/neutron_plugin/tests/test_rbac_policy.py +++ b/neutron_plugin/tests/test_rbac_policy.py @@ -180,6 +180,9 @@ def mock_all(self, relationships=None, **kwargs): autospec=True, return_value=None ) + @mock.patch('openstack_plugin_common' + '._check_valid_resource_id_with_operation', + autospec=True, return_value=True) def test_create_and_delete(self, *_): # given ctx, neutron_client, _ = self.mock_all() @@ -229,6 +232,9 @@ def test_create_and_delete(self, *_): autospec=True, return_value=None ) + @mock.patch('openstack_plugin_common' + '._check_valid_resource_id_with_operation', + autospec=True, return_value=True) def test_create_and_delete_external_resource(self, *_): # given ctx, neutron_client, _ = self.mock_all(use_external_resource=True) @@ -276,6 +282,9 @@ def test_create_and_delete_external_resource(self, *_): autospec=True, return_value=None ) + @mock.patch('openstack_plugin_common' + '._check_valid_resource_id_with_operation', + autospec=True, return_value=True) def test_create_and_delete_using_relationship(self, *_): # given ctx, neutron_client, _ = self.mock_all( @@ -338,6 +347,9 @@ def test_create_and_delete_using_relationship(self, *_): autospec=True, return_value=None ) + @mock.patch('openstack_plugin_common' + '._check_valid_resource_id_with_operation', + autospec=True, return_value=True) def test_fail_create_using_multiple_relationships(self, *_): # given ctx, neutron_client, _ = self.mock_all( @@ -362,6 +374,9 @@ def test_fail_create_using_multiple_relationships(self, *_): autospec=True, return_value=None ) + @mock.patch('openstack_plugin_common' + '._check_valid_resource_id_with_operation', + autospec=True, return_value=True) def test_fail_create_using_relationship_with_missing_data(self, *_): # given ctx, neutron_client, _ = self.mock_all( @@ -387,6 +402,9 @@ def test_fail_create_using_relationship_with_missing_data(self, *_): autospec=True, return_value=None ) + @mock.patch('openstack_plugin_common' + '._check_valid_resource_id_with_operation', + autospec=True, return_value=True) def test_fail_create_using_relationship_and_properties(self, *_): # given ctx, neutron_client, _ = self.mock_all( @@ -409,6 +427,9 @@ def test_fail_create_using_relationship_and_properties(self, *_): autospec=True, return_value=None ) + @mock.patch('openstack_plugin_common' + '._check_valid_resource_id_with_operation', + autospec=True, return_value=True) def test_create_and_delete_using_args(self, *_): # given ctx, neutron_client, _ = self.mock_all( @@ -463,6 +484,9 @@ def test_create_and_delete_using_args(self, *_): autospec=True, return_value=None ) + @mock.patch('openstack_plugin_common' + '._check_valid_resource_id_with_operation', + autospec=True, return_value=True) def test_fail_create_using_relationship_and_args(self, *_): # given ctx, neutron_client, _ = self.mock_all( @@ -489,6 +513,9 @@ def test_fail_create_using_relationship_and_args(self, *_): autospec=True, return_value=None ) + @mock.patch('openstack_plugin_common' + '._check_valid_resource_id_with_operation', + autospec=True, return_value=True) def test_list(self, *_): # given ctx, neutron_client, _ = self.mock_all() @@ -515,6 +542,9 @@ def test_list(self, *_): autospec=True, return_value=None ) + @mock.patch('openstack_plugin_common' + '._check_valid_resource_id_with_operation', + autospec=True, return_value=True) def test_find_and_delete(self, *_): # given ctx, neutron_client, _ = self.mock_all() diff --git a/nova_plugin/flavor.py b/nova_plugin/flavor.py index 1c08f803..1100ff79 100644 --- a/nova_plugin/flavor.py +++ b/nova_plugin/flavor.py @@ -63,7 +63,7 @@ def _set_tenants_access(ctx, nova_client, flavor): ctx.instance.runtime_properties[TENANTS_PROPERTY] = tenants -@operation +@operation(resumable=True) @with_nova_client def create(nova_client, args, **kwargs): if use_external_resource(ctx, nova_client, FLAVOR_OPENSTACK_TYPE): @@ -79,7 +79,7 @@ def create(nova_client, args, **kwargs): _set_tenants_access(ctx, nova_client, flavor) -@operation +@operation(resumable=True) @with_nova_client def delete(nova_client, **kwargs): delete_resource_and_runtime_properties( @@ -91,6 +91,7 @@ def delete(nova_client, **kwargs): delete_runtime_properties(ctx, [EXTRA_SPECS_PROPERTY, TENANTS_PROPERTY]) +@operation(resumable=True) @with_nova_client def list_flavors(nova_client, args, **kwargs): flavor_list = nova_client.flavors.list(**args) diff --git a/nova_plugin/floatingip.py b/nova_plugin/floatingip.py index e770c540..dd4cd3d5 100644 --- a/nova_plugin/floatingip.py +++ b/nova_plugin/floatingip.py @@ -31,7 +31,7 @@ # nova..findall) and lists, which won't return such unallocated # resources. -@operation +@operation(resumable=True) @with_nova_client def create(nova_client, args, **kwargs): @@ -48,13 +48,13 @@ def create(nova_client, args, **kwargs): set_floatingip_runtime_properties(fip.id, fip.ip) -@operation +@operation(resumable=True) @with_nova_client def delete(nova_client, **kwargs): delete_floatingip(nova_client) -@operation +@operation(resumable=True) @with_nova_client def creation_validation(nova_client, **kwargs): floatingip_creation_validation(nova_client, 'ip') diff --git a/nova_plugin/host_aggregate.py b/nova_plugin/host_aggregate.py index 5222603e..c8baa951 100644 --- a/nova_plugin/host_aggregate.py +++ b/nova_plugin/host_aggregate.py @@ -83,7 +83,7 @@ def _remove_hosts(ctx, nova_client, host_aggregate_id, hosts): delete_runtime_properties(ctx, HOSTS_PROPERTY) -@operation +@operation(resumable=True) @with_nova_client def create(nova_client, args, **kwargs): if use_external_resource(ctx, nova_client, HOST_AGGREGATE_OPENSTACK_TYPE): @@ -107,7 +107,7 @@ def create(nova_client, args, **kwargs): ) -@operation +@operation(resumable=True) @with_nova_client def delete(nova_client, **kwargs): if not is_external_resource(ctx): @@ -129,7 +129,7 @@ def delete(nova_client, **kwargs): ) -@operation +@operation(resumable=True) @with_nova_client def update(nova_client, args, **kwargs): if HOST_AGGREGATE_OPENSTACK_TYPE in args: @@ -147,7 +147,7 @@ def update(nova_client, args, **kwargs): _set_metadata(ctx, nova_client, get_openstack_id(ctx), args) -@operation +@operation(resumable=True) @with_nova_client def list_host_aggregates(nova_client, **kwargs): host_aggregates_list = nova_client.aggregates.list() @@ -159,13 +159,13 @@ def list_host_aggregates(nova_client, **kwargs): ) -@operation +@operation(resumable=True) @with_nova_client def add_hosts(nova_client, hosts, **kwargs): _add_hosts(ctx, nova_client, get_openstack_id(ctx), hosts) -@operation +@operation(resumable=True) @with_nova_client def remove_hosts(nova_client, hosts, **kwargs): _remove_hosts(ctx, nova_client, get_openstack_id(ctx), hosts) diff --git a/nova_plugin/keypair.py b/nova_plugin/keypair.py index 4c0298bb..94e4ac49 100644 --- a/nova_plugin/keypair.py +++ b/nova_plugin/keypair.py @@ -41,7 +41,7 @@ PRIVATE_KEY_PATH_PROP = 'private_key_path' -@operation +@operation(resumable=True) @with_nova_client def create(nova_client, args, **kwargs): @@ -86,7 +86,7 @@ def create(nova_client, args, **kwargs): raise -@operation +@operation(resumable=True) @with_nova_client def delete(nova_client, **kwargs): if not is_external_resource(ctx): @@ -102,13 +102,14 @@ def delete(nova_client, **kwargs): delete_runtime_properties(ctx, RUNTIME_PROPERTIES_KEYS) +@operation(resumable=True) @with_nova_client def list_keypairs(nova_client, args, **kwargs): keypair_list = nova_client.keypairs.list(**args) add_list_to_runtime_properties(ctx, KEYPAIR_OPENSTACK_TYPE, keypair_list) -@operation +@operation(resumable=True) @with_nova_client def creation_validation(nova_client, **kwargs): diff --git a/nova_plugin/security_group.py b/nova_plugin/security_group.py index 283eae85..6d6ef335 100644 --- a/nova_plugin/security_group.py +++ b/nova_plugin/security_group.py @@ -31,7 +31,7 @@ ) -@operation +@operation(resumable=True) @with_nova_client def create(nova_client, args, **kwargs): @@ -69,13 +69,13 @@ def create(nova_client, args, **kwargs): raise -@operation +@operation(resumable=True) @with_nova_client def delete(nova_client, **kwargs): delete_sg(nova_client) -@operation +@operation(resumable=True) @with_nova_client def creation_validation(nova_client, **kwargs): sg_creation_validation(nova_client, 'cidr') diff --git a/nova_plugin/server.py b/nova_plugin/server.py index b707fe3f..17f3c999 100644 --- a/nova_plugin/server.py +++ b/nova_plugin/server.py @@ -257,7 +257,7 @@ def _handle_boot_volume(server, ctx): boot_volume.runtime_properties[OPENSTACK_AZ_PROPERTY] -@operation +@operation(resumable=True) @with_nova_client @with_neutron_client def create(nova_client, neutron_client, args, **kwargs): @@ -412,7 +412,7 @@ def get_network(port_id): return map(get_network, port_ids) -@operation +@operation(resumable=True) @with_nova_client def start(nova_client, start_retry_interval=30, @@ -479,7 +479,7 @@ def start(nova_client, server_state_fault)) -@operation +@operation(resumable=True) @with_nova_client def stop(nova_client, **kwargs): """ @@ -536,7 +536,7 @@ def _server_start(nova_client, server): ctx.logger.info('Server is already started?') -@operation +@operation(resumable=True) @with_nova_client def reboot(nova_client, reboot_type='soft', **kwargs): @@ -612,7 +612,7 @@ def _check_finished_upload(nova_client, server, waiting_list): retry_after=30) -@operation +@operation(resumable=True) @with_nova_client def freeze_suspend(nova_client, **kwargs): """ @@ -623,7 +623,7 @@ def freeze_suspend(nova_client, **kwargs): _server_suspend(nova_client, server) -@operation +@operation(resumable=True) @with_nova_client def freeze_resume(nova_client, **kwargs): """ @@ -634,7 +634,7 @@ def freeze_resume(nova_client, **kwargs): _server_resume(nova_client, server) -@operation +@operation(resumable=True) @with_nova_client @with_glance_client def snapshot_create(nova_client, glance_client, **kwargs): @@ -692,7 +692,7 @@ def _get_image(glance_client, snapshot_name, snapshot_incremental): return None, None -@operation +@operation(resumable=True) @with_nova_client @with_glance_client def snapshot_apply(nova_client, glance_client, **kwargs): @@ -759,7 +759,7 @@ def _image_delete(glance_client, snapshot_name, snapshot_incremental): retry_after=30) -@operation +@operation(resumable=True) @with_nova_client @with_glance_client def snapshot_delete(nova_client, glance_client, **kwargs): @@ -781,7 +781,7 @@ def snapshot_delete(nova_client, glance_client, **kwargs): return _image_delete(glance_client, snapshot_name, snapshot_incremental) -@operation +@operation(resumable=True) @with_nova_client def delete(nova_client, **kwargs): if not is_external_resource(ctx): @@ -796,7 +796,7 @@ def delete(nova_client, **kwargs): delete_runtime_properties(ctx, RUNTIME_PROPERTIES_KEYS) -@operation +@operation(resumable=True) @with_nova_client def list_servers(nova_client, args, **kwargs): server_list = nova_client.servers.list(**args) @@ -896,7 +896,7 @@ def _get_mgmt_ip(net_ips): ctx.instance.runtime_properties[IPV6_PROPERTY] = '' -@operation +@operation(resumable=True) @with_nova_client def connect_floatingip(nova_client, fixed_ip='', **kwargs): server_id = get_openstack_id(ctx.source) @@ -925,7 +925,7 @@ def connect_floatingip(nova_client, fixed_ip='', **kwargs): .format(floating_ip_address, server_id)) -@operation +@operation(resumable=True) @with_nova_client @with_neutron_client def disconnect_floatingip(nova_client, neutron_client, **kwargs): @@ -945,7 +945,7 @@ def disconnect_floatingip(nova_client, neutron_client, **kwargs): .format(server_floating_ip['floating_ip_address'])) -@operation +@operation(resumable=True) @with_nova_client def connect_security_group(nova_client, **kwargs): server_id = get_openstack_id(ctx.source) @@ -987,7 +987,7 @@ def group_matches(security_group): is_connected=True) -@operation +@operation(resumable=True) @with_nova_client def disconnect_security_group(nova_client, **kwargs): if is_external_relationship(ctx): @@ -1015,7 +1015,7 @@ def disconnect_security_group(nova_client, **kwargs): is_connected=False) -@operation +@operation(resumable=True) @with_nova_client @with_cinder_client def attach_volume(nova_client, @@ -1110,7 +1110,7 @@ def _detach_volume(nova_client, cinder_client, server_id, volume_id, timeout=status_timeout) -@operation +@operation(resumable=True) @with_nova_client @with_cinder_client def detach_volume(nova_client, @@ -1227,7 +1227,7 @@ def _get_properties_by_node_instance_id(node_instance_id): return node.properties -@operation +@operation(resumable=True) @with_nova_client def creation_validation(nova_client, args, **kwargs): diff --git a/nova_plugin/server_group.py b/nova_plugin/server_group.py index 4aefdda8..3d3af2ac 100644 --- a/nova_plugin/server_group.py +++ b/nova_plugin/server_group.py @@ -32,7 +32,7 @@ SERVER_GROUP_OPENSTACK_TYPE = 'server_group' -@operation +@operation(resumable=True) @with_nova_client def create(nova_client, args, **kwargs): if use_external_resource(ctx, nova_client, SERVER_GROUP_OPENSTACK_TYPE): @@ -50,7 +50,7 @@ def create(nova_client, args, **kwargs): SERVER_GROUP_OPENSTACK_TYPE) -@operation +@operation(resumable=True) @with_nova_client def delete(nova_client, **kwargs): if not is_external_resource(ctx): @@ -64,6 +64,7 @@ def delete(nova_client, **kwargs): delete_runtime_properties(ctx, RUNTIME_PROPERTIES_KEYS) +@operation(resumable=True) @with_nova_client def list_servergroups(nova_client, args, **kwargs): server_group_list = nova_client.server_groups.list(**args) @@ -72,7 +73,7 @@ def list_servergroups(nova_client, args, **kwargs): server_group_list) -@operation +@operation(resumable=True) @with_nova_client def creation_validation(nova_client, **kwargs): validate_resource(ctx, nova_client, SERVER_GROUP_OPENSTACK_TYPE) diff --git a/nova_plugin/tests/test_flavor.py b/nova_plugin/tests/test_flavor.py index 64a7b5c1..d384d11a 100644 --- a/nova_plugin/tests/test_flavor.py +++ b/nova_plugin/tests/test_flavor.py @@ -95,6 +95,9 @@ def mock_ctx(self, autospec=True, return_value=None ) + @mock.patch('openstack_plugin_common' + '._check_valid_resource_id_with_operation', + autospec=True, return_value=True) def test_flavor_create_and_delete(self, *_): # given test_vars = { @@ -146,6 +149,9 @@ def test_flavor_create_and_delete(self, *_): autospec=True, return_value=None ) + @mock.patch('openstack_plugin_common' + '._check_valid_resource_id_with_operation', + autospec=True, return_value=True) def test_flavor_create_and_delete_with_extra_specs_and_tenants(self, *_): # given test_vars_tenant_id = 'some_tenant_id' @@ -222,6 +228,9 @@ def test_flavor_create_and_delete_with_extra_specs_and_tenants(self, *_): ctx.instance.runtime_properties ) + @mock.patch('openstack_plugin_common' + '._check_valid_resource_id_with_operation', + autospec=True, return_value=True) def test_list_flavors(self, *_): # given test_vars = { diff --git a/nova_plugin/tests/test_host_aggregate.py b/nova_plugin/tests/test_host_aggregate.py index 8482dd88..21f12c22 100644 --- a/nova_plugin/tests/test_host_aggregate.py +++ b/nova_plugin/tests/test_host_aggregate.py @@ -101,6 +101,9 @@ def mock_ctx(self, autospec=True, return_value=None ) + @mock.patch('openstack_plugin_common' + '._check_valid_resource_id_with_operation', + autospec=True, return_value=True) def test_add_hosts(self, *_): # given test_vars_host1 = 'cf4301' @@ -158,6 +161,9 @@ def test_add_hosts(self, *_): autospec=True, return_value=None ) + @mock.patch('openstack_plugin_common' + '._check_valid_resource_id_with_operation', + autospec=True, return_value=True) def test_remove_hosts(self, *_): # given test_vars_host1 = 'cf4301' @@ -214,6 +220,9 @@ def test_remove_hosts(self, *_): autospec=True, return_value=None ) + @mock.patch('openstack_plugin_common' + '._check_valid_resource_id_with_operation', + autospec=True, return_value=True) def test_create_and_delete(self, *_): # given test_vars_host1 = 'cf4301' @@ -309,6 +318,9 @@ def test_create_and_delete(self, *_): autospec=True, return_value=None ) + @mock.patch('openstack_plugin_common' + '._check_valid_resource_id_with_operation', + autospec=True, return_value=True) @mock.patch( 'openstack_plugin_common.get_resource_by_name_or_id', autospec=True, @@ -377,6 +389,9 @@ def test_create_and_delete_external_resource(self, *_): autospec=True, return_value=None ) + @mock.patch('openstack_plugin_common' + '._check_valid_resource_id_with_operation', + autospec=True, return_value=True) def test_update(self, *_): # given test_vars_hosts = ['cf4301', 'openstack-kilo-t2.novalocal'] @@ -455,6 +470,9 @@ def test_update(self, *_): autospec=True, return_value=None ) + @mock.patch('openstack_plugin_common' + '._check_valid_resource_id_with_operation', + autospec=True, return_value=True) def test_list(self, *_): # given test_vars_host1 = 'cf4301' diff --git a/nova_plugin/tests/test_server.py b/nova_plugin/tests/test_server.py index 1e892d5f..5068cd48 100644 --- a/nova_plugin/tests/test_server.py +++ b/nova_plugin/tests/test_server.py @@ -53,6 +53,9 @@ class TestServer(unittest.TestCase): @mock.patch('nova_plugin.server.create') @mock.patch('nova_plugin.server._set_network_and_ip_runtime_properties') + @mock.patch('openstack_plugin_common' + '._check_valid_resource_id_with_operation', + autospec=True, return_value=True) @workflow_test(blueprint_path, copy_plugin_yaml=True) def test_nova_server_lifecycle_start(self, cfy_local, *_): @@ -80,6 +83,9 @@ def mock_get_server_by_context(*_): @workflow_test(blueprint_path, copy_plugin_yaml=True) @mock.patch('nova_plugin.server.create') @mock.patch('nova_plugin.server._set_network_and_ip_runtime_properties') + @mock.patch('openstack_plugin_common' + '._check_valid_resource_id_with_operation', + autospec=True, return_value=True) def test_nova_server_lifecycle_start_after_stop(self, cfy_local, *_): test_vars = { @@ -111,6 +117,9 @@ def mock_get_server_by_context(_): @workflow_test(blueprint_path, copy_plugin_yaml=True) @mock.patch('nova_plugin.server.create') @mock.patch('nova_plugin.server._set_network_and_ip_runtime_properties') + @mock.patch('openstack_plugin_common' + '._check_valid_resource_id_with_operation', + autospec=True, return_value=True) def test_nova_server_lifecycle_start_unknown_status(self, cfy_local, *_): test_vars = { 'counter': 0, diff --git a/openstack_plugin_common/__init__.py b/openstack_plugin_common/__init__.py index d802efba..ef1ab215 100644 --- a/openstack_plugin_common/__init__.py +++ b/openstack_plugin_common/__init__.py @@ -52,7 +52,7 @@ OPENSTACK_RESOURCE_PROPERTY = 'external_resource' # resource's parameters CONDITIONALLY_CREATED = 'conditionally_created' # resource was # conditionally created -CONFIG_RUNTIME_PROPERTY = CONFIG_PROPERTY # openstack configuration +CONFIG_RUNTIME_PROPERTY = CONFIG_PROPERTY # openstack configuration # operation inputs CONFIG_INPUT = CONFIG_PROPERTY @@ -99,6 +99,36 @@ 'glanceclient.v2.client'] } +CLOUDIFY_CREATE_OPERATION = 'cloudify.interfaces.lifecycle.create' +CLOUDIFY_PRE_CONFIGURE_OPERATION = 'cloudify.interfaces.lifecycle.preconfigure' +CLOUDIFY_CONFIGURE_OPERATION = 'cloudify.interfaces.lifecycle.configure' +CLOUDIFY_POST_CONFIGURE_OPERATION = 'cloudify.interfaces.lifecycle.' \ + 'postconfigure' +CLOUDIFY_START_OPERATION = 'cloudify.interfaces.lifecycle.start' +CLOUDIFY_STOP_OPERATION = 'cloudify.interfaces.lifecycle.stop' +CLOUDIFY_DELETE_OPERATION = 'cloudify.interfaces.lifecycle.delete' +CLOUDIFY_UNLINK_OPERATION = 'cloudify.interfaces.relationship_lifecycle.unlink' +CLOUDIFY_ESTABLISH_OPERATION = 'cloudify.interfaces.relationship_lifecycle.' \ + 'establish' + +CLOUDIFY_CONF_CYCLE_OPERATION = (CLOUDIFY_CREATE_OPERATION, + CLOUDIFY_CONFIGURE_OPERATION, + CLOUDIFY_START_OPERATION, + CLOUDIFY_STOP_OPERATION) + +CLOUDIFY_RELATION_CYCLE_OPERATION = (CLOUDIFY_ESTABLISH_OPERATION, + CLOUDIFY_PRE_CONFIGURE_OPERATION, + CLOUDIFY_POST_CONFIGURE_OPERATION) + +CLOUDIFY_OPERATIONS_WITHOUT_CREATE = (CLOUDIFY_PRE_CONFIGURE_OPERATION, + CLOUDIFY_CONFIGURE_OPERATION, + CLOUDIFY_POST_CONFIGURE_OPERATION, + CLOUDIFY_START_OPERATION, + CLOUDIFY_STOP_OPERATION, + CLOUDIFY_DELETE_OPERATION, + CLOUDIFY_UNLINK_OPERATION, + CLOUDIFY_ESTABLISH_OPERATION) + # TODO: Move this to cloudify-plugins-common (code freeze currently # in effect). @@ -108,6 +138,7 @@ class CloudifyCtxLogHandler(logging.Handler): A logger attached to this handler will result in logging being passed through to the Cloudify logger. """ + def __init__(self, ctx): """ Constructor. @@ -299,7 +330,6 @@ def get_property(ctx, property_name, kwargs={}, default=None): def transform_resource_name(ctx, res): - if isinstance(res, basestring): res = {'name': res} @@ -320,7 +350,7 @@ def transform_resource_name(ctx, res): "already has this prefix".format(name, pfx)) else: ctx.logger.info("Transformed resource name '{0}' to '{1}'".format( - name, res['name'])) + name, res['name'])) return res['name'] @@ -341,7 +371,6 @@ def _get_resource_by_name_or_id_from_ctx(ctx, name_field_name, openstack_type, def get_resource_by_name_or_id( resource_id, openstack_type, sugared_client, raise_if_not_found=True, name_field_name='name'): - # search for resource by name (or name-equivalent field) search_param = {name_field_name: resource_id} resource = sugared_client.cosmo_get_if_exists(openstack_type, @@ -437,9 +466,9 @@ def validate_resource(ctx, sugared_client, openstack_type, or resource_quota == INFINITE_RESOURCE_QUOTA: ctx.logger.debug( 'OK: {0} (node {1}) can be created. provisioned {2}: {3}, ' - 'quota: {4}' - .format(openstack_type, ctx.node.id, openstack_type_plural, - resource_amount, resource_quota)) + 'quota: {4}'.format(openstack_type, ctx.node.id, + openstack_type_plural, + resource_amount, resource_quota)) else: err = ('{0} (node {1}) cannot be created due to quota limitations.' ' provisioned {2}: {3}, quota: {4}' @@ -473,15 +502,15 @@ def is_external_resource(ctx): def is_external_resource_not_conditionally_created(ctx): return is_external_resource_by_properties(ctx.node.properties) and \ - not ctx.instance.runtime_properties.get(CONDITIONALLY_CREATED) + not ctx.instance.runtime_properties.get(CONDITIONALLY_CREATED) def is_external_relationship_not_conditionally_created(ctx): return is_external_resource_by_properties(ctx.source.node.properties) and \ - is_external_resource_by_properties(ctx.target.node.properties) and \ - not ctx.source.instance.runtime_properties.get( - CONDITIONALLY_CREATED) and not \ - ctx.target.instance.runtime_properties.get(CONDITIONALLY_CREATED) + is_external_resource_by_properties(ctx.target.node.properties) and \ + not ctx.source.instance.runtime_properties.get( + CONDITIONALLY_CREATED) and not \ + ctx.target.instance.runtime_properties.get(CONDITIONALLY_CREATED) def is_create_if_missing(ctx): @@ -490,17 +519,17 @@ def is_create_if_missing(ctx): def is_external_relationship(ctx): return is_external_resource_by_properties(ctx.source.node.properties) and \ - is_external_resource_by_properties(ctx.target.node.properties) + is_external_resource_by_properties(ctx.target.node.properties) def is_external_resource_by_properties(properties): return USE_EXTERNAL_RESOURCE_PROPERTY in properties and \ - properties[USE_EXTERNAL_RESOURCE_PROPERTY] + properties[USE_EXTERNAL_RESOURCE_PROPERTY] def is_create_if_missing_by_properties(properties): return CREATE_IF_MISSING_PROPERTY in properties and \ - properties[CREATE_IF_MISSING_PROPERTY] + properties[CREATE_IF_MISSING_PROPERTY] def delete_runtime_properties(ctx, runtime_properties_keys): @@ -542,7 +571,6 @@ def create_object_dict(ctx, object_name, args, object_dict=None): def add_list_to_runtime_properties(ctx, openstack_type_name, object_list): - objects = [] for obj in object_list: @@ -574,7 +602,6 @@ def set_neutron_runtime_properties(ctx, openstack_object, openstack_type): class Config(object): - OPENSTACK_CONFIG_PATH_ENV_VAR = 'OPENSTACK_CONFIG_PATH' OPENSTACK_CONFIG_PATH_DEFAULT_PATH = '~/openstack_config.json' OPENSTACK_ENV_VAR_PREFIX = 'OS_' @@ -613,7 +640,6 @@ def update_config(overridden_cfg, overriding_cfg): class OpenStackClient(object): - COMMON = {'username', 'password', 'auth_url'} AUTH_SETS = [ COMMON | {'tenant_name'}, @@ -635,7 +661,7 @@ def __init__(self, client_name, client_class, config=None, *args, **kw): # This check to make sure that blueprint openstack config # contains all the required auth params + any non-auth param - if set(config.keys())\ + if set(config.keys()) \ in self.AUTH_SETS and config.keys() in self.NON_AUTH_ITEMS: # Check if there is any value exists on ``cfg`` @@ -837,7 +863,6 @@ def _cosmo_get(self, obj_type_single, if_exists, **kw): class GlanceClient(OpenStackClient): - # Can't glance_url be figured out from keystone REQUIRED_CONFIG_PARAMS = \ ['username', 'password', 'tenant_name', 'auth_url'] @@ -880,14 +905,18 @@ def with_neutron_client(f): @wraps(f) def wrapper(*args, **kw): _handle_kw('neutron_client', NeutronClientWithSugar, kw) + if not _check_valid_resource_id_with_operation(kw): + return try: return f(*args, **kw) except neutron_exceptions.NeutronClientException as e: + _check_valid_resource_id_with_operation(kw, True) if e.status_code in _non_recoverable_error_codes: _re_raise(e, recoverable=False, status_code=e.status_code) else: raise + return wrapper @@ -895,16 +924,21 @@ def with_nova_client(f): @wraps(f) def wrapper(*args, **kw): _handle_kw('nova_client', NovaClientWithSugar, kw) + if not _check_valid_resource_id_with_operation(kw): + return try: return f(*args, **kw) except nova_exceptions.OverLimit as e: + _check_valid_resource_id_with_operation(kw, True) _re_raise(e, recoverable=True, retry_after=e.retry_after) except nova_exceptions.ClientException as e: + _check_valid_resource_id_with_operation(kw, True) if e.code in _non_recoverable_error_codes: _re_raise(e, recoverable=False, status_code=e.code) else: raise + return wrapper @@ -912,14 +946,18 @@ def with_cinder_client(f): @wraps(f) def wrapper(*args, **kw): _handle_kw('cinder_client', CinderClientWithSugar, kw) + if not _check_valid_resource_id_with_operation(kw): + return try: return f(*args, **kw) except cinder_exceptions.ClientException as e: + _check_valid_resource_id_with_operation(kw, True) if e.code in _non_recoverable_error_codes: _re_raise(e, recoverable=False, status_code=e.code) else: raise + return wrapper @@ -927,14 +965,18 @@ def with_glance_client(f): @wraps(f) def wrapper(*args, **kw): _handle_kw('glance_client', GlanceClientWithSugar, kw) + if not _check_valid_resource_id_with_operation(kw): + return try: return f(*args, **kw) except glance_exceptions.ClientException as e: + _check_valid_resource_id_with_operation(kw, True) if e.code in _non_recoverable_error_codes: _re_raise(e, recoverable=False, status_code=e.code) else: raise + return wrapper @@ -942,21 +984,227 @@ def with_keystone_client(f): @wraps(f) def wrapper(*args, **kw): _handle_kw('keystone_client', KeystoneClientWithSugar, kw) + if not _check_valid_resource_id_with_operation(kw): + return try: return f(*args, **kw) except keystone_exceptions.HTTPError as e: + _check_valid_resource_id_with_operation(kw, True) if e.http_status in _non_recoverable_error_codes: _re_raise(e, recoverable=False, status_code=e.http_status) else: raise except keystone_exceptions.ClientException as e: + _check_valid_resource_id_with_operation(kw, True) _re_raise(e, recoverable=False) + return wrapper -def _handle_kw(client_name, client_class, kw): +def handle_conf_cycle_operations(_ctx, operation_name, instance_id, exception): + """ + function to handle CLOUDIFY_CONF_CYCLE_OPERATION given exception + if no exception the function add runtime property for that operation + if exception remove the runtime property, handle start/stop operations flag + :param _ctx: + :param operation_name: + :param instance_id: + :param exception: + :return: + """ + op_prefix = operation_name.split(".")[-1] + runtime_prop = op_prefix + '_' + instance_id + + if not exception: + # if runtime for this operation already set return false + if _ctx.instance.runtime_properties.get(runtime_prop): + _ctx.logger.info(op_prefix + ' already done') + return False + else: + # set runtime property of the operation and return true + _ctx.instance.runtime_properties[runtime_prop] = True + + # if operation start , delete stop property if exists + if operation_name == CLOUDIFY_START_OPERATION and \ + _ctx.instance.runtime_properties.get( + 'stop_' + instance_id): + del _ctx.instance.runtime_properties['stop_' + + instance_id] + + # if operation stop , delete start property if exists + elif operation_name == CLOUDIFY_STOP_OPERATION and \ + _ctx.instance.runtime_properties.get( + 'start_' + instance_id): + del _ctx.instance.runtime_properties['start_' + + instance_id] + else: # in exception + # remove the runtime for the operation + del _ctx.instance.runtime_properties[runtime_prop] + + # if operation start , add stop property + if operation_name == CLOUDIFY_START_OPERATION: + _ctx.instance.runtime_properties[ + 'stop_' + instance_id] = True + + # if operation stop , add start property + elif operation_name == CLOUDIFY_STOP_OPERATION: + _ctx.instance.runtime_properties[ + 'start_' + instance_id] = True + return True + + +def handle_delete_operations(_ctx, instance_id, exception): + """ + function to handle delete operation given exception + affected flags are for : configure,create and stop operations + if exception set the affected flags to True + if no exception delete the affected flags + :param _ctx: + :param instance_id: + :param exception: + :return: + """ + # in case no exception delete configure, create and stop properties + # in case of exception add configure, create and stop properties + for op in (CLOUDIFY_CREATE_OPERATION, + CLOUDIFY_CONFIGURE_OPERATION, + CLOUDIFY_STOP_OPERATION): + runtime_prop = op.split(".")[-1] + '_' + instance_id + if not exception: + if _ctx.instance.runtime_properties.get(runtime_prop): + del _ctx.instance.runtime_properties[runtime_prop] + else: + _ctx.instance.runtime_properties[runtime_prop] = True + return True + + +def handle_rel_cycle_operations(_ctx, operation_name, exception): + """ + function to handle CLOUDIFY_RELATION_CYCLE_OPERATION given exception + if no exception add the runtime flags for the operations + if exception delete the runtime flags + :param _ctx: + :param operation_name: + :param exception: + :return: + """ + # rely on target since relation could be to more than one instance + op_prefix = operation_name.split(".")[-1] + target_id = _ctx.target.instance.id + runtime_prop = op_prefix + '_' + target_id + if not exception: + if _ctx.source.instance.runtime_properties.get( + runtime_prop): + _ctx.logger.info(op_prefix + ' already done') + return False + else: + _ctx.source.instance.runtime_properties[runtime_prop] \ + = True + else: + del _ctx.source.instance.runtime_properties[runtime_prop] + return True + +def handle_unlink_operations(_ctx, exception): + """ + function to handle unlink operation given exception + if no exception we remove establish runtime flag + if exception we set establish runtime flag to True + :param _ctx: + :param exception: + :return: + """ + # in case of unlink operation we remove establish in case of + # no exception otherwise we return the establish property + target_id = _ctx.target.instance.id + runtime_prop = CLOUDIFY_ESTABLISH_OPERATION.split(".")[-1] + '_' + \ + target_id + if not exception: + if _ctx.source.instance.runtime_properties.get( + runtime_prop): + del _ctx.source.instance.runtime_properties[ + runtime_prop] + else: + _ctx.source.instance.runtime_properties[runtime_prop] \ + = True + return True + + +def handle_no_resource_id_operations(_ctx, operation_name, instance_id, + exception): + """ + function to handle instance operations with no resource_id given exception + if no exception and create_operation set create runtime flag to true , + skip any other since we don't have resource_id to take action + if exception and create_operation delete create runtime flag + :param _ctx: + :param operation_name: + :param instance_id: + :param exception: + :return: + """ + op_prefix = operation_name.split(".")[-1] + runtime_prop = op_prefix + '_' + instance_id + + if operation_name == CLOUDIFY_CREATE_OPERATION: + if not exception: + _ctx.instance.runtime_properties[runtime_prop] = True + else: + del _ctx.instance.runtime_properties[runtime_prop] + # skip operations since resource_id is not assigned to take action + elif operation_name in CLOUDIFY_OPERATIONS_WITHOUT_CREATE: + _ctx.logger.info("ignoring action since resource_id is not set") + return False + return True + + +def _check_valid_resource_id_with_operation(kw, exception=False): + """ + function used to check if we should do the requested operation from + workflow or not ;given runtime properties + :param kw: + :return: + """ + + _ctx = _find_context_in_kw(kw) or ctx + resource_id = None + instance_id = None + + operation_name = _ctx.operation.name or "" + + if _ctx.type == context.NODE_INSTANCE: + resource_id = _ctx.instance.runtime_properties.get( + OPENSTACK_ID_PROPERTY) + instance_id = _ctx.instance.id + elif _ctx.type == context.RELATIONSHIP_INSTANCE: + resource_id = _ctx.source.instance.runtime_properties.get( + OPENSTACK_ID_PROPERTY) + instance_id = _ctx.source.instance.id + + # check resource_id + if resource_id: + if operation_name in CLOUDIFY_CONF_CYCLE_OPERATION: + return handle_conf_cycle_operations(_ctx, operation_name, + instance_id, exception) + + elif operation_name == CLOUDIFY_DELETE_OPERATION: + return handle_delete_operations(_ctx, instance_id, exception) + + elif operation_name in CLOUDIFY_RELATION_CYCLE_OPERATION: + return handle_rel_cycle_operations(_ctx, operation_name, exception) + + elif operation_name == CLOUDIFY_UNLINK_OPERATION: + return handle_unlink_operations(_ctx, exception) + + else: + return handle_no_resource_id_operations(_ctx, operation_name, + instance_id, exception) + + return True + + +def _handle_kw(client_name, client_class, kw): _ctx = _find_context_in_kw(kw) or ctx if _ctx.type == context.NODE_INSTANCE: config = _ctx.node.properties.get(CONFIG_PROPERTY) @@ -1100,7 +1348,7 @@ def cosmo_list(self, obj_type_single, **kw): """ Sugar for list_XXXs()['XXXs'] """ obj_type_plural = self.cosmo_plural(obj_type_single) for obj in getattr(self, 'list_' + obj_type_plural)(**kw)[ - obj_type_plural]: + obj_type_plural]: yield obj def cosmo_delete_resource(self, obj_type_single, obj_id): @@ -1125,7 +1373,7 @@ def cosmo_list_prefixed(self, obj_type_single, name_prefix): def cosmo_delete_prefixed(self, name_prefix): # Cleanup all neutron.list_XXX() objects with names starting # with self.name_prefix - for obj_type_single in 'port', 'router', 'network', 'subnet',\ + for obj_type_single in 'port', 'router', 'network', 'subnet', \ 'security_group': for obj in self.cosmo_list_prefixed(obj_type_single, name_prefix): if obj_type_single == 'router': @@ -1185,7 +1433,7 @@ def get_quota(self, obj_type_single): class KeystoneClientWithSugar(OpenStackClient): # keystone does not have resource quota - KEYSTONE_INFINITE_RESOURCE_QUOTA = 10**9 + KEYSTONE_INFINITE_RESOURCE_QUOTA = 10 ** 9 def __init__(self, *args, **kw): super(KeystoneClientWithSugar, self).__init__( @@ -1211,7 +1459,7 @@ def get_quota(self, obj_type_single): class GlanceClientWithSugar(OpenStackClient): - GLANCE_INIFINITE_RESOURCE_QUOTA = 10**9 + GLANCE_INIFINITE_RESOURCE_QUOTA = 10 ** 9 def __init__(self, *args, **kw): super(GlanceClientWithSugar, self).__init__( diff --git a/openstack_plugin_common/tests/openstack_client_tests.py b/openstack_plugin_common/tests/openstack_client_tests.py index b833582f..b52e1317 100644 --- a/openstack_plugin_common/tests/openstack_client_tests.py +++ b/openstack_plugin_common/tests/openstack_client_tests.py @@ -20,9 +20,13 @@ import __builtin__ as builtins import mock +from cloudify.constants import NODE_INSTANCE, RELATIONSHIP_INSTANCE from cloudify.exceptions import NonRecoverableError -from cloudify.mocks import MockCloudifyContext +from cloudify.mocks import MockCloudifyContext, MockNodeInstanceContext, \ + MockContext, MockNodeContext +from cloudify.state import current_ctx + import openstack_plugin_common as common @@ -316,16 +320,16 @@ def test___init___multi_region(self, m_session): } with mock.patch.object( - builtins, 'open', - mock.mock_open( - read_data=""" + builtins, 'open', + mock.mock_open( + read_data=""" { "region": "region from file", "other": "this one should get through" } """ - ), - create=True, + ), + create=True, ): common.OpenStackClient('fred', mock_client_class, cfg) @@ -333,7 +337,7 @@ def test___init___multi_region(self, m_session): 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): @@ -739,10 +743,12 @@ def _test_quota_validation(self, amount, quota, failure_expected): 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: @@ -837,6 +843,7 @@ def _raise_error(*_): def _return_something(*_): return mock.MagicMock() + return_value = _return_something if exists else _raise_error if exists: properties.update({'resource_id': 'rid'}) @@ -879,3 +886,501 @@ def test_raise_quota_error(self): create_if_missing=True, exists=False, client_mock_provided=client_mock) + + +class ValidateResourceIdTests(unittest.TestCase): + + def mock_ctx(self, + ctx_type, + test_vars, + test_id, + test_deployment_id, + test_operation_name, + runtime_properties=None): + ctx = MockContext() + + ctx.node = MockNodeContext(properties=test_vars) + ctx.instance = \ + MockNodeInstanceContext(id=test_id, + runtime_properties=runtime_properties) + ctx.deployment = mock.Mock() + ctx.deployment.id = test_deployment_id + ctx.operation = mock.Mock() + ctx.operation.name = test_operation_name + ctx.type = ctx_type + if ctx_type == RELATIONSHIP_INSTANCE: + ctx.instance = None + ctx.source = MockNodeContext(properties=test_vars) + ctx.source.instance = \ + MockNodeInstanceContext(id=test_id, + runtime_properties=runtime_properties) + ctx.target = MockNodeContext(properties={}) + ctx.target.instance = \ + MockNodeInstanceContext(id='port_12c45', runtime_properties={}) + ctx.logger = mock.Mock() + + current_ctx.set(ctx) + return ctx + + def _test_validate_resource_id(self, + ctx_type, + instance_id, + operation_name, + runtime_properties=None, + exception=False): + node_context = self.mock_ctx(ctx_type, + test_vars={}, + test_id=instance_id, + test_deployment_id='1-sdtw2', + test_operation_name=operation_name, + runtime_properties=runtime_properties) + + kwargs = { + 'ctx': node_context + } + + result = common._check_valid_resource_id_with_operation(kwargs, + exception) + + return node_context, result + + def test_create_with_no_resource_id_no_exception(self): + result, validate_res = \ + self._test_validate_resource_id(NODE_INSTANCE, + 'xyz1', common. + CLOUDIFY_CREATE_OPERATION, {}, + False) + runtime_prop = 'create_xyz1' + self.assertTrue(result.instance.runtime_properties.get(runtime_prop)) + self.assertTrue(validate_res) + + def test_create_with_no_resource_id_with_exception(self): + result, validate_res = \ + self._test_validate_resource_id(NODE_INSTANCE, + 'xyz1', common. + CLOUDIFY_CREATE_OPERATION, + {'create_xyz1': True}, + True) + runtime_prop = 'create_xyz1' + self.assertIsNone(result.instance.runtime_properties.get(runtime_prop)) + self.assertTrue(validate_res) + + def test_create_with_resource_id_no_exception(self): + result, validate_res = \ + self._test_validate_resource_id(NODE_INSTANCE, + 'xyz1', common. + CLOUDIFY_CREATE_OPERATION, + {'external_id': '123-43-312'}, + False) + runtime_prop = 'create_xyz1' + self.assertTrue(result.instance.runtime_properties.get(runtime_prop)) + self.assertTrue(validate_res) + + def test_create_with_resource_id_with_exception(self): + result, validate_res = \ + self._test_validate_resource_id(NODE_INSTANCE, + 'xyz1', common. + CLOUDIFY_CREATE_OPERATION, + {'external_id': '123-43-312', + 'create_xyz1': True}, + True) + runtime_prop = 'create_xyz1' + self.assertIsNone(result.instance.runtime_properties.get(runtime_prop)) + self.assertTrue(validate_res) + + def test_configure_with_resource_id_no_exception(self): + result, validate_res = \ + self._test_validate_resource_id(NODE_INSTANCE, + 'xyz1', common. + CLOUDIFY_CONFIGURE_OPERATION, + {'external_id': '123-43-312'}, + False) + runtime_prop = 'configure_xyz1' + self.assertTrue(result.instance.runtime_properties.get(runtime_prop)) + self.assertTrue(validate_res) + + def test_configure_with_resource_id_with_exception(self): + result, validate_res = \ + self._test_validate_resource_id(NODE_INSTANCE, + 'xyz1', common. + CLOUDIFY_CONFIGURE_OPERATION, + {'configure_xyz1': True, + 'external_id': '123-43-312'}, + True) + runtime_prop = 'configure_xyz1' + self.assertIsNone(result.instance.runtime_properties.get(runtime_prop)) + self.assertTrue(validate_res) + + def test_configure_no_resource_id_with_exception(self): + result, validate_res = \ + self._test_validate_resource_id(NODE_INSTANCE, + 'xyz1', common. + CLOUDIFY_CONFIGURE_OPERATION, + {}, + True) + runtime_prop = 'configure_xyz1' + self.assertIsNone(result.instance.runtime_properties.get(runtime_prop)) + self.assertFalse(validate_res) + + def test_configure_no_resource_id_no_exception(self): + result, validate_res = \ + self._test_validate_resource_id(NODE_INSTANCE, + 'xyz1', common. + CLOUDIFY_CONFIGURE_OPERATION, + {}, + False) + runtime_prop = 'configure_xyz1' + self.assertIsNone(result.instance.runtime_properties.get(runtime_prop)) + self.assertFalse(validate_res) + + def test_start_with_resource_id_no_exception(self): + result, validate_res = \ + self._test_validate_resource_id(NODE_INSTANCE, + 'xyz1', common. + CLOUDIFY_START_OPERATION, + {'external_id': '123-43-312'}, + False) + runtime_prop = 'start_xyz1' + self.assertTrue(result.instance.runtime_properties.get(runtime_prop)) + self.assertTrue(validate_res) + + def test_start_no_resource_id_no_exception(self): + result, validate_res = \ + self._test_validate_resource_id(NODE_INSTANCE, + 'xyz1', common. + CLOUDIFY_START_OPERATION, + {}, + False) + runtime_prop = 'start_xyz1' + self.assertIsNone(result.instance.runtime_properties.get(runtime_prop)) + self.assertFalse(validate_res) + + def test_start_no_resource_id_with_exception(self): + result, validate_res = \ + self._test_validate_resource_id(NODE_INSTANCE, + 'xyz1', common. + CLOUDIFY_START_OPERATION, + {}, + True) + runtime_prop = 'start_xyz1' + self.assertIsNone(result.instance.runtime_properties.get(runtime_prop)) + self.assertFalse(validate_res) + + def test_start_with_resource_id_with_exception(self): + result, validate_res = \ + self._test_validate_resource_id(NODE_INSTANCE, + 'xyz1', common. + CLOUDIFY_START_OPERATION, + {'start_xyz1': True, + 'external_id': '123-43-312'}, + True) + runtime_prop = 'start_xyz1' + self.assertIsNone(result.instance.runtime_properties.get(runtime_prop)) + self.assertTrue(validate_res) + + def test_stop_with_resource_id_no_exception(self): + result, validate_res = \ + self._test_validate_resource_id(NODE_INSTANCE, + 'xyz1', common. + CLOUDIFY_STOP_OPERATION, + {'external_id': '123-43-312'}, + False) + runtime_prop = 'stop_xyz1' + self.assertTrue(result.instance.runtime_properties.get(runtime_prop)) + self.assertTrue(validate_res) + + def test_stop_with_resource_id_with_exception(self): + result, validate_res = \ + self._test_validate_resource_id(NODE_INSTANCE, + 'xyz1', common. + CLOUDIFY_STOP_OPERATION, + {'stop_xyz1': True, + 'external_id': '123-43-312'}, + True) + runtime_prop = 'stop_xyz1' + self.assertIsNone(result.instance.runtime_properties.get(runtime_prop)) + self.assertTrue(validate_res) + + def test_stop_no_resource_id_no_exception(self): + result, validate_res = \ + self._test_validate_resource_id(NODE_INSTANCE, + 'xyz1', common. + CLOUDIFY_STOP_OPERATION, + {}, + False) + runtime_prop = 'stop_xyz1' + self.assertIsNone(result.instance.runtime_properties.get(runtime_prop)) + self.assertFalse(validate_res) + + def test_stop_no_resource_id_with_exception(self): + result, validate_res = \ + self._test_validate_resource_id(NODE_INSTANCE, + 'xyz1', common. + CLOUDIFY_STOP_OPERATION, + {}, + True) + runtime_prop = 'stop_xyz1' + self.assertIsNone(result.instance.runtime_properties.get(runtime_prop)) + self.assertFalse(validate_res) + + def test_delete_with_resource_id_no_exception(self): + result, validate_res = \ + self._test_validate_resource_id(NODE_INSTANCE, + 'xyz1', common. + CLOUDIFY_DELETE_OPERATION, + {'external_id': '123-43-312', + 'create_xyz1': True, + 'configure_xyz1': True}, + False) + runtime_prop = 'create_xyz1' + self.assertIsNone(result.instance.runtime_properties.get(runtime_prop)) + runtime_prop = 'configure_xyz1' + self.assertIsNone(result.instance.runtime_properties.get(runtime_prop)) + self.assertTrue(validate_res) + + def test_delete_with_resource_id_with_exception(self): + result, validate_res = \ + self._test_validate_resource_id(NODE_INSTANCE, + 'xyz1', common. + CLOUDIFY_DELETE_OPERATION, + {'external_id': '123-43-312'}, + True) + runtime_prop = 'create_xyz1' + self.assertTrue(result.instance.runtime_properties.get(runtime_prop)) + runtime_prop = 'configure_xyz1' + self.assertTrue(result.instance.runtime_properties.get(runtime_prop)) + self.assertTrue(validate_res) + + def test_delete_no_resource_id_no_exception(self): + result, validate_res = \ + self._test_validate_resource_id(NODE_INSTANCE, + 'xyz1', common. + CLOUDIFY_DELETE_OPERATION, + {}, + False) + runtime_prop = 'create_xyz1' + self.assertIsNone(result.instance.runtime_properties.get(runtime_prop)) + self.assertFalse(validate_res) + + def test_delete_no_resource_id_with_exception(self): + result, validate_res = \ + self._test_validate_resource_id(NODE_INSTANCE, + 'xyz1', common. + CLOUDIFY_DELETE_OPERATION, + {}, + True) + runtime_prop = 'create_xyz1' + self.assertIsNone(result.instance.runtime_properties.get(runtime_prop)) + self.assertFalse(validate_res) + + def test_pre_conf_with_resource_id_no_exception(self): + result, validate_res = \ + self._test_validate_resource_id(RELATIONSHIP_INSTANCE, 'xyz1', + common. + CLOUDIFY_PRE_CONFIGURE_OPERATION, + {'external_id': '123-43-312', + 'create_xyz1': True, + 'configure_xyz1': True}, + False) + runtime_prop = 'preconfigure_port_12c45' + self.assertTrue(result.source.instance.runtime_properties. + get(runtime_prop)) + self.assertTrue(validate_res) + + def test_pre_conf_with_resource_id_with_exception(self): + result, validate_res = \ + self._test_validate_resource_id(RELATIONSHIP_INSTANCE, 'xyz1', + common. + CLOUDIFY_PRE_CONFIGURE_OPERATION, + {'external_id': '123-43-312', + 'create_xyz1': True, + 'configure_xyz1': True, + 'preconfigure_port_12c45': True}, + True) + runtime_prop = 'preconfigure_port_12c45' + self.assertIsNone(result.source.instance.runtime_properties. + get(runtime_prop)) + self.assertTrue(validate_res) + + def test_pre_conf_no_resource_id_no_exception(self): + result, validate_res = \ + self._test_validate_resource_id(RELATIONSHIP_INSTANCE, 'xyz1', + common. + CLOUDIFY_PRE_CONFIGURE_OPERATION, + {}, + False) + runtime_prop = 'preconfigure_port_12c45' + self.assertIsNone(result.source.instance.runtime_properties. + get(runtime_prop)) + self.assertFalse(validate_res) + + def test_pre_conf_no_resource_id_with_exception(self): + result, validate_res = \ + self._test_validate_resource_id(RELATIONSHIP_INSTANCE, 'xyz1', + common. + CLOUDIFY_PRE_CONFIGURE_OPERATION, + {}, + True) + runtime_prop = 'preconfigure_port_12c45' + self.assertIsNone(result.source.instance.runtime_properties. + get(runtime_prop)) + self.assertFalse(validate_res) + + def test_post_conf_with_resource_id_no_exception(self): + result, validate_res = \ + self._test_validate_resource_id(RELATIONSHIP_INSTANCE, 'xyz1', + common. + CLOUDIFY_POST_CONFIGURE_OPERATION, + {'external_id': '123-43-312', + 'create_xyz1': True, + 'configure_xyz1': True}, + False) + runtime_prop = 'postconfigure_port_12c45' + self.assertTrue(result.source.instance.runtime_properties. + get(runtime_prop)) + self.assertTrue(validate_res) + + def test_post_conf_with_resource_id_with_exception(self): + result, validate_res = \ + self._test_validate_resource_id(RELATIONSHIP_INSTANCE, 'xyz1', + common. + CLOUDIFY_POST_CONFIGURE_OPERATION, + {'external_id': '123-43-312', + 'create_xyz1': True, + 'configure_xyz1': True, + 'postconfigure_port_12c45': True}, + True) + runtime_prop = 'postconfigure_port_12c45' + self.assertIsNone(result.source.instance.runtime_properties. + get(runtime_prop)) + self.assertTrue(validate_res) + + def test_post_conf_no_resource_id_no_exception(self): + result, validate_res = \ + self._test_validate_resource_id(RELATIONSHIP_INSTANCE, 'xyz1', + common. + CLOUDIFY_POST_CONFIGURE_OPERATION, + {}, + False) + runtime_prop = 'postconfigure_port_12c45' + self.assertIsNone(result.source.instance.runtime_properties. + get(runtime_prop)) + self.assertFalse(validate_res) + + def test_post_conf_no_resource_id_with_exception(self): + result, validate_res = \ + self._test_validate_resource_id(RELATIONSHIP_INSTANCE, 'xyz1', + common. + CLOUDIFY_POST_CONFIGURE_OPERATION, + {}, + True) + runtime_prop = 'postconfigure_port_12c45' + self.assertIsNone(result.source.instance.runtime_properties. + get(runtime_prop)) + self.assertFalse(validate_res) + + def test_establish_with_resource_id_no_exception(self): + result, validate_res = \ + self._test_validate_resource_id(RELATIONSHIP_INSTANCE, 'xyz1', + common. + CLOUDIFY_ESTABLISH_OPERATION, + {'external_id': '123-43-312', + 'create_xyz1': True, + 'configure_xyz1': True}, + False) + runtime_prop = 'establish_port_12c45' + self.assertTrue(result.source.instance.runtime_properties. + get(runtime_prop)) + self.assertTrue(validate_res) + + def test_establish_with_resource_id_with_exception(self): + result, validate_res = \ + self._test_validate_resource_id(RELATIONSHIP_INSTANCE, 'xyz1', + common. + CLOUDIFY_ESTABLISH_OPERATION, + {'external_id': '123-43-312', + 'create_xyz1': True, + 'configure_xyz1': True, + 'establish_port_12c45': True}, + True) + runtime_prop = 'establish_port_12c45' + self.assertIsNone(result.source.instance.runtime_properties. + get(runtime_prop)) + self.assertTrue(validate_res) + + def test_establish_no_resource_id_no_exception(self): + result, validate_res = \ + self._test_validate_resource_id(RELATIONSHIP_INSTANCE, 'xyz1', + common. + CLOUDIFY_ESTABLISH_OPERATION, + {}, + False) + runtime_prop = 'establish_port_12c45' + self.assertIsNone(result.source.instance.runtime_properties. + get(runtime_prop)) + self.assertFalse(validate_res) + + def test_establish_no_resource_id_with_exception(self): + result, validate_res = \ + self._test_validate_resource_id(RELATIONSHIP_INSTANCE, 'xyz1', + common. + CLOUDIFY_ESTABLISH_OPERATION, + {}, + True) + runtime_prop = 'establish_port_12c45' + self.assertIsNone(result.source.instance.runtime_properties. + get(runtime_prop)) + self.assertFalse(validate_res) + + def test_unlink_with_resource_id_no_exception(self): + result, validate_res = \ + self._test_validate_resource_id(RELATIONSHIP_INSTANCE, 'xyz1', + common. + CLOUDIFY_UNLINK_OPERATION, + {'external_id': '123-43-312', + 'create_xyz1': True, + 'configure_xyz1': True, + 'establish_port_12c45': True}, + False) + runtime_prop = 'establish_port_12c45' + self.assertIsNone(result.source.instance.runtime_properties. + get(runtime_prop)) + self.assertTrue(validate_res) + + def test_unlink_with_resource_id_with_exception(self): + result, validate_res = \ + self._test_validate_resource_id(RELATIONSHIP_INSTANCE, 'xyz1', + common. + CLOUDIFY_UNLINK_OPERATION, + {'external_id': '123-43-312', + 'create_xyz1': True, + 'configure_xyz1': True}, + True) + runtime_prop = 'establish_port_12c45' + self.assertTrue(result.source.instance.runtime_properties. + get(runtime_prop)) + self.assertTrue(validate_res) + + def test_unlink_no_resource_id_no_exception(self): + result, validate_res = \ + self._test_validate_resource_id(RELATIONSHIP_INSTANCE, 'xyz1', + common. + CLOUDIFY_UNLINK_OPERATION, + {}, + False) + runtime_prop = 'establish_port_12c45' + self.assertIsNone(result.source.instance.runtime_properties. + get(runtime_prop)) + self.assertFalse(validate_res) + + def test_unlink_no_resource_id_with_exception(self): + result, validate_res = \ + self._test_validate_resource_id(RELATIONSHIP_INSTANCE, 'xyz1', + common. + CLOUDIFY_UNLINK_OPERATION, + {}, + True) + runtime_prop = 'establish_port_12c45' + self.assertIsNone(result.source.instance.runtime_properties. + get(runtime_prop)) + self.assertFalse(validate_res) diff --git a/plugin.yaml b/plugin.yaml index 7fd972c0..77bfb1a9 100644 --- a/plugin.yaml +++ b/plugin.yaml @@ -5,9 +5,9 @@ plugins: openstack: executor: central_deployment_agent - source: https://github.com/cloudify-cosmo/cloudify-openstack-plugin/archive/2.14.12.zip + source: https://github.com/cloudify-cosmo/cloudify-openstack-plugin/archive/2.14.13.zip package_name: cloudify-openstack-plugin - package_version: '2.14.12' + package_version: '2.14.13' data_types: cloudify.openstack.types.custom_configuration: diff --git a/setup.py b/setup.py index ccd81af7..54f5b38f 100644 --- a/setup.py +++ b/setup.py @@ -19,7 +19,7 @@ setup( zip_safe=True, name='cloudify-openstack-plugin', - version='2.14.12', + version='2.14.13', author='Cloudify', author_email='hello@cloudify.co', packages=[