Mercurial > farol > farolluz
comparison farolluz/producttree.py @ 26:809db989cac5
Reorganize the code in smaller mpodules
author | Benoît Allard <benoit.allard@greenbone.net> |
---|---|
date | Fri, 24 Oct 2014 17:01:26 +0200 |
parents | farolluz/cvrf.py@4004b67216a9 |
children | b87f2a6e613a |
comparison
equal
deleted
inserted
replaced
25:3cab052872f4 | 26:809db989cac5 |
---|---|
1 # -*- coding: utf-8 -*- | |
2 # | |
3 # Authors: | |
4 # BenoƮt Allard <benoit.allard@greenbone.net> | |
5 # | |
6 # Copyright: | |
7 # Copyright (C) 2014 Greenbone Networks GmbH | |
8 # | |
9 # This program is free software; you can redistribute it and/or | |
10 # modify it under the terms of the GNU General Public License | |
11 # as published by the Free Software Foundation; either version 2 | |
12 # of the License, or (at your option) any later version. | |
13 # | |
14 # This program is distributed in the hope that it will be useful, | |
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 # GNU General Public License for more details. | |
18 # | |
19 # You should have received a copy of the GNU General Public License | |
20 # along with this program; if not, write to the Free Software | |
21 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
22 | |
23 """\ | |
24 Product Tree Objects related to CVRF Documents | |
25 """ | |
26 | |
27 from .common import ValidationError | |
28 | |
29 class CVRFProductTree(object): | |
30 def __init__(self): | |
31 # All the branches, they can be order with their `parent` attribute | |
32 self._branches = [] | |
33 self._groups = [] | |
34 self._relationships = [] | |
35 self._products = [] | |
36 self._groups = [] | |
37 | |
38 def addProduct(self, product): | |
39 """ Add to the product list """ | |
40 self._products.append(product) | |
41 | |
42 def addRelationship(self, rel): | |
43 self._relationships.append(rel) | |
44 | |
45 def addGroup(self, group): | |
46 self._groups.append(group) | |
47 | |
48 def getProductForID(self, productid): | |
49 for product in self._products: | |
50 if product._productid == productid: | |
51 return product | |
52 raise KeyError(productid) | |
53 | |
54 def getGroupForID(self, groupid): | |
55 for group in self._groups: | |
56 if group._groupid == groupid: | |
57 return group | |
58 raise KeyError(groupid) | |
59 | |
60 def decomposeProduct(self, productid): | |
61 """ In case of product defined as a relationship (product X installed | |
62 on OS Y), this gives us the following tuple: (OS, product). """ | |
63 product = self.getProductForID(productid) | |
64 parent = product._parent | |
65 if parent is None: | |
66 return (None, None) | |
67 if not isinstance(parent, CVRFRelationship): | |
68 return (None, None) | |
69 relationtype = parent._relationtype.replace(' ', '').lower() | |
70 if relationtype not in ('defaultcomponentof', 'installedon'): | |
71 return (None, None) | |
72 return ( | |
73 self.getProductForID(parent._relatestoproductreference), | |
74 self.getProductForID(parent._productreference) | |
75 ) | |
76 | |
77 def getBranch(self, path): | |
78 if len(path) == 0: | |
79 return self | |
80 branches = self._branches | |
81 node = None | |
82 for idx in path: | |
83 node = branches[idx] | |
84 branches = node._childs | |
85 return node | |
86 | |
87 def getBranches(self): | |
88 for branch in self._branches: | |
89 yield branch | |
90 for sub_branch in branch.getBranches(): | |
91 yield sub_branch | |
92 | |
93 def getPath(self): | |
94 return () | |
95 | |
96 def getNameOfRelationship(self, relationship): | |
97 if relationship is None: | |
98 return '' | |
99 return ' '.join((self.getProductForID(relationship._productreference)._name, 'as', | |
100 relationship._relationtype.lower(), | |
101 self.getProductForID(relationship._relatestoproductreference)._name)) | |
102 | |
103 def getOrphanedBranches(self, product=None): | |
104 """ The branches that could accept `product` as Product Definition """ | |
105 white_list = [] | |
106 if product is not None: | |
107 white_list = [product._parent] | |
108 for branch in self.getBranches(): | |
109 if (branch in white_list) or branch.isOrphaned(): | |
110 yield branch | |
111 | |
112 def getNotTerminalBranches(self, b2=None): | |
113 """\ | |
114 The branches that could accept `b2` as new sub-branches | |
115 Note that b2 and all its sub-branches cannot be listed | |
116 """ | |
117 black_list = [] | |
118 if b2 is not None: | |
119 black_list = [b2] + list(b2.getBranches()) | |
120 for branch in self.getBranches(): | |
121 if branch in black_list: | |
122 continue | |
123 if branch._product is None: | |
124 yield branch | |
125 | |
126 def getOrphanedRelationships(self, product=None): | |
127 """ The relationships that need a product defninition """ | |
128 white_list = [] | |
129 if product is not None: | |
130 white_list = [product.getCurrentRelationship()] | |
131 for i, relationship in enumerate(self._relationships): | |
132 if (relationship in white_list) or relationship.isOrphaned(): | |
133 yield (i, relationship) | |
134 | |
135 def nbProducts(self): | |
136 """ Amount of 'raw' Products """ | |
137 return len([p for p in self._products if p._parent is self]) | |
138 | |
139 def validate(self): | |
140 for branch in self._branches: | |
141 branch.validate() | |
142 productids = set() | |
143 for product in self._products: | |
144 product.validate() | |
145 if product._productid in productids: | |
146 raise ValidationError('Each ProductID must be unique (%s)' % product._productid) | |
147 productids.add(product._productid) | |
148 for relationship in self._relationships: | |
149 relationship.validate() | |
150 for productid in (relationship._productreference, | |
151 relationship._relatestoproductreference): | |
152 if productid not in productids: | |
153 raise ValidationError('ProductID %s is unknown' % productid) | |
154 groupids = set() | |
155 for group in self._groups: | |
156 group.validate() | |
157 if group._groupid in groupids: | |
158 raise ValidationError('Duplicated GroupID: %s' % group._groupid) | |
159 groupids.add(group._groupid) | |
160 for productid in group._productids: | |
161 if productid not in productids: | |
162 raise ValidationError('ProductID %s is unknown' % productid) | |
163 return productids, groupids | |
164 | |
165 def __str__(self): | |
166 return 'Products: %s' % '\n'.join(str(p) for p in self._products) | |
167 | |
168 | |
169 class CVRFProductBranch(object): | |
170 TYPES = ('Vendor', 'Product Family', 'Product Name', 'Product Version', | |
171 'Patch Level', 'Service Pack', 'Architecture', 'Language', | |
172 'Legacy', 'Specification') | |
173 def __init__(self, _type, name, parentbranch): | |
174 self._type = _type | |
175 self._name = name | |
176 self._childs = [] | |
177 self._product = None | |
178 self.link(parentbranch) | |
179 | |
180 def getParent(self): | |
181 return self._parentbranch | |
182 | |
183 def getPath(self, string=False): | |
184 """ return the path to that branch element as a tuple """ | |
185 if self.isRoot(): | |
186 for i, b in enumerate(self._parentbranch._branches): | |
187 if b is self: | |
188 if string: | |
189 return '%d' % i | |
190 return (i, ) | |
191 else: | |
192 for i, b in enumerate(self._parentbranch._childs): | |
193 if b is self: | |
194 if string: | |
195 return '/'.join([self._parentbranch.getPath(string), '%d' % i]) | |
196 return self._parentbranch.getPath(string) + (i,) | |
197 if string: | |
198 return '' | |
199 return () | |
200 | |
201 def getTree(self): | |
202 """ this returns a list of tuples (type, name) leading to here""" | |
203 if self.isRoot(): | |
204 return [(self._type, self._name)] | |
205 return self._parentbranch.getTree() + [(self._type, self._name)] | |
206 | |
207 def getName(self): | |
208 return ' / '.join("%s: %s" % (type_, name) for type_, name in self.getTree()) | |
209 | |
210 def getParentPath(self): | |
211 """ return as string the path to the parent """ | |
212 return '/'.join('%s' % p for p in self.getPath()[:-1]) | |
213 | |
214 def isRoot(self): | |
215 return isinstance(self._parentbranch, CVRFProductTree) | |
216 | |
217 def isOrphaned(self): | |
218 """ Has no childs and no product """ | |
219 return len(self._childs) == 0 and (self._product is None) | |
220 | |
221 def getBranches(self): | |
222 for branch in self._childs: | |
223 yield branch | |
224 for sub_branch in branch.getBranches(): | |
225 yield sub_branch | |
226 | |
227 def unlink(self): | |
228 """ Unset our _parent, and remove us from the _parent._childs """ | |
229 if self.isRoot(): | |
230 self.getParent()._branches.remove(self) | |
231 else: | |
232 self.getParent()._childs.remove(self) | |
233 self._parentbranch = None | |
234 | |
235 def link(self, parent): | |
236 """ Actually, only set the parent """ | |
237 self._parentbranch = parent | |
238 if self.isRoot(): | |
239 parent._branches.append(self) | |
240 else: | |
241 parent._childs.append(self) | |
242 | |
243 | |
244 def validate(self): | |
245 if not self._type: | |
246 raise ValidationError('A Branch must have a Type') | |
247 if self._type not in self.TYPES: | |
248 raise ValidationError('A Branch Type must be one of %s' % ', '.join(self.TYPES)) | |
249 if not self._name: | |
250 raise ValidationError('A Branch must have a Name') | |
251 for branch in self._childs: | |
252 branch.validate() | |
253 if self.isOrphaned(): | |
254 raise ValidationError('A Branch must have at least a sub-product or sub-branches') | |
255 | |
256 def __str__(self): | |
257 return "%s: %s" % (self._type, self._name) | |
258 | |
259 | |
260 class CVRFFullProductName(object): | |
261 def __init__(self, productid, name, parent, cpe=None): | |
262 self._productid = productid | |
263 self._name = name | |
264 self._cpe = cpe | |
265 # Can be None (directly under the tree), a ProductBranch, or a | |
266 # Relationship | |
267 self.link(parent) | |
268 | |
269 def isRoot(self): | |
270 return isinstance(self._parent, CVRFProductTree) | |
271 | |
272 def isRelationship(self): | |
273 return isinstance(self._parent, CVRFRelationship) | |
274 | |
275 def getTree(self): | |
276 if not isinstance(self._parent, CVRFProductBranch): | |
277 return [] | |
278 return self._parent.getTree() | |
279 | |
280 def getParentPath(self): | |
281 if self.isRoot() or self.isRelationship(): | |
282 return '' | |
283 return self._parent.getPath(True) | |
284 | |
285 def getCurrentRelationship(self): | |
286 if self.isRelationship(): | |
287 return self._parent | |
288 return None | |
289 | |
290 def unlink(self): | |
291 """ Unset our _parent, and remove us from the _parent._childs | |
292 We are still in the product list. | |
293 """ | |
294 if not self.isRoot(): | |
295 self._parent._product = None | |
296 self._parent = None | |
297 | |
298 def link(self, parent): | |
299 self._parent = parent | |
300 if not self.isRoot(): | |
301 parent._product = self | |
302 | |
303 def validate(self): | |
304 if not self._productid: | |
305 raise ValidationError('A Product must have a ProductID') | |
306 if not self._name: | |
307 raise ValidationError('A Product must have a Name') | |
308 | |
309 def __str__(self): | |
310 return "%s (%s)" % (self._productid, self._name) | |
311 | |
312 | |
313 class CVRFRelationship(object): | |
314 TYPES = ('Default Component Of', 'Optional Component Of', | |
315 'External Component Of', 'Installed On', 'Installed With') | |
316 def __init__(self, productref, reltype, relatestoproductref): | |
317 self._productreference = productref | |
318 self._relationtype = reltype | |
319 self._relatestoproductreference = relatestoproductref | |
320 self._product = None | |
321 | |
322 def getParent(self): | |
323 """ All parent element of a FullProductName should implement that | |
324 method """ | |
325 return None | |
326 | |
327 def isOrphaned(self): | |
328 return self._product is None | |
329 | |
330 def validate(self): | |
331 if not self._productreference: | |
332 raise ValidationError('A Relationship must have a Product Reference') | |
333 if not self._relationtype: | |
334 raise ValidationError('A Relationship must have a Relation Type') | |
335 if self._relationtype not in self.TYPES: | |
336 raise ValidationError('Relation Type must be one of %s' % ', '.join(self.TYPES)) | |
337 if not self._relatestoproductreference: | |
338 raise ValidationError('A Relationship must have a "Relates To product Reference"') | |
339 if self._productreference == self._relatestoproductreference: | |
340 raise ValidationError('A Relationship cannot reference twice the same Product') | |
341 | |
342 | |
343 class CVRFGroup(object): | |
344 def __init__(self, groupid): | |
345 self._groupid = groupid | |
346 self._description = None | |
347 self._productids = [] | |
348 | |
349 def setDescription(self, description): | |
350 self._description = description | |
351 | |
352 def addProductID(self, productid): | |
353 self._productids.append(productid) | |
354 | |
355 def getTitle(self): | |
356 if self._description: | |
357 return "%s (%d products)" % (self._description, len(self._productids)) | |
358 return "#%s (%d products)" % (self._groupid, len(self._productids)) | |
359 | |
360 def validate(self): | |
361 if not self._groupid: | |
362 raise ValidationError('A Group must have a GroupID') | |
363 if not self._productids or len(self._productids) < 2: | |
364 raise ValidationError('A Group must contain at least two products') | |
365 |