Coverage for watcher/db/sqlalchemy/models.py: 98%

166 statements  

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

1# Copyright 2013 Hewlett-Packard Development Company, L.P. 

2# 

3# Licensed under the Apache License, Version 2.0 (the "License"); you may 

4# not use this file except in compliance with the License. You may obtain 

5# a copy of the License at 

6# 

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

8# 

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

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

11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 

12# License for the specific language governing permissions and limitations 

13# under the License. 

14 

15""" 

16SQLAlchemy models for watcher service 

17""" 

18 

19from oslo_db.sqlalchemy import models 

20from oslo_serialization import jsonutils 

21from sqlalchemy import Boolean 

22from sqlalchemy import Column 

23from sqlalchemy import DateTime 

24from sqlalchemy.ext.declarative import declarative_base 

25from sqlalchemy import Float 

26from sqlalchemy import ForeignKey 

27from sqlalchemy import Integer 

28from sqlalchemy import LargeBinary 

29from sqlalchemy import Numeric 

30from sqlalchemy import orm 

31from sqlalchemy import String 

32from sqlalchemy import Text 

33from sqlalchemy.types import TypeDecorator, TEXT 

34from sqlalchemy import UniqueConstraint 

35import urllib.parse as urlparse 

36from watcher import conf 

37 

38CONF = conf.CONF 

39 

40 

41def table_args(): 

42 engine_name = urlparse.urlparse(CONF.database.connection).scheme 

43 if engine_name == 'mysql': 43 ↛ 44line 43 didn't jump to line 44 because the condition on line 43 was never true

44 return {'mysql_engine': CONF.database.mysql_engine, 

45 'mysql_charset': "utf8"} 

46 return None 

47 

48 

49class JsonEncodedType(TypeDecorator): 

50 """Abstract base type serialized as json-encoded string in db.""" 

51 

52 type = None 

53 impl = TEXT 

54 

55 def process_bind_param(self, value, dialect): 

56 if value is None: 

57 # Save default value according to current type to keep the 

58 # interface the consistent. 

59 value = self.type() 

60 elif not isinstance(value, self.type): 60 ↛ 61line 60 didn't jump to line 61 because the condition on line 60 was never true

61 raise TypeError("%s supposes to store %s objects, but %s given" 

62 % (self.__class__.__name__, 

63 self.type.__name__, 

64 type(value).__name__)) 

65 serialized_value = jsonutils.dumps(value) 

66 return serialized_value 

67 

68 def process_result_value(self, value, dialect): 

69 if value is not None: 

70 value = jsonutils.loads(value) 

71 return value 

72 

73 

74class JSONEncodedDict(JsonEncodedType): 

75 """Represents dict serialized as json-encoded string in db.""" 

76 

77 type = dict 

78 

79 

80class JSONEncodedList(JsonEncodedType): 

81 """Represents list serialized as json-encoded string in db.""" 

82 

83 type = list 

84 

85 

86class WatcherBase(models.SoftDeleteMixin, 

87 models.TimestampMixin, models.ModelBase): 

88 metadata = None 

89 

90 def as_dict(self): 

91 d = {} 

92 for c in self.__table__.columns: 

93 d[c.name] = self[c.name] 

94 return d 

95 

96 

97Base = declarative_base(cls=WatcherBase) 

98 

99 

100class Goal(Base): 

101 """Represents a goal.""" 

102 

103 __tablename__ = 'goals' 

104 __table_args__ = ( 

105 UniqueConstraint('uuid', name='uniq_goals0uuid'), 

106 UniqueConstraint('name', 'deleted', name='uniq_goals0name'), 

107 table_args(), 

108 ) 

109 id = Column(Integer, primary_key=True, autoincrement=True) 

110 uuid = Column(String(36)) 

111 name = Column(String(63), nullable=False) 

112 display_name = Column(String(63), nullable=False) 

113 efficacy_specification = Column(JSONEncodedList, nullable=False) 

114 

115 

116class Strategy(Base): 

117 """Represents a strategy.""" 

118 

119 __tablename__ = 'strategies' 

120 __table_args__ = ( 

121 UniqueConstraint('uuid', name='uniq_strategies0uuid'), 

122 UniqueConstraint('name', 'deleted', name='uniq_strategies0name'), 

123 table_args() 

124 ) 

125 id = Column(Integer, primary_key=True, autoincrement=True) 

126 uuid = Column(String(36)) 

127 name = Column(String(63), nullable=False) 

128 display_name = Column(String(63), nullable=False) 

129 goal_id = Column(Integer, ForeignKey('goals.id'), nullable=False) 

130 parameters_spec = Column(JSONEncodedDict, nullable=True) 

131 

132 goal = orm.relationship(Goal, foreign_keys=goal_id, lazy=None) 

133 

134 

135class AuditTemplate(Base): 

136 """Represents an audit template.""" 

137 

138 __tablename__ = 'audit_templates' 

139 __table_args__ = ( 

140 UniqueConstraint('uuid', name='uniq_audit_templates0uuid'), 

141 UniqueConstraint('name', 'deleted', name='uniq_audit_templates0name'), 

142 table_args() 

143 ) 

144 id = Column(Integer, primary_key=True) 

145 uuid = Column(String(36)) 

146 name = Column(String(63), nullable=True) 

147 description = Column(String(255), nullable=True) 

148 goal_id = Column(Integer, ForeignKey('goals.id'), nullable=False) 

149 strategy_id = Column(Integer, ForeignKey('strategies.id'), nullable=True) 

150 scope = Column(JSONEncodedList) 

151 

152 goal = orm.relationship(Goal, foreign_keys=goal_id, lazy=None) 

153 strategy = orm.relationship(Strategy, foreign_keys=strategy_id, lazy=None) 

154 

155 

156class Audit(Base): 

157 """Represents an audit.""" 

158 

159 __tablename__ = 'audits' 

160 __table_args__ = ( 

161 UniqueConstraint('uuid', name='uniq_audits0uuid'), 

162 UniqueConstraint('name', 'deleted', name='uniq_audits0name'), 

163 table_args() 

164 ) 

165 id = Column(Integer, primary_key=True, autoincrement=True) 

166 uuid = Column(String(36)) 

167 name = Column(String(63), nullable=True) 

168 audit_type = Column(String(20)) 

169 state = Column(String(20), nullable=True) 

170 parameters = Column(JSONEncodedDict, nullable=True) 

171 interval = Column(String(36), nullable=True) 

172 goal_id = Column(Integer, ForeignKey('goals.id'), nullable=False) 

173 strategy_id = Column(Integer, ForeignKey('strategies.id'), nullable=True) 

174 scope = Column(JSONEncodedList, nullable=True) 

175 auto_trigger = Column(Boolean, nullable=False) 

176 next_run_time = Column(DateTime, nullable=True) 

177 hostname = Column(String(255), nullable=True) 

178 start_time = Column(DateTime, nullable=True) 

179 end_time = Column(DateTime, nullable=True) 

180 force = Column(Boolean, nullable=False) 

181 

182 goal = orm.relationship(Goal, foreign_keys=goal_id, lazy=None) 

183 strategy = orm.relationship(Strategy, foreign_keys=strategy_id, lazy=None) 

184 

185 

186class ActionPlan(Base): 

187 """Represents an action plan.""" 

188 

189 __tablename__ = 'action_plans' 

190 __table_args__ = ( 

191 UniqueConstraint('uuid', name='uniq_action_plans0uuid'), 

192 table_args() 

193 ) 

194 id = Column(Integer, primary_key=True, autoincrement=True) 

195 uuid = Column(String(36)) 

196 audit_id = Column(Integer, ForeignKey('audits.id'), nullable=False) 

197 strategy_id = Column(Integer, ForeignKey('strategies.id'), nullable=False) 

198 state = Column(String(20), nullable=True) 

199 global_efficacy = Column(JSONEncodedList, nullable=True) 

200 hostname = Column(String(255), nullable=True) 

201 

202 audit = orm.relationship(Audit, foreign_keys=audit_id, lazy=None) 

203 strategy = orm.relationship(Strategy, foreign_keys=strategy_id, lazy=None) 

204 

205 

206class Action(Base): 

207 """Represents an action.""" 

208 

209 __tablename__ = 'actions' 

210 __table_args__ = ( 

211 UniqueConstraint('uuid', name='uniq_actions0uuid'), 

212 table_args() 

213 ) 

214 id = Column(Integer, primary_key=True, autoincrement=True) 

215 uuid = Column(String(36), nullable=False) 

216 action_plan_id = Column(Integer, ForeignKey('action_plans.id'), 

217 nullable=False) 

218 # only for the first version 

219 action_type = Column(String(255), nullable=False) 

220 input_parameters = Column(JSONEncodedDict, nullable=True) 

221 state = Column(String(20), nullable=True) 

222 parents = Column(JSONEncodedList, nullable=True) 

223 

224 action_plan = orm.relationship( 

225 ActionPlan, foreign_keys=action_plan_id, lazy=None) 

226 

227 

228class EfficacyIndicator(Base): 

229 """Represents an efficacy indicator.""" 

230 

231 __tablename__ = 'efficacy_indicators' 

232 __table_args__ = ( 

233 UniqueConstraint('uuid', name='uniq_efficacy_indicators0uuid'), 

234 table_args() 

235 ) 

236 id = Column(Integer, primary_key=True, autoincrement=True) 

237 uuid = Column(String(36)) 

238 name = Column(String(63)) 

239 description = Column(String(255), nullable=True) 

240 unit = Column(String(63), nullable=True) 

241 # this column is deprecated due to bug 

242 # https://bugs.launchpad.net/watcher/+bug/2103458 

243 value = Column(Numeric()) 

244 data = Column(Float()) 

245 action_plan_id = Column(Integer, ForeignKey('action_plans.id'), 

246 nullable=False) 

247 

248 action_plan = orm.relationship( 

249 ActionPlan, foreign_keys=action_plan_id, lazy=None) 

250 

251 

252class ScoringEngine(Base): 

253 """Represents a scoring engine.""" 

254 

255 __tablename__ = 'scoring_engines' 

256 __table_args__ = ( 

257 UniqueConstraint('uuid', name='uniq_scoring_engines0uuid'), 

258 UniqueConstraint('name', 'deleted', name='uniq_scoring_engines0name'), 

259 table_args() 

260 ) 

261 id = Column(Integer, primary_key=True, autoincrement=True) 

262 uuid = Column(String(36), nullable=False) 

263 name = Column(String(63), nullable=False) 

264 description = Column(String(255), nullable=True) 

265 # Metainfo might contain some additional information about the data model. 

266 # The format might vary between different models (e.g. be JSON, XML or 

267 # even some custom format), the blob type should cover all scenarios. 

268 metainfo = Column(Text, nullable=True) 

269 

270 

271class Service(Base): 

272 """Represents a service entity""" 

273 

274 __tablename__ = 'services' 

275 __table_args__ = ( 

276 UniqueConstraint('host', 'name', 'deleted', 

277 name="uniq_services0host0name0deleted"), 

278 table_args() 

279 ) 

280 id = Column(Integer, primary_key=True) 

281 name = Column(String(255), nullable=False) 

282 host = Column(String(255), nullable=False) 

283 last_seen_up = Column(DateTime, nullable=True) 

284 

285 

286class ActionDescription(Base): 

287 """Represents a action description""" 

288 

289 __tablename__ = 'action_descriptions' 

290 __table_args__ = ( 

291 UniqueConstraint('action_type', 

292 name="uniq_action_description0action_type"), 

293 table_args() 

294 ) 

295 id = Column(Integer, primary_key=True) 

296 action_type = Column(String(255), nullable=False) 

297 description = Column(String(255), nullable=False) 

298 

299 

300class APScheulerJob(Base): 

301 """Represents apscheduler jobs""" 

302 

303 __tablename__ = 'apscheduler_jobs' 

304 __table_args__ = ( 

305 UniqueConstraint('id', 

306 name="uniq_apscheduler_jobs0id"), 

307 table_args() 

308 ) 

309 id = Column(String(191), nullable=False, primary_key=True) 

310 next_run_time = Column(Float(25), index=True) 

311 job_state = Column(LargeBinary, nullable=False) 

312 tag = Column(JSONEncodedDict(), nullable=True) 

313 service_id = Column(Integer, ForeignKey('services.id'), 

314 nullable=False) 

315 

316 service = orm.relationship( 

317 Service, foreign_keys=service_id, lazy=None)