3 from logging
import getLogger; log =
getLogger(
"DQDefects.virtual_defect_mixin")
4 from DQUtils
import fetch_iovs
6 from .exceptions
import DefectUnknownError, DefectExistsError
7 from .ids
import choose_new_defect_id
8 from .virtual_logic
import DefectLogic
11 from typing
import Optional, Iterable, Mapping, Set, Sequence, List
14 NONHEAD_MODIFICATION_MSG = (
"Operations which modify virtual defects can only "
15 "be done on the HEAD tag.")
19 Navigate the node to determine dependencies. Circular references raise an
22 Shamelessly stolen algorithm from:
23 http://www.electricmonk.nl/log/2008/08/07/dependency-resolving-algorithm/
25 if resolved
is None: resolved = []
26 if unresolved
is None: unresolved = []
27 unresolved.append(node)
28 for dependency
in node.dependencies:
29 if dependency
not in resolved:
30 if dependency
in unresolved:
31 raise Exception(
'Circular reference detected: %s -> %s'
32 % (node.name, dependency.name))
36 unresolved.remove(node)
42 A DefectsDB mixin for managing virtual defects
47 super(DefectsDBVirtualDefectsMixin, self).
__init__()
50 all_defects = self.defect_names | self.virtual_defect_names
51 for clause
in clauses.split():
52 if clause[0]
in (
'!',
'-'):
54 assert clause
in all_defects, (clause +
" is not a known defect")
59 assert self.
logics_tag ==
"HEAD", NONHEAD_MODIFICATION_MSG
60 assert not self._read_only,
"Insertion on read-only database"
64 comment: Optional[str] =
None, tag: Optional[str] =
None) ->
None:
65 defect_id = self.defect_chan_as_id(defect_name)
67 assert self.defect_is_virtual(defect_id), (
"Tried to update nonvirtual"
68 " defect with update_virtual_defect()")
72 tag = tag
if tag
is not None else 'HEAD'
73 ucomment = comment.encode(
'utf-8')
if comment
is not None else None
75 self._defect_logic_payload[
"clauses"] = clauses.encode(
'ascii')
77 store = self.defect_logic_folder.storeObject
78 store(0, 2**63-1, self._defect_logic_payload, defect_id, tag.encode(
'ascii'),
79 (
True if tag !=
'HEAD' else False))
81 if comment
is not None:
82 self.defect_logic_folder.setChannelDescription(defect_id, ucomment)
84 def rename_defect(self, defect_name: str, new_defect_name: str) ->
None:
86 Rename a defect (primary or virtual). Will keep data and channel ID.
87 Will fix up all virtual defect dependencies in all tags.
89 assert not self._read_only,
"Channel rename on read-only database"
92 oldname = self.normalize_defect_names(new_defect_name)
94 except DefectUnknownError:
97 defect_id = self.defect_chan_as_id(defect_name)
99 anew_defect_name = new_defect_name.encode(
'ascii')
100 if self.defect_is_virtual(defect_id):
101 self.defect_logic_folder.setChannelName(defect_id, anew_defect_name)
107 self.defects_folder.setChannelName(defect_id, anew_defect_name)
113 @contextlib.contextmanager
114 def logics_unlocking(tag):
115 atag = tag.encode(
'ascii')
116 log.info(
'Unlocking tag %s', tag)
117 folder = self.defect_logic_folder
118 orig_status = folder.tagLockStatus(atag)
120 folder.setTagLockStatus(atag, 0)
123 folder.setTagLockStatus(atag, orig_status)
124 log.info(
'Done with tag %s', tag)
126 def logic_substitute(clause, oldname, newname):
129 if clause[0]
in (
'!',):
132 if clause != oldname:
135 return prefix + newname
137 for tag
in [
'HEAD'] + self.logics_tags:
139 for vd, logic
in logics.items():
140 if defect_name
in logic.realclauses:
141 newclauses =
' '.
join([logic_substitute(x, defect_name,
143 for x
in logic.clauses
145 with logics_unlocking(tag):
151 Create a new virtual defect
153 assert self.
logics_tag ==
"HEAD", NONHEAD_MODIFICATION_MSG
154 assert not self._read_only,
"Insertion on read-only database"
155 from DQUtils.channel_mapping
import get_channel_ids_names
158 store = self.defect_logic_folder.storeObject
159 p = self._defect_logic_payload
163 p[
"clauses"] = clauses.encode(
'ascii')
169 log.debug(
"Creating new defect %s: 0x%08x", defect_name, defect_id)
172 oldname = self.normalize_defect_names(defect_name)
174 except DefectUnknownError:
177 if (defect_id
in ids):
179 self.defect_logic_folder.createChannel(defect_id, defect_name.encode(
'ascii'), comment.encode(
'utf-8'))
180 store(0, 2**63-1, p, defect_id)
181 self._new_virtual_defect(defect_id, defect_name)
186 Returns a list of DefectLogic objects that need to be evaluated,
187 in the correct order for them to be consistent.
189 `defects` should be a list of names
203 dependencies = logics
206 resolved.remove(MasterNode)
211 Determine which primary flags are used for
212 a given list of input `virtual_defect_names`.
214 primary_defects =
set()
215 for defect_logic
in defect_logics:
216 primary_defects |= defect_logic.primary_defects
218 return primary_defects
223 Returns all virtual defects in the form {"defect_name" : DefectLogic()}
224 for the tag DefectDB was constructed on.
234 if tag !=
"HEAD" and not self.defect_logic_folder.existsUserTag(tag.encode(
'ascii')):
238 logics =
fetch_iovs(self.defect_logic_folder, tag=tag.encode(
'ascii'),
241 logics = {l.channel:
DefectLogic(l)
for l
in logics}
243 for _, defect_logic
in six.iteritems (logics):
244 defect_logic._populate(logics)
249 exclude: Sequence[str] = [
'TIGHT',
'IDTIGHT',
'PHYS_.*']):
251 Returns primary defects that are depended on by a virtual defect
252 if old_primary_only == True, only return those depended on by a
253 virtual defect with no _ in the name
254 ignores virtual defects in exclude list
255 accepts regexes in exclude list
259 vdl = dict(l
for l
in vdl.items()
if '_' not in l[0])
262 rex = re.compile(
'(%s)' %
'|'.
join(exclude))
263 vdl = dict(l
for l
in vdl.items()
if not rex.match(l[0]))
268 When called, uses an assertion to check that there are no missing
269 defects. This is a database consistency check which should never be
270 violated (but was when DQDefects was new).
272 all_defects = self.defect_names | self.virtual_defect_names
277 used_defects.update(logic.realclauses)
278 for clause
in logic.realclauses:
279 used_by.setdefault(clause, []).
append(defect)
281 missing_defects = used_defects - all_defects
284 log.error(
"-- The missing defects are used by the following virtual defects")
285 log.error(
"<missing> : <used by>")
286 for missing
in missing_defects:
287 log.error(missing +
":" +
", ".
join(used_by[missing]))
289 assert not missing_defects, (
"The following defects are used but not "
290 "defined anywhere: %r" % missing_defects)