# -*- encoding: utf-8 -*- # Software Management Providers # # Copyright (C) 2012-2013 Red Hat, Inc. All rights reserved. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library 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 # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # # Authors: Michal Minar # """ Module holding an abstraction for YUM repository. """ from datetime import datetime import logging import yum import yum.Errors # maps names of Repository properties to their corresponding property # names in YumRepository object PROPERTY_NAME_MAP = { "repoid" : "id", "base_urls" : "baseurl", "config_file" : "repofile", "cost" : "cost", "enabled" : "enabled", "gpg_check" : "gpgcheck", "last_edit" : "repo_config_age", "mirror_list" : "mirrorlist", "mirror_urls" : "mirrorurls", "name" : "name", "pkg_dir" : "pkgdir", "ready" : "ready", "repo_gpg_check" : "repo_gpgcheck", "timeout" : "timeout" } def get_prop_from_yum_repo(repo, prop_name): """ @param prop_name is one Repository properties @return value of property from object of YumRepository """ if not isinstance(repo, yum.yumRepo.YumRepository): raise TypeError("repo must be in instance of yum.yumRepo.YumRepository") if prop_name in PROPERTY_NAME_MAP: val = getattr(repo, PROPERTY_NAME_MAP[prop_name]) if prop_name == "last_edit": val = datetime.fromtimestamp(val) elif prop_name == "mirror_urls" and not repo.mirrorlist: val = None elif prop_name == "ready": val = val() elif prop_name in {"arch", "basearch", "releasever"}: val = repo.yumvar[prop_name] elif prop_name in {"revision", "last_update"}: if repo.enabled and repo.repoXML: md = repo.repoXML if prop_name == "last_update": val = datetime.fromtimestamp(md.timestamp) elif prop_name == "revision": val = long(md.revision) else: val = getattr(repo.repoXML, prop_name) else: val = None elif prop_name == "pkg_count": # this needs populated sack: ydb.repos.populateSack(repo.id) val = len(repo.sack) else: raise ValueError('Unknown repository property: "%s"' % prop_name) return val class Repository(object): """ Container for repository metadata. It represents yum repository. It's supposed to be passed from YumWorker to YumDB client and vice-versa. """ __slots__ = ( "objid", # [int] id of python object on server process "repoid", # [string] repository id name # (name of config file) "arch", # [string] architecture of packages "basearch", # [string] base system architecture "base_urls", # [list] base urls as strings #"cache", #"cache_dir", "name", # [string] repository descriptive name "config_file", # [string] file path to corresponding # config file "cost", # [int] cost of repository "enabled", # [boolean] whether repo is enabled "gpg_check", # [boolean] whether to check gpg signature #"metadata_expire", # how long are metadata considered valid "last_edit", # datetime of last config modification "last_update", # datetime of last change of repository # on server "mirror_list", # url of mirrorlist, or None "mirror_urls", # list of urls obtained from mirrorlist or None #"persist_dir", # #"pkg_count", # number of packages in directory "pkg_dir", # directory with packages #"proxy", # boolean saying whether this is a proxy "ready", # boolean saying, whether it's ready for use "releasever", # version of targeted distribution "repo_gpg_check", # [boolean] whether to check gpg # signarues of data "revision", "timeout", # timeout for requests ) def __init__(self, objid, repoid, arch, basearch, base_urls, config_file, cost, enabled, gpg_check, last_edit, last_update, name, pkg_dir, ready, releasever, repo_gpg_check, revision, timeout, mirror_list=None, mirror_urls=None): for arg in ('last_edit', 'last_update'): if ( locals()[arg] is not None and not isinstance(locals()[arg], datetime)): raise TypeError("%s must be an instance of datetime" % arg) if not isinstance(timeout, float): raise TypeError("timeout must be a float") for arg in ('cost', 'revision'): if ( locals()[arg] is not None and not isinstance(locals()[arg], (int, long))): raise TypeError("%s must be an integer" % arg) self.objid = objid self.repoid = repoid self.arch = arch self.basearch = basearch self.base_urls = list(base_urls) self.config_file = config_file self.cost = cost self.enabled = bool(enabled) self.gpg_check = bool(gpg_check) self.last_edit = last_edit self.last_update = last_update self.mirror_list = "" if not mirror_list else mirror_list self.mirror_urls = [] if not mirror_urls else list(mirror_urls) self.name = name #self.pkg_count = pkg_count self.pkg_dir = pkg_dir self.ready = bool(ready) self.releasever = releasever self.repo_gpg_check = bool(repo_gpg_check) self.revision = revision self.timeout = timeout def __str__(self): return self.repoid def __getstate__(self): """ Used for serialization with pickle. @return container content that will be serialized """ return dict((k, getattr(self, k)) for k in self.__slots__) def __setstate__(self, state): """ Used for deserialization with pickle. Restores the object from serialized form. @param state is an object created by __setstate__() method """ for k, value in state.items(): setattr(self, k, value) def make_repository_from_db(repo): """ Create instance of Repository from instance of yum.yumRepo.YumRepository @return instance of Repository """ if not isinstance(repo, yum.yumRepo.YumRepository): raise TypeError("repo must be in instance of yum.yumRepo.YumRepository") metadata = {} for prop_name in Repository.__slots__[1:]: try: metadata[prop_name] = get_prop_from_yum_repo(repo, prop_name) except yum.Errors.RepoError as exc: # some properties can cause error (like requesting ready) logging.getLogger(__name__).warn( 'failed to get property "%s" of repo "%s": %s' % ( prop_name, repo.name, exc)) if prop_name == "ready": metadata[prop_name] = False continue res = Repository(id(repo), **metadata) return res