Coverage for watcher/decision_engine/goal/efficacy/indicators.py: 90%

116 statements  

« prev     ^ index     » next       coverage.py v7.8.2, created at 2025-06-17 12:22 +0000

1# -*- encoding: utf-8 -*- 

2# Copyright (c) 2016 b<>com 

3# 

4# Licensed under the Apache License, Version 2.0 (the "License"); 

5# you may not use this file except in compliance with the License. 

6# You may obtain a copy of the License at 

7# 

8# http://www.apache.org/licenses/LICENSE-2.0 

9# 

10# Unless required by applicable law or agreed to in writing, software 

11# distributed under the License is distributed on an "AS IS" BASIS, 

12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 

13# implied. 

14# See the License for the specific language governing permissions and 

15# limitations under the License. 

16 

17import abc 

18import jsonschema 

19from jsonschema import SchemaError 

20from jsonschema import ValidationError 

21 

22from oslo_log import log 

23from oslo_serialization import jsonutils 

24 

25from watcher._i18n import _ 

26from watcher.common import exception 

27 

28LOG = log.getLogger(__name__) 

29 

30 

31class IndicatorSpecification(object, metaclass=abc.ABCMeta): 

32 

33 def __init__(self, name=None, description=None, unit=None, required=True): 

34 self.name = name 

35 self.description = description 

36 self.unit = unit 

37 self.required = required 

38 

39 @property 

40 @abc.abstractmethod 

41 def schema(self): 

42 """JsonSchema used to validate the indicator value 

43 

44 :return: A Schema 

45 """ 

46 raise NotImplementedError() 

47 

48 @classmethod 

49 def validate(cls, solution): 

50 """Validate the given solution 

51 

52 :raises: :py:class:`~.InvalidIndicatorValue` when the validation fails 

53 """ 

54 indicator = cls() 

55 value = None 

56 try: 

57 value = getattr(solution, indicator.name) 

58 jsonschema.validate(value, cls.schema) 

59 except (SchemaError, ValidationError) as exc: 

60 LOG.exception(exc) 

61 raise 

62 except Exception as exc: 

63 LOG.exception(exc) 

64 raise exception.InvalidIndicatorValue( 

65 name=indicator.name, value=value, spec_type=type(indicator)) 

66 

67 def to_dict(self): 

68 return { 

69 "name": self.name, 

70 "description": self.description, 

71 "unit": self.unit, 

72 "schema": jsonutils.dumps(self.schema) if self.schema else None, 

73 } 

74 

75 def __str__(self): 

76 return str(self.to_dict()) 

77 

78 

79class ComputeNodesCount(IndicatorSpecification): 

80 def __init__(self): 

81 super(ComputeNodesCount, self).__init__( 

82 name="compute_nodes_count", 

83 description=_("The total number of enabled compute nodes."), 

84 unit=None, 

85 ) 

86 

87 @property 

88 def schema(self): 

89 return { 

90 "type": "integer", 

91 "minimum": 0 

92 } 

93 

94 

95class ReleasedComputeNodesCount(IndicatorSpecification): 

96 def __init__(self): 

97 super(ReleasedComputeNodesCount, self).__init__( 

98 name="released_compute_nodes_count", 

99 description=_("The number of compute nodes to be released."), 

100 unit=None, 

101 ) 

102 

103 @property 

104 def schema(self): 

105 return { 

106 "type": "integer", 

107 "minimum": 0 

108 } 

109 

110 

111class InstancesCount(IndicatorSpecification): 

112 def __init__(self): 

113 super(InstancesCount, self).__init__( 

114 name="instances_count", 

115 description=_("The total number of audited instances in " 

116 "strategy."), 

117 unit=None, 

118 required=False, 

119 ) 

120 

121 @property 

122 def schema(self): 

123 return { 

124 "type": "integer", 

125 "minimum": 0 

126 } 

127 

128 

129class InstanceMigrationsCount(IndicatorSpecification): 

130 def __init__(self): 

131 super(InstanceMigrationsCount, self).__init__( 

132 name="instance_migrations_count", 

133 description=_("The number of VM migrations to be performed."), 

134 unit=None, 

135 ) 

136 

137 @property 

138 def schema(self): 

139 return { 

140 "type": "integer", 

141 "minimum": 0 

142 } 

143 

144 

145class LiveInstanceMigrateCount(IndicatorSpecification): 

146 def __init__(self): 

147 super(LiveInstanceMigrateCount, self).__init__( 

148 name="live_migrate_instance_count", 

149 description=_("The number of instances actually live migrated."), 

150 unit=None, 

151 ) 

152 

153 @property 

154 def schema(self): 

155 return { 

156 "type": "integer", 

157 "minimum": 0 

158 } 

159 

160 

161class PlannedLiveInstanceMigrateCount(IndicatorSpecification): 

162 def __init__(self): 

163 super(PlannedLiveInstanceMigrateCount, self).__init__( 

164 name="planned_live_migrate_instance_count", 

165 description=_("The number of instances planned to live migrate."), 

166 unit=None, 

167 ) 

168 

169 @property 

170 def schema(self): 

171 return { 

172 "type": "integer", 

173 "minimum": 0 

174 } 

175 

176 

177class ColdInstanceMigrateCount(IndicatorSpecification): 

178 def __init__(self): 

179 super(ColdInstanceMigrateCount, self).__init__( 

180 name="cold_migrate_instance_count", 

181 description=_("The number of instances actually cold migrated."), 

182 unit=None, 

183 ) 

184 

185 @property 

186 def schema(self): 

187 return { 

188 "type": "integer", 

189 "minimum": 0 

190 } 

191 

192 

193class PlannedColdInstanceMigrateCount(IndicatorSpecification): 

194 def __init__(self): 

195 super(PlannedColdInstanceMigrateCount, self).__init__( 

196 name="planned_cold_migrate_instance_count", 

197 description=_("The number of instances planned to cold migrate."), 

198 unit=None, 

199 ) 

200 

201 @property 

202 def schema(self): 

203 return { 

204 "type": "integer", 

205 "minimum": 0 

206 } 

207 

208 

209class VolumeMigrateCount(IndicatorSpecification): 

210 def __init__(self): 

211 super(VolumeMigrateCount, self).__init__( 

212 name="volume_migrate_count", 

213 description=_("The number of detached volumes actually migrated."), 

214 unit=None, 

215 ) 

216 

217 @property 

218 def schema(self): 

219 return { 

220 "type": "integer", 

221 "minimum": 0 

222 } 

223 

224 

225class PlannedVolumeMigrateCount(IndicatorSpecification): 

226 def __init__(self): 

227 super(PlannedVolumeMigrateCount, self).__init__( 

228 name="planned_volume_migrate_count", 

229 description=_("The number of detached volumes planned" 

230 " to migrate."), 

231 unit=None, 

232 ) 

233 

234 @property 

235 def schema(self): 

236 return { 

237 "type": "integer", 

238 "minimum": 0 

239 } 

240 

241 

242class VolumeUpdateCount(IndicatorSpecification): 

243 def __init__(self): 

244 super(VolumeUpdateCount, self).__init__( 

245 name="volume_update_count", 

246 description=_("The number of attached volumes actually" 

247 " migrated."), 

248 unit=None, 

249 ) 

250 

251 @property 

252 def schema(self): 

253 return { 

254 "type": "integer", 

255 "minimum": 0 

256 } 

257 

258 

259class PlannedVolumeUpdateCount(IndicatorSpecification): 

260 def __init__(self): 

261 super(PlannedVolumeUpdateCount, self).__init__( 

262 name="planned_volume_update_count", 

263 description=_("The number of attached volumes planned to" 

264 " migrate."), 

265 unit=None, 

266 ) 

267 

268 @property 

269 def schema(self): 

270 return { 

271 "type": "integer", 

272 "minimum": 0 

273 } 

274 

275 

276class StandardDeviationValue(IndicatorSpecification): 

277 def __init__(self): 

278 super(StandardDeviationValue, self).__init__( 

279 name="standard_deviation_after_audit", 

280 description=_("The value of resulted standard deviation."), 

281 unit=None, 

282 required=False, 

283 ) 

284 

285 @property 

286 def schema(self): 

287 return { 

288 "type": "number", 

289 "minimum": 0 

290 } 

291 

292 

293class OriginalStandardDeviationValue(IndicatorSpecification): 

294 def __init__(self): 

295 super(OriginalStandardDeviationValue, self).__init__( 

296 name="standard_deviation_before_audit", 

297 description=_("The value of original standard deviation."), 

298 unit=None, 

299 required=False, 

300 ) 

301 

302 @property 

303 def schema(self): 

304 return { 

305 "type": "number", 

306 "minimum": 0 

307 }