Exemplo n.º 1
0
class Container(Mutable):
    """
    General container class for handling sets of objects.

    Provides the add() and remove() methods for adding and removing
    objects and the internal _add() and _remove() which perform the
    actual update to the server (implemented by respective class).
    """

    # List of all object attributes (used for init & expiration)
    _attributes = ["current", "original"]

    # Class of objects to be contained (defined in each container)
    _class = None

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #  Container Properties
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    id = property(_getter("id"), doc="Related object id.")

    @property
    def _items(self):
        """ Set representation containing the items """
        if self._current is NitrateNone:
            self._fetch()
        # Fetch the whole container if there are uncached items (except when
        # the container is modified otherwise we would lose local changes).
        if not self._modified and not self._class._is_cached(self._current):
            self._fetch()
        return self._current

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #  Container Special
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    def __new__(cls, object, inset=None):
        """ Create new container objects based on the object id """
        return super(Container, cls).__new__(cls, object.id)

    def __init__(self, object, inset=None):
        """ Initialize container for specified object """
        # Initialize (unless already done)
        if self._id is not None:
            # Initialized but not fetched ---> fetch from the inset if given
            if inset is not None and not self._is_cached(self):
                self._fetch(inset)
            return
        Mutable.__init__(self, object.id)
        self._identifier = object.identifier
        self._object = object
        # Initialize directly if initial set provided
        if inset is not None:
            self._fetch(inset)

    def __iter__(self):
        """ Container iterator """
        for item in self._items:
            yield item

    def __getitem__(self, index):
        """ Indexing support """
        if isinstance(index, int):
            return list(self)[index]
        elif isinstance(index, slice):
            return list(self)[index.start:index.stop:index.step]
        else:
            raise IndexError("Invalid index '{0}'".format(index))

    def __contains__(self, item):
        """ Container 'in' operator """
        return item in self._items

    def __len__(self):
        """ Number of container items """
        return len(self._items)

    def __unicode__(self):
        """ Display items as a list for printing """
        if self._items:
            # List of identifiers
            try:
                return listed(sorted([item.identifier
                                      for item in self._items]))
            # If no identifiers, just join strings
            except AttributeError:
                return listed(self._items, quote="'")
        else:
            return "[None]"

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #  Container Methods
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    def _fetch(self, inset=None):
        """ Save cache timestamp and initialize from inset if given """
        Nitrate._fetch(self)
        # Create copies of the initial set (if given)
        if inset is not None:
            log.debug("Initializing {0} for {1} from the inset".format(
                self.__class__.__name__, self._identifier))
            log.debug(pretty(inset))
            self._current = set(inset)
            self._original = set(inset)
        # Cache into container class
        if config.get_cache_level() >= config.CACHE_OBJECTS:
            self.__class__._cache[self._id] = self
        # Return True if the data are already initialized
        return inset is not None

    def add(self, items):
        """ Add an item or a list of items to the container """

        # Convert to set representation
        if isinstance(items, list):
            items = set(items)
        else:
            items = set([items])

        # If there are any new items
        add_items = items - self._items
        if add_items:
            log.info("Adding {0} to {1}'s {2}".format(
                listed([item.identifier for item in add_items],
                       self._class.__name__,
                       max=10), self._object.identifier,
                self.__class__.__name__))
            self._items.update(items)
            if config.get_cache_level() != config.CACHE_NONE:
                self._modified = True
            else:
                self._update()

    def remove(self, items):
        """ Remove an item or a list of items from the container """

        # Convert to set representation
        if isinstance(items, list):
            items = set(items)
        else:
            items = set([items])

        # If there are any items to be removed
        remove_items = items.intersection(self._items)
        if remove_items:
            log.info("Removing {0} from {1}'s {2}".format(
                listed([item.identifier for item in remove_items],
                       self._class.__name__,
                       max=10), self._object.identifier,
                self.__class__.__name__))
            self._items.difference_update(items)
            if config.get_cache_level() != config.CACHE_NONE:
                self._modified = True
            else:
                self._update()

    def clear(self):
        """ Remove all items from the container """
        self.remove(list(self._items))

    def _add(self, items):
        """ Add provided items to the server """
        raise NitrateError("To be implemented by respective class.")

    def _remove(self, items):
        """ Remove provided items from the server """
        raise NitrateError("To be implemented by respective class.")

    def _update(self):
        """ Update container changes to the server """
        # Added items
        added = self._current - self._original
        if added: self._add(added)

        # Removed items
        removed = self._original - self._current
        if removed: self._remove(removed)

        # Save the current state as the original (for future updates)
        self._original = set(self._current)

    def _sleep(self):
        """ Prepare container items for caching """
        # When restoring the container from the cache, unpickling failed
        # because of trying to construct set() of objects which were not
        # fully rebuild yet (__hash__ failed because of missing self._id).
        # So we need to convert containers into list of ids before the
        # cache dump and instantiate the objects back after cache restore.
        if self._current is NitrateNone: return
        self._original = [item.id for item in self._original]
        self._current = [item.id for item in self._current]

    def _wake(self):
        """ Restore container object after loading from cache """
        # See _sleep() method above for explanation why this is necessary
        if self._current is NitrateNone: return
        if self._class._is_cached(list(self._original)):
            log.cache("Waking up {0} for {1}".format(self.__class__.__name__,
                                                     self._identifier))
            self._original = set([self._class(id) for id in self._original])
            self._current = set([self._class(id) for id in self._current])
        else:
            log.cache("Skipping wake up of {0} for {1}".format(
                self.__class__.__name__, self._identifier))
            self._init()
Exemplo n.º 2
0
class User(Nitrate):
    """ User """

    # Local cache of User objects indexed by user id
    _cache = {}

    # List of all object attributes (used for init & expiration)
    _attributes = ["name", "login", "email"]

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #  User Properties
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    # Read-only properties
    id = property(_getter("id"), doc="User id.")
    login = property(_getter("login"), doc="Login username.")
    email = property(_getter("email"), doc="User email address.")
    name = property(_getter("name"), doc="User first name and last name.")

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #  User Decorated
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    @classmethod
    def _cache_lookup(cls, id, **kwargs):
        """ Look up cached objects, return found instance and search key """
        # Return current user
        if id is None and 'login' not in kwargs and 'email' not in kwargs:
            return cls._cache["i-am-current-user"], "current user"
        # Search by login & email
        if "login" in kwargs:
            return cls._cache[kwargs["login"]], kwargs["login"]
        if "email" in kwargs:
            return cls._cache[kwargs["email"]], kwargs["email"]
        # Default search by id
        return super(User, cls)._cache_lookup(id, **kwargs)

    @staticmethod
    def search(**query):
        """ Search for users """
        return [User(hash)
                for hash in Nitrate()._server.User.filter(dict(query))]

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #  User Special
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    def __new__(cls, id=None, *args, **kwargs):
        """ Create a new object, handle caching if enabled """
        # Convert login or email into name for better logging
        if "login" in kwargs or "email" in kwargs:
            name = kwargs.get("login", kwargs.get("email"))
            return Nitrate.__new__(cls, id=id, name=name, *args, **kwargs)
        else:
            return Nitrate.__new__(cls, id=id, *args, **kwargs)

    def __init__(self, id=None, login=None, email=None):
        """
        Initialize by user id, login or email

        Defaults to the current user if no id, login or email provided.
        If xmlrpc initial object dict provided as the first argument,
        data are initialized directly from it.
        """

        # Initialize (unless already done)
        id, name, inject, initialized = self._is_initialized(
                id or login or email)
        if initialized: return
        Nitrate.__init__(self, id, prefix="UID")

        # If inject given, fetch data from it
        if inject:
            self._fetch(inject)
        # Otherwise initialize by login or email
        elif name is not None:
            if "@" in name:
                self._email = name
            else:
                self._login = name
            self._index(name)

    def __unicode__(self):
        """ User login for printing """
        return self.name if self.name is not None else u"No Name"

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #  User Methods
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    def _fetch(self, inject=None):
        """ Fetch user data from the server """
        Nitrate._fetch(self, inject)

        if inject is None:
            # Search by id
            if self._id is not NitrateNone:
                try:
                    log.info("Fetching user " + self.identifier)
                    inject = self._server.User.filter({"id": self.id})[0]
                except IndexError:
                    raise NitrateError(
                            "Cannot find user for " + self.identifier)
            # Search by login
            elif self._login is not NitrateNone:
                try:
                    log.info(
                            "Fetching user for login '{0}'".format(self.login))
                    inject = self._server.User.filter(
                            {"username": self.login})[0]
                except IndexError:
                    raise NitrateError("No user found for login '{0}'".format(
                            self.login))
            # Search by email
            elif self._email is not NitrateNone:
                try:
                    log.info("Fetching user for email '{0}'".format(
                            self.email))
                    inject = self._server.User.filter({"email": self.email})[0]
                except IndexError:
                    raise NitrateError("No user found for email '{0}'".format(
                            self.email))
            # Otherwise initialize to the current user
            else:
                log.info("Fetching the current user")
                inject = self._server.User.get_me()
                self._index("i-am-current-user")

        # Initialize data from the inject and index into cache
        log.debug("Initializing user UID#{0}".format(inject["id"]))
        log.data(pretty(inject))
        self._inject = inject
        self._id = inject["id"]
        self._login = inject["username"]
        self._email = inject["email"]
        if inject["first_name"] and inject["last_name"]:
            self._name = inject["first_name"] + " " + inject["last_name"]
        else:
            self._name = None
        self._index(self.login, self.email)
Exemplo n.º 3
0
class Version(Nitrate):
    """ Product version """

    # Local cache of Version
    _cache = {}

    # List of all object attributes (used for init & expiration)
    _attributes = ["name", "product"]

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #  Version Properties
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    # Read-only properties
    id = property(_getter("id"), doc="Version id")
    name = property(_getter("name"), doc="Version name")
    product = property(_getter("product"), doc="Version product")

    @classmethod
    def _cache_lookup(cls, id, **kwargs):
        """ Look up cached objects, return found instance and search key """

        # Search cache by the version name and product
        if "product" in kwargs and ("version" in kwargs or "name" in kwargs):
            product = kwargs.get("product")
            if isinstance(product, Product):
                product = product.name
            name = kwargs.get("name", kwargs.get("version"))
            return cls._cache["{0}---in---{1}".format(name, product)], name

        # Default search by id otherwise
        return super(Version, cls)._cache_lookup(id, **kwargs)

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #  Version Special
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    def __init__(self, id=None, name=None, product=None, **kwargs):
        """ Initialize by version id or product and version """

        # Backward compatibility for 'version' argument (now called 'name')
        name = name if name is not None else kwargs.get("version")

        # Initialize (unless already done)
        id, ignore, inject, initialized = self._is_initialized(id)
        if initialized: return
        Nitrate.__init__(self, id)

        # If inject given, fetch tag data from it
        if inject:
            self._fetch(inject)
        # Initialize by version name and product
        elif name is not None and product is not None:
            self._name = name
            # Convert product into object if necessary
            if isinstance(product, Product):
                self._product = product
            else:
                self._product = Product(product)
            # Index by name/product (but only when the product name is known)
            if self.product._name is not NitrateNone:
                self._index("{0}---in---{1}".format(
                        self.name, self.product.name))
        # Otherwise just make sure the version id was provided
        elif not id:
            raise NitrateError("Need either version id or both product "
                    "and version name to initialize the Version object.")

    def __unicode__(self):
        """ Version name for printing """
        return self.name

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #  Version Methods
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    def _fetch(self, inject=None):
        """ Fetch version data from the server """
        Nitrate._fetch(self, inject)

        # Directly fetch from the initial object dict
        if inject is not None:
            log.debug("Processing Version ID#{0} inject".format(inject["id"]))
        # Search by version id
        elif self._id is not NitrateNone:
            try:
                log.info("Fetching version {0}".format(self.identifier))
                inject = self._server.Product.filter_versions(
                        {'id': self.id})[0]
            except IndexError:
                raise NitrateError(
                        "Cannot find version for {0}".format(self.identifier))
        # Search by product and name
        else:
            try:
                log.info(u"Fetching version '{0}' of '{1}'".format(
                        self.name, self.product.name))
                inject = self._server.Product.filter_versions(
                        {'product': self.product.id, 'value': self.name})[0]
            except IndexError:
                raise NitrateError(
                        "Cannot find version for '{0}'".format(self.name))
        # Initialize data from the inject and index into cache
        log.debug("Initializing Version ID#{0}".format(inject["id"]))
        log.data(pretty(inject))
        self._inject = inject
        self._id = inject["id"]
        self._name = inject["value"]
        self._product = Product(inject["product_id"])
        # Index by product name & version name (if product is cached)
        if self.product._name is not NitrateNone:
            self._index("{0}---in---{1}".format(self.name, self.product.name))
        # Otherwise index by id only
        else:
            self._index()
Exemplo n.º 4
0
class Build(Nitrate):
    """ Product build """

    # Local cache of Build
    _cache = {}

    # List of all object attributes (used for init & expiration)
    _attributes = ["name", "product"]

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #  Build Properties
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    # Read-only properties
    id = property(_getter("id"), doc="Build id.")
    name = property(_getter("name"), doc="Build name.")
    product = property(_getter("product"), doc="Relevant product.")

    @classmethod
    def _cache_lookup(cls, id, **kwargs):
        """ Look up cached objects, return found instance and search key """

        # Name and product check
        if "product" in kwargs and ("name" in kwargs or "build" in kwargs):
            product = kwargs.get("product")
            if isinstance(product, Product):
                product = product.name
            name = kwargs.get("name", kwargs.get("build"))
            return cls._cache["{0}---in---{1}".format(name, product)], name

        return super(Build, cls)._cache_lookup(id, **kwargs)

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #  Build Special
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    def __init__(self, id=None, name=None, product=None, **kwargs):
        """ Initialize by build id or product and build name """

        # Backward compatibility for 'build' argument (now called 'name')
        name = name if name is not None else kwargs.get("build")

        # Initialize (unless already done)
        id, ignore, inject, initialized = self._is_initialized(id or name)
        if initialized: return
        Nitrate.__init__(self, id)

        # If inject given, fetch build data from it
        if inject:
            self._fetch(inject)
        # Initialized by build name and product
        elif name is not None and product is not None:
            self._name = name
            # Detect product format
            if isinstance(product, Product):
                self._product = product
            else:
                self._product = Product(product)
            # Index by name-product (only when the product name is known)
            if self.product._name is not NitrateNone:
                self._index("{0}---in---{1}".format(
                        self.name, self.product.name))
        # Otherwise just check that the id was provided
        elif not id:
            raise NitrateError("Need either build id or both build name "
                    "and product to initialize the Build object.")

    def __unicode__(self):
        """ Build name for printing """
        return self.name

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #  Build Methods
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    def _fetch(self, inject=None):
        """ Get the missing build data """
        Nitrate._fetch(self, inject)
        # Directly fetch from the initial object dict
        if inject is not None:
            log.info("Processing build ID#{0} inject".format(
                    inject["build_id"]))
        # Search by build id
        elif self._id is not NitrateNone:
            try:
                log.info("Fetching build " + self.identifier)
                inject = self._server.Build.get(self.id)
            except xmlrpclib.Fault as error:
                log.debug(error)
                raise NitrateError(
                        "Cannot find build for " + self.identifier)
        # Search by build name and product
        else:
            try:
                log.info(u"Fetching build '{0}' of '{1}'".format(
                        self.name, self.product.name))
                inject = self._server.Build.check_build(
                        self.name, self.product.id)
                self._id = inject["build_id"]
            except xmlrpclib.Fault as error:
                log.debug(error)
                raise NitrateError("Build '{0}' not found in '{1}'".format(
                    self.name, self.product.name))
            except KeyError:
                if "args" in inject:
                    log.debug(inject["args"])
                raise NitrateError("Build '{0}' not found in '{1}'".format(
                    self.name, self.product.name))

        # Initialize data from the inject and index into cache
        log.debug("Initializing Build ID#{0}".format(inject["build_id"]))
        log.data(pretty(inject))
        self._inject = inject
        self._id = inject["build_id"]
        self._name = inject["name"]
        self._product = Product(
                {"id": inject["product_id"], "name": inject["product"]})
        self._index("{0}---in---{1}".format(self.name, self.product.name))
Exemplo n.º 5
0
class Product(Nitrate):
    """ Product """

    # Local cache of Product
    _cache = {}

    # List of all object attributes (used for init & expiration)
    _attributes = ["name"]

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #  Product Properties
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    # Read-only properties
    id = property(_getter("id"), doc="Product id")
    name = property(_getter("name"), doc="Product name")

    @classmethod
    def _cache_lookup(cls, id, **kwargs):
        """ Look up cached objects, return found instance and search key """
        # Search the cache by product name
        if "name" in kwargs:
            name = kwargs.get("name")
            return cls._cache[name], name

        return super(Product, cls)._cache_lookup(id, **kwargs)

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #  Product Special
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    def __init__(self, id=None, name=None):
        """
        Initialize the Product by id or name

        Examples:

        Product(60)
        Product(id=60)
        Product("Red Hat Enterprise Linux 6")
        Product(name="Red Hat Enterprise Linux 6")
        """

        # Initialize (unless already done)
        id, name, inject, initialized = self._is_initialized(id or name)
        if initialized: return
        Nitrate.__init__(self, id)

        # If inject given, fetch test case data from it
        if inject:
            self._fetch(inject)
        # Initialize by name
        elif name is not None:
            self._name = name
            self._index(name)
        # Otherwise just check that the product id was provided
        elif not id:
            raise NitrateError("Need id or name to initialize Product")

    def __unicode__(self):
        """ Product name for printing """
        return self.name

    @staticmethod
    def search(**query):
        """ Search for products """
        return [Product(hash["id"])
                for hash in Nitrate()._server.Product.filter(dict(query))]

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #  Product Methods
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    def _fetch(self, inject=None):
        """ Fetch product data from the server """
        Nitrate._fetch(self, inject)

        # Directly fetch from the initial object dict
        if inject is not None:
            log.debug("Initializing Product ID#{0}".format(inject["id"]))
            log.data(pretty(inject))
            self._id = inject["id"]
            self._name = inject["name"]
        # Search by product id
        elif self._id is not NitrateNone:
            try:
                log.info("Fetching product " + self.identifier)
                inject = self._server.Product.filter({'id': self.id})[0]
                log.debug("Initializing product " + self.identifier)
                log.data(pretty(inject))
                self._inject = inject
                self._name = inject["name"]
            except IndexError:
                raise NitrateError(
                        "Cannot find product for " + self.identifier)
        # Search by product name
        else:
            try:
                log.info(u"Fetching product '{0}'".format(self.name))
                inject = self._server.Product.filter({'name': self.name})[0]
                log.debug(u"Initializing product '{0}'".format(self.name))
                log.data(pretty(inject))
                self._inject = inject
                self._id = inject["id"]
            except IndexError:
                raise NitrateError(
                        "Cannot find product for '{0}'".format(self.name))
        # Index the fetched object into cache
        self._index(self.name)
Exemplo n.º 6
0
class PlanType(Nitrate):
    """ Plan type """

    # Local cache of PlanType objects indexed by plan type id
    _cache = {}

    # By default we cache PlanType objects for ever
    _expiration = config.NEVER_EXPIRE

    # List of all object attributes (used for init & expiration)
    _attributes = ["name"]

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #  PlanType Properties
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    # Read-only properties
    id = property(_getter("id"), doc="Test plan type id")
    name = property(_getter("name"), doc="Test plan type name")

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #  PlanType Decorated
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    @classmethod
    def _cache_lookup(cls, id, **kwargs):
        """ Look up cached objects, return found instance and search key """
        # Search cache by plan type name
        if "name" in kwargs:
            return cls._cache[kwargs["name"]], kwargs["name"]
        # Othewise perform default search by id
        return super(PlanType, cls)._cache_lookup(id, **kwargs)

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #  PlanType Special
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    def __init__(self, id=None, name=None):
        """ Initialize by test plan type id or name """

        # Initialize (unless already done)
        id, name, inject, initialized = self._is_initialized(id or name)
        if initialized: return
        Nitrate.__init__(self, id)

        # If inject given, fetch data from it
        if inject:
            self._fetch(inject)
        # Initialize by name
        elif name is not None:
            self._name = name
            self._index(name)
        # Otherwise just check that the test plan type id was provided
        elif not id:
            raise NitrateError(
                    "Need either id or name to initialize the PlanType object")

    def __unicode__(self):
        """ PlanType name for printing """
        return self.name

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #  PlanType Methods
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    def _fetch(self, inject=None):
        """ Get the missing test plan type data """
        Nitrate._fetch(self, inject)

        # Directly fetch from the initial object dict
        if inject is not None:
            log.info("Processing PlanType ID#{0} inject".format(inject["id"]))
        # Search by test plan type id
        elif self._id is not NitrateNone:
            try:
                log.info("Fetching test plan type " + self.identifier)
                inject = self._server.TestPlan.get_plan_type(self.id)
            except xmlrpclib.Fault as error:
                log.debug(error)
                raise NitrateError(
                        "Cannot find test plan type for " + self.identifier)
        # Search by test plan type name
        else:
            try:
                log.info(u"Fetching test plan type '{0}'".format(self.name))
                inject = self._server.TestPlan.check_plan_type(self.name)
            except xmlrpclib.Fault as error:
                log.debug(error)
                raise NitrateError("PlanType '{0}' not found".format(
                        self.name))
        # Initialize data from the inject and index into cache
        log.debug("Initializing PlanType ID#{0}".format(inject["id"]))
        log.data(pretty(inject))
        self._inject = inject
        self._id = inject["id"]
        self._name = inject["name"]
        self._index(self.name)
Exemplo n.º 7
0
class Category(Nitrate):
    """ Test case category """

    # Local cache of Category objects indexed by category id
    _cache = {}

    # List of all object attributes (used for init & expiration)
    _attributes = ["name", "product", "description"]

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #  Category Properties
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    # Read-only properties
    id = property(_getter("id"), doc="Category id.")
    name = property(_getter("name"), doc="Category name.")
    product = property(_getter("product"), doc="Relevant product.")
    description = property(_getter("description"), doc="Category description.")

    @property
    def synopsis(self):
        """ Short category summary (including product info) """
        return "{0}, {1}".format(self.name, self.product)

    @classmethod
    def _cache_lookup(cls, id, **kwargs):
        """ Look up cached objects, return found instance and search key """

        # Name and product check
        if "product" in kwargs and ("name" in kwargs or "category" in kwargs):
            product = kwargs.get("product")
            if isinstance(product, Product):
                product = product.name
            name = kwargs.get("name", kwargs.get("category"))
            return cls._cache["{0}---in---{1}".format(name, product)], name

        return super(Category, cls)._cache_lookup(id, **kwargs)

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #  Category Special
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    def __init__(self, id=None, name=None, product=None, **kwargs):
        """ Initialize by category id or category name and product """

        # Backward compatibility for 'category' argument (now called 'name')
        name = name if name is not None else kwargs.get("category")

        # Initialize (unless already done)
        id, ignore, inject, initialized = self._is_initialized(id or name)
        if initialized: return
        Nitrate.__init__(self, id)

        # If inject given, fetch tag data from it
        if inject:
            self._fetch(inject)
        # Initialized by category name and product
        elif name is not None and product is not None:
            self._name = name
            # Detect product format
            if isinstance(product, Product):
                self._product = product
            else:
                self._product = Product(product)
            # Index by name-product (only when the product name is known)
            if self.product._name is not NitrateNone:
                self._index("{0}---in---{1}".format(
                        self.name, self.product.name))
        # Otherwise just check that the id was provided
        elif not id:
            raise NitrateError("Need either category id or both category "
                    "name and product to initialize the Category object.")

    def __unicode__(self):
        """ Category name for printing """
        return self.name

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #  Category Methods
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    def _fetch(self, inject=None):
        """ Get the missing category data """
        Nitrate._fetch(self, inject)

        # Directly fetch from the initial object dict
        if inject is not None:
            log.info("Processing category ID#{0} inject".format(inject["id"]))
        # Search by category id
        elif self._id is not NitrateNone:
            try:
                log.info("Fetching category {0}".format(self.identifier))
                inject = self._server.Product.get_category(self.id)
            except xmlrpclib.Fault as error:
                log.debug(error)
                raise NitrateError(
                        "Cannot find category for " + self.identifier)
        # Search by category name and product
        else:
            try:
                log.info(u"Fetching category '{0}' of '{1}'".format(
                        self.name, self.product.name))
                inject = self._server.Product.check_category(
                        self.name, self.product.id)
            except xmlrpclib.Fault as error:
                log.debug(error)
                raise NitrateError("Category '{0}' not found in"
                        " '{1}'".format(self.name, self.product.name))

        # Initialize data from the inject and index into cache
        log.debug("Initializing category ID#{0}".format(inject["id"]))
        log.data(pretty(inject))
        self._inject = inject
        self._id = inject["id"]
        self._name = inject["name"]
        self._product = Product(
                {"id": inject["product_id"], "name": inject["product"]})
        self._index("{0}---in---{1}".format(self.name, self.product.name))
Exemplo n.º 8
0
class Tag(Nitrate):
    """ Tag Class """

    # List of all object attributes (used for init & expiration)
    _attributes = ["name"]

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #  Tag Properties
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    # Read-only properties
    id = property(_getter("id"), doc="Tag id")
    name = property(_getter("name"), doc="Tag name")

    # Local cache for Tag
    _cache = {}

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #  Tag Special
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    def __init__(self, id=None, name=None):
        """ Initialize by tag id or tag name """

        # Initialize (unless already done)
        id, name, inject, initialized = self._is_initialized(id or name)
        if initialized: return
        Nitrate.__init__(self, id)

        # If inject given, fetch tag data from it
        if inject:
            self._fetch(inject)
        # Initialize by name
        elif name is not None:
            self._name = name
            self._index(name)
        # Otherwise just check that the tag name or id was provided
        elif not id:
            raise NitrateError("Need either tag id or tag name "
                    "to initialize the Tag object.")

    def __unicode__(self):
        """ Tag name for printing """
        return self.name

    def __hash__(self):
        """ Use tag name for hashing """
        # This is necessary until BZ#1084301 is fixed
        return hash(self.name)

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #  Tag Methods
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    def _fetch(self, inject=None):
        """ Fetch tag data from the server """
        Nitrate._fetch(self, inject)

        # Directly fetch from the initial object dict
        if inject is not None:
            log.debug("Initializing Tag ID#{0}".format(inject["id"]))
            log.data(pretty(inject))
            self._id = inject["id"]
            self._name = inject["name"]
        # Search by tag id
        elif self._id is not NitrateNone:
            try:
                log.info("Fetching tag " + self.identifier)
                inject = self._server.Tag.get_tags({'ids': [self.id]})
                log.debug("Initializing tag " + self.identifier)
                log.data(pretty(inject))
                self._inject = inject
                self._name = inject[0]["name"]
            except IndexError:
                raise NitrateError(
                        "Cannot find tag for {0}".format(self.identifier))
        # Search by tag name
        else:
            try:
                log.info(u"Fetching tag '{0}'".format(self.name))
                inject = self._server.Tag.get_tags({'names': [self.name]})
                log.debug(u"Initializing tag '{0}'".format(self.name))
                log.data(pretty(inject))
                self._inject = inject
                self._id = inject[0]["id"]
            except IndexError:
                raise NitrateError(
                        "Cannot find tag '{0}'".format(self.name))
        # Index the fetched object into cache
        self._index(self.name)
Exemplo n.º 9
0
class Bug(Nitrate):
    """ Bug related to a test case or a case run """

    # Local cache of Bug objects indexed by internal bug id
    _cache = {}

    # List of all object attributes (used for init & expiration)
    _attributes = ["bug", "system", "testcase", "caserun"]

    # Prefixes for bug systems, identifier width
    _prefixes = {1: "BZ"}
    _identifier_width = 7

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #  Bug Properties
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    # Read-only properties
    id = property(_getter("id"), doc="Bug id (internal).")
    bug = property(_getter("bug"), doc="Bug (external id).")
    system = property(_getter("system"), doc="Bug system.")
    testcase = property(_getter("testcase"), doc="Test case.")
    caserun = property(_getter("caserun"), doc="Case run.")

    @property
    def synopsis(self):
        """ Short summary about the bug """
        # Summary in the form: BUG#123456 (BZ#123, TC#456, CR#789)
        return "{0} ({1})".format(self.identifier, ", ".join([str(self)] +
                [obj.identifier for obj in (self.testcase, self.caserun)
                if obj is not None]))

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #  Bug Special
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    def __init__(self, id=None, bug=None, system=1, **kwargs):
        """
        Initialize the bug

        Provide external bug id, optionally bug system (Bugzilla by default).
        """

        # Initialize (unless already done)
        id, ignore, inject, initialized = self._is_initialized(id)
        if initialized: return
        Nitrate.__init__(self, id, prefix="BUG")

        # If inject given, fetch bug data from it
        if inject:
            self._fetch(inject)
        # Initialized by bug id and system id
        elif bug is not None and system is not None:
            self._bug = bug
            self._system = system
        # Otherwise just check that the id was provided
        elif id is None:
            raise NitrateError("Need bug id to initialize the Bug object.")

    def __eq__(self, other):
        """
        Custom bug comparison

        Primarily decided by id. If unknown, compares by bug id & bug system.
        """
        # Decide by internal id
        if self._id is not NitrateNone and other._id is not NitrateNone:
            return self.id == other.id
        # Compare external id and bug system id
        return self.bug == other.bug and self.system == other.system

    def __unicode__(self):
        """ Bug name for printing """
        try:
            prefix = self._prefixes[self.system]
        except KeyError:
            prefix = "BZ"
        return u"{0}#{1}".format(prefix, str(self.bug).rjust(
                self._identifier_width, "0"))

    def __hash__(self):
        """ Construct the uniqe hash from bug id and bug system id """
        return _idify([self.system, self.bug])

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #  Bug Methods
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    def _fetch(self, inject=None):
        """ Fetch bug info from the server """
        Nitrate._fetch(self, inject)
        # No direct xmlrpc function for fetching so far
        if inject is None:
            raise NotImplementedError("Direct bug fetching not implemented")
        # Process provided inject
        self._id = int(inject["id"])
        self._bug = int(inject["bug_id"])
        self._system = int(inject["bug_system_id"])
        self._testcase = TestCase(int(inject["case_id"]))
        if inject["case_run_id"] is not None:
            self._caserun = CaseRun(int(inject["case_run_id"]))
        # Index the fetched object into cache
        self._index()
Exemplo n.º 10
0
class Component(Nitrate):
    """ Test case component """

    # Local cache of Component objects indexed by component id plus
    # additionaly by name-in-product pairs
    _cache = {}

    # List of all object attributes (used for init & expiration)
    _attributes = ["name", "product"]

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #  Component Properties
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    # Read-only properties
    id = property(_getter("id"), doc="Component id.")
    name = property(_getter("name"), doc="Component name.")
    product = property(_getter("product"), doc="Relevant product.")

    @property
    def synopsis(self):
        """ Short component summary (including product info) """
        return "{0}, {1}".format(self.name, self.product)

    @classmethod
    def _cache_lookup(cls, id, **kwargs):
        """ Look up cached objects, return found instance and search key """

        # Name and product check
        if 'product' in kwargs and 'name' in kwargs:
            product = kwargs.get("product")
            if isinstance(product, Product):
                product = product.name
            name = kwargs.get("name")
            return cls._cache["{0}---in---{1}".format(name, product)], name

        return super(Component, cls)._cache_lookup(id, **kwargs)

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #  Component Special
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    def __init__(self, id=None, name=None, product=None, **kwargs):
        """ Initialize by component id or component name and product """

        # Initialize (unless already done)
        id, ignore, inject, initialized = self._is_initialized(id)
        if initialized: return
        Nitrate.__init__(self, id)

        # If inject given, fetch component data from it
        if inject:
            self._fetch(inject)
        # Initialized by product and component name
        elif name is not None and product is not None:
            # Detect product format
            if isinstance(product, Product):
                self._product = product
            else:
                self._product = Product(product)
            self._name = name
            # Index by name-product (only when the product name is known)
            if self.product._name is not NitrateNone:
                self._index("{0}---in---{1}".format(
                        self.name, self.product.name))
        # Otherwise just check that the id was provided
        elif id is None:
            raise NitrateError("Need either component id or both product "
                    "and component name to initialize the Component object.")

    def __unicode__(self):
        """ Component name for printing """
        return self.name

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    #  Component Methods
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    def _fetch(self, inject=None):
        """ Get the missing component data """
        Nitrate._fetch(self, inject)

        # Directly fetch from the initial object dict
        if inject is not None:
            log.info("Processing component ID#{0} inject".format(inject["id"]))
        # Search by component id
        elif self._id is not NitrateNone:
            try:
                log.info("Fetching component " + self.identifier)
                inject = self._server.Product.get_component(self.id)
            except xmlrpclib.Fault as error:
                log.debug(error)
                raise NitrateError(
                        "Cannot find component for " + self.identifier)
        # Search by component name and product
        else:
            try:
                log.info(u"Fetching component '{0}' of '{1}'".format(
                        self.name, self.product.name))
                inject = self._server.Product.check_component(
                        self.name, self.product.id)
            except xmlrpclib.Fault as error:
                log.debug(error)
                raise NitrateError("Component '{0}' not found in"
                        " '{1}'".format(self.name, self.product.name))

        # Initialize data from the inject and index into cache
        log.debug("Initializing component ID#{0}".format(inject["id"]))
        log.data(pretty(inject))
        self._inject = inject
        self._id = inject["id"]
        self._name = inject["name"]
        self._product = Product(
                {"id": inject["product_id"], "name": inject["product"]})
        self._index("{0}---in---{1}".format(self.name, self.product.name))

    @staticmethod
    def search(**query):
        """ Search for components """
        return [Component(hash) for hash in
                Nitrate()._server.Product.filter_components(dict(query))]