diff 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
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/farolluz/producttree.py	Fri Oct 24 17:01:26 2014 +0200
@@ -0,0 +1,365 @@
+# -*- coding: utf-8 -*-
+#
+# Authors:
+# BenoƮt Allard <benoit.allard@greenbone.net>
+#
+# Copyright:
+# Copyright (C) 2014 Greenbone Networks GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+
+"""\
+Product Tree Objects related to CVRF Documents
+"""
+
+from .common import ValidationError
+
+class CVRFProductTree(object):
+    def __init__(self):
+        # All the branches, they can be order with their `parent` attribute
+        self._branches = []
+        self._groups = []
+        self._relationships = []
+        self._products = []
+        self._groups = []
+
+    def addProduct(self, product):
+        """ Add to the product list """
+        self._products.append(product)
+
+    def addRelationship(self, rel):
+        self._relationships.append(rel)
+
+    def addGroup(self, group):
+        self._groups.append(group)
+
+    def getProductForID(self, productid):
+        for product in self._products:
+            if product._productid == productid:
+                return product
+        raise KeyError(productid)
+
+    def getGroupForID(self, groupid):
+        for group in self._groups:
+            if group._groupid == groupid:
+                return group
+        raise KeyError(groupid)
+
+    def decomposeProduct(self, productid):
+        """ In case of product defined as a relationship (product X installed
+        on OS Y), this gives us the following tuple: (OS, product). """
+        product = self.getProductForID(productid)
+        parent = product._parent
+        if parent is None:
+            return (None, None)
+        if not isinstance(parent, CVRFRelationship):
+            return (None, None)
+        relationtype = parent._relationtype.replace(' ', '').lower()
+        if relationtype not in ('defaultcomponentof', 'installedon'):
+            return (None, None)
+        return (
+            self.getProductForID(parent._relatestoproductreference),
+            self.getProductForID(parent._productreference)
+        )
+
+    def getBranch(self, path):
+        if len(path) == 0:
+            return self
+        branches = self._branches
+        node = None
+        for idx in path:
+            node = branches[idx]
+            branches = node._childs
+        return node
+
+    def getBranches(self):
+        for branch in self._branches:
+            yield branch
+            for sub_branch in branch.getBranches():
+                yield sub_branch
+
+    def getPath(self):
+        return ()
+
+    def getNameOfRelationship(self, relationship):
+        if relationship is None:
+            return ''
+        return ' '.join((self.getProductForID(relationship._productreference)._name, 'as',
+                relationship._relationtype.lower(),
+                self.getProductForID(relationship._relatestoproductreference)._name))
+
+    def getOrphanedBranches(self, product=None):
+        """ The branches that could accept `product` as Product Definition """
+        white_list = []
+        if product is not None:
+            white_list = [product._parent]
+        for branch in self.getBranches():
+            if (branch in white_list) or branch.isOrphaned():
+                yield branch
+
+    def getNotTerminalBranches(self, b2=None):
+        """\
+        The branches that could accept `b2` as new sub-branches
+        Note that b2 and all its sub-branches cannot be listed
+        """
+        black_list = []
+        if b2 is not None:
+            black_list = [b2] + list(b2.getBranches())
+        for branch in self.getBranches():
+            if branch in black_list:
+                continue
+            if branch._product is None:
+                yield branch
+
+    def getOrphanedRelationships(self, product=None):
+        """ The relationships that need a product defninition """
+        white_list = []
+        if product is not None:
+            white_list = [product.getCurrentRelationship()]
+        for i, relationship in enumerate(self._relationships):
+            if (relationship in white_list) or relationship.isOrphaned():
+                yield (i, relationship)
+
+    def nbProducts(self):
+        """ Amount of 'raw' Products """
+        return len([p for p in self._products if p._parent is self])
+
+    def validate(self):
+        for branch in self._branches:
+            branch.validate()
+        productids = set()
+        for product in self._products:
+            product.validate()
+            if product._productid in productids:
+                raise ValidationError('Each ProductID must be unique (%s)' % product._productid)
+            productids.add(product._productid)
+        for relationship in self._relationships:
+            relationship.validate()
+            for productid in (relationship._productreference,
+                              relationship._relatestoproductreference):
+                if productid not in productids:
+                    raise ValidationError('ProductID %s is unknown' % productid)
+        groupids = set()
+        for group in self._groups:
+            group.validate()
+            if group._groupid in groupids:
+                raise ValidationError('Duplicated GroupID: %s' % group._groupid)
+            groupids.add(group._groupid)
+            for productid in group._productids:
+                if productid not in productids:
+                    raise ValidationError('ProductID %s is unknown' % productid)
+        return productids, groupids
+
+    def __str__(self):
+        return 'Products: %s' % '\n'.join(str(p) for p in self._products)
+
+
+class CVRFProductBranch(object):
+    TYPES = ('Vendor', 'Product Family', 'Product Name', 'Product Version',
+             'Patch Level', 'Service Pack', 'Architecture', 'Language',
+             'Legacy', 'Specification')
+    def __init__(self, _type, name, parentbranch):
+        self._type = _type
+        self._name = name
+        self._childs = []
+        self._product = None
+        self.link(parentbranch)
+
+    def getParent(self):
+        return self._parentbranch
+
+    def getPath(self, string=False):
+        """ return the path to that branch element as a tuple """
+        if self.isRoot():
+            for i, b in enumerate(self._parentbranch._branches):
+                if b is self:
+                    if string:
+                        return '%d' % i
+                    return (i, )
+        else:
+            for i, b in enumerate(self._parentbranch._childs):
+                if b is self:
+                    if string:
+                        return '/'.join([self._parentbranch.getPath(string), '%d' % i])
+                    return self._parentbranch.getPath(string) + (i,)
+        if string:
+            return ''
+        return ()
+
+    def getTree(self):
+        """ this returns a list of tuples (type, name) leading to here"""
+        if self.isRoot():
+            return [(self._type, self._name)]
+        return self._parentbranch.getTree() + [(self._type, self._name)]
+
+    def getName(self):
+        return ' / '.join("%s: %s" % (type_, name) for type_, name in self.getTree())
+
+    def getParentPath(self):
+        """ return as string the path to the parent """
+        return '/'.join('%s' % p for p in self.getPath()[:-1])
+
+    def isRoot(self):
+        return isinstance(self._parentbranch, CVRFProductTree)
+
+    def isOrphaned(self):
+        """ Has no childs and no product """
+        return len(self._childs) == 0 and (self._product is None)
+
+    def getBranches(self):
+        for branch in self._childs:
+            yield branch
+            for sub_branch in branch.getBranches():
+                yield sub_branch
+
+    def unlink(self):
+        """ Unset our _parent, and remove us from the _parent._childs """
+        if self.isRoot():
+            self.getParent()._branches.remove(self)
+        else:
+            self.getParent()._childs.remove(self)
+        self._parentbranch = None
+
+    def link(self, parent):
+        """ Actually, only set the parent """
+        self._parentbranch = parent
+        if self.isRoot():
+            parent._branches.append(self)
+        else:
+            parent._childs.append(self)
+
+
+    def validate(self):
+        if not self._type:
+            raise ValidationError('A Branch must have a Type')
+        if self._type not in self.TYPES:
+            raise ValidationError('A Branch Type must be one of %s' % ', '.join(self.TYPES))
+        if not self._name:
+            raise ValidationError('A Branch must have a Name')
+        for branch in self._childs:
+            branch.validate()
+        if self.isOrphaned():
+            raise ValidationError('A Branch must have at least a sub-product or sub-branches')
+
+    def __str__(self):
+        return "%s: %s" % (self._type, self._name)
+
+
+class CVRFFullProductName(object):
+    def __init__(self, productid, name, parent, cpe=None):
+        self._productid = productid
+        self._name = name
+        self._cpe = cpe
+        # Can be None (directly under the tree), a ProductBranch, or a
+        # Relationship
+        self.link(parent)
+
+    def isRoot(self):
+        return isinstance(self._parent, CVRFProductTree)
+
+    def isRelationship(self):
+        return isinstance(self._parent, CVRFRelationship)
+
+    def getTree(self):
+        if not isinstance(self._parent, CVRFProductBranch):
+            return []
+        return self._parent.getTree()
+
+    def getParentPath(self):
+        if self.isRoot() or self.isRelationship():
+            return ''
+        return self._parent.getPath(True)
+
+    def getCurrentRelationship(self):
+        if self.isRelationship():
+            return self._parent
+        return None
+
+    def unlink(self):
+        """ Unset our _parent, and remove us from the _parent._childs
+        We are still in the product list.
+        """
+        if not self.isRoot():
+            self._parent._product = None
+        self._parent = None
+
+    def link(self, parent):
+        self._parent = parent
+        if not self.isRoot():
+            parent._product = self
+
+    def validate(self):
+        if not self._productid:
+            raise ValidationError('A Product must have a ProductID')
+        if not self._name:
+            raise ValidationError('A Product must have a Name')
+
+    def __str__(self):
+        return "%s (%s)" % (self._productid, self._name)
+
+
+class CVRFRelationship(object):
+    TYPES = ('Default Component Of', 'Optional Component Of',
+             'External Component Of', 'Installed On', 'Installed With')
+    def __init__(self, productref, reltype, relatestoproductref):
+        self._productreference = productref
+        self._relationtype = reltype
+        self._relatestoproductreference = relatestoproductref
+        self._product = None
+
+    def getParent(self):
+        """ All parent element of a FullProductName should implement that
+        method """
+        return None
+
+    def isOrphaned(self):
+        return self._product is None
+
+    def validate(self):
+        if not self._productreference:
+            raise ValidationError('A Relationship must have a Product Reference')
+        if not self._relationtype:
+            raise ValidationError('A Relationship must have a Relation Type')
+        if self._relationtype not in self.TYPES:
+            raise ValidationError('Relation Type must be one of %s' % ', '.join(self.TYPES))
+        if not self._relatestoproductreference:
+            raise ValidationError('A Relationship must have a "Relates To product Reference"')
+        if self._productreference == self._relatestoproductreference:
+            raise ValidationError('A Relationship cannot reference twice the same Product')
+
+
+class CVRFGroup(object):
+    def __init__(self, groupid):
+        self._groupid = groupid
+        self._description = None
+        self._productids = []
+
+    def setDescription(self, description):
+        self._description = description
+
+    def addProductID(self, productid):
+        self._productids.append(productid)
+
+    def getTitle(self):
+        if self._description:
+            return "%s (%d products)" % (self._description, len(self._productids))
+        return "#%s (%d products)" % (self._groupid, len(self._productids))
+
+    def validate(self):
+        if not self._groupid:
+            raise ValidationError('A Group must have a GroupID')
+        if not self._productids or len(self._productids) < 2:
+            raise ValidationError('A Group must contain at least two products')
+
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)