|
Packit |
3a9065 |
# download.py, supplies the 'download' command.
|
|
Packit |
3a9065 |
#
|
|
Packit |
3a9065 |
# Copyright (C) 2013-2015 Red Hat, Inc.
|
|
Packit |
3a9065 |
#
|
|
Packit |
3a9065 |
# This copyrighted material is made available to anyone wishing to use,
|
|
Packit |
3a9065 |
# modify, copy, or redistribute it subject to the terms and conditions of
|
|
Packit |
3a9065 |
# the GNU General Public License v.2, or (at your option) any later version.
|
|
Packit |
3a9065 |
# This program is distributed in the hope that it will be useful, but WITHOUT
|
|
Packit |
3a9065 |
# ANY WARRANTY expressed or implied, including the implied warranties of
|
|
Packit |
3a9065 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
|
Packit |
3a9065 |
# Public License for more details. You should have received a copy of the
|
|
Packit |
3a9065 |
# GNU General Public License along with this program; if not, write to the
|
|
Packit |
3a9065 |
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
Packit |
3a9065 |
# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the
|
|
Packit |
3a9065 |
# source code or documentation are not subject to the GNU General Public
|
|
Packit |
3a9065 |
# License and may only be used or replicated with the express permission of
|
|
Packit |
3a9065 |
# Red Hat, Inc.
|
|
Packit |
3a9065 |
#
|
|
Packit |
3a9065 |
|
|
Packit |
3a9065 |
from __future__ import absolute_import
|
|
Packit |
3a9065 |
from __future__ import unicode_literals
|
|
Packit |
3a9065 |
from dnfpluginscore import _, logger
|
|
Packit |
3a9065 |
from dnf.cli.option_parser import OptionParser
|
|
Packit |
3a9065 |
|
|
Packit |
3a9065 |
import dnf
|
|
Packit |
3a9065 |
import dnf.cli
|
|
Packit |
3a9065 |
import dnf.exceptions
|
|
Packit |
3a9065 |
import dnf.i18n
|
|
Packit |
3a9065 |
import dnf.subject
|
|
Packit |
3a9065 |
import dnf.util
|
|
Packit |
3a9065 |
import hawkey
|
|
Packit |
3a9065 |
import itertools
|
|
Packit |
3a9065 |
import os
|
|
Packit |
3a9065 |
import shutil
|
|
Packit |
3a9065 |
|
|
Packit |
3a9065 |
|
|
Packit |
3a9065 |
@dnf.plugin.register_command
|
|
Packit |
3a9065 |
class DownloadCommand(dnf.cli.Command):
|
|
Packit |
3a9065 |
|
|
Packit |
3a9065 |
aliases = ['download']
|
|
Packit |
3a9065 |
summary = _('Download package to current directory')
|
|
Packit |
3a9065 |
|
|
Packit |
3a9065 |
def __init__(self, cli):
|
|
Packit |
3a9065 |
super(DownloadCommand, self).__init__(cli)
|
|
Packit |
3a9065 |
self.opts = None
|
|
Packit |
3a9065 |
self.parser = None
|
|
Packit |
3a9065 |
|
|
Packit |
3a9065 |
@staticmethod
|
|
Packit |
3a9065 |
def set_argparser(parser):
|
|
Packit |
3a9065 |
parser.add_argument('packages', nargs='+',
|
|
Packit |
3a9065 |
help=_('packages to download'))
|
|
Packit |
3a9065 |
parser.add_argument("--source", action='store_true',
|
|
Packit |
3a9065 |
help=_('download the src.rpm instead'))
|
|
Packit |
3a9065 |
parser.add_argument("--debuginfo", action='store_true',
|
|
Packit |
3a9065 |
help=_('download the -debuginfo package instead'))
|
|
Packit |
3a9065 |
parser.add_argument("--debugsource", action='store_true',
|
|
Packit |
3a9065 |
help=_('download the -debugsource package instead'))
|
|
Packit |
3a9065 |
parser.add_argument("--arch", '--archlist', dest='arches', default=[],
|
|
Packit |
3a9065 |
action=OptionParser._SplitCallback, metavar='[arch]',
|
|
Packit |
3a9065 |
help=_("limit the query to packages of given architectures."))
|
|
Packit |
3a9065 |
parser.add_argument('--resolve', action='store_true',
|
|
Packit |
3a9065 |
help=_('resolve and download needed dependencies'))
|
|
Packit |
3a9065 |
parser.add_argument('--alldeps', action='store_true',
|
|
Packit |
3a9065 |
help=_('when running with --resolve, download all dependencies '
|
|
Packit |
3a9065 |
'(do not exclude already installed ones)'))
|
|
Packit |
3a9065 |
parser.add_argument('--url', '--urls', action='store_true', dest='url',
|
|
Packit |
3a9065 |
help=_('print list of urls where the rpms '
|
|
Packit |
3a9065 |
'can be downloaded instead of downloading'))
|
|
Packit |
3a9065 |
parser.add_argument('--urlprotocols', action='append',
|
|
Packit |
3a9065 |
choices=['http', 'https', 'rsync', 'ftp'],
|
|
Packit |
3a9065 |
default=[],
|
|
Packit |
3a9065 |
help=_('when running with --url, '
|
|
Packit |
3a9065 |
'limit to specific protocols'))
|
|
Packit |
3a9065 |
|
|
Packit |
3a9065 |
def configure(self):
|
|
Packit |
3a9065 |
# setup sack and populate it with enabled repos
|
|
Packit |
3a9065 |
demands = self.cli.demands
|
|
Packit |
3a9065 |
demands.sack_activation = True
|
|
Packit |
3a9065 |
demands.available_repos = True
|
|
Packit |
3a9065 |
if self.opts.resolve and self.opts.alldeps:
|
|
Packit |
3a9065 |
demands.load_system_repo = False
|
|
Packit |
3a9065 |
|
|
Packit |
3a9065 |
if self.opts.source:
|
|
Packit |
3a9065 |
self.base.repos.enable_source_repos()
|
|
Packit |
3a9065 |
|
|
Packit |
3a9065 |
if self.opts.debuginfo or self.opts.debugsource:
|
|
Packit |
3a9065 |
self.base.repos.enable_debug_repos()
|
|
Packit |
3a9065 |
|
|
Packit |
3a9065 |
if self.opts.destdir:
|
|
Packit |
3a9065 |
self.base.conf.destdir = self.opts.destdir
|
|
Packit |
3a9065 |
else:
|
|
Packit |
3a9065 |
self.base.conf.destdir = dnf.i18n.ucd(os.getcwd())
|
|
Packit |
3a9065 |
|
|
Packit |
3a9065 |
def run(self):
|
|
Packit |
3a9065 |
"""Execute the util action here."""
|
|
Packit |
3a9065 |
|
|
Packit |
3a9065 |
if (not self.opts.source
|
|
Packit |
3a9065 |
and not self.opts.debuginfo
|
|
Packit |
3a9065 |
and not self.opts.debugsource):
|
|
Packit |
3a9065 |
pkgs = self._get_pkg_objs_rpms(self.opts.packages)
|
|
Packit |
3a9065 |
else:
|
|
Packit |
3a9065 |
pkgs = []
|
|
Packit |
3a9065 |
if self.opts.source:
|
|
Packit |
3a9065 |
pkgs.extend(self._get_pkg_objs_source(self.opts.packages))
|
|
Packit |
3a9065 |
|
|
Packit |
3a9065 |
if self.opts.debuginfo:
|
|
Packit |
3a9065 |
pkgs.extend(self._get_pkg_objs_debuginfo(self.opts.packages))
|
|
Packit |
3a9065 |
|
|
Packit |
3a9065 |
if self.opts.debugsource:
|
|
Packit |
3a9065 |
pkgs.extend(self._get_pkg_objs_debugsource(self.opts.packages))
|
|
Packit |
3a9065 |
|
|
Packit |
3a9065 |
# If user asked for just urls then print them and we're done
|
|
Packit |
3a9065 |
if self.opts.url:
|
|
Packit |
3a9065 |
for pkg in pkgs:
|
|
Packit |
3a9065 |
# command line repo packages do not have .remote_location
|
|
Packit |
3a9065 |
if pkg.repoid != hawkey.CMDLINE_REPO_NAME:
|
|
Packit |
3a9065 |
url = pkg.remote_location(schemes=self.opts.urlprotocols)
|
|
Packit |
3a9065 |
if url:
|
|
Packit |
3a9065 |
print(url)
|
|
Packit |
3a9065 |
else:
|
|
Packit |
3a9065 |
msg = _("Failed to get mirror for package: %s") % pkg.name
|
|
Packit |
3a9065 |
if self.base.conf.strict:
|
|
Packit |
3a9065 |
raise dnf.exceptions.Error(msg)
|
|
Packit |
3a9065 |
logger.warning(msg)
|
|
Packit |
3a9065 |
return
|
|
Packit |
3a9065 |
else:
|
|
Packit |
3a9065 |
self._do_downloads(pkgs) # download rpms
|
|
Packit |
3a9065 |
|
|
Packit |
3a9065 |
def _do_downloads(self, pkgs):
|
|
Packit |
3a9065 |
"""
|
|
Packit |
3a9065 |
Perform the download for a list of packages
|
|
Packit |
3a9065 |
"""
|
|
Packit |
3a9065 |
pkg_dict = {}
|
|
Packit |
3a9065 |
for pkg in pkgs:
|
|
Packit |
3a9065 |
pkg_dict.setdefault(str(pkg), []).append(pkg)
|
|
Packit |
3a9065 |
|
|
Packit |
3a9065 |
to_download = []
|
|
Packit |
3a9065 |
cmdline = []
|
|
Packit |
3a9065 |
for pkg_list in pkg_dict.values():
|
|
Packit |
3a9065 |
pkgs_cmdline = [pkg for pkg in pkg_list
|
|
Packit |
3a9065 |
if pkg.repoid == hawkey.CMDLINE_REPO_NAME]
|
|
Packit |
3a9065 |
if pkgs_cmdline:
|
|
Packit |
3a9065 |
cmdline.append(pkgs_cmdline[0])
|
|
Packit |
3a9065 |
continue
|
|
Packit |
3a9065 |
pkg_list.sort(key=lambda x: (x.repo.priority, x.repo.cost))
|
|
Packit |
3a9065 |
to_download.append(pkg_list[0])
|
|
Packit |
3a9065 |
if to_download:
|
|
Packit |
3a9065 |
self.base.download_packages(to_download, self.base.output.progress)
|
|
Packit |
3a9065 |
if cmdline:
|
|
Packit |
3a9065 |
# command line repo packages are either local files or already downloaded urls
|
|
Packit |
3a9065 |
# just copy them to the destination
|
|
Packit |
3a9065 |
for pkg in cmdline:
|
|
Packit |
3a9065 |
# python<3.4 shutil module does not raise SameFileError, check manually
|
|
Packit |
3a9065 |
src = pkg.localPkg()
|
|
Packit |
3a9065 |
dst = os.path.join(self.base.conf.destdir, os.path.basename(src))
|
|
Packit |
3a9065 |
if os.path.exists(dst) and os.path.samefile(src, dst):
|
|
Packit |
3a9065 |
continue
|
|
Packit |
3a9065 |
shutil.copy(src, self.base.conf.destdir)
|
|
Packit |
3a9065 |
locations = sorted([pkg.localPkg() for pkg in to_download + cmdline])
|
|
Packit |
3a9065 |
return locations
|
|
Packit |
3a9065 |
|
|
Packit |
3a9065 |
def _get_pkg_objs_rpms(self, pkg_specs):
|
|
Packit |
3a9065 |
"""
|
|
Packit |
3a9065 |
Return a list of dnf.Package objects that represent the rpms
|
|
Packit |
3a9065 |
to download.
|
|
Packit |
3a9065 |
"""
|
|
Packit |
3a9065 |
if self.opts.resolve:
|
|
Packit |
3a9065 |
pkgs = self._get_packages_with_deps(pkg_specs)
|
|
Packit |
3a9065 |
else:
|
|
Packit |
3a9065 |
pkgs = self._get_packages(pkg_specs)
|
|
Packit |
3a9065 |
return pkgs
|
|
Packit |
3a9065 |
|
|
Packit |
3a9065 |
def _get_pkg_objs_source(self, pkg_specs):
|
|
Packit |
3a9065 |
"""
|
|
Packit |
3a9065 |
Return a list of dnf.Package objects that represent the source
|
|
Packit |
3a9065 |
rpms to download.
|
|
Packit |
3a9065 |
"""
|
|
Packit |
3a9065 |
pkgs = self._get_pkg_objs_rpms(pkg_specs)
|
|
Packit |
3a9065 |
source_pkgs = self._get_source_packages(pkgs)
|
|
Packit |
3a9065 |
pkgs = set(self._get_packages(source_pkgs, source=True))
|
|
Packit |
3a9065 |
return pkgs
|
|
Packit |
3a9065 |
|
|
Packit |
3a9065 |
def _get_pkg_objs_debuginfo(self, pkg_specs):
|
|
Packit |
3a9065 |
"""
|
|
Packit |
3a9065 |
Return a list of dnf.Package objects that represent the debuginfo
|
|
Packit |
3a9065 |
rpms to download.
|
|
Packit |
3a9065 |
"""
|
|
Packit |
3a9065 |
dbg_pkgs = set()
|
|
Packit |
3a9065 |
q = self.base.sack.query().available()
|
|
Packit |
3a9065 |
|
|
Packit |
3a9065 |
for pkg in self._get_packages(pkg_specs):
|
|
Packit |
3a9065 |
for dbg_name in [pkg.debug_name, pkg.source_debug_name]:
|
|
Packit |
3a9065 |
dbg_available = q.filter(
|
|
Packit |
3a9065 |
name=dbg_name,
|
|
Packit |
3a9065 |
epoch=int(pkg.epoch),
|
|
Packit |
3a9065 |
version=pkg.version,
|
|
Packit |
3a9065 |
release=pkg.release,
|
|
Packit |
3a9065 |
arch=pkg.arch
|
|
Packit |
3a9065 |
)
|
|
Packit |
3a9065 |
|
|
Packit |
3a9065 |
if not dbg_available:
|
|
Packit |
3a9065 |
continue
|
|
Packit |
3a9065 |
|
|
Packit |
3a9065 |
for p in dbg_available:
|
|
Packit |
3a9065 |
dbg_pkgs.add(p)
|
|
Packit |
3a9065 |
|
|
Packit |
3a9065 |
break
|
|
Packit |
3a9065 |
|
|
Packit |
3a9065 |
return dbg_pkgs
|
|
Packit |
3a9065 |
|
|
Packit |
3a9065 |
def _get_pkg_objs_debugsource(self, pkg_specs):
|
|
Packit |
3a9065 |
"""
|
|
Packit |
3a9065 |
Return a list of dnf.Package objects that represent the debugsource
|
|
Packit |
3a9065 |
rpms to download.
|
|
Packit |
3a9065 |
"""
|
|
Packit |
3a9065 |
dbg_pkgs = set()
|
|
Packit |
3a9065 |
q = self.base.sack.query().available()
|
|
Packit |
3a9065 |
|
|
Packit |
3a9065 |
for pkg in self._get_packages(pkg_specs):
|
|
Packit |
3a9065 |
dbg_available = q.filter(
|
|
Packit |
3a9065 |
name=pkg.debugsource_name,
|
|
Packit |
3a9065 |
epoch=int(pkg.epoch),
|
|
Packit |
3a9065 |
version=pkg.version,
|
|
Packit |
3a9065 |
release=pkg.release,
|
|
Packit |
3a9065 |
arch=pkg.arch
|
|
Packit |
3a9065 |
)
|
|
Packit |
3a9065 |
|
|
Packit |
3a9065 |
for p in dbg_available:
|
|
Packit |
3a9065 |
dbg_pkgs.add(p)
|
|
Packit |
3a9065 |
|
|
Packit |
3a9065 |
return dbg_pkgs
|
|
Packit |
3a9065 |
|
|
Packit |
3a9065 |
def _get_packages(self, pkg_specs, source=False):
|
|
Packit |
3a9065 |
"""Get packages matching pkg_specs."""
|
|
Packit |
3a9065 |
func = self._get_query_source if source else self._get_query
|
|
Packit |
3a9065 |
queries = []
|
|
Packit |
3a9065 |
for pkg_spec in pkg_specs:
|
|
Packit |
3a9065 |
try:
|
|
Packit |
3a9065 |
queries.append(func(pkg_spec))
|
|
Packit |
3a9065 |
except dnf.exceptions.PackageNotFoundError as e:
|
|
Packit |
3a9065 |
logger.error(dnf.i18n.ucd(e))
|
|
Packit |
3a9065 |
if self.base.conf.strict:
|
|
Packit |
3a9065 |
logger.error(_("Exiting due to strict setting."))
|
|
Packit |
3a9065 |
raise dnf.exceptions.Error(e)
|
|
Packit |
3a9065 |
|
|
Packit |
3a9065 |
pkgs = list(itertools.chain(*queries))
|
|
Packit |
3a9065 |
return pkgs
|
|
Packit |
3a9065 |
|
|
Packit |
3a9065 |
def _get_packages_with_deps(self, pkg_specs, source=False):
|
|
Packit |
3a9065 |
"""Get packages matching pkg_specs and the deps."""
|
|
Packit |
3a9065 |
pkgs = self._get_packages(pkg_specs)
|
|
Packit |
3a9065 |
pkg_set = set(pkgs)
|
|
Packit |
3a9065 |
for pkg in pkgs:
|
|
Packit |
3a9065 |
goal = hawkey.Goal(self.base.sack)
|
|
Packit |
3a9065 |
goal.install(pkg)
|
|
Packit |
3a9065 |
rc = goal.run()
|
|
Packit |
3a9065 |
if rc:
|
|
Packit |
3a9065 |
pkg_set.update(goal.list_installs())
|
|
Packit |
3a9065 |
pkg_set.update(goal.list_upgrades())
|
|
Packit |
3a9065 |
else:
|
|
Packit |
3a9065 |
msg = [_('Error in resolve of packages:')]
|
|
Packit |
3a9065 |
logger.warning("\n ".join(msg + [str(pkg) for pkg in pkgs]))
|
|
Packit |
3a9065 |
logger.warning(dnf.util._format_resolve_problems(goal.problem_rules()))
|
|
Packit |
3a9065 |
return []
|
|
Packit |
3a9065 |
return pkg_set
|
|
Packit |
3a9065 |
|
|
Packit |
3a9065 |
@staticmethod
|
|
Packit |
3a9065 |
def _get_source_packages(pkgs):
|
|
Packit |
3a9065 |
"""Get list of source rpm names for a list of packages."""
|
|
Packit |
3a9065 |
source_pkgs = set()
|
|
Packit |
3a9065 |
for pkg in pkgs:
|
|
Packit |
3a9065 |
if pkg.sourcerpm:
|
|
Packit |
3a9065 |
source_pkgs.add(pkg.sourcerpm)
|
|
Packit |
3a9065 |
logger.debug(' --> Package : %s Source : %s',
|
|
Packit |
3a9065 |
str(pkg), pkg.sourcerpm)
|
|
Packit |
3a9065 |
elif pkg.arch == 'src':
|
|
Packit |
3a9065 |
source_pkgs.add("%s-%s.src.rpm" % (pkg.name, pkg.evr))
|
|
Packit |
3a9065 |
else:
|
|
Packit |
3a9065 |
logger.info(_("No source rpm defined for %s"), str(pkg))
|
|
Packit |
3a9065 |
return list(source_pkgs)
|
|
Packit |
3a9065 |
|
|
Packit |
3a9065 |
def _get_query(self, pkg_spec):
|
|
Packit |
3a9065 |
"""Return a query to match a pkg_spec."""
|
|
Packit |
3a9065 |
schemes = dnf.pycomp.urlparse.urlparse(pkg_spec)[0]
|
|
Packit |
3a9065 |
is_url = schemes and schemes in ('http', 'ftp', 'file', 'https')
|
|
Packit |
3a9065 |
if is_url or (pkg_spec.endswith('.rpm') and os.path.isfile(pkg_spec)):
|
|
Packit |
3a9065 |
pkgs = self.base.add_remote_rpms([pkg_spec], progress=self.base.output.progress)
|
|
Packit |
3a9065 |
return self.base.sack.query().filterm(pkg=pkgs)
|
|
Packit |
3a9065 |
subj = dnf.subject.Subject(pkg_spec)
|
|
Packit |
3a9065 |
q = subj.get_best_query(self.base.sack, with_src=self.opts.source)
|
|
Packit |
3a9065 |
q = q.available()
|
|
Packit |
3a9065 |
q = q.filterm(latest_per_arch_by_priority=True)
|
|
Packit |
3a9065 |
if self.opts.arches:
|
|
Packit |
3a9065 |
q = q.filter(arch=self.opts.arches)
|
|
Packit |
3a9065 |
if len(q.run()) == 0:
|
|
Packit |
3a9065 |
msg = _("No package %s available.") % (pkg_spec)
|
|
Packit |
3a9065 |
raise dnf.exceptions.PackageNotFoundError(msg)
|
|
Packit |
3a9065 |
return q
|
|
Packit |
3a9065 |
|
|
Packit |
3a9065 |
def _get_query_source(self, pkg_spec):
|
|
Packit |
3a9065 |
"""Return a query to match a source rpm file name."""
|
|
Packit |
3a9065 |
pkg_spec = pkg_spec[:-4] # skip the .rpm
|
|
Packit |
3a9065 |
subj = dnf.subject.Subject(pkg_spec)
|
|
Packit |
3a9065 |
for nevra_obj in subj.get_nevra_possibilities():
|
|
Packit |
3a9065 |
tmp_query = nevra_obj.to_query(self.base.sack).available()
|
|
Packit |
3a9065 |
if tmp_query:
|
|
Packit |
3a9065 |
return tmp_query.latest()
|
|
Packit |
3a9065 |
|
|
Packit |
3a9065 |
msg = _("No package %s available.") % (pkg_spec)
|
|
Packit |
3a9065 |
raise dnf.exceptions.PackageNotFoundError(msg)
|