Coverage for watcher/decision_engine/scope/storage.py: 94%
104 statements
« prev ^ index » next coverage.py v7.8.2, created at 2025-06-17 12:22 +0000
« prev ^ index » next coverage.py v7.8.2, created at 2025-06-17 12:22 +0000
1# Licensed under the Apache License, Version 2.0 (the "License");
2# you may not use this file except in compliance with the License.
3# You may obtain a copy of the License at
4#
5# http://www.apache.org/licenses/LICENSE-2.0
6#
7# Unless required by applicable law or agreed to in writing, software
8# distributed under the License is distributed on an "AS IS" BASIS,
9# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
10# implied.
11# See the License for the specific language governing permissions and
12# limitations under the License.
14from watcher.common import cinder_helper
15from watcher.common import exception
17from watcher.decision_engine.scope import base
20class StorageScope(base.BaseScope):
21 """Storage Audit Scope Handler"""
23 def __init__(self, scope, config, osc=None):
24 super(StorageScope, self).__init__(scope, config)
25 self._osc = osc
26 self.wrapper = cinder_helper.CinderHelper(osc=self._osc)
28 def _collect_vtype(self, volume_types, allowed_nodes):
29 service_list = self.wrapper.get_storage_node_list()
31 vt_names = [volume_type['name'] for volume_type in volume_types]
32 include_all_nodes = False
33 if '*' in vt_names:
34 if len(vt_names) == 1:
35 include_all_nodes = True
36 else:
37 raise exception.WildcardCharacterIsUsed(
38 resource="volume_types")
39 for service in service_list:
40 if include_all_nodes:
41 allowed_nodes.append(service.host)
42 continue
43 backend = service.host.split('@')[1]
44 v_types = self.wrapper.get_volume_type_by_backendname(
45 backend)
46 for volume_type in v_types:
47 if volume_type in vt_names:
48 # Note(adisky): It can generate duplicate values
49 # but it will later converted to set
50 allowed_nodes.append(service.host)
52 def _collect_zones(self, availability_zones, allowed_nodes):
53 service_list = self.wrapper.get_storage_node_list()
54 zone_names = [zone['name'] for zone
55 in availability_zones]
56 include_all_nodes = False
57 if '*' in zone_names:
58 if len(zone_names) == 1:
59 include_all_nodes = True
60 else:
61 raise exception.WildcardCharacterIsUsed(
62 resource="availability zones")
63 for service in service_list:
64 if service.zone in zone_names or include_all_nodes:
65 allowed_nodes.append(service.host)
67 def exclude_resources(self, resources, **kwargs):
68 pools_to_exclude = kwargs.get('pools')
69 volumes_to_exclude = kwargs.get('volumes')
70 projects_to_exclude = kwargs.get('projects')
72 for resource in resources:
73 if 'storage_pools' in resource:
74 pools_to_exclude.extend(
75 [storage_pool['name'] for storage_pool
76 in resource['storage_pools']])
78 elif 'volumes' in resource:
79 volumes_to_exclude.extend(
80 [volume['uuid'] for volume in
81 resource['volumes']])
83 elif 'projects' in resource: 83 ↛ 72line 83 didn't jump to line 72 because the condition on line 83 was always true
84 projects_to_exclude.extend(
85 [project['uuid'] for project in
86 resource['projects']])
88 def exclude_pools(self, pools_to_exclude, cluster_model):
89 for pool_name in pools_to_exclude:
90 pool = cluster_model.get_pool_by_pool_name(pool_name)
91 volumes = cluster_model.get_pool_volumes(pool)
92 for volume in volumes:
93 cluster_model.remove_volume(volume)
94 cluster_model.remove_pool(pool)
96 def exclude_volumes(self, volumes_to_exclude, cluster_model):
97 for volume_uuid in volumes_to_exclude:
98 volume = cluster_model.get_volume_by_uuid(volume_uuid)
99 cluster_model.remove_volume(volume)
101 def exclude_projects(self, projects_to_exclude, cluster_model):
102 all_volumes = cluster_model.get_all_volumes()
103 for volume_uuid in all_volumes:
104 volume = all_volumes.get(volume_uuid)
105 if volume.project_id in projects_to_exclude: 105 ↛ 106line 105 didn't jump to line 106 because the condition on line 105 was never true
106 cluster_model.remove_volume(volume)
108 def remove_nodes_from_model(self, nodes_to_remove, cluster_model):
109 for hostname in nodes_to_remove:
110 node = cluster_model.get_node_by_name(hostname)
111 pools = cluster_model.get_node_pools(node)
112 for pool in pools:
113 volumes = cluster_model.get_pool_volumes(pool)
114 for volume in volumes:
115 cluster_model.remove_volume(volume)
116 cluster_model.remove_pool(pool)
117 cluster_model.remove_node(node)
119 def get_scoped_model(self, cluster_model):
120 """Leave only nodes, pools and volumes proposed in the audit scope"""
121 if not cluster_model: 121 ↛ 122line 121 didn't jump to line 122 because the condition on line 121 was never true
122 return None
124 allowed_nodes = []
125 nodes_to_remove = set()
126 volumes_to_exclude = []
127 projects_to_exclude = []
128 pools_to_exclude = []
130 model_hosts = list(cluster_model.get_all_storage_nodes().keys())
132 storage_scope = []
134 for scope in self.scope:
135 storage_scope = scope.get('storage')
136 if storage_scope: 136 ↛ 134line 136 didn't jump to line 134 because the condition on line 136 was always true
137 break
139 if not storage_scope:
140 return cluster_model
142 for rule in storage_scope:
143 if 'volume_types' in rule: 143 ↛ 144line 143 didn't jump to line 144 because the condition on line 143 was never true
144 self._collect_vtype(rule['volume_types'],
145 allowed_nodes, cluster_model)
146 elif 'availability_zones' in rule:
147 self._collect_zones(rule['availability_zones'],
148 allowed_nodes)
149 elif 'exclude' in rule: 149 ↛ 142line 149 didn't jump to line 142 because the condition on line 149 was always true
150 self.exclude_resources(
151 rule['exclude'], pools=pools_to_exclude,
152 volumes=volumes_to_exclude,
153 projects=projects_to_exclude)
155 if allowed_nodes: 155 ↛ 158line 155 didn't jump to line 158 because the condition on line 155 was always true
156 nodes_to_remove = set(model_hosts) - set(allowed_nodes)
158 self.remove_nodes_from_model(nodes_to_remove, cluster_model)
159 self.exclude_pools(pools_to_exclude, cluster_model)
160 self.exclude_volumes(volumes_to_exclude, cluster_model)
161 self.exclude_projects(projects_to_exclude, cluster_model)
163 return cluster_model