Coverage for watcher/objects/audit_template.py: 100%

62 statements  

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

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

2# Copyright 2013 IBM Corp. 

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 

17""" 

18An :ref:`Audit <audit_definition>` may be launched several times with the same 

19settings (:ref:`Goal <goal_definition>`, thresholds, ...). Therefore it makes 

20sense to save those settings in some sort of Audit preset object, which is 

21known as an :ref:`Audit Template <audit_template_definition>`. 

22 

23An :ref:`Audit Template <audit_template_definition>` contains at least the 

24:ref:`Goal <goal_definition>` of the :ref:`Audit <audit_definition>`. 

25 

26It may also contain some error handling settings indicating whether: 

27 

28- :ref:`Watcher Applier <watcher_applier_definition>` stops the 

29 entire operation 

30- :ref:`Watcher Applier <watcher_applier_definition>` performs a rollback 

31 

32and how many retries should be attempted before failure occurs (also the latter 

33can be complex: for example the scenario in which there are many first-time 

34failures on ultimately successful :ref:`Actions <action_definition>`). 

35 

36Moreover, an :ref:`Audit Template <audit_template_definition>` may contain some 

37settings related to the level of automation for the 

38:ref:`Action Plan <action_plan_definition>` that will be generated by the 

39:ref:`Audit <audit_definition>`. 

40A flag will indicate whether the :ref:`Action Plan <action_plan_definition>` 

41will be launched automatically or will need a manual confirmation from the 

42:ref:`Administrator <administrator_definition>`. 

43 

44Last but not least, an :ref:`Audit Template <audit_template_definition>` may 

45contain a list of extra parameters related to the 

46:ref:`Strategy <strategy_definition>` configuration. These parameters can be 

47provided as a list of key-value pairs. 

48""" 

49 

50from watcher.common import exception 

51from watcher.common import utils 

52from watcher.db import api as db_api 

53from watcher import objects 

54from watcher.objects import base 

55from watcher.objects import fields as wfields 

56 

57 

58@base.WatcherObjectRegistry.register 

59class AuditTemplate(base.WatcherPersistentObject, base.WatcherObject, 

60 base.WatcherObjectDictCompat): 

61 

62 # Version 1.0: Initial version 

63 # Version 1.1: Added 'goal' and 'strategy' object field 

64 VERSION = '1.1' 

65 

66 dbapi = db_api.get_instance() 

67 

68 fields = { 

69 'id': wfields.IntegerField(), 

70 'uuid': wfields.UUIDField(), 

71 'name': wfields.StringField(), 

72 'description': wfields.StringField(nullable=True), 

73 'scope': wfields.FlexibleListOfDictField(nullable=True), 

74 'goal_id': wfields.IntegerField(), 

75 'strategy_id': wfields.IntegerField(nullable=True), 

76 

77 'goal': wfields.ObjectField('Goal', nullable=True), 

78 'strategy': wfields.ObjectField('Strategy', nullable=True), 

79 } 

80 

81 object_fields = { 

82 'goal': (objects.Goal, 'goal_id'), 

83 'strategy': (objects.Strategy, 'strategy_id'), 

84 } 

85 

86 @base.remotable_classmethod 

87 def get(cls, context, audit_template_id, eager=False): 

88 """Find an audit template based on its id or uuid 

89 

90 :param context: Security context. NOTE: This should only 

91 be used internally by the indirection_api. 

92 Unfortunately, RPC requires context as the first 

93 argument, even though we don't use it. 

94 A context should be set when instantiating the 

95 object, e.g.: AuditTemplate(context) 

96 :param audit_template_id: the id *or* uuid of a audit_template. 

97 :param eager: Load object fields if True (Default: False) 

98 :returns: a :class:`AuditTemplate` object. 

99 """ 

100 if utils.is_int_like(audit_template_id): 

101 return cls.get_by_id(context, audit_template_id, eager=eager) 

102 elif utils.is_uuid_like(audit_template_id): 

103 return cls.get_by_uuid(context, audit_template_id, eager=eager) 

104 else: 

105 raise exception.InvalidIdentity(identity=audit_template_id) 

106 

107 @base.remotable_classmethod 

108 def get_by_id(cls, context, audit_template_id, eager=False): 

109 """Find an audit template based on its integer id 

110 

111 :param context: Security context. NOTE: This should only 

112 be used internally by the indirection_api. 

113 Unfortunately, RPC requires context as the first 

114 argument, even though we don't use it. 

115 A context should be set when instantiating the 

116 object, e.g.: AuditTemplate(context) 

117 :param audit_template_id: the id of a audit_template. 

118 :param eager: Load object fields if True (Default: False) 

119 :returns: a :class:`AuditTemplate` object. 

120 """ 

121 db_audit_template = cls.dbapi.get_audit_template_by_id( 

122 context, audit_template_id, eager=eager) 

123 audit_template = cls._from_db_object( 

124 cls(context), db_audit_template, eager=eager) 

125 return audit_template 

126 

127 @base.remotable_classmethod 

128 def get_by_uuid(cls, context, uuid, eager=False): 

129 """Find an audit template based on uuid 

130 

131 :param context: Security context. NOTE: This should only 

132 be used internally by the indirection_api. 

133 Unfortunately, RPC requires context as the first 

134 argument, even though we don't use it. 

135 A context should be set when instantiating the 

136 object, e.g.: AuditTemplate(context) 

137 :param uuid: the uuid of a audit_template. 

138 :param eager: Load object fields if True (Default: False) 

139 :returns: a :class:`AuditTemplate` object. 

140 """ 

141 db_audit_template = cls.dbapi.get_audit_template_by_uuid( 

142 context, uuid, eager=eager) 

143 audit_template = cls._from_db_object( 

144 cls(context), db_audit_template, eager=eager) 

145 return audit_template 

146 

147 @base.remotable_classmethod 

148 def get_by_name(cls, context, name, eager=False): 

149 """Find an audit template based on name 

150 

151 :param name: the logical name of a audit_template. 

152 :param context: Security context 

153 :param eager: Load object fields if True (Default: False) 

154 :returns: a :class:`AuditTemplate` object. 

155 """ 

156 db_audit_template = cls.dbapi.get_audit_template_by_name( 

157 context, name, eager=eager) 

158 audit_template = cls._from_db_object( 

159 cls(context), db_audit_template, eager=eager) 

160 return audit_template 

161 

162 @base.remotable_classmethod 

163 def list(cls, context, filters=None, limit=None, marker=None, 

164 sort_key=None, sort_dir=None, eager=False): 

165 """Return a list of :class:`AuditTemplate` objects. 

166 

167 :param context: Security context. NOTE: This should only 

168 be used internally by the indirection_api. 

169 Unfortunately, RPC requires context as the first 

170 argument, even though we don't use it. 

171 A context should be set when instantiating the 

172 object, e.g.: AuditTemplate(context) 

173 :param filters: dict mapping the filter key to a value. 

174 :param limit: maximum number of resources to return in a single result. 

175 :param marker: pagination marker for large data sets. 

176 :param sort_key: column to sort results by. 

177 :param sort_dir: direction to sort. "asc" or "desc". 

178 :param eager: Load object fields if True (Default: False) 

179 :returns: a list of :class:`AuditTemplate` object. 

180 """ 

181 db_audit_templates = cls.dbapi.get_audit_template_list( 

182 context, 

183 filters=filters, 

184 limit=limit, 

185 marker=marker, 

186 sort_key=sort_key, 

187 sort_dir=sort_dir, 

188 eager=eager) 

189 

190 return [cls._from_db_object(cls(context), obj, eager=eager) 

191 for obj in db_audit_templates] 

192 

193 @base.remotable 

194 def create(self): 

195 """Create a :class:`AuditTemplate` record in the DB 

196 

197 :returns: An :class:`AuditTemplate` object. 

198 """ 

199 values = self.obj_get_changes() 

200 db_audit_template = self.dbapi.create_audit_template(values) 

201 # Note(v-francoise): Always load eagerly upon creation so we can send 

202 # notifications containing information about the related relationships 

203 self._from_db_object(self, db_audit_template, eager=True) 

204 

205 def destroy(self): 

206 """Delete the :class:`AuditTemplate` from the DB""" 

207 self.dbapi.destroy_audit_template(self.uuid) 

208 self.obj_reset_changes() 

209 

210 @base.remotable 

211 def save(self): 

212 """Save updates to this :class:`AuditTemplate`. 

213 

214 Updates will be made column by column based on the result 

215 of self.what_changed(). 

216 """ 

217 updates = self.obj_get_changes() 

218 db_obj = self.dbapi.update_audit_template(self.uuid, updates) 

219 obj = self._from_db_object(self, db_obj, eager=False) 

220 self.obj_refresh(obj) 

221 self.obj_reset_changes() 

222 

223 @base.remotable 

224 def refresh(self, eager=False): 

225 """Loads updates for this :class:`AuditTemplate`. 

226 

227 Loads a audit_template with the same uuid from the database and 

228 checks for updated attributes. Updates are applied from 

229 the loaded audit_template column by column, if there are any updates. 

230 :param eager: Load object fields if True (Default: False) 

231 """ 

232 current = self.get_by_uuid(self._context, uuid=self.uuid, eager=eager) 

233 self.obj_refresh(current) 

234 

235 @base.remotable 

236 def soft_delete(self): 

237 """Soft Delete the :class:`AuditTemplate` from the DB""" 

238 db_obj = self.dbapi.soft_delete_audit_template(self.uuid) 

239 obj = self._from_db_object( 

240 self.__class__(self._context), db_obj, eager=False) 

241 self.obj_refresh(obj)