Skip to content

Commit

Permalink
implemented functionality of yum-config-manager
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaelMraka committed Mar 5, 2015
1 parent dc33668 commit 1971b3c
Show file tree
Hide file tree
Showing 8 changed files with 586 additions and 6 deletions.
2 changes: 2 additions & 0 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,8 @@ def version_readout():
man_pages = [
('builddep', 'dnf.plugin.builddep', u'DNF builddep Plugin',
AUTHORS, 8),
('config_manager', 'dnf.plugin.config_manager',
u'DNF config-manager Plugin', AUTHORS, 8),
('copr', 'dnf.plugin.copr', u'DNF copr Plugin',
AUTHORS, 8),
('debuginfo-install', 'dnf.plugin.debuginfo-install',
Expand Down
79 changes: 79 additions & 0 deletions doc/config_manager.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
..
Copyright (C) 2015 Red Hat, Inc.
This copyrighted material is made available to anyone wishing to use,
modify, copy, or redistribute it subject to the terms and conditions of
the GNU General Public License v.2, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY expressed or implied, including the implied warranties 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 Street, Fifth Floor, Boston, MA
02110-1301, USA. Any Red Hat trademarks that are incorporated in the
source code or documentation are not subject to the GNU General Public
License and may only be used or replicated with the express permission of
Red Hat, Inc.

==========================
DNF config-manager Plugin
==========================

Manage main DNF configuration options, toggle which
repositories are enabled or disabled, and add new repositories.

--------
Synopsis
--------

``dnf config-manager [options] <repo>...``

---------
Arguments
---------

``<repo>``
Display / modify specified repository. If not specified display / modify main DNF configuration.

-------
Options
-------

``--help-cmd``
Show this help.

``--add-repo=URL``
Add (and enable) the repo from the specified file or url.

``--dump``
Print dump of current configuration values to stdout.

``--set-disabled``
Disable the specified repos (automatically saves).

``--set-enabled``
Enable the specified repos (automatically saves).

``--save``
Save the current options (useful with --setopt).

--------
Examples
--------
``dnf config-manager --add-repo http://example.com/some/additional.repo``
Download additional.repo and store it in repodir.

``dnf config-manager --add-repo http://example.com/different/repo``
Create new repo file with http://example.com/different/repo as baseurl and enable it.

``dnf config-manager``
Display main DNF configuration.

``dnf config-manager repo``
Display configuration of repo.

``dnf config-manager --set-enabled repo``
Enable repo and make the change permanent.

``dnf config-manager --setopt proxy=http://proxy.example.com:3128/ repo1 repo2 --save``
Update proxy setting in repo1 and repo2 and make the change permanent.
1 change: 1 addition & 0 deletions doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ This documents core plugins of DNF:

release_notes
builddep
config_manager
copr
debuginfo-install
download
Expand Down
12 changes: 6 additions & 6 deletions package/dnf-plugins-core.spec
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ Requires: pykickstart
Requires: python-requests

%description
Core Plugins for DNF. This package enhance DNF with builddep, copr,
debuginfo-install, download, kickstart, needs-restarting, repoquery and
Core Plugins for DNF. This package enhance DNF with builddep, config-manager,
copr, debuginfo-install, download, kickstart, needs-restarting, repoquery and
reposync commands. Additionally provides generate_completion_cache, noroot and
protected_packages passive plugins.

Expand All @@ -40,10 +40,10 @@ BuildRequires: python3-sphinx
Requires: python3-dnf = %{dnf_version}

%description -n python3-dnf-plugins-core
Core Plugins for DNF, Python 3 version. This package enhance DNF with builddep, copr,
debuginfo-install, download, kickstart, needs-restarting, repoquery and
reposync commands. Additionally provides generate_completion_cache, noroot and
protected_packages passive plugins.
Core Plugins for DNF, Python 3 version. This package enhance DNF with builddep,
config-manager, copr, debuginfo-install, download, kickstart, needs-restarting,
repoquery and reposync commands. Additionally provides generate_completion_cache,
noroot and protected_packages passive plugins.

%prep
%setup -q -n dnf-plugins-core
Expand Down
1 change: 1 addition & 0 deletions plugins/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
INSTALL (FILES builddep.py DESTINATION ${PYTHON_INSTALL_DIR}/dnf-plugins)
INSTALL (FILES debuginfo-install.py DESTINATION ${PYTHON_INSTALL_DIR}/dnf-plugins)
INSTALL (FILES config_manager.py DESTINATION ${PYTHON_INSTALL_DIR}/dnf-plugins)
INSTALL (FILES copr.py DESTINATION ${PYTHON_INSTALL_DIR}/dnf-plugins)
INSTALL (FILES download.py DESTINATION ${PYTHON_INSTALL_DIR}/dnf-plugins)
INSTALL (FILES generate_completion_cache.py DESTINATION ${PYTHON_INSTALL_DIR}/dnf-plugins)
Expand Down
255 changes: 255 additions & 0 deletions plugins/config_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
#
# Copyright (C) 2015 Red Hat, Inc.
#
# This copyrighted material is made available to anyone wishing to use,
# modify, copy, or redistribute it subject to the terms and conditions of
# the GNU General Public License v.2, or (at your option) any later version.
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY expressed or implied, including the implied warranties 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 Street, Fifth Floor, Boston, MA
# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the
# source code or documentation are not subject to the GNU General Public
# License and may only be used or replicated with the express permission of
# Red Hat, Inc.
#

from __future__ import absolute_import
from __future__ import unicode_literals
from dnfpluginscore import _, logger

import dnf
import dnf.cli
import dnf.pycomp
import dnfpluginscore
import dnfpluginscore.lib
import os
import re
import shutil


class ConfigManager(dnf.Plugin):

name = 'config-manager'

def __init__(self, base, cli):
super(ConfigManager, self).__init__(base, cli)
self.base = base
self.cli = cli
if self.cli is not None:
self.cli.register_command(ConfigManagerCommand)


class ConfigManagerCommand(dnf.cli.Command):

aliases = ['config-manager']
summary = _('manage dnf configuration options and repositories')
usage = '[%s] [%s]' % (_('OPTIONS'), _('KEYWORDS'))

def __init__(self, cli):
super(ConfigManagerCommand, self).__init__(cli)
self.opts = None
self.parser = None

def configure(self, args):
# setup sack and populate it with enabled repos
demands = self.cli.demands
demands.available_repos = True

self.parser = dnfpluginscore.ArgumentParser(self.aliases[0])
self.parser.add_argument(
'repo', nargs='*',
help=_('repo to modify'))
self.parser.add_argument(
'--save', default=False, action='store_true',
help=_('save the current options (useful with --setopt)'))
self.parser.add_argument(
'--set-enabled', default=False, action='store_true',
help=_('enable the specified repos (automatically saves)'))
self.parser.add_argument(
'--set-disabled', default=False, action='store_true',
help=_('disable the specified repos (automatically saves)'))
self.parser.add_argument(
'--add-repo', default=[], action='append', metavar='URL',
help=_('add (and enable) the repo from the specified file or url'))
self.parser.add_argument(
'--dump', default=False, action='store_true',
help=_('print current configuration values to stdout'))

self.opts = self.parser.parse_args(args)

if self.opts.help_cmd:
print(self.parser.format_help())
return

if (self.opts.save or self.opts.set_enabled or
self.opts.set_disabled or self.opts.add_repo):
demands.root_user = True


def run(self, _args):
"""Execute the util action here."""

if self.opts.help_cmd:
return
if self.opts.set_enabled and self.opts.set_disabled:
logger.error(
_("Error: Trying to enable and disable repos at the same time."))
self.opts.set_enabled = self.opts.set_disabled = False
if self.opts.set_enabled and not self.opts.repo:
logger.error(_("Error: Trying to enable already enabled repos."))
self.opts.set_enabled = False

if self.opts.add_repo:
self.add_repo()
else:
self.modify_repo()

def modify_repo(self):
""" process --set-enabled, --set-disabled and --setopt options """

sbc = self.base.conf
modify = []
if hasattr(self.cli, 'main_setopts') and self.cli.main_setopts:
modify = self.cli.main_setopts.items
if not self.opts.repo or 'main' in self.opts.repo:
if self.opts.dump:
print(self.base.output.fmtSection('main'))
print(self.base.conf.dump())
if self.opts.save and modify:
# modify [main] in dnf.conf
dnfpluginscore.lib.write_raw_configfile(dnf.const.CONF_FILENAME,
'main', sbc.substitutions,
sbc.cfg.options,
sbc.iteritems,
sbc.optionobj,
modify)

if self.opts.set_enabled or self.opts.set_disabled:
self.opts.save = True
modify.append('enabled')

if self.opts.repo:
matched = []
for name in self.opts.repo:
matched.extend(self.base.repos.get_matching(name))
else:
matched = self.base.repos.iter_enabled()

if not matched:
raise dnf.exceptions.Error(_("No matching repo to modify: %s.")
% ', '.join(self.opts.repo))
for repo in sorted(matched):
if self.opts.dump:
print(self.base.output.fmtSection('repo: ' + repo.id))
if self.opts.set_enabled and not repo.enabled:
repo.enable()
elif self.opts.set_disabled and repo.enabled:
repo.disable()
if self.opts.dump:
print(repo.dump())
repo_modify = modify[:]
if (hasattr(self.cli, 'repo_setopts')
and repo.id in self.cli.repo_setopts):
repo_modify.extend(self.cli.repo_setopts[repo.id].items)
if self.opts.save and modify:
dnfpluginscore.lib.write_raw_configfile(repo.repofile,
repo.id,
sbc.substitutions,
repo.cfg.options,
repo.iteritems,
repo.optionobj,
repo_modify)

def add_repo(self):
""" process --add-repo option """

# put repo file into first reposdir which exists or create it
myrepodir = None
for rdir in self.base.conf.reposdir:
if os.path.exists(rdir):
myrepodir = rdir
break

if not myrepodir:
myrepodir = self.base.conf.reposdir[0]
dnf.util.ensure_dir(myrepodir)

for url in self.opts.add_repo:
if dnf.pycomp.urlparse.urlparse(url).scheme == '':
url = 'file://' + os.path.abspath(url)
logger.info(_('Adding repo from: %s'), url)
if url.endswith('.repo'):
# .repo file - download, put into reposdir and enable it
destname = os.path.basename(url)
destname = os.path.join(myrepodir, destname)
try:
f = dnfpluginscore.lib.urlopen(self, None, url, 'w+')
shutil.copy2(f.name, destname)
f.close()
except IOError as e:
logger.error(e)
continue
else:
# just url to repo, create .repo file on our own
repoid = sanitize_url_to_fs(url)
reponame = 'created by dnf config-manager from %s' % url
destname = os.path.join(myrepodir, "%s.repo" % repoid)
content = "[%s]\nname=%s\nbaseurl=%s\nenabled=1\n" % \
(repoid, reponame, url)
if not save_to_file(destname, content):
continue


def save_to_file(filename, content):
try:
with open(filename, 'w+') as fd:
dnf.pycomp.write_to_file(fd, content)
except (IOError, OSError) as e:
logger.error(_('Could not save repo to repofile %s: %s'),
filename, e)
return False
return True

# Regular expressions to sanitise cache filenames
RE_SCHEME = re.compile(r'^\w+:/*(\w+:|www\.)?')
RE_SLASH = re.compile(r'[?/:&#|]+')
RE_BEGIN = re.compile(r'^[,.]*')
RE_FINAL = re.compile(r'[,.]*$')

def sanitize_url_to_fs(url):
"""Return a filename suitable for the filesystem
Strips dangerous and common characters to create a filename we
can use to store the cache in.
"""

try:
if RE_SCHEME.match(url):
if dnf.pycomp.PY3:
url = url.encode('idna').decode('utf-8')
else:
if isinstance(url, str):
url = url.decode('utf-8').encode('idna')
else:
url = url.encode('idna')
if isinstance(url, unicode):
url = url.encode('utf-8')
except (UnicodeDecodeError, UnicodeEncodeError, UnicodeError, TypeError):
pass
url = RE_SCHEME.sub("", url)
url = RE_SLASH.sub("_", url)
url = RE_BEGIN.sub("", url)
url = RE_FINAL.sub("", url)

# limit length of url
if len(url) > 250:
parts = url[:185].split('_')
lastindex = 185-len(parts[-1])
csum = dnf.yum.misc.Checksums(['sha256'])
csum.update(url[lastindex:])
url = url[:lastindex] + '_' + csum.hexdigest()

return url

0 comments on commit 1971b3c

Please sign in to comment.