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

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. 

13 

14from watcher.common import cinder_helper 

15from watcher.common import exception 

16 

17from watcher.decision_engine.scope import base 

18 

19 

20class StorageScope(base.BaseScope): 

21 """Storage Audit Scope Handler""" 

22 

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) 

27 

28 def _collect_vtype(self, volume_types, allowed_nodes): 

29 service_list = self.wrapper.get_storage_node_list() 

30 

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) 

51 

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) 

66 

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') 

71 

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']]) 

77 

78 elif 'volumes' in resource: 

79 volumes_to_exclude.extend( 

80 [volume['uuid'] for volume in 

81 resource['volumes']]) 

82 

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']]) 

87 

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) 

95 

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) 

100 

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) 

107 

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) 

118 

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 

123 

124 allowed_nodes = [] 

125 nodes_to_remove = set() 

126 volumes_to_exclude = [] 

127 projects_to_exclude = [] 

128 pools_to_exclude = [] 

129 

130 model_hosts = list(cluster_model.get_all_storage_nodes().keys()) 

131 

132 storage_scope = [] 

133 

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 

138 

139 if not storage_scope: 

140 return cluster_model 

141 

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) 

154 

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) 

157 

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) 

162 

163 return cluster_model