|
Packit Service |
27f74b |
#
|
|
Packit Service |
27f74b |
# Copyright (C) 2015 Red Hat, Inc.
|
|
Packit Service |
27f74b |
#
|
|
Packit Service |
27f74b |
# This copyrighted material is made available to anyone wishing to use,
|
|
Packit Service |
27f74b |
# modify, copy, or redistribute it subject to the terms and conditions of
|
|
Packit Service |
27f74b |
# the GNU General Public License v.2, or (at your option) any later version.
|
|
Packit Service |
27f74b |
# This program is distributed in the hope that it will be useful, but WITHOUT
|
|
Packit Service |
27f74b |
# ANY WARRANTY expressed or implied, including the implied warranties of
|
|
Packit Service |
27f74b |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
|
Packit Service |
27f74b |
# Public License for more details. You should have received a copy of the
|
|
Packit Service |
27f74b |
# GNU General Public License along with this program; if not, write to the
|
|
Packit Service |
27f74b |
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
Packit Service |
27f74b |
# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the
|
|
Packit Service |
27f74b |
# source code or documentation are not subject to the GNU General Public
|
|
Packit Service |
27f74b |
# License and may only be used or replicated with the express permission of
|
|
Packit Service |
27f74b |
# Red Hat, Inc.
|
|
Packit Service |
27f74b |
#
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
from __future__ import absolute_import
|
|
Packit Service |
27f74b |
from __future__ import unicode_literals
|
|
Packit Service |
27f74b |
from dnfpluginscore import _, logger, P_
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
import dnf
|
|
Packit Service |
27f74b |
import dnf.cli
|
|
Packit Service |
27f74b |
import dnf.pycomp
|
|
Packit Service |
27f74b |
import dnf.util
|
|
Packit Service |
27f74b |
import fnmatch
|
|
Packit Service |
27f74b |
import os
|
|
Packit Service |
27f74b |
import re
|
|
Packit Service |
27f74b |
import shutil
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
@dnf.plugin.register_command
|
|
Packit Service |
27f74b |
class ConfigManagerCommand(dnf.cli.Command):
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
aliases = ['config-manager']
|
|
Packit Service |
27f74b |
summary = _('manage {prog} configuration options and repositories').format(
|
|
Packit Service |
27f74b |
prog=dnf.util.MAIN_PROG)
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
@staticmethod
|
|
Packit Service |
27f74b |
def set_argparser(parser):
|
|
Packit Service |
27f74b |
parser.add_argument(
|
|
Packit Service |
27f74b |
'crepo', nargs='*', metavar='repo',
|
|
Packit Service |
27f74b |
help=_('repo to modify'))
|
|
Packit Service |
27f74b |
parser.add_argument(
|
|
Packit Service |
27f74b |
'--save', default=False, action='store_true',
|
|
Packit Service |
27f74b |
help=_('save the current options (useful with --setopt)'))
|
|
Packit Service |
27f74b |
parser.add_argument(
|
|
Packit Service |
27f74b |
'--add-repo', default=[], action='append', metavar='URL',
|
|
Packit Service |
27f74b |
help=_('add (and enable) the repo from the specified file or url'))
|
|
Packit Service |
27f74b |
parser.add_argument(
|
|
Packit Service |
27f74b |
'--dump', default=False, action='store_true',
|
|
Packit Service |
27f74b |
help=_('print current configuration values to stdout'))
|
|
Packit Service |
27f74b |
parser.add_argument(
|
|
Packit Service |
27f74b |
'--dump-variables', default=False, action='store_true',
|
|
Packit Service |
27f74b |
help=_('print variable values to stdout'))
|
|
Packit Service |
27f74b |
enable_group = parser.add_mutually_exclusive_group()
|
|
Packit Service |
27f74b |
enable_group.add_argument("--set-enabled", default=False,
|
|
Packit Service |
27f74b |
dest="set_enabled", action="store_true",
|
|
Packit Service |
27f74b |
help=_("enable repos (automatically saves)"))
|
|
Packit Service |
27f74b |
enable_group.add_argument("--set-disabled", default=False,
|
|
Packit Service |
27f74b |
dest="set_disabled", action="store_true",
|
|
Packit Service |
27f74b |
help=_("disable repos (automatically saves)"))
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
def configure(self):
|
|
Packit Service |
27f74b |
# setup sack and populate it with enabled repos
|
|
Packit Service |
27f74b |
demands = self.cli.demands
|
|
Packit Service |
27f74b |
demands.available_repos = True
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
# if no argument was passed then error
|
|
Packit Service |
27f74b |
if (not (self.opts.add_repo != [] or
|
|
Packit Service |
27f74b |
self.opts.save or
|
|
Packit Service |
27f74b |
self.opts.dump or
|
|
Packit Service |
27f74b |
self.opts.dump_variables or
|
|
Packit Service |
27f74b |
self.opts.set_disabled or
|
|
Packit Service |
27f74b |
self.opts.set_enabled) ):
|
|
Packit Service |
27f74b |
self.cli.optparser.error(_("one of the following arguments is required: {}")
|
|
Packit Service |
27f74b |
.format(' '.join([
|
|
Packit Service |
27f74b |
"--save", "--add-repo",
|
|
Packit Service |
27f74b |
"--dump", "--dump-variables",
|
|
Packit Service |
27f74b |
"--set-enabled", "--enable",
|
|
Packit Service |
27f74b |
"--set-disabled", "--disable"])))
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
# warn with hint if --enablerepo or --disablerepo argument was passed
|
|
Packit Service |
27f74b |
if self.opts.repos_ed != []:
|
|
Packit Service |
27f74b |
logger.warning(_("Warning: --enablerepo/--disablerepo arguments have no meaning"
|
|
Packit Service |
27f74b |
"with config manager. Use --set-enabled/--set-disabled instead."))
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
if (self.opts.save or self.opts.set_enabled or
|
|
Packit Service |
27f74b |
self.opts.set_disabled or self.opts.add_repo):
|
|
Packit Service |
27f74b |
demands.root_user = True
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
def run(self):
|
|
Packit Service |
27f74b |
"""Execute the util action here."""
|
|
Packit Service |
27f74b |
if self.opts.add_repo:
|
|
Packit Service |
27f74b |
self.add_repo()
|
|
Packit Service |
27f74b |
else:
|
|
Packit Service |
27f74b |
self.modify_repo()
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
def modify_repo(self):
|
|
Packit Service |
27f74b |
""" process --set-enabled, --set-disabled and --setopt options """
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
matching_repos = [] # list of matched repositories
|
|
Packit Service |
27f74b |
not_matching_repos_id = set() # IDs of not matched repositories
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
def match_repos(key, add_matching_repos):
|
|
Packit Service |
27f74b |
matching = self.base.repos.get_matching(key)
|
|
Packit Service |
27f74b |
if not matching:
|
|
Packit Service |
27f74b |
not_matching_repos_id.add(name)
|
|
Packit Service |
27f74b |
elif add_matching_repos:
|
|
Packit Service |
27f74b |
matching_repos.extend(matching)
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
if self.opts.crepo:
|
|
Packit Service |
27f74b |
for name in self.opts.crepo:
|
|
Packit Service |
27f74b |
match_repos(name, True)
|
|
Packit Service |
27f74b |
if hasattr(self.opts, 'repo_setopts'):
|
|
Packit Service |
27f74b |
for name in self.opts.repo_setopts.keys():
|
|
Packit Service |
27f74b |
match_repos(name, False)
|
|
Packit Service |
27f74b |
else:
|
|
Packit Service |
27f74b |
if hasattr(self.opts, 'repo_setopts'):
|
|
Packit Service |
27f74b |
for name in self.opts.repo_setopts.keys():
|
|
Packit Service |
27f74b |
match_repos(name, True)
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
if not_matching_repos_id:
|
|
Packit Service |
27f74b |
raise dnf.exceptions.Error(_("No matching repo to modify: %s.")
|
|
Packit Service |
27f74b |
% ', '.join(not_matching_repos_id))
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
sbc = self.base.conf
|
|
Packit Service |
27f74b |
modify = {}
|
|
Packit Service |
27f74b |
if hasattr(self.opts, 'main_setopts') and self.opts.main_setopts:
|
|
Packit Service |
27f74b |
modify = self.opts.main_setopts
|
|
Packit Service |
27f74b |
if self.opts.dump_variables:
|
|
Packit Service |
27f74b |
for name, val in self.base.conf.substitutions.items():
|
|
Packit Service |
27f74b |
print("%s = %s" % (name, val))
|
|
Packit Service |
27f74b |
if not self.opts.crepo or 'main' in self.opts.crepo:
|
|
Packit Service |
27f74b |
if self.opts.save and modify:
|
|
Packit Service |
27f74b |
# modify [main] in global configuration file
|
|
Packit Service |
27f74b |
self.base.conf.write_raw_configfile(self.base.conf.config_file_path, 'main',
|
|
Packit Service |
27f74b |
sbc.substitutions, modify)
|
|
Packit Service |
27f74b |
if self.opts.dump:
|
|
Packit Service |
27f74b |
print(self.base.output.fmtSection('main'))
|
|
Packit Service |
27f74b |
print(self.base.conf.dump())
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
if not matching_repos:
|
|
Packit Service |
27f74b |
return
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
if self.opts.set_enabled or self.opts.set_disabled:
|
|
Packit Service |
27f74b |
self.opts.save = True
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
for repo in sorted(matching_repos):
|
|
Packit Service |
27f74b |
repo_modify = {}
|
|
Packit Service |
27f74b |
if self.opts.set_enabled:
|
|
Packit Service |
27f74b |
repo_modify['enabled'] = "1"
|
|
Packit Service |
27f74b |
elif self.opts.set_disabled:
|
|
Packit Service |
27f74b |
repo_modify['enabled'] = "0"
|
|
Packit Service |
27f74b |
if hasattr(self.opts, 'repo_setopts'):
|
|
Packit Service |
27f74b |
for repoid, setopts in self.opts.repo_setopts.items():
|
|
Packit Service |
27f74b |
if fnmatch.fnmatch(repo.id, repoid):
|
|
Packit Service |
27f74b |
repo_modify.update(setopts)
|
|
Packit Service |
27f74b |
if self.opts.save and repo_modify:
|
|
Packit Service |
27f74b |
self.base.conf.write_raw_configfile(repo.repofile, repo.id, sbc.substitutions, repo_modify)
|
|
Packit Service |
27f74b |
if self.opts.dump:
|
|
Packit Service |
27f74b |
print(self.base.output.fmtSection('repo: ' + repo.id))
|
|
Packit Service |
27f74b |
print(repo.dump())
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
def add_repo(self):
|
|
Packit Service |
27f74b |
""" process --add-repo option """
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
# Get the reposdir location
|
|
Packit Service |
27f74b |
myrepodir = self.base.conf.get_reposdir
|
|
Packit Service |
27f74b |
errors_count = 0
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
for url in self.opts.add_repo:
|
|
Packit Service |
27f74b |
if dnf.pycomp.urlparse.urlparse(url).scheme == '':
|
|
Packit Service |
27f74b |
url = 'file://' + os.path.abspath(url)
|
|
Packit Service |
27f74b |
logger.info(_('Adding repo from: %s'), url)
|
|
Packit Service |
27f74b |
if url.endswith('.repo'):
|
|
Packit Service |
27f74b |
# .repo file - download, put into reposdir and enable it
|
|
Packit Service |
27f74b |
destname = os.path.basename(url)
|
|
Packit Service |
27f74b |
destname = os.path.join(myrepodir, destname)
|
|
Packit Service |
27f74b |
try:
|
|
Packit Service |
27f74b |
f = self.base.urlopen(url, mode='w+')
|
|
Packit Service |
27f74b |
shutil.copy2(f.name, destname)
|
|
Packit Service |
27f74b |
os.chmod(destname, 0o644)
|
|
Packit Service |
27f74b |
f.close()
|
|
Packit Service |
27f74b |
except IOError as e:
|
|
Packit Service |
27f74b |
errors_count += 1
|
|
Packit Service |
27f74b |
logger.error(e)
|
|
Packit Service |
27f74b |
continue
|
|
Packit Service |
27f74b |
else:
|
|
Packit Service |
27f74b |
# just url to repo, create .repo file on our own
|
|
Packit Service |
27f74b |
repoid = sanitize_url_to_fs(url)
|
|
Packit Service |
27f74b |
reponame = 'created by {} config-manager from {}'.format(dnf.util.MAIN_PROG, url)
|
|
Packit Service |
27f74b |
destname = os.path.join(myrepodir, "%s.repo" % repoid)
|
|
Packit Service |
27f74b |
content = "[%s]\nname=%s\nbaseurl=%s\nenabled=1\n" % \
|
|
Packit Service |
27f74b |
(repoid, reponame, url)
|
|
Packit Service |
27f74b |
if not save_to_file(destname, content):
|
|
Packit Service |
27f74b |
continue
|
|
Packit Service |
27f74b |
if errors_count:
|
|
Packit Service |
27f74b |
raise dnf.exceptions.Error(P_("Configuration of repo failed",
|
|
Packit Service |
27f74b |
"Configuration of repos failed", errors_count))
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
def save_to_file(filename, content):
|
|
Packit Service |
27f74b |
try:
|
|
Packit Service |
27f74b |
with open(filename, 'w+') as fd:
|
|
Packit Service |
27f74b |
dnf.pycomp.write_to_file(fd, content)
|
|
Packit Service |
27f74b |
os.chmod(filename, 0o644)
|
|
Packit Service |
27f74b |
except (IOError, OSError) as e:
|
|
Packit Service |
27f74b |
logger.error(_('Could not save repo to repofile %s: %s'),
|
|
Packit Service |
27f74b |
filename, e)
|
|
Packit Service |
27f74b |
return False
|
|
Packit Service |
27f74b |
return True
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
# Regular expressions to sanitise cache filenames
|
|
Packit Service |
27f74b |
RE_SCHEME = re.compile(r'^\w+:/*(\w+:|www\.)?')
|
|
Packit Service |
27f74b |
RE_SLASH = re.compile(r'[?/:&#|~\*\[\]\(\)\'\\]+')
|
|
Packit Service |
27f74b |
RE_BEGIN = re.compile(r'^[,.]*')
|
|
Packit Service |
27f74b |
RE_FINAL = re.compile(r'[,.]*$')
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
def sanitize_url_to_fs(url):
|
|
Packit Service |
27f74b |
"""Return a filename suitable for the filesystem and for repo id
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
Strips dangerous and common characters to create a filename we
|
|
Packit Service |
27f74b |
can use to store the cache in.
|
|
Packit Service |
27f74b |
"""
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
try:
|
|
Packit Service |
27f74b |
if RE_SCHEME.match(url):
|
|
Packit Service |
27f74b |
if dnf.pycomp.PY3:
|
|
Packit Service |
27f74b |
url = url.encode('idna').decode('utf-8')
|
|
Packit Service |
27f74b |
else:
|
|
Packit Service |
27f74b |
if isinstance(url, str):
|
|
Packit Service |
27f74b |
url = url.decode('utf-8').encode('idna')
|
|
Packit Service |
27f74b |
else:
|
|
Packit Service |
27f74b |
url = url.encode('idna')
|
|
Packit Service |
27f74b |
if isinstance(url, unicode):
|
|
Packit Service |
27f74b |
url = url.encode('utf-8')
|
|
Packit Service |
27f74b |
except (UnicodeDecodeError, UnicodeEncodeError, UnicodeError, TypeError):
|
|
Packit Service |
27f74b |
pass
|
|
Packit Service |
27f74b |
url = RE_SCHEME.sub("", url)
|
|
Packit Service |
27f74b |
url = RE_SLASH.sub("_", url)
|
|
Packit Service |
27f74b |
url = RE_BEGIN.sub("", url)
|
|
Packit Service |
27f74b |
url = RE_FINAL.sub("", url)
|
|
Packit Service |
27f74b |
|
|
Packit Service |
27f74b |
# limit length of url
|
|
Packit Service |
27f74b |
if len(url) > 250:
|
|
Packit Service |
27f74b |
parts = url[:185].split('_')
|
|
Packit Service |
27f74b |
lastindex = 185-len(parts[-1])
|
|
Packit Service |
27f74b |
csum = dnf.yum.misc.Checksums(['sha256'])
|
|
Packit Service |
27f74b |
csum.update(url[lastindex:])
|
|
Packit Service |
27f74b |
url = url[:lastindex] + '_' + csum.hexdigest()
|
|
Packit Service |
27f74b |
# remove all not allowed characters
|
|
Packit Service |
27f74b |
allowed_regex = "[^abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.:-]"
|
|
Packit Service |
27f74b |
return re.sub(allowed_regex, '', url)
|