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
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)