1# repodict.py 2# Managing repo configuration in DNF. 3# 4# Copyright (C) 2013-2016 Red Hat, Inc. 5# 6# This copyrighted material is made available to anyone wishing to use, 7# modify, copy, or redistribute it subject to the terms and conditions of 8# the GNU General Public License v.2, or (at your option) any later version. 9# This program is distributed in the hope that it will be useful, but WITHOUT 10# ANY WARRANTY expressed or implied, including the implied warranties of 11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 12# Public License for more details. You should have received a copy of the 13# GNU General Public License along with this program; if not, write to the 14# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 15# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the 16# source code or documentation are not subject to the GNU General Public 17# License and may only be used or replicated with the express permission of 18# Red Hat, Inc. 19# 20 21from __future__ import unicode_literals 22from dnf.exceptions import ConfigError 23from dnf.i18n import _ 24 25import dnf.util 26import libdnf.conf 27import fnmatch 28import os 29 30logger = dnf.util.logger 31 32 33class RepoDict(dict): 34 # :api 35 def add(self, repo): 36 # :api 37 id_ = repo.id 38 if id_ in self: 39 msg = 'Repository %s is listed more than once in the configuration' 40 raise ConfigError(msg % id_) 41 try: 42 repo._repo.verify() 43 except RuntimeError as e: 44 raise ConfigError("{0}".format(e)) 45 self[id_] = repo 46 47 def all(self): 48 # :api 49 return dnf.util.MultiCallList(self.values()) 50 51 def _any_enabled(self): 52 return not dnf.util.empty(self.iter_enabled()) 53 54 def _enable_sub_repos(self, sub_name_fn): 55 for repo in self.iter_enabled(): 56 for found in self.get_matching(sub_name_fn(repo.id)): 57 if not found.enabled: 58 logger.info(_('enabling %s repository'), found.id) 59 found.enable() 60 61 def add_new_repo(self, repoid, conf, baseurl=(), **kwargs): 62 # :api 63 """ 64 Creates new repo object and add it into RepoDict. Variables in provided values will be 65 automatically substituted using conf.substitutions (like $releasever, ...) 66 67 @param repoid: Repo ID - string 68 @param conf: dnf Base().conf object 69 @param baseurl: List of strings 70 @param kwargs: keys and values that will be used to setattr on dnf.repo.Repo() object 71 @return: dnf.repo.Repo() object 72 """ 73 def substitute(values): 74 if isinstance(values, str): 75 return libdnf.conf.ConfigParser.substitute(values, conf.substitutions) 76 elif isinstance(values, list) or isinstance(values, tuple): 77 substituted = [] 78 for value in values: 79 if isinstance(value, str): 80 substituted.append( 81 libdnf.conf.ConfigParser.substitute(value, conf.substitutions)) 82 if substituted: 83 return substituted 84 return values 85 86 repo = dnf.repo.Repo(repoid, conf) 87 for path in baseurl: 88 if '://' not in path: 89 path = 'file://{}'.format(os.path.abspath(path)) 90 repo.baseurl += [substitute(path)] 91 for (key, value) in kwargs.items(): 92 setattr(repo, key, substitute(value)) 93 self.add(repo) 94 logger.info(_("Added %s repo from %s"), repoid, ', '.join(baseurl)) 95 return repo 96 97 def enable_debug_repos(self): 98 # :api 99 """enable debug repos corresponding to already enabled binary repos""" 100 101 def debug_name(name): 102 return ("{}-debug-rpms".format(name[:-5]) if name.endswith("-rpms") 103 else "{}-debuginfo".format(name)) 104 105 self._enable_sub_repos(debug_name) 106 107 def enable_source_repos(self): 108 # :api 109 """enable source repos corresponding to already enabled binary repos""" 110 111 def source_name(name): 112 return ("{}-source-rpms".format(name[:-5]) if name.endswith("-rpms") 113 else "{}-source".format(name)) 114 115 self._enable_sub_repos(source_name) 116 117 def get_matching(self, key): 118 # :api 119 if dnf.util.is_glob_pattern(key): 120 l = [self[k] for k in self if fnmatch.fnmatch(k, key)] 121 return dnf.util.MultiCallList(l) 122 repo = self.get(key, None) 123 if repo is None: 124 return dnf.util.MultiCallList([]) 125 return dnf.util.MultiCallList([repo]) 126 127 def iter_enabled(self): 128 # :api 129 return (r for r in self.values() if r.enabled) 130 131 def items(self): 132 """return repos sorted by priority""" 133 return (item for item in sorted(super(RepoDict, self).items(), 134 key=lambda x: (x[1].priority, x[1].cost))) 135 136 def __iter__(self): 137 return self.keys() 138 139 def keys(self): 140 return (k for k, v in self.items()) 141 142 def values(self): 143 return (v for k, v in self.items()) 144