blob: d6e89167d76e4dfe0982bd5684c7edab926a7f27 [file] [log] [blame]
Kevin Rocard326e39e2012-12-14 16:11:05 +01001#!/usr/bin/env python3
2
Kevin Rocardfe753d42013-02-08 11:37:39 +01003# INTEL CONFIDENTIAL
4# Copyright 2013 Intel
5# Corporation All Rights Reserved.
6#
7# The source code contained or described herein and all documents related to
8# the source code ("Material") are owned by Intel Corporation or its suppliers
9# or licensors. Title to the Material remains with Intel Corporation or its
10# suppliers and licensors. The Material contains trade secrets and proprietary
11# and confidential information of Intel or its suppliers and licensors. The
12# Material is protected by worldwide copyright and trade secret laws and
13# treaty provisions. No part of the Material may be used, copied, reproduced,
14# modified, published, uploaded, posted, transmitted, distributed, or
15# disclosed in any way without Intels prior express written permission.
16#
17# No license under any patent, copyright, trade secret or other intellectual
18# property right is granted to or conferred upon you by disclosure or delivery
19# of the Materials, either expressly, by implication, inducement, estoppel or
20# otherwise. Any license under such intellectual property rights must be
21# express and approved by Intel in writing.
22
Kevin Rocard326e39e2012-12-14 16:11:05 +010023"""
24Generate a coverage report by parsing parameter framework log.
25
26The coverage report contains the:
27 - domain
28 - configuration
29 - rule
30 - criterion
31basic coverage statistics.
32"""
33
34import xml.dom.minidom
35import sys
36import re
37import logging
38
39FORMAT = '%(levelname)s: %(message)s'
40logging.basicConfig(stream=sys.stderr, level=logging.WARNING, format=FORMAT)
41logger = logging.getLogger("Coverage")
42
Kevin Rocard3aa0db42013-02-13 17:15:38 +010043class CustomError(Exception):
Kevin Rocard804e0642013-06-25 12:09:35 +020044 pass
Kevin Rocard3aa0db42013-02-13 17:15:38 +010045
46class ChildError(CustomError):
Kevin Rocard804e0642013-06-25 12:09:35 +020047 def __init__(self, parent, child):
48 self.parent = parent
49 self.child = child
Kevin Rocard3aa0db42013-02-13 17:15:38 +010050
51class ChildNotFoundError(ChildError):
Kevin Rocard804e0642013-06-25 12:09:35 +020052 def __str__(self):
53 return "Unable to find the child %s in %s" % (self.child, self.parent)
Kevin Rocard3aa0db42013-02-13 17:15:38 +010054
55class DuplicatedChildError(ChildError):
Kevin Rocard804e0642013-06-25 12:09:35 +020056 def __str__(self):
57 return "Add existing child %s in %s." % (self.child, self.parent)
Kevin Rocard326e39e2012-12-14 16:11:05 +010058
59class Element():
Kevin Rocard804e0642013-06-25 12:09:35 +020060 """Root class for all coverage elements"""
61 tag = "element"
Kevin Rocard326e39e2012-12-14 16:11:05 +010062
Kevin Rocard804e0642013-06-25 12:09:35 +020063 def __init__(self, name):
Kevin Rocard326e39e2012-12-14 16:11:05 +010064
Kevin Rocard804e0642013-06-25 12:09:35 +020065 self.parent = None
66 self.children = []
Kevin Rocard326e39e2012-12-14 16:11:05 +010067
Kevin Rocard804e0642013-06-25 12:09:35 +020068 self.nbUse = 0
Kevin Rocard326e39e2012-12-14 16:11:05 +010069
Kevin Rocard804e0642013-06-25 12:09:35 +020070 self.name = name
Kevin Rocard326e39e2012-12-14 16:11:05 +010071
Kevin Rocard804e0642013-06-25 12:09:35 +020072 self.debug("New element")
Kevin Rocard326e39e2012-12-14 16:11:05 +010073
74
Kevin Rocard804e0642013-06-25 12:09:35 +020075 def __str__(self):
76 return "%s (%s)" % (self.name, self.tag)
Kevin Rocard326e39e2012-12-14 16:11:05 +010077
Kevin Rocard804e0642013-06-25 12:09:35 +020078 def __eq__(self, compared):
79 return (self.name == compared.name) and (self.children == compared.children)
Kevin Rocard326e39e2012-12-14 16:11:05 +010080
Kevin Rocard804e0642013-06-25 12:09:35 +020081 def getName(self, default=""):
82 return self.name or default
Kevin Rocard326e39e2012-12-14 16:11:05 +010083
Kevin Rocard804e0642013-06-25 12:09:35 +020084 def hasChildren(self):
85 return bool(self.children)
Kevin Rocard326e39e2012-12-14 16:11:05 +010086
Kevin Rocard804e0642013-06-25 12:09:35 +020087 def getChildren(self):
88 return self.children
Kevin Rocard326e39e2012-12-14 16:11:05 +010089
Kevin Rocard804e0642013-06-25 12:09:35 +020090 def _getDescendants(self):
91 for child in self.children:
92 yield child
93 for descendant in child._getDescendants() :
94 yield descendant
Kevin Rocard326e39e2012-12-14 16:11:05 +010095
Kevin Rocard804e0642013-06-25 12:09:35 +020096 def getChildFromName(self, childName):
Kevin Rocard326e39e2012-12-14 16:11:05 +010097
Kevin Rocard804e0642013-06-25 12:09:35 +020098 for child in self.children :
Kevin Rocard326e39e2012-12-14 16:11:05 +010099
Kevin Rocard804e0642013-06-25 12:09:35 +0200100 if child.getName() == childName :
101 return child
Kevin Rocard326e39e2012-12-14 16:11:05 +0100102
Kevin Rocard804e0642013-06-25 12:09:35 +0200103 self.debug("Child %s not found" % childName, logging.ERROR)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100104
Kevin Rocard804e0642013-06-25 12:09:35 +0200105 self.debug("Child list :")
Kevin Rocard326e39e2012-12-14 16:11:05 +0100106
Kevin Rocard804e0642013-06-25 12:09:35 +0200107 for child in self.children :
108 self.debug(" - %s" % child)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100109
Kevin Rocard804e0642013-06-25 12:09:35 +0200110 raise ChildNotFoundError(self, childName)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100111
112
Kevin Rocard804e0642013-06-25 12:09:35 +0200113 def addChild(self, child):
114 self.debug("new child: " + child.name)
115 self.children.append(child)
116 child._adoptedBy(self)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100117
Kevin Rocard804e0642013-06-25 12:09:35 +0200118 def _adoptedBy(self, parent):
119 assert(not self.parent)
120 self.parent = parent
Kevin Rocard326e39e2012-12-14 16:11:05 +0100121
Kevin Rocard804e0642013-06-25 12:09:35 +0200122 def _getElementNames(self, elementList):
123 return (substate.name for substate in elementList)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100124
Kevin Rocard804e0642013-06-25 12:09:35 +0200125 def _description(self, withCoverage, withNbUse):
126 description = self.name
Kevin Rocard326e39e2012-12-14 16:11:05 +0100127
Kevin Rocard804e0642013-06-25 12:09:35 +0200128 if withNbUse or withCoverage :
129 description += " has been used " + str(self.nbUse) + " time"
Kevin Rocard326e39e2012-12-14 16:11:05 +0100130
Kevin Rocard804e0642013-06-25 12:09:35 +0200131 if withCoverage :
132 description += self._coverageFormating(self._getCoverage())
Kevin Rocard326e39e2012-12-14 16:11:05 +0100133
Kevin Rocard804e0642013-06-25 12:09:35 +0200134 return description
Kevin Rocard326e39e2012-12-14 16:11:05 +0100135
136
Kevin Rocard804e0642013-06-25 12:09:35 +0200137 def _getCoverage(self):
138 """Return the coverage of the element between 0 and 1
Kevin Rocard326e39e2012-12-14 16:11:05 +0100139
Kevin Rocard804e0642013-06-25 12:09:35 +0200140 If the element has no coverage dependency (usually child) return 0 or 1.
141 otherwise the element coverage is the dependency coverage average"""
142 coverageDependanceElements = list(self._getCoverageDependanceElements())
Kevin Rocard326e39e2012-12-14 16:11:05 +0100143
Kevin Rocard804e0642013-06-25 12:09:35 +0200144 nbcoverageDependence = len(coverageDependanceElements)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100145
Kevin Rocard804e0642013-06-25 12:09:35 +0200146 if nbcoverageDependence == 0:
147 if self.nbUse == 0:
148 return 0
149 else:
150 return 1
Kevin Rocard326e39e2012-12-14 16:11:05 +0100151
Kevin Rocard804e0642013-06-25 12:09:35 +0200152 coverageDependenceValues = (depElement._getCoverage()
153 for depElement in coverageDependanceElements)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100154
Kevin Rocard804e0642013-06-25 12:09:35 +0200155 return sum(coverageDependenceValues) / nbcoverageDependence
Kevin Rocard326e39e2012-12-14 16:11:05 +0100156
Kevin Rocard804e0642013-06-25 12:09:35 +0200157 def _getCoverageDependanceElements(self):
158 return self.children
Kevin Rocard326e39e2012-12-14 16:11:05 +0100159
Kevin Rocard804e0642013-06-25 12:09:35 +0200160 def _coverageFormating(self, coverage):
161 # If no coverage provided
162 if not coverage :
163 return ""
Kevin Rocard326e39e2012-12-14 16:11:05 +0100164
Kevin Rocard804e0642013-06-25 12:09:35 +0200165 # Calculate coverage
166 return " (%s coverage)" % self._number2percent(coverage)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100167
Kevin Rocard804e0642013-06-25 12:09:35 +0200168 @staticmethod
169 def _number2percent(number):
170 """Format a number to a integer % string
Kevin Rocard326e39e2012-12-14 16:11:05 +0100171
Kevin Rocard804e0642013-06-25 12:09:35 +0200172 example: _number2percent(0.6666) -> "67%"
173 """
174 return "{0:.0f}%".format(100 * number)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100175
176
Kevin Rocard804e0642013-06-25 12:09:35 +0200177 def _dumpDescription(self, withCoverage, withNbUse):
Kevin Rocard326e39e2012-12-14 16:11:05 +0100178
Kevin Rocard804e0642013-06-25 12:09:35 +0200179 self.debug("yelding description")
180 yield RankedLine(self._description(withCoverage, withNbUse), lineSuffix="")
Kevin Rocard326e39e2012-12-14 16:11:05 +0100181
Kevin Rocard804e0642013-06-25 12:09:35 +0200182 for dumped in self._dumpPropagate(withCoverage, withNbUse) :
183 yield dumped
Kevin Rocard326e39e2012-12-14 16:11:05 +0100184
Kevin Rocard804e0642013-06-25 12:09:35 +0200185 def _dumpPropagate(self, withCoverage, withNbUse):
Kevin Rocard326e39e2012-12-14 16:11:05 +0100186
Kevin Rocard804e0642013-06-25 12:09:35 +0200187 for child in self.children :
188 for dumpedDescription in child._dumpDescription(withCoverage, withNbUse) :
189 yield dumpedDescription.increasedRank()
Kevin Rocard326e39e2012-12-14 16:11:05 +0100190
191
Kevin Rocard804e0642013-06-25 12:09:35 +0200192 def dump(self, withCoverage=False, withNbUse=True):
Kevin Rocard326e39e2012-12-14 16:11:05 +0100193
Kevin Rocard804e0642013-06-25 12:09:35 +0200194 return "\n".join(
195 str(dumpedDescription) for dumpedDescription in
196 self._dumpDescription(withCoverage, withNbUse))
Kevin Rocard326e39e2012-12-14 16:11:05 +0100197
Kevin Rocard804e0642013-06-25 12:09:35 +0200198 def exportToXML(self, domElement=None):
199 if domElement == None:
200 domElement = xml.dom.minidom.Element(self.tag)
Kevin Rocard02726ec2013-06-20 10:28:54 +0200201
Kevin Rocard804e0642013-06-25 12:09:35 +0200202 self._XMLaddAttributes(domElement)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100203
Kevin Rocard804e0642013-06-25 12:09:35 +0200204 for child in self.children :
205 domElement.appendChild(child.exportToXML())
Kevin Rocard326e39e2012-12-14 16:11:05 +0100206
Kevin Rocard804e0642013-06-25 12:09:35 +0200207 return domElement
Kevin Rocard326e39e2012-12-14 16:11:05 +0100208
Kevin Rocard804e0642013-06-25 12:09:35 +0200209 def _XMLaddAttributes(self, domElement):
210 attributes = self._getXMLAttributes()
Kevin Rocard326e39e2012-12-14 16:11:05 +0100211
Kevin Rocard804e0642013-06-25 12:09:35 +0200212 coverage = self._getCoverage()
213 if coverage != None :
214 attributes["Coverage"] = self._number2percent(coverage)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100215
Kevin Rocard804e0642013-06-25 12:09:35 +0200216 for key, value in attributes.items():
217 domElement.setAttribute(key, value)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100218
Kevin Rocard804e0642013-06-25 12:09:35 +0200219 def _getXMLAttributes(self):
220 return {
221 "Name": self.name,
222 "NbUse": str(self.nbUse)
223 }
Kevin Rocard326e39e2012-12-14 16:11:05 +0100224
Kevin Rocard804e0642013-06-25 12:09:35 +0200225 def _incNbUse(self):
226 self.nbUse += 1
Kevin Rocard326e39e2012-12-14 16:11:05 +0100227
Kevin Rocard804e0642013-06-25 12:09:35 +0200228 def childUsed(self, child):
229 self._incNbUse()
230 # Propagate to parent
231 self._tellParentThatChildUsed()
Kevin Rocard326e39e2012-12-14 16:11:05 +0100232
Kevin Rocard804e0642013-06-25 12:09:35 +0200233 def _tellParentThatChildUsed(self):
234 if self.parent :
235 self.parent.childUsed(self)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100236
237
Kevin Rocard804e0642013-06-25 12:09:35 +0200238 def parentUsed(self):
239 self._incNbUse()
240 # Propagate to children
241 for child in self.children :
242 child.parentUsed()
Kevin Rocard326e39e2012-12-14 16:11:05 +0100243
Kevin Rocard804e0642013-06-25 12:09:35 +0200244 def hasBeenUsed(self):
245 return self.nbUse > 0
Kevin Rocard326e39e2012-12-14 16:11:05 +0100246
Kevin Rocard804e0642013-06-25 12:09:35 +0200247 def operationOnChild(self, path, operation):
Kevin Rocard326e39e2012-12-14 16:11:05 +0100248
Kevin Rocard804e0642013-06-25 12:09:35 +0200249 if path:
250 return self._operationPropagate(path, operation)
251 else :
252 self.debug("operating on self")
253 return operation(self)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100254
Kevin Rocard804e0642013-06-25 12:09:35 +0200255 def _operationPropagate(self, path, operation):
Kevin Rocard326e39e2012-12-14 16:11:05 +0100256
Kevin Rocard804e0642013-06-25 12:09:35 +0200257 childName = path.pop(0)
258 child = self.getChildFromName(childName)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100259
Kevin Rocard804e0642013-06-25 12:09:35 +0200260 return child.operationOnChild(path, operation)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100261
262
263
Kevin Rocard804e0642013-06-25 12:09:35 +0200264 def debug(self, stringOrFunction, level=logging.DEBUG):
265 """Print a debug line on stderr in tree form
Kevin Rocard326e39e2012-12-14 16:11:05 +0100266
Kevin Rocard804e0642013-06-25 12:09:35 +0200267 If the debug line is expensive to generate, provide callable
268 object, it will be called if log is enable for this level.
269 This callable object should return the logline string.
270 """
271 if logger.isEnabledFor(level):
Kevin Rocard326e39e2012-12-14 16:11:05 +0100272
Kevin Rocard804e0642013-06-25 12:09:35 +0200273 # TODO: use buildin callable if python >= 3.2
274 if hasattr(stringOrFunction, "__call__"):
275 string = stringOrFunction()
276 else:
277 string = stringOrFunction
Kevin Rocard326e39e2012-12-14 16:11:05 +0100278
Kevin Rocard804e0642013-06-25 12:09:35 +0200279 rankedLine = DebugRankedLine("%s: %s" % (self, string))
280 self._logDebug(rankedLine, level)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100281
Kevin Rocard804e0642013-06-25 12:09:35 +0200282 def _logDebug(self, rankedLine, level):
Kevin Rocard326e39e2012-12-14 16:11:05 +0100283
Kevin Rocard804e0642013-06-25 12:09:35 +0200284 if self.parent:
285 self.parent._logDebug(rankedLine.increasedRank(), level)
286 else :
287 logger.log(level, str(rankedLine))
Kevin Rocard326e39e2012-12-14 16:11:05 +0100288
289
290
291
292class FromDomElement(Element):
Kevin Rocard804e0642013-06-25 12:09:35 +0200293 def __init__(self, DomElement):
294 self._initFromDom(DomElement)
295 super().__init__(self.name)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100296
297
Kevin Rocard804e0642013-06-25 12:09:35 +0200298 def _initFromDom(self, DomElement):
299 self.name = DomElement.getAttribute("Name")
Kevin Rocard326e39e2012-12-14 16:11:05 +0100300
301
302
303class DomElementLocation():
Kevin Rocard804e0642013-06-25 12:09:35 +0200304 def __init__(self, classConstructor, path=None):
305 self.classConstructor = classConstructor
306 if path :
307 self.path = path
308 else :
309 self.path = []
Kevin Rocard326e39e2012-12-14 16:11:05 +0100310
Kevin Rocard804e0642013-06-25 12:09:35 +0200311 self.path.append(classConstructor.tag)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100312
313
314class DomPopulatedElement(Element):
Kevin Rocard804e0642013-06-25 12:09:35 +0200315 """Default child populate
Kevin Rocard326e39e2012-12-14 16:11:05 +0100316
Kevin Rocard804e0642013-06-25 12:09:35 +0200317 Look for each dom element with tag specified in self.tag
318 and instantiate it with the dom element
319 """
320 childClasses = []
Kevin Rocard326e39e2012-12-14 16:11:05 +0100321
Kevin Rocard804e0642013-06-25 12:09:35 +0200322 def populate(self, dom):
Kevin Rocard326e39e2012-12-14 16:11:05 +0100323
Kevin Rocard804e0642013-06-25 12:09:35 +0200324 for childDomElementLocation in self.childClasses :
Kevin Rocard326e39e2012-12-14 16:11:05 +0100325
Kevin Rocard804e0642013-06-25 12:09:35 +0200326 self.debug("Looking for child %s in path %s" % (
327 childDomElementLocation.path[-1], childDomElementLocation.path))
Kevin Rocard326e39e2012-12-14 16:11:05 +0100328
Kevin Rocard804e0642013-06-25 12:09:35 +0200329 for childDomElement in self._findChildFromTagPath(dom, childDomElementLocation.path) :
Kevin Rocard326e39e2012-12-14 16:11:05 +0100330
Kevin Rocard804e0642013-06-25 12:09:35 +0200331 childElement = childDomElementLocation.classConstructor(childDomElement)
332 self.addChild(childElement)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100333
Kevin Rocard804e0642013-06-25 12:09:35 +0200334 childElement.populate(childDomElement)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100335
Kevin Rocard804e0642013-06-25 12:09:35 +0200336 def _findChildFromTagPath(self, dom, path):
337 if not path :
338 yield dom
339 else :
340 # Copy list
341 path = list(path)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100342
Kevin Rocard804e0642013-06-25 12:09:35 +0200343 tag = path.pop(0)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100344
Kevin Rocard804e0642013-06-25 12:09:35 +0200345 # Find element with tag
346 self.debug("Going to find elements with tag %s in %s" % (tag, dom))
347 self.debug(lambda: "Nb of solutions: %s" % len(dom.getElementsByTagName(tag)))
Kevin Rocard326e39e2012-12-14 16:11:05 +0100348
Kevin Rocard804e0642013-06-25 12:09:35 +0200349 for elementByTag in dom.getElementsByTagName(tag) :
Kevin Rocard326e39e2012-12-14 16:11:05 +0100350
Kevin Rocard804e0642013-06-25 12:09:35 +0200351 self.debug("Found element: %s" % elementByTag)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100352
Kevin Rocard804e0642013-06-25 12:09:35 +0200353 # If the same tag is found
354 if elementByTag in dom.childNodes :
Kevin Rocard326e39e2012-12-14 16:11:05 +0100355
Kevin Rocard804e0642013-06-25 12:09:35 +0200356 # Yield next level
357 for element in self._findChildFromTagPath(elementByTag, path) :
358 yield element
Kevin Rocard326e39e2012-12-14 16:11:05 +0100359
360
361class Rule(Element):
362
Kevin Rocard804e0642013-06-25 12:09:35 +0200363 def usedIfApplicable(self, criteria):
364 childApplicability = (child.usedIfApplicable(criteria)
365 for child in self.children)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100366
Kevin Rocard804e0642013-06-25 12:09:35 +0200367 isApplicable = self._isApplicable(criteria, childApplicability)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100368
Kevin Rocard804e0642013-06-25 12:09:35 +0200369 if isApplicable :
370 self._incNbUse()
Kevin Rocard326e39e2012-12-14 16:11:05 +0100371
Kevin Rocard804e0642013-06-25 12:09:35 +0200372 self.debug("Rule applicability: %s" % isApplicable)
373 assert(isApplicable == True or isApplicable == False)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100374
Kevin Rocard804e0642013-06-25 12:09:35 +0200375 return isApplicable
Kevin Rocard326e39e2012-12-14 16:11:05 +0100376
377
Kevin Rocard804e0642013-06-25 12:09:35 +0200378 def _isApplicable(self, criteria, childApplicability):
379 """Return the rule applicability depending on children applicability.
Kevin Rocardcf031992013-06-14 18:09:54 +0200380
Kevin Rocard804e0642013-06-25 12:09:35 +0200381 If at least one child is applicable, return true"""
382 # Lazy evaluation as in the PFW
383 return all(childApplicability)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100384
385
386class CriterionRule(FromDomElement, DomPopulatedElement, Rule):
Kevin Rocard804e0642013-06-25 12:09:35 +0200387 tag = "SelectionCriterionRule"
388 childClasses = []
389 isApplicableOperations = {
390 "Includes" : lambda criterion, value: criterion.stateIncludes(value),
391 "Excludes" : lambda criterion, value: not criterion.stateIncludes(value),
392 "Is" : lambda criterion, value: criterion.stateIs(value),
393 "IsNot" : lambda criterion, value: not criterion.stateIs(value)
394 }
Kevin Rocard326e39e2012-12-14 16:11:05 +0100395
Kevin Rocard804e0642013-06-25 12:09:35 +0200396 def _initFromDom(self, DomElement):
397 self.selectionCriterion = DomElement.getAttribute("SelectionCriterion")
398 self.matchesWhen = DomElement.getAttribute("MatchesWhen")
399 self.value = DomElement.getAttribute("Value")
400 self.name = "%s %s %s" % (self.selectionCriterion, self.matchesWhen, self.value)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100401
Kevin Rocard804e0642013-06-25 12:09:35 +0200402 applicableOperationWithoutValue = self.isApplicableOperations[self.matchesWhen]
403 self.isApplicableOperation = lambda criterion: applicableOperationWithoutValue(criterion, self.value)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100404
Kevin Rocard804e0642013-06-25 12:09:35 +0200405 def _isApplicable(self, criteria, childApplicability):
Kevin Rocard326e39e2012-12-14 16:11:05 +0100406
Kevin Rocard804e0642013-06-25 12:09:35 +0200407 return criteria.operationOnChild([self.selectionCriterion],
408 self.isApplicableOperation)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100409
410
411class CompoundRule(FromDomElement, DomPopulatedElement, Rule):
Kevin Rocard804e0642013-06-25 12:09:35 +0200412 """CompoundRule can be of type ALL or ANY"""
413 tag = "CompoundRule"
414 # Declare childClasses but define it at first class instantiation
415 childClasses = None
Kevin Rocard326e39e2012-12-14 16:11:05 +0100416
Kevin Rocard804e0642013-06-25 12:09:35 +0200417 def __init__(self, dom):
418 # Define childClasses at first class instantiation
419 if self.childClasses == None :
420 self.childClasses = [DomElementLocation(CriterionRule),
421 DomElementLocation(CompoundRule)]
422 super().__init__(dom)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100423
Kevin Rocard804e0642013-06-25 12:09:35 +0200424 def _initFromDom(self, DomElement):
Kevin Rocard326e39e2012-12-14 16:11:05 +0100425
Kevin Rocard804e0642013-06-25 12:09:35 +0200426 type = DomElement.getAttribute("Type")
427 self.ofTypeAll = {"All" : True, "Any" : False}[type]
428 self.name = type
Kevin Rocard326e39e2012-12-14 16:11:05 +0100429
Kevin Rocard804e0642013-06-25 12:09:35 +0200430 def _isApplicable(self, criteria, childApplicability):
431 if self.ofTypeAll :
432 applicability = super()._isApplicable(criteria, childApplicability)
433 else:
434 # Lazy evaluation as in the PFW
435 applicability = any(childApplicability)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100436
Kevin Rocard804e0642013-06-25 12:09:35 +0200437 return applicability
Kevin Rocard326e39e2012-12-14 16:11:05 +0100438
439class RootRule(DomPopulatedElement, Rule):
Kevin Rocard804e0642013-06-25 12:09:35 +0200440 tag = "RootRule"
441 childClasses = [DomElementLocation(CompoundRule)]
Kevin Rocard326e39e2012-12-14 16:11:05 +0100442
Kevin Rocard804e0642013-06-25 12:09:35 +0200443 def populate(self, dom):
444 super().populate(dom)
445 self.debug("Children: %s" % self.children)
446 # A configuration can only have one or no rule
447 assert(len(self.children) <= 1)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100448
Kevin Rocard804e0642013-06-25 12:09:35 +0200449 def _getCoverageDependanceElements(self):
450 return self._getDescendants()
Kevin Rocard326e39e2012-12-14 16:11:05 +0100451
452
453class CriteronStates(Element):
Kevin Rocard804e0642013-06-25 12:09:35 +0200454 """Root of configuration application criterion state"""
455 tag = "CriterionStates"
Kevin Rocard326e39e2012-12-14 16:11:05 +0100456
Kevin Rocard804e0642013-06-25 12:09:35 +0200457 def parentUsed(self, criteria):
458 """Add criteria to child if not exist, if exist increase it's nbUse"""
459 self._incNbUse()
Kevin Rocard326e39e2012-12-14 16:11:05 +0100460
Kevin Rocard804e0642013-06-25 12:09:35 +0200461 matches = [child for child in self.children if child == criteria]
Kevin Rocard326e39e2012-12-14 16:11:05 +0100462
Kevin Rocard804e0642013-06-25 12:09:35 +0200463 assert(len(matches) <= 1)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100464
Kevin Rocard804e0642013-06-25 12:09:35 +0200465 if matches :
466 self.debug("Criteria state has already been encounter")
467 currentcriteria = matches[0]
468 else :
469 self.debug("Criteria state has never been encounter, saving it")
470 currentcriteria = criteria
471 self.addChild(criteria)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100472
Kevin Rocard804e0642013-06-25 12:09:35 +0200473 currentcriteria.parentUsed()
Kevin Rocard326e39e2012-12-14 16:11:05 +0100474
Kevin Rocard326e39e2012-12-14 16:11:05 +0100475
476
477class Configuration(FromDomElement, DomPopulatedElement):
Kevin Rocard804e0642013-06-25 12:09:35 +0200478 tag = "Configuration"
479 childClasses = []
Kevin Rocard326e39e2012-12-14 16:11:05 +0100480
Kevin Rocard804e0642013-06-25 12:09:35 +0200481 class IneligibleConfigurationAppliedError(CustomError):
Kevin Rocard97dbd352013-06-10 15:15:56 +0200482
Kevin Rocard804e0642013-06-25 12:09:35 +0200483 def __init__(self, configuration, criteria):
484 self.configuration = configuration
485 self.criteria = criteria
Kevin Rocard97dbd352013-06-10 15:15:56 +0200486
Kevin Rocard804e0642013-06-25 12:09:35 +0200487 def __str__(self):
Kevin Rocard97dbd352013-06-10 15:15:56 +0200488
Kevin Rocard804e0642013-06-25 12:09:35 +0200489 return ("Applying ineligible %s, "
490 "rule:\n%s\n"
491 "Criteria current state:\n%s" % (
492 self.configuration,
493 self.configuration.rootRule.dump(withCoverage=False, withNbUse=False),
494 self.criteria.dump(withCoverage=False, withNbUse=False)
495 ))
Kevin Rocard97dbd352013-06-10 15:15:56 +0200496
Kevin Rocard804e0642013-06-25 12:09:35 +0200497 def __init__(self, DomElement):
498 super().__init__(DomElement)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100499
Kevin Rocard804e0642013-06-25 12:09:35 +0200500 self.rootRule = RootRule("RootRule")
501 self.addChild(self.rootRule)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100502
Kevin Rocard804e0642013-06-25 12:09:35 +0200503 self.criteronStates = CriteronStates("CriterionStates")
504 self.addChild(self.criteronStates)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100505
Kevin Rocard804e0642013-06-25 12:09:35 +0200506 def populate(self, dom):
507 # Delegate to rootRule
508 self.rootRule.populate(dom)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100509
Kevin Rocard804e0642013-06-25 12:09:35 +0200510 def _getCoverage(self):
511 # Delegate to rootRule
512 return self.rootRule._getCoverage()
Kevin Rocard326e39e2012-12-14 16:11:05 +0100513
Kevin Rocard804e0642013-06-25 12:09:35 +0200514 def used(self, criteria):
Kevin Rocard326e39e2012-12-14 16:11:05 +0100515
Kevin Rocard804e0642013-06-25 12:09:35 +0200516 self._incNbUse()
Kevin Rocard326e39e2012-12-14 16:11:05 +0100517
Kevin Rocard804e0642013-06-25 12:09:35 +0200518 # Propagate use to parents
519 self._tellParentThatChildUsed()
Kevin Rocard326e39e2012-12-14 16:11:05 +0100520
Kevin Rocard804e0642013-06-25 12:09:35 +0200521 # Propagate to criterion coverage
522 self.criteronStates.parentUsed(criteria.export())
Kevin Rocard326e39e2012-12-14 16:11:05 +0100523
Kevin Rocard804e0642013-06-25 12:09:35 +0200524 # Propagate to rules
525 if not self.rootRule.usedIfApplicable(criteria) :
Kevin Rocard326e39e2012-12-14 16:11:05 +0100526
Kevin Rocard804e0642013-06-25 12:09:35 +0200527 self.debug("Applied but rule does not match current "
Kevin Rocard3aa0db42013-02-13 17:15:38 +0100528 "criteria (parent: %s) " % self.parent.name,
Kevin Rocard804e0642013-06-25 12:09:35 +0200529 logging.ERROR)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100530
Kevin Rocard804e0642013-06-25 12:09:35 +0200531 raise self.IneligibleConfigurationAppliedError(self, criteria.export())
Kevin Rocard326e39e2012-12-14 16:11:05 +0100532
Kevin Rocard804e0642013-06-25 12:09:35 +0200533 def _dumpPropagate(self, withCoverage, withNbUse):
534 self.debug("Going to ask %s for description" % self.rootRule)
535 for dumpedDescription in self.rootRule._dumpDescription(
536 withCoverage=withCoverage,
537 withNbUse=withNbUse) :
538 yield dumpedDescription.increasedRank()
Kevin Rocard326e39e2012-12-14 16:11:05 +0100539
Kevin Rocard804e0642013-06-25 12:09:35 +0200540 self.debug("Going to ask %s for description" % self.criteronStates)
541 for dumpedDescription in self.criteronStates._dumpDescription(
542 withCoverage=False,
543 withNbUse=withNbUse) :
544 yield dumpedDescription.increasedRank()
Kevin Rocard326e39e2012-12-14 16:11:05 +0100545
546
547class Domain(FromDomElement, DomPopulatedElement):
Kevin Rocard804e0642013-06-25 12:09:35 +0200548 tag = "ConfigurableDomain"
549 childClasses = [DomElementLocation(Configuration, ["Configurations"])]
Kevin Rocard326e39e2012-12-14 16:11:05 +0100550
551
552class Domains(DomPopulatedElement):
Kevin Rocard804e0642013-06-25 12:09:35 +0200553 tag = "Domains"
554 childClasses = [DomElementLocation(Domain, ["ConfigurableDomains"])]
Kevin Rocard326e39e2012-12-14 16:11:05 +0100555
556
557class RankedLine():
Kevin Rocard804e0642013-06-25 12:09:35 +0200558 def __init__(self, string,
559 stringPrefix="|-- ",
560 rankString="| ",
561 linePrefix="",
562 lineSuffix="\n"):
563 self.string = string
564 self.rank = 0
565 self.stringPrefix = stringPrefix
566 self.rankString = rankString
567 self.linePrefix = linePrefix
568 self.lineSuffix = lineSuffix
Kevin Rocard326e39e2012-12-14 16:11:05 +0100569
Kevin Rocard804e0642013-06-25 12:09:35 +0200570 def increasedRank(self):
571 self.rank += 1
572 return self
Kevin Rocard326e39e2012-12-14 16:11:05 +0100573
Kevin Rocard804e0642013-06-25 12:09:35 +0200574 def __str__(self):
575 return self.linePrefix + \
576 self.rank * self.rankString + \
577 self.stringPrefix + \
578 self.string + \
579 self.lineSuffix
Kevin Rocard326e39e2012-12-14 16:11:05 +0100580
581class DebugRankedLine(RankedLine):
582
Kevin Rocard804e0642013-06-25 12:09:35 +0200583 def __init__(self, string, lineSuffix=""):
584 super().__init__(string,
585 stringPrefix="",
586 rankString=" ",
587 linePrefix="",
588 lineSuffix=lineSuffix)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100589
590
591class CriterionState(Element):
Kevin Rocard804e0642013-06-25 12:09:35 +0200592 tag = "CriterionState"
593 def used(self):
594 self._incNbUse()
Kevin Rocard326e39e2012-12-14 16:11:05 +0100595
596
597class Criterion(Element):
Kevin Rocard804e0642013-06-25 12:09:35 +0200598 tag = "Criterion"
599 inclusivenessTranslate = {True: "Inclusive", False: "Exclusive"}
Kevin Rocard3aa0db42013-02-13 17:15:38 +0100600
Kevin Rocard804e0642013-06-25 12:09:35 +0200601 class ChangeRequestToNonAccessibleState(CustomError):
602 def __init__(self, requestedState, detail):
603 self.requestedState = requestedState
604 self.detail = detail
Kevin Rocard556538e2013-06-10 15:20:10 +0200605
Kevin Rocard804e0642013-06-25 12:09:35 +0200606 def __str__(self):
607 return ("Change request to non accessible state %s. Detail: %s" %
608 (self.requestedState, self.detail))
Kevin Rocard556538e2013-06-10 15:20:10 +0200609
Kevin Rocard804e0642013-06-25 12:09:35 +0200610 def __init__(self, name, isInclusif,
611 stateNamesList, currentStateNamesList,
612 ignoreIntegrity=False):
613 super().__init__(name)
614 self.isInclusif = isInclusif
Kevin Rocard326e39e2012-12-14 16:11:05 +0100615
Kevin Rocard804e0642013-06-25 12:09:35 +0200616 for state in stateNamesList :
617 self.addChild(CriterionState(state))
Kevin Rocard326e39e2012-12-14 16:11:05 +0100618
Kevin Rocard804e0642013-06-25 12:09:35 +0200619 self.currentState = []
620 self.initStateNamesList = list(currentStateNamesList)
621 self.changeState(self.initStateNamesList, ignoreIntegrity)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100622
Kevin Rocard804e0642013-06-25 12:09:35 +0200623 def reset(self):
624 # Set current state as provided at initialisation
625 self.changeState(self.initStateNamesList, ignoreIntegrity=True)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100626
Kevin Rocard804e0642013-06-25 12:09:35 +0200627 def changeState(self, subStateNames, ignoreIntegrity=False):
628 self.debug("Changing state from: %s to: %s" % (
629 list(self._getElementNames(self.currentState)),
630 subStateNames))
Kevin Rocard326e39e2012-12-14 16:11:05 +0100631
Kevin Rocard804e0642013-06-25 12:09:35 +0200632 if not ignoreIntegrity and not self.isIntegre(subStateNames):
633 raise self.ChangeRequestToNonAccessibleState(subStateNames,
634 "An exclusive criterion must have a non empty state")
Kevin Rocard326e39e2012-12-14 16:11:05 +0100635
Kevin Rocard804e0642013-06-25 12:09:35 +0200636 newCurrentState = []
637 for subStateName in subStateNames :
638 subState = self.getChildFromName(subStateName)
639 subState.used()
640 newCurrentState.append(subState)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100641
Kevin Rocard804e0642013-06-25 12:09:35 +0200642 self.currentState = newCurrentState
Kevin Rocard326e39e2012-12-14 16:11:05 +0100643
Kevin Rocard804e0642013-06-25 12:09:35 +0200644 self._incNbUse()
645 self._tellParentThatChildUsed()
Kevin Rocard326e39e2012-12-14 16:11:05 +0100646
Kevin Rocard804e0642013-06-25 12:09:35 +0200647 def isIntegre(self, subStateNames):
648 return self.isInclusif or len(subStateNames) == 1
Kevin Rocard556538e2013-06-10 15:20:10 +0200649
Kevin Rocard804e0642013-06-25 12:09:35 +0200650 def childUsed(self, child):
651 self.currentState = child
652 super().childUsed(child)
Kevin Rocard556538e2013-06-10 15:20:10 +0200653
Kevin Rocard804e0642013-06-25 12:09:35 +0200654 def export(self):
655 subStateNames = self._getElementNames(self.currentState)
656 return Criterion(self.name, self.isInclusif, subStateNames, subStateNames,
657 ignoreIntegrity=True)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100658
Kevin Rocard804e0642013-06-25 12:09:35 +0200659 def stateIncludes(self, subStateName):
660 subStateCurrentNames = list(self._getElementNames(self.currentState))
Kevin Rocard326e39e2012-12-14 16:11:05 +0100661
Kevin Rocard804e0642013-06-25 12:09:35 +0200662 self.debug("Testing if %s is included in %s" % (subStateName, subStateCurrentNames))
Kevin Rocard326e39e2012-12-14 16:11:05 +0100663
Kevin Rocard804e0642013-06-25 12:09:35 +0200664 isIncluded = subStateName in subStateCurrentNames
665 self.debug("IsIncluded: %s" % isIncluded)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100666
Kevin Rocard804e0642013-06-25 12:09:35 +0200667 return isIncluded
Kevin Rocard326e39e2012-12-14 16:11:05 +0100668
669
Kevin Rocard804e0642013-06-25 12:09:35 +0200670 def stateIs(self, subStateNames):
671 if len(self.currentState) != 1 :
672 return False
673 else :
674 return self.stateIncludes(subStateNames)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100675
Kevin Rocard804e0642013-06-25 12:09:35 +0200676 def _getXMLAttributes(self):
677 attributes = super()._getXMLAttributes()
678 attributes["Type"] = self.inclusivenessTranslate[self.isInclusif]
679 return attributes
Kevin Rocard3aa0db42013-02-13 17:15:38 +0100680
Kevin Rocard326e39e2012-12-14 16:11:05 +0100681
682class Criteria(Element):
Kevin Rocard804e0642013-06-25 12:09:35 +0200683 tag = "Criteria"
Kevin Rocard326e39e2012-12-14 16:11:05 +0100684
Kevin Rocard804e0642013-06-25 12:09:35 +0200685 class DuplicatedCriterionError(DuplicatedChildError):
686 pass
Kevin Rocardd077c552013-06-10 15:31:32 +0200687
Kevin Rocard804e0642013-06-25 12:09:35 +0200688 def export(self):
689 self.debug("Exporting criteria")
690 assert(self.children)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100691
Kevin Rocard804e0642013-06-25 12:09:35 +0200692 exported = Criteria(self.name)
693 for child in self.children :
694 exported.addChild(child.export())
695 return exported
Kevin Rocard326e39e2012-12-14 16:11:05 +0100696
Kevin Rocard804e0642013-06-25 12:09:35 +0200697 def addChild(self, child):
698 if child in self.children:
699 raise self.DuplicatedCriterionError(self, child)
700 super().addChild(child)
Kevin Rocard3aa0db42013-02-13 17:15:38 +0100701
702class ConfigAppliedWithoutCriteriaError(CustomError):
Kevin Rocard804e0642013-06-25 12:09:35 +0200703 def __init__(self, configurationName, domainName):
704 self.configurationName = configurationName
705 self.domainName = domainName
706 def __str__(self):
707 return ('Applying configuration "%s" from domain "%s" before declaring criteria' %
708 (self.configurationName, self.domainName))
Kevin Rocard326e39e2012-12-14 16:11:05 +0100709
710class ParsePFWlog():
Kevin Rocard804e0642013-06-25 12:09:35 +0200711 MATCH = "match"
712 ACTION = "action"
Kevin Rocard326e39e2012-12-14 16:11:05 +0100713
Kevin Rocard804e0642013-06-25 12:09:35 +0200714 def __init__(self, domains, criteria, ErrorsToIgnore=()):
Kevin Rocard326e39e2012-12-14 16:11:05 +0100715
Kevin Rocard804e0642013-06-25 12:09:35 +0200716 self.domains = domains;
717 self.criteria = criteria;
718 self.ErrorsToIgnore = ErrorsToIgnore
Kevin Rocard326e39e2012-12-14 16:11:05 +0100719
Kevin Rocard804e0642013-06-25 12:09:35 +0200720 configApplicationRegext = r""".*Applying configuration "(.*)" from domain "([^"]*)"""
721 matchConfigApplicationLine = re.compile(configApplicationRegext).match
Kevin Rocard326e39e2012-12-14 16:11:05 +0100722
Kevin Rocard804e0642013-06-25 12:09:35 +0200723 criterionCreationRegext = ", ".join([
724 r""".*Criterion name: (.*)""",
725 r"""type kind: (.*)""",
726 r"""current state: (.*)""",
727 r"""states: {(.*)}"""
728 ])
729 matchCriterionCreationLine = re.compile(criterionCreationRegext).match
Kevin Rocard326e39e2012-12-14 16:11:05 +0100730
Kevin Rocard804e0642013-06-25 12:09:35 +0200731 changingCriterionRegext = r""".*Selection criterion changed event: Criterion name: (.*), current state: ([^\n\r]*)"""
732 matchChangingCriterionLine = re.compile(changingCriterionRegext).match
Kevin Rocard326e39e2012-12-14 16:11:05 +0100733
Kevin Rocard804e0642013-06-25 12:09:35 +0200734 self.lineLogTypes = [
735 {
736 self.MATCH: matchConfigApplicationLine,
737 self.ACTION: self._configApplication
738 }, {
739 self.MATCH: matchCriterionCreationLine,
740 self.ACTION: self._criterionCreation
741 }, {
742 self.MATCH: matchChangingCriterionLine,
743 self.ACTION: self._changingCriterion
744 }
745 ]
Kevin Rocard326e39e2012-12-14 16:11:05 +0100746
Kevin Rocard804e0642013-06-25 12:09:35 +0200747 @staticmethod
748 def _formatCriterionList(liststring, separator):
749 list = liststring.split(separator)
750 if len(list) == 1 and list[0] == "<none>":
751 list = []
752 return list
Kevin Rocard326e39e2012-12-14 16:11:05 +0100753
Kevin Rocard804e0642013-06-25 12:09:35 +0200754 def _criterionCreation(self, matchCriterionCreation):
755 # Unpack
756 criterionName, criterionType, currentCriterionStates, criterionStates = matchCriterionCreation.group(1, 2, 3, 4)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100757
Kevin Rocard804e0642013-06-25 12:09:35 +0200758 criterionStateList = self._formatCriterionList(criterionStates, ", ")
Kevin Rocard326e39e2012-12-14 16:11:05 +0100759
Kevin Rocard804e0642013-06-25 12:09:35 +0200760 criterionIsInclusif = {"exclusive" : False, "inclusive" : True}[criterionType]
Kevin Rocard326e39e2012-12-14 16:11:05 +0100761
Kevin Rocard804e0642013-06-25 12:09:35 +0200762 currentcriterionStateList = self._formatCriterionList(currentCriterionStates, "|")
Kevin Rocard326e39e2012-12-14 16:11:05 +0100763
Kevin Rocard804e0642013-06-25 12:09:35 +0200764 logger.info("Creating criterion: " + criterionName +
765 " (" + criterionType + ") " +
766 " with current state: " + str(currentcriterionStateList) +
767 ", possible states:" + str(criterionStateList))
Kevin Rocard326e39e2012-12-14 16:11:05 +0100768
Kevin Rocard804e0642013-06-25 12:09:35 +0200769 try:
770 self.criteria.addChild(Criterion(
771 criterionName,
772 criterionIsInclusif,
773 criterionStateList,
774 currentcriterionStateList
775 ))
776 except self.criteria.DuplicatedCriterionError as ex:
777 logger.debug(ex)
778 logger.warning("Reseting criterion %s. Did you reset the PFW ?" % criterionName)
779 self.criteria.operationOnChild(
780 [criterionName],
781 lambda criterion: criterion.reset()
782 )
Kevin Rocardea874222013-06-10 16:00:08 +0200783
784
Kevin Rocard326e39e2012-12-14 16:11:05 +0100785
Kevin Rocard804e0642013-06-25 12:09:35 +0200786 def _changingCriterion(self, matchChangingCriterion):
787 # Unpack
788 criterionName, newCriterionSubStateNames = matchChangingCriterion.group(1, 2)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100789
Kevin Rocard804e0642013-06-25 12:09:35 +0200790 newCriterionState = self._formatCriterionList(newCriterionSubStateNames, "|")
Kevin Rocard326e39e2012-12-14 16:11:05 +0100791
Kevin Rocard804e0642013-06-25 12:09:35 +0200792 logger.info("Changing criterion %s to %s" % (criterionName , newCriterionState))
Kevin Rocard3aa0db42013-02-13 17:15:38 +0100793
Kevin Rocard804e0642013-06-25 12:09:35 +0200794 path = [criterionName]
795 changeCriterionOperation = lambda criterion : criterion.changeState(newCriterionState)
796 self.criteria.operationOnChild(path, changeCriterionOperation)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100797
Kevin Rocard804e0642013-06-25 12:09:35 +0200798 def _configApplication(self, matchConfig):
799 # Unpack
800 configurationName, domainName = matchConfig.group(1, 2)
Kevin Rocard3aa0db42013-02-13 17:15:38 +0100801
Kevin Rocard804e0642013-06-25 12:09:35 +0200802 # Check that at least one criterion exist
803 if not self.criteria.hasChildren() :
804 logger.error("Applying configuration before declaring criteria")
805 logger.info("Is the log starting at PFW boot ?")
806 raise ConfigAppliedWithoutCriteriaError(configurationName, domainName)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100807
Kevin Rocard804e0642013-06-25 12:09:35 +0200808 # Change criterion state
809 path = [domainName, configurationName]
810 usedOperation = lambda element : element.used(self.criteria)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100811
Kevin Rocard804e0642013-06-25 12:09:35 +0200812 logger.info("Applying configuration %s from domain %s" % (
813 configurationName, domainName))
Kevin Rocard326e39e2012-12-14 16:11:05 +0100814
Kevin Rocard804e0642013-06-25 12:09:35 +0200815 self.domains.operationOnChild(path, usedOperation)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100816
817
Kevin Rocard804e0642013-06-25 12:09:35 +0200818 def _digest(self, lineLogType, lineLog):
Kevin Rocard9050c812013-06-10 16:01:21 +0200819
Kevin Rocard804e0642013-06-25 12:09:35 +0200820 match = lineLogType[self.MATCH](lineLog)
821 if match :
822 lineLogType[self.ACTION](match)
823 return True
824 return False
Kevin Rocard326e39e2012-12-14 16:11:05 +0100825
Kevin Rocard9050c812013-06-10 16:01:21 +0200826
Kevin Rocard804e0642013-06-25 12:09:35 +0200827 def parsePFWlog(self, lines):
828 for lineNb, lineLog in enumerate(lines):
Kevin Rocard326e39e2012-12-14 16:11:05 +0100829
Kevin Rocard804e0642013-06-25 12:09:35 +0200830 logger.debug("Parsing line :%s" % lineLog.rstrip())
Kevin Rocard326e39e2012-12-14 16:11:05 +0100831
Kevin Rocard804e0642013-06-25 12:09:35 +0200832 digested = (self._digest(lineLogType, lineLog)
833 for lineLogType in self.lineLogTypes)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100834
Kevin Rocard804e0642013-06-25 12:09:35 +0200835 try:
836 success = any(digested)
Kevin Rocard9050c812013-06-10 16:01:21 +0200837
Kevin Rocard804e0642013-06-25 12:09:35 +0200838 # Catch some exception in order to print the current parsing line,
839 # then raise the exception again if not continue of error
840 except CustomError as ex:
841 logger.error('Error raised while parsing line %s: "%s"' %
842 (lineNb, repr(lineLog)))
Kevin Rocard9050c812013-06-10 16:01:21 +0200843
Kevin Rocard804e0642013-06-25 12:09:35 +0200844 # If exception is a subclass of ErrorsToIgnore, log it and continue
845 # otherwise raise it again.
846 if not issubclass(type(ex), self.ErrorsToIgnore):
847 raise ex
848 else:
849 logger.error('Ignoring exception:"%s", '
850 'can not guarantee database integrity' % ex)
851 else:
852 if not success:
853 logger.debug("Line does not match, dropped")
Kevin Rocard326e39e2012-12-14 16:11:05 +0100854
855
856class Root(Element):
Kevin Rocard804e0642013-06-25 12:09:35 +0200857 tag = "CoverageReport"
858 def __init__(self, name, dom):
859 super().__init__(name)
860 # Create domain tree
861 self.domains = Domains("Domains")
862 self.domains.populate(dom)
863 self.addChild(self.domains)
864 # Create criterion list
865 self.criteria = Criteria("CriterionRoot")
866 self.addChild(self.criteria)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100867
Kevin Rocard804e0642013-06-25 12:09:35 +0200868 def exportToXML(self):
869 """Export tree to an xml document"""
870 impl = xml.dom.minidom.getDOMImplementation()
871 newdoc = impl.createDocument(namespaceURI=None, qualifiedName=self.tag, doctype=None)
872 super().exportToXML(newdoc.documentElement)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100873
Kevin Rocard804e0642013-06-25 12:09:35 +0200874 return newdoc
Kevin Rocard326e39e2012-12-14 16:11:05 +0100875
876# ============================
877# Command line argument parser
878# ============================
879
880
881class ArgumentParser:
Kevin Rocard804e0642013-06-25 12:09:35 +0200882 """class that parse command line arguments with argparse library
Kevin Rocard326e39e2012-12-14 16:11:05 +0100883
Kevin Rocard804e0642013-06-25 12:09:35 +0200884 Result of parsing are the class attributes.
885 """
886 levelTranslate = [logging.ERROR, logging.WARNING, logging.INFO, logging.DEBUG]
Kevin Rocard326e39e2012-12-14 16:11:05 +0100887
Kevin Rocard804e0642013-06-25 12:09:35 +0200888 def __init__(self):
Kevin Rocard326e39e2012-12-14 16:11:05 +0100889
Kevin Rocard804e0642013-06-25 12:09:35 +0200890 try:
891 # As argparse is only in the stdlib since python 3.2,
892 # testing its availability
893 import argparse
Kevin Rocard326e39e2012-12-14 16:11:05 +0100894
Kevin Rocard804e0642013-06-25 12:09:35 +0200895 except ImportError:
896 logger.warning("Unable to import argparse "
897 "(parser for command-line options and arguments), "
898 "using default argument values:")
Kevin Rocard326e39e2012-12-14 16:11:05 +0100899
Kevin Rocard804e0642013-06-25 12:09:35 +0200900 logger.warning(" - InputFile: stdin")
901 self.inputFile = sys.stdin
Kevin Rocard326e39e2012-12-14 16:11:05 +0100902
Kevin Rocard804e0642013-06-25 12:09:35 +0200903 logger.warning(" - OutputFile: stdout")
904 self.outputFile = sys.stdout
Kevin Rocard326e39e2012-12-14 16:11:05 +0100905
Kevin Rocard804e0642013-06-25 12:09:35 +0200906 try:
907 self.domainsFile = sys.argv[1]
908 except IndexError as ex:
909 logger.fatal("No domain file provided (first argument)")
910 raise ex
911 else:
912 logger.warning(" - Domain file: " + self.domainsFile)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100913
Kevin Rocard804e0642013-06-25 12:09:35 +0200914 logger.warning(" - Output format: xml")
915 self.XMLreport = True
Kevin Rocard326e39e2012-12-14 16:11:05 +0100916
Kevin Rocard804e0642013-06-25 12:09:35 +0200917 logger.warning(" - Debug level: error")
918 self.debugLevel = logging.ERROR
919 else :
Kevin Rocard326e39e2012-12-14 16:11:05 +0100920
Kevin Rocard804e0642013-06-25 12:09:35 +0200921 myArgParser = argparse.ArgumentParser(description='Generate PFW report')
Kevin Rocard326e39e2012-12-14 16:11:05 +0100922
Kevin Rocard804e0642013-06-25 12:09:35 +0200923 myArgParser.add_argument(
924 'domainsFile',
925 type=argparse.FileType('r'),
926 help="the PFW domain XML file"
927 )
928 myArgParser.add_argument(
929 'pfwlog', nargs='?',
930 type=argparse.FileType('r'), default=sys.stdin,
931 help="the PFW log file, default stdin"
932 )
933 myArgParser.add_argument(
934 '-o', '--output',
935 dest="outputFile",
936 type=argparse.FileType('w'), default=sys.stdout,
937 help="the coverage report output file, default stdout"
938 )
939 myArgParser.add_argument(
940 '-v', '--verbose',
941 dest="debugLevel", default=0,
942 action='count',
943 help="print debug warnings from warning (default) to debug (-vv)"
944 )
Kevin Rocard326e39e2012-12-14 16:11:05 +0100945
Kevin Rocard804e0642013-06-25 12:09:35 +0200946 outputFormatGroupe = myArgParser.add_mutually_exclusive_group(required=False)
Kevin Rocard326e39e2012-12-14 16:11:05 +0100947
Kevin Rocard804e0642013-06-25 12:09:35 +0200948 outputFormatGroupe.add_argument(
949 '--xml',
950 dest="xmlFlag",
951 action='store_true',
952 help=" XML coverage output report"
953 )
954 outputFormatGroupe.add_argument(
955 '--raw',
956 dest="rawFlag",
957 action='store_true',
958 help="raw coverage output report"
959 )
Kevin Rocard326e39e2012-12-14 16:11:05 +0100960
Kevin Rocard804e0642013-06-25 12:09:35 +0200961 myArgParser.add_argument(
962 '--ignore-incoherent-criterion-state',
963 dest="incoherentCriterionFlag",
964 action='store_true',
965 help="ignore criterion transition to incoherent state"
966 )
Kevin Rocard9050c812013-06-10 16:01:21 +0200967
Kevin Rocard804e0642013-06-25 12:09:35 +0200968 myArgParser.add_argument(
969 '--ignore-ineligible-configuration-application',
970 dest="ineligibleConfigurationApplicationFlag",
971 action='store_true',
972 help="ignore application of configuration with a false rule "
973 "(not applicable configuration)"
974 )
Kevin Rocard9050c812013-06-10 16:01:21 +0200975
Kevin Rocard804e0642013-06-25 12:09:35 +0200976 # Process command line arguments
977 options = myArgParser.parse_args()
Kevin Rocard326e39e2012-12-14 16:11:05 +0100978
Kevin Rocard804e0642013-06-25 12:09:35 +0200979 # Mapping to attributes
980 self.inputFile = options.pfwlog
981 self.outputFile = options.outputFile
982 self.domainsFile = options.domainsFile
Kevin Rocard326e39e2012-12-14 16:11:05 +0100983
Kevin Rocard804e0642013-06-25 12:09:35 +0200984 # Output report in xml if flag not set
985 self.XMLreport = not options.rawFlag
Kevin Rocard326e39e2012-12-14 16:11:05 +0100986
Kevin Rocard804e0642013-06-25 12:09:35 +0200987 # Setting logger level
988 levelCapped = min(options.debugLevel, len(self.levelTranslate) - 1)
989 self.debugLevel = self.levelTranslate[levelCapped]
Kevin Rocard326e39e2012-12-14 16:11:05 +0100990
Kevin Rocard804e0642013-06-25 12:09:35 +0200991 # Setting ignore options
992 errorToIgnore = []
993 if options.ineligibleConfigurationApplicationFlag :
994 errorToIgnore.append(Configuration.IneligibleConfigurationAppliedError)
Kevin Rocard9050c812013-06-10 16:01:21 +0200995
Kevin Rocard804e0642013-06-25 12:09:35 +0200996 if options.incoherentCriterionFlag:
997 errorToIgnore.append(Criterion.ChangeRequestToNonAccessibleState)
Kevin Rocard9050c812013-06-10 16:01:21 +0200998
Kevin Rocard804e0642013-06-25 12:09:35 +0200999 self.errorToIgnore = tuple(errorToIgnore)
Kevin Rocard9050c812013-06-10 16:01:21 +02001000
Kevin Rocard326e39e2012-12-14 16:11:05 +01001001
1002
1003def main():
1004
Kevin Rocard804e0642013-06-25 12:09:35 +02001005 errorDuringLogParsing = -1
1006 errorDuringArgumentParsing = 1
Kevin Rocard53493b22013-02-04 15:16:10 +01001007
Kevin Rocard804e0642013-06-25 12:09:35 +02001008 try:
1009 commandLineArguments = ArgumentParser()
1010 except LookupError as ex:
1011 logger.error("Error during argument parsing")
1012 logger.debug(str(ex))
1013 sys.exit(errorDuringArgumentParsing)
Kevin Rocard326e39e2012-12-14 16:11:05 +01001014
Kevin Rocard804e0642013-06-25 12:09:35 +02001015 # Setting logger level
1016 logger.setLevel(commandLineArguments.debugLevel)
1017 logger.info("Log level set to: %s" %
1018 logging.getLevelName(commandLineArguments.debugLevel))
Kevin Rocard326e39e2012-12-14 16:11:05 +01001019
Kevin Rocard804e0642013-06-25 12:09:35 +02001020 # Create tree from XML
1021 dom = xml.dom.minidom.parse(commandLineArguments.domainsFile)
Kevin Rocard326e39e2012-12-14 16:11:05 +01001022
Kevin Rocard804e0642013-06-25 12:09:35 +02001023 # Create element tree
1024 root = Root("DomainCoverage", dom)
Kevin Rocard326e39e2012-12-14 16:11:05 +01001025
Kevin Rocard804e0642013-06-25 12:09:35 +02001026 # Parse PFW events
1027 parser = ParsePFWlog(root.domains, root.criteria, commandLineArguments.errorToIgnore)
Kevin Rocard53493b22013-02-04 15:16:10 +01001028
Kevin Rocard804e0642013-06-25 12:09:35 +02001029 try:
1030 parser.parsePFWlog(commandLineArguments.inputFile.readlines())
1031 except CustomError as ex:
1032 logger.fatal("Error during parsing log file %s: %s" %
1033 (commandLineArguments.inputFile, ex))
1034 sys.exit(errorDuringLogParsing)
Kevin Rocard326e39e2012-12-14 16:11:05 +01001035
Kevin Rocard804e0642013-06-25 12:09:35 +02001036 # Output report
1037 outputFile = commandLineArguments.outputFile
Kevin Rocard326e39e2012-12-14 16:11:05 +01001038
Kevin Rocard804e0642013-06-25 12:09:35 +02001039 if not commandLineArguments.XMLreport :
1040 outputFile.write("%s\n" % root.dump(withCoverage=True, withNbUse=True))
1041 else :
1042 outputFile.write(root.exportToXML().toprettyxml())
Kevin Rocard326e39e2012-12-14 16:11:05 +01001043
1044
Kevin Rocard326e39e2012-12-14 16:11:05 +01001045if __name__ == "__main__" :
Kevin Rocard804e0642013-06-25 12:09:35 +02001046 """ Execute main if the python interpreter is running this module as the main program """
1047 main()
Kevin Rocard326e39e2012-12-14 16:11:05 +01001048