From fc388d829232cd95251ac487b56d2b1945081b62 Mon Sep 17 00:00:00 2001 From: aditi Date: Thu, 29 Mar 2018 11:48:08 +0000 Subject: [PATCH] Exclude Project By Audit Scope This patch adds project_id in compute CDM, It also adds logic for excluding project_id in audit scope. Change-Id: Ife228e3d1855b65abee637516470e463ba8a2815 Implements: blueprint audit-scope-exclude-project --- ...cope-exclude-project-511a7720aac00dff.yaml | 6 ++ .../decision_engine/model/collector/nova.py | 15 +++- .../decision_engine/model/element/instance.py | 1 + .../model/notification/nova.py | 1 + watcher/decision_engine/scope/compute.py | 19 ++++- .../decision_engine/cluster/test_nova_cdmc.py | 1 + .../decision_engine/model/data/scenario_1.xml | 70 +++++++++--------- .../scenario_1_with_1_node_unavailable.xml | 72 +++++++++---------- .../model/data/scenario_1_with_metrics.xml | 4 +- .../model/data/scenario_2_with_metrics.xml | 12 ++-- .../model/data/scenario_3_with_2_nodes.xml | 4 +- .../model/data/scenario_3_with_metrics.xml | 8 +-- .../data/scenario_5_with_instance_disk_0.xml | 2 +- .../model/data/scenario_6_with_2_nodes.xml | 8 +-- .../model/data/scenario_7_with_2_nodes.xml | 8 +-- .../model/data/scenario_8_with_4_nodes.xml | 12 ++-- ..._9_with_3_active_plus_1_disabled_nodes.xml | 12 ++-- .../model/faker_cluster_state.py | 4 +- .../decision_engine/scope/test_compute.py | 21 +++++- 19 files changed, 169 insertions(+), 111 deletions(-) create mode 100644 releasenotes/notes/bp-audit-scope-exclude-project-511a7720aac00dff.yaml diff --git a/releasenotes/notes/bp-audit-scope-exclude-project-511a7720aac00dff.yaml b/releasenotes/notes/bp-audit-scope-exclude-project-511a7720aac00dff.yaml new file mode 100644 index 000000000..ccca33368 --- /dev/null +++ b/releasenotes/notes/bp-audit-scope-exclude-project-511a7720aac00dff.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + Feature to exclude instances from audit scope based on project_id is added. + Now instances from particular project in OpenStack can be excluded from audit + defining scope in audit templates. diff --git a/watcher/decision_engine/model/collector/nova.py b/watcher/decision_engine/model/collector/nova.py index 55d06c00f..d4fccdb5c 100644 --- a/watcher/decision_engine/model/collector/nova.py +++ b/watcher/decision_engine/model/collector/nova.py @@ -104,6 +104,18 @@ class NovaClusterDataModelCollector(base.BaseClusterDataModelCollector): "items": { "type": "object" } + }, + "projects": { + "type": "array", + "items": { + "type": "object", + "properties": { + "uuid": { + "type": "string" + } + }, + "additionalProperties": False + } } }, "additionalProperties": False @@ -348,7 +360,8 @@ class ModelBuilder(object): "disk_capacity": flavor["disk"], "vcpus": flavor["vcpus"], "state": getattr(instance, "OS-EXT-STS:vm_state"), - "metadata": instance.metadata} + "metadata": instance.metadata, + "project_id": instance.tenant_id} # node_attributes = dict() # node_attributes["layer"] = "virtual" diff --git a/watcher/decision_engine/model/element/instance.py b/watcher/decision_engine/model/element/instance.py index 5b4dca935..c0ef7dcaa 100644 --- a/watcher/decision_engine/model/element/instance.py +++ b/watcher/decision_engine/model/element/instance.py @@ -52,6 +52,7 @@ class Instance(compute_resource.ComputeResource): "disk_capacity": wfields.NonNegativeIntegerField(), "vcpus": wfields.NonNegativeIntegerField(), "metadata": wfields.JsonField(), + "project_id": wfields.UUIDField(), } def accept(self, visitor): diff --git a/watcher/decision_engine/model/notification/nova.py b/watcher/decision_engine/model/notification/nova.py index b788113a2..f8640badf 100644 --- a/watcher/decision_engine/model/notification/nova.py +++ b/watcher/decision_engine/model/notification/nova.py @@ -76,6 +76,7 @@ class NovaNotification(base.NotificationEndpoint): 'disk': disk_gb, 'disk_capacity': disk_gb, 'metadata': instance_metadata, + 'tenant_id': instance_data['tenant_id'] }) try: diff --git a/watcher/decision_engine/scope/compute.py b/watcher/decision_engine/scope/compute.py index 75764fe2c..903642833 100644 --- a/watcher/decision_engine/scope/compute.py +++ b/watcher/decision_engine/scope/compute.py @@ -87,6 +87,7 @@ class ComputeScope(base.BaseScope): instances_to_exclude = kwargs.get('instances') nodes_to_exclude = kwargs.get('nodes') instance_metadata = kwargs.get('instance_metadata') + projects_to_exclude = kwargs.get('projects') for resource in resources: if 'instances' in resource: @@ -105,6 +106,9 @@ class ComputeScope(base.BaseScope): elif 'instance_metadata' in resource: instance_metadata.extend( [metadata for metadata in resource['instance_metadata']]) + elif 'projects' in resource: + projects_to_exclude.extend( + [project['uuid'] for project in resource['projects']]) def remove_nodes_from_model(self, nodes_to_remove, cluster_model): for node_uuid in nodes_to_remove: @@ -144,6 +148,13 @@ class ComputeScope(base.BaseScope): if str(value).lower() == str(metadata.get(key)).lower(): instances_to_remove.add(uuid) + def exclude_instances_with_given_project( + self, projects_to_exclude, cluster_model, instances_to_exclude): + all_instances = cluster_model.get_all_instances() + for uuid, instance in all_instances.items(): + if instance.project_id in projects_to_exclude: + instances_to_exclude.add(uuid) + def get_scoped_model(self, cluster_model): """Leave only nodes and instances proposed in the audit scope""" if not cluster_model: @@ -154,6 +165,7 @@ class ComputeScope(base.BaseScope): nodes_to_remove = set() instances_to_exclude = [] instance_metadata = [] + projects_to_exclude = [] compute_scope = [] model_hosts = list(cluster_model.get_all_compute_nodes().keys()) @@ -177,7 +189,8 @@ class ComputeScope(base.BaseScope): self.exclude_resources( rule['exclude'], instances=instances_to_exclude, nodes=nodes_to_exclude, - instance_metadata=instance_metadata) + instance_metadata=instance_metadata, + projects=projects_to_exclude) instances_to_exclude = set(instances_to_exclude) if allowed_nodes: @@ -190,6 +203,10 @@ class ComputeScope(base.BaseScope): self.exclude_instances_with_given_metadata( instance_metadata, cluster_model, instances_to_exclude) + if projects_to_exclude: + self.exclude_instances_with_given_project( + projects_to_exclude, cluster_model, instances_to_exclude) + self.update_exclude_instance_in_model(instances_to_exclude, cluster_model) diff --git a/watcher/tests/decision_engine/cluster/test_nova_cdmc.py b/watcher/tests/decision_engine/cluster/test_nova_cdmc.py index 715ed01d6..424b4d93b 100644 --- a/watcher/tests/decision_engine/cluster/test_nova_cdmc.py +++ b/watcher/tests/decision_engine/cluster/test_nova_cdmc.py @@ -60,6 +60,7 @@ class TestNovaClusterDataModelCollector(base.TestCase): human_id='fake_instance', flavor={'ram': 333, 'disk': 222, 'vcpus': 4, 'id': 1}, metadata={'hi': 'hello'}, + tenant_id='ff560f7e-dbc8-771f-960c-164482fce21b', ) setattr(fake_instance, 'OS-EXT-STS:vm_state', 'VM_STATE') setattr(fake_instance, 'OS-EXT-SRV-ATTR:host', 'test_hostname') diff --git a/watcher/tests/decision_engine/model/data/scenario_1.xml b/watcher/tests/decision_engine/model/data/scenario_1.xml index 95cba68a9..da8f7b2a0 100644 --- a/watcher/tests/decision_engine/model/data/scenario_1.xml +++ b/watcher/tests/decision_engine/model/data/scenario_1.xml @@ -1,47 +1,47 @@ - - + + - + - - - + + + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/watcher/tests/decision_engine/model/data/scenario_1_with_1_node_unavailable.xml b/watcher/tests/decision_engine/model/data/scenario_1_with_1_node_unavailable.xml index fb19025f1..aad624bb9 100644 --- a/watcher/tests/decision_engine/model/data/scenario_1_with_1_node_unavailable.xml +++ b/watcher/tests/decision_engine/model/data/scenario_1_with_1_node_unavailable.xml @@ -1,50 +1,50 @@ - - + + - + - - - + + + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/watcher/tests/decision_engine/model/data/scenario_1_with_metrics.xml b/watcher/tests/decision_engine/model/data/scenario_1_with_metrics.xml index fffa2fcc2..cc624882e 100644 --- a/watcher/tests/decision_engine/model/data/scenario_1_with_metrics.xml +++ b/watcher/tests/decision_engine/model/data/scenario_1_with_metrics.xml @@ -1,8 +1,8 @@ - + - + diff --git a/watcher/tests/decision_engine/model/data/scenario_2_with_metrics.xml b/watcher/tests/decision_engine/model/data/scenario_2_with_metrics.xml index 6e94543f4..177d3f249 100644 --- a/watcher/tests/decision_engine/model/data/scenario_2_with_metrics.xml +++ b/watcher/tests/decision_engine/model/data/scenario_2_with_metrics.xml @@ -1,11 +1,11 @@ - - - - - - + + + + + + diff --git a/watcher/tests/decision_engine/model/data/scenario_3_with_2_nodes.xml b/watcher/tests/decision_engine/model/data/scenario_3_with_2_nodes.xml index 209a83684..aa96f89ce 100644 --- a/watcher/tests/decision_engine/model/data/scenario_3_with_2_nodes.xml +++ b/watcher/tests/decision_engine/model/data/scenario_3_with_2_nodes.xml @@ -1,8 +1,8 @@ - + - + diff --git a/watcher/tests/decision_engine/model/data/scenario_3_with_metrics.xml b/watcher/tests/decision_engine/model/data/scenario_3_with_metrics.xml index e734d3595..996499d70 100644 --- a/watcher/tests/decision_engine/model/data/scenario_3_with_metrics.xml +++ b/watcher/tests/decision_engine/model/data/scenario_3_with_metrics.xml @@ -1,9 +1,9 @@ - - - - + + + + diff --git a/watcher/tests/decision_engine/model/data/scenario_5_with_instance_disk_0.xml b/watcher/tests/decision_engine/model/data/scenario_5_with_instance_disk_0.xml index 0d71f90f8..5555d9bd0 100644 --- a/watcher/tests/decision_engine/model/data/scenario_5_with_instance_disk_0.xml +++ b/watcher/tests/decision_engine/model/data/scenario_5_with_instance_disk_0.xml @@ -1,5 +1,5 @@ - + diff --git a/watcher/tests/decision_engine/model/data/scenario_6_with_2_nodes.xml b/watcher/tests/decision_engine/model/data/scenario_6_with_2_nodes.xml index a75be0abe..bccc33e85 100644 --- a/watcher/tests/decision_engine/model/data/scenario_6_with_2_nodes.xml +++ b/watcher/tests/decision_engine/model/data/scenario_6_with_2_nodes.xml @@ -1,10 +1,10 @@ - - + + - - + + diff --git a/watcher/tests/decision_engine/model/data/scenario_7_with_2_nodes.xml b/watcher/tests/decision_engine/model/data/scenario_7_with_2_nodes.xml index 2e9e2a134..e66cb4cd2 100644 --- a/watcher/tests/decision_engine/model/data/scenario_7_with_2_nodes.xml +++ b/watcher/tests/decision_engine/model/data/scenario_7_with_2_nodes.xml @@ -1,10 +1,10 @@ - - + + - - + + diff --git a/watcher/tests/decision_engine/model/data/scenario_8_with_4_nodes.xml b/watcher/tests/decision_engine/model/data/scenario_8_with_4_nodes.xml index dac56c1bf..aaa318980 100644 --- a/watcher/tests/decision_engine/model/data/scenario_8_with_4_nodes.xml +++ b/watcher/tests/decision_engine/model/data/scenario_8_with_4_nodes.xml @@ -1,16 +1,16 @@ - - - + + + - + - + - + diff --git a/watcher/tests/decision_engine/model/data/scenario_9_with_3_active_plus_1_disabled_nodes.xml b/watcher/tests/decision_engine/model/data/scenario_9_with_3_active_plus_1_disabled_nodes.xml index c66239edb..a87b9a37b 100644 --- a/watcher/tests/decision_engine/model/data/scenario_9_with_3_active_plus_1_disabled_nodes.xml +++ b/watcher/tests/decision_engine/model/data/scenario_9_with_3_active_plus_1_disabled_nodes.xml @@ -1,16 +1,16 @@ - - - + + + - + - + - + diff --git a/watcher/tests/decision_engine/model/faker_cluster_state.py b/watcher/tests/decision_engine/model/faker_cluster_state.py index 0e05714dc..e5fc503c4 100644 --- a/watcher/tests/decision_engine/model/faker_cluster_state.py +++ b/watcher/tests/decision_engine/model/faker_cluster_state.py @@ -83,6 +83,7 @@ class FakerModelCollector(base.BaseClusterDataModelCollector): for i in range(0, instance_count): instance_uuid = "INSTANCE_{0}".format(i) + project_id = "project_{0}".format(i) instance_attributes = { "uuid": instance_uuid, "memory": 2, @@ -90,7 +91,8 @@ class FakerModelCollector(base.BaseClusterDataModelCollector): "disk_capacity": 20, "vcpus": 10, "metadata": - '{"optimize": true,"top": "floor","nested": {"x": "y"}}' + '{"optimize": true,"top": "floor","nested": {"x": "y"}}', + "project_id": project_id } instance = element.Instance(**instance_attributes) diff --git a/watcher/tests/decision_engine/scope/test_compute.py b/watcher/tests/decision_engine/scope/test_compute.py index e714b8bac..03979e6ba 100644 --- a/watcher/tests/decision_engine/scope/test_compute.py +++ b/watcher/tests/decision_engine/scope/test_compute.py @@ -198,14 +198,18 @@ class TestComputeScope(base.TestCase): {'compute_nodes': [{'name': 'Node_2'}, {'name': 'Node_3'}]}, {'instance_metadata': [{'optimize': True}, - {'optimize1': False}]}] + {'optimize1': False}]}, + {'projects': [{'uuid': 'PROJECT_1'}, + {'uuid': 'PROJECT_2'}]}] instances_to_exclude = [] nodes_to_exclude = [] instance_metadata = [] + projects_to_exclude = [] compute.ComputeScope([], mock.Mock(), osc=mock.Mock()).exclude_resources( resources_to_exclude, instances=instances_to_exclude, - nodes=nodes_to_exclude, instance_metadata=instance_metadata) + nodes=nodes_to_exclude, instance_metadata=instance_metadata, + projects=projects_to_exclude) self.assertEqual(['Node_0', 'Node_1', 'Node_2', 'Node_3'], sorted(nodes_to_exclude)) @@ -213,6 +217,8 @@ class TestComputeScope(base.TestCase): sorted(instances_to_exclude)) self.assertEqual([{'optimize': True}, {'optimize1': False}], instance_metadata) + self.assertEqual(['PROJECT_1', 'PROJECT_2'], + sorted(projects_to_exclude)) def test_exclude_instances_with_given_metadata(self): cluster = self.fake_cluster.generate_scenario_1() @@ -233,6 +239,17 @@ class TestComputeScope(base.TestCase): instance_metadata, cluster, instances_to_remove) self.assertEqual(set(), instances_to_remove) + def test_exclude_instances_with_given_project(self): + cluster = self.fake_cluster.generate_scenario_1() + instances_to_exclude = set() + projects_to_exclude = ['project_1', 'project_2'] + compute.ComputeScope( + [], mock.Mock(), + osc=mock.Mock()).exclude_instances_with_given_project( + projects_to_exclude, cluster, instances_to_exclude) + self.assertEqual(['INSTANCE_1', 'INSTANCE_2'], + sorted(instances_to_exclude)) + def test_remove_nodes_from_model(self): model = self.fake_cluster.generate_scenario_1() compute.ComputeScope([], mock.Mock(),