3492 lines
126 KiB
Python
3492 lines
126 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
|
|
@author: Fabio Erculiani <lxnay@sabayon.org>
|
|
@contact: lxnay@sabayon.org
|
|
@copyright: Fabio Erculiani
|
|
@license: GPL-2
|
|
|
|
B{Entropy Package Manager Server Mirrors Interfaces}.
|
|
|
|
"""
|
|
import os
|
|
import tempfile
|
|
import shutil
|
|
import time
|
|
|
|
from entropy.exceptions import OnlineMirrorError, ConnectionError, \
|
|
EntropyPackageException, TransceiverError
|
|
from entropy.output import red, darkgreen, bold, brown, blue, darkred, \
|
|
darkblue, purple
|
|
from entropy.const import etpConst, const_setup_perms
|
|
from entropy.cache import EntropyCacher
|
|
from entropy.i18n import _
|
|
from entropy.misc import RSS
|
|
from entropy.server.interfaces.rss import ServerRssMetadata
|
|
from entropy.transceivers import EntropyTransceiver
|
|
from entropy.security import Repository as RepositorySecurity
|
|
from entropy.transceivers.uri_handlers.skel import EntropyUriHandler
|
|
from entropy.db.exceptions import Error
|
|
from entropy.core.settings.base import SystemSettings
|
|
|
|
import entropy.tools
|
|
import entropy.dump
|
|
|
|
class ServerNoticeBoardMixin:
|
|
|
|
def download_notice_board(self, repo = None):
|
|
|
|
if repo is None:
|
|
repo = self._entropy.default_repository
|
|
mirrors = self._entropy.get_remote_mirrors(repo)
|
|
rss_path = self._entropy._get_local_database_notice_board_file(repo)
|
|
mytmpdir = tempfile.mkdtemp()
|
|
|
|
self._entropy.output(
|
|
"[repo:%s] %s %s" % (
|
|
brown(repo),
|
|
blue(_("downloading notice board from mirrors to")),
|
|
red(rss_path),
|
|
),
|
|
importance = 1,
|
|
level = "info",
|
|
header = blue(" @@ ")
|
|
)
|
|
|
|
downloaded = False
|
|
for uri in mirrors:
|
|
crippled_uri = EntropyTransceiver.get_uri_name(uri)
|
|
downloader = self.TransceiverServerHandler(
|
|
self._entropy, [uri],
|
|
[rss_path], download = True,
|
|
local_basedir = mytmpdir, critical_files = [rss_path],
|
|
repo = repo
|
|
)
|
|
errors, m_fine_uris, m_broken_uris = downloader.go()
|
|
if not errors:
|
|
self._entropy.output(
|
|
"[repo:%s] %s: %s" % (
|
|
brown(repo),
|
|
blue(_("notice board downloaded successfully from")),
|
|
red(crippled_uri),
|
|
),
|
|
importance = 1,
|
|
level = "info",
|
|
header = blue(" @@ ")
|
|
)
|
|
downloaded = True
|
|
break
|
|
|
|
if downloaded:
|
|
shutil.move(os.path.join(mytmpdir, os.path.basename(rss_path)),
|
|
rss_path)
|
|
|
|
return downloaded
|
|
|
|
def remove_notice_board(self, repo = None):
|
|
|
|
if repo is None:
|
|
repo = self._entropy.default_repository
|
|
mirrors = self._entropy.get_remote_mirrors(repo)
|
|
rss_path = self._entropy._get_local_database_notice_board_file(repo)
|
|
rss_file = os.path.basename(rss_path)
|
|
|
|
self._entropy.output(
|
|
"[repo:%s] %s %s" % (
|
|
brown(repo),
|
|
blue(_("removing notice board from")),
|
|
red(rss_file),
|
|
),
|
|
importance = 1,
|
|
level = "info",
|
|
header = blue(" @@ ")
|
|
)
|
|
|
|
destroyer = self.TransceiverServerHandler(
|
|
self._entropy,
|
|
mirrors,
|
|
[rss_file],
|
|
critical_files = [rss_file],
|
|
remove = True,
|
|
repo = repo
|
|
)
|
|
errors, m_fine_uris, m_broken_uris = destroyer.go()
|
|
if errors:
|
|
m_broken_uris = sorted(m_broken_uris)
|
|
m_broken_uris = [EntropyTransceiver.get_uri_name(x) \
|
|
for x in m_broken_uris]
|
|
self._entropy.output(
|
|
"[repo:%s] %s %s" % (
|
|
brown(repo),
|
|
blue(_("notice board removal failed on")),
|
|
red(', '.join(m_broken_uris)),
|
|
),
|
|
importance = 1,
|
|
level = "info",
|
|
header = blue(" @@ ")
|
|
)
|
|
return False
|
|
self._entropy.output(
|
|
"[repo:%s] %s" % (
|
|
brown(repo),
|
|
blue(_("notice board removal success")),
|
|
),
|
|
importance = 1,
|
|
level = "info",
|
|
header = blue(" @@ ")
|
|
)
|
|
return True
|
|
|
|
|
|
def upload_notice_board(self, repo = None):
|
|
|
|
if repo is None:
|
|
repo = self._entropy.default_repository
|
|
mirrors = self._entropy.get_remote_mirrors(repo)
|
|
rss_path = self._entropy._get_local_database_notice_board_file(repo)
|
|
|
|
self._entropy.output(
|
|
"[repo:%s] %s %s" % (
|
|
brown(repo),
|
|
blue(_("uploading notice board from")),
|
|
red(rss_path),
|
|
),
|
|
importance = 1,
|
|
level = "info",
|
|
header = blue(" @@ ")
|
|
)
|
|
|
|
uploader = self.TransceiverServerHandler(
|
|
self._entropy,
|
|
mirrors,
|
|
[rss_path],
|
|
critical_files = [rss_path],
|
|
repo = repo
|
|
)
|
|
errors, m_fine_uris, m_broken_uris = uploader.go()
|
|
if errors:
|
|
m_broken_uris = sorted(m_broken_uris)
|
|
m_broken_uris = [EntropyTransceiver.get_uri_name(x) \
|
|
for x in m_broken_uris]
|
|
self._entropy.output(
|
|
"[repo:%s] %s %s" % (
|
|
brown(repo),
|
|
blue(_("notice board upload failed on")),
|
|
red(', '.join(m_broken_uris)),
|
|
),
|
|
importance = 1,
|
|
level = "info",
|
|
header = blue(" @@ ")
|
|
)
|
|
return False
|
|
self._entropy.output(
|
|
"[repo:%s] %s" % (
|
|
brown(repo),
|
|
blue(_("notice board upload success")),
|
|
),
|
|
importance = 1,
|
|
level = "info",
|
|
header = blue(" @@ ")
|
|
)
|
|
return True
|
|
|
|
|
|
def update_notice_board(self, title, notice_text, link = None, repo = None):
|
|
|
|
rss_title = "%s Notice Board" % (self._settings['system']['name'],)
|
|
rss_description = "Inform about important distribution activities."
|
|
rss_path = self._entropy._get_local_database_notice_board_file(repo)
|
|
srv_set = self._settings[self.sys_settings_plugin_id]['server']
|
|
if not link:
|
|
link = srv_set['rss']['website_url']
|
|
|
|
self.download_notice_board(repo)
|
|
rss_main = RSS(rss_path, rss_title, rss_description,
|
|
maxentries = 20)
|
|
rss_main.add_item(title, link, description = notice_text)
|
|
rss_main.write_changes()
|
|
dict_list, items = rss_main.get_entries()
|
|
if items == 0:
|
|
status = self.remove_notice_board(repo = repo)
|
|
else:
|
|
status = self.upload_notice_board(repo = repo)
|
|
return status
|
|
|
|
def read_notice_board(self, do_download = True, repo = None):
|
|
|
|
rss_path = self._entropy._get_local_database_notice_board_file(repo)
|
|
if do_download:
|
|
self.download_notice_board(repo)
|
|
if not (os.path.isfile(rss_path) and os.access(rss_path, os.R_OK)):
|
|
return None
|
|
rss_main = RSS(rss_path, '', '')
|
|
return rss_main.get_entries()
|
|
|
|
def remove_from_notice_board(self, identifier, repo = None):
|
|
|
|
rss_path = self._entropy._get_local_database_notice_board_file(repo)
|
|
rss_title = "%s Notice Board" % (self._settings['system']['name'],)
|
|
rss_description = "Inform about important distribution activities."
|
|
if not (os.path.isfile(rss_path) and os.access(rss_path, os.R_OK)):
|
|
return 0
|
|
rss_main = RSS(rss_path, rss_title, rss_description)
|
|
data = rss_main.remove_entry(identifier)
|
|
rss_main.write_changes()
|
|
return data
|
|
|
|
class Server(ServerNoticeBoardMixin):
|
|
|
|
import socket
|
|
def __init__(self, server, repo = None):
|
|
|
|
from entropy.server.transceivers import TransceiverServerHandler
|
|
from entropy.server.interfaces.main import Server as MainServer
|
|
|
|
if not isinstance(server, MainServer):
|
|
raise AttributeError("entropy.server.interfaces.main.Server needed")
|
|
|
|
self._entropy = server
|
|
self.TransceiverServerHandler = TransceiverServerHandler
|
|
self.Cacher = EntropyCacher()
|
|
self.sys_settings_plugin_id = \
|
|
etpConst['system_settings_plugins_ids']['server_plugin']
|
|
self._settings = SystemSettings()
|
|
|
|
mytxt = blue("%s:") % (_("Entropy Server Mirrors Interface loaded"),)
|
|
self._entropy.output(
|
|
mytxt,
|
|
importance = 2,
|
|
level = "info",
|
|
header = red(" @@ ")
|
|
)
|
|
mytxt = _("mirror")
|
|
for mirror in self._entropy.get_remote_mirrors(repo = repo):
|
|
mirror = EntropyTransceiver.hide_sensible_data(mirror)
|
|
self._entropy.output(
|
|
blue("%s: %s") % (mytxt, darkgreen(mirror),),
|
|
importance = 0,
|
|
level = "info",
|
|
header = brown(" # ")
|
|
)
|
|
|
|
def _read_remote_file_in_branches(self, filename, repo = None,
|
|
excluded_branches = None):
|
|
"""
|
|
Reads a file remotely located in all the available branches.
|
|
|
|
@param filename: name of the file that should be located inside
|
|
repository database directory
|
|
@type filename: string
|
|
@keyword repo: repository identifier
|
|
@type repo: string
|
|
@keyword excluded_branches: list of branch identifiers excluded or None
|
|
@type excluded_branches: list or None
|
|
@return: dictionary with branches as key and raw file content as value:
|
|
{'4': 'abcd\n', '5': 'defg\n'}
|
|
@rtype: dict
|
|
"""
|
|
if repo is None:
|
|
repo = self._entropy.default_repository
|
|
if excluded_branches is None:
|
|
excluded_branches = []
|
|
|
|
branch_data = {}
|
|
mirrors = self._entropy.get_remote_mirrors(repo)
|
|
for uri in mirrors:
|
|
|
|
crippled_uri = EntropyTransceiver.get_uri_name(uri)
|
|
|
|
self._entropy.output(
|
|
"[repo:%s] %s: %s => %s" % (
|
|
brown(repo),
|
|
blue(_("looking for file in mirror")),
|
|
darkgreen(crippled_uri),
|
|
filename,
|
|
),
|
|
importance = 1,
|
|
level = "info",
|
|
header = brown(" @@ ")
|
|
)
|
|
|
|
branches_path = self._entropy._get_remote_database_relative_path(
|
|
repo)
|
|
txc = self._entropy.Transceiver(uri)
|
|
txc.set_verbosity(False)
|
|
|
|
with txc as handler:
|
|
|
|
branches = handler.list_content(branches_path)
|
|
for branch in branches:
|
|
|
|
# is branch excluded ?
|
|
if branch in excluded_branches:
|
|
continue
|
|
|
|
if branch_data.get(branch) != None:
|
|
# already read
|
|
continue
|
|
|
|
mypath = os.path.join("/", branches_path, branch, filename)
|
|
if not handler.is_file(mypath):
|
|
# nothing to do, not a file
|
|
continue
|
|
|
|
tmp_dir = tempfile.mkdtemp()
|
|
down_path = os.path.join(tmp_dir,
|
|
os.path.basename(filename))
|
|
tries = 4
|
|
success = False
|
|
while tries:
|
|
downloaded = handler.download(mypath, down_path)
|
|
if not downloaded:
|
|
tries -= 1
|
|
continue # argh!
|
|
success = True
|
|
break
|
|
|
|
if success and os.path.isfile(down_path):
|
|
down_f = open(down_path)
|
|
branch_data[branch] = down_f.read()
|
|
down_f.close()
|
|
|
|
shutil.rmtree(tmp_dir, True)
|
|
|
|
return branch_data
|
|
|
|
def lock_mirrors(self, lock = True, mirrors = None, repo = None):
|
|
|
|
if repo is None:
|
|
repo = self._entropy.default_repository
|
|
|
|
if not mirrors:
|
|
mirrors = self._entropy.get_remote_mirrors(repo)
|
|
|
|
issues = False
|
|
for uri in mirrors:
|
|
|
|
crippled_uri = EntropyTransceiver.get_uri_name(uri)
|
|
|
|
lock_text = _("unlocking")
|
|
if lock:
|
|
lock_text = _("locking")
|
|
self._entropy.output(
|
|
"[repo:%s|%s] %s %s" % (
|
|
brown(repo),
|
|
darkgreen(crippled_uri),
|
|
bold(lock_text),
|
|
blue("%s...") % (_("mirror"),),
|
|
),
|
|
importance = 1,
|
|
level = "info",
|
|
header = brown(" * "),
|
|
back = True
|
|
)
|
|
|
|
base_path = os.path.join(
|
|
self._entropy._get_remote_database_relative_path(repo),
|
|
self._settings['repositories']['branch'])
|
|
lock_file = os.path.join(base_path,
|
|
etpConst['etpdatabaselockfile'])
|
|
|
|
txc = self._entropy.Transceiver(uri)
|
|
txc.set_verbosity(False)
|
|
|
|
with txc as handler:
|
|
|
|
if lock and handler.is_file(lock_file):
|
|
self._entropy.output(
|
|
"[repo:%s|%s] %s" % (
|
|
brown(repo),
|
|
darkgreen(crippled_uri),
|
|
blue(_("mirror already locked")),
|
|
),
|
|
importance = 1,
|
|
level = "info",
|
|
header = darkgreen(" * ")
|
|
)
|
|
continue
|
|
|
|
elif not lock and not handler.is_file(lock_file):
|
|
self._entropy.output(
|
|
"[repo:%s|%s] %s" % (
|
|
brown(repo),
|
|
darkgreen(crippled_uri),
|
|
blue(_("mirror already unlocked")),
|
|
),
|
|
importance = 1,
|
|
level = "info",
|
|
header = darkgreen(" * ")
|
|
)
|
|
continue
|
|
|
|
if lock:
|
|
rc_lock = self._do_mirror_lock(uri, handler, repo = repo)
|
|
else:
|
|
rc_lock = self._do_mirror_unlock(uri, handler, repo = repo)
|
|
|
|
if not rc_lock:
|
|
issues = True
|
|
|
|
if not issues:
|
|
db_taint_file = self._entropy._get_local_database_taint_file(repo)
|
|
if os.path.isfile(db_taint_file):
|
|
os.remove(db_taint_file)
|
|
|
|
return issues
|
|
|
|
|
|
def lock_mirrors_for_download(self, lock = True, mirrors = None,
|
|
repo = None):
|
|
"""
|
|
This functions makes entropy clients to not download anything
|
|
from the chosen mirrors. it is used to avoid clients to
|
|
download databases while we're uploading a new one
|
|
"""
|
|
|
|
if mirrors is None:
|
|
mirrors = []
|
|
|
|
if repo is None:
|
|
repo = self._entropy.default_repository
|
|
|
|
if not mirrors:
|
|
mirrors = self._entropy.get_remote_mirrors(repo)
|
|
|
|
issues = False
|
|
for uri in mirrors:
|
|
|
|
crippled_uri = EntropyTransceiver.get_uri_name(uri)
|
|
|
|
lock_text = _("unlocking")
|
|
if lock:
|
|
lock_text = _("locking")
|
|
self._entropy.output(
|
|
"[repo:%s|%s] %s %s..." % (
|
|
blue(repo),
|
|
red(crippled_uri),
|
|
bold(lock_text),
|
|
blue(_("mirror for download")),
|
|
),
|
|
importance = 1,
|
|
level = "info",
|
|
header = red(" @@ "),
|
|
back = True
|
|
)
|
|
|
|
lock_file = etpConst['etpdatabasedownloadlockfile']
|
|
my_path = os.path.join(
|
|
self._entropy._get_remote_database_relative_path(repo),
|
|
self._settings['repositories']['branch'])
|
|
lock_file = os.path.join(my_path, lock_file)
|
|
|
|
txc = self._entropy.Transceiver(uri)
|
|
txc.set_verbosity(False)
|
|
|
|
with txc as handler:
|
|
|
|
if lock and handler.is_file(lock_file):
|
|
self._entropy.output(
|
|
"[repo:%s|%s] %s" % (
|
|
blue(repo),
|
|
red(crippled_uri),
|
|
blue(_("mirror already locked for download")),
|
|
),
|
|
importance = 1,
|
|
level = "info",
|
|
header = red(" @@ ")
|
|
)
|
|
continue
|
|
|
|
elif not lock and not handler.is_file(lock_file):
|
|
self._entropy.output(
|
|
"[repo:%s|%s] %s" % (
|
|
blue(repo),
|
|
red(crippled_uri),
|
|
blue(_("mirror already unlocked for download")),
|
|
),
|
|
importance = 1,
|
|
level = "info",
|
|
header = red(" @@ ")
|
|
)
|
|
continue
|
|
|
|
if lock:
|
|
rc_lock = self._do_mirror_lock(uri, handler, dblock = False,
|
|
repo = repo)
|
|
else:
|
|
rc_lock = self._do_mirror_unlock(uri, handler,
|
|
dblock = False, repo = repo)
|
|
if not rc_lock:
|
|
issues = True
|
|
|
|
return issues
|
|
|
|
def _do_mirror_lock(self, uri, txc_handler, dblock = True, repo = None):
|
|
|
|
if repo is None:
|
|
repo = self._entropy.default_repository
|
|
|
|
my_path = os.path.join(
|
|
self._entropy._get_remote_database_relative_path(repo),
|
|
self._settings['repositories']['branch'])
|
|
|
|
# create path to lock file if it doesn't exist
|
|
if not txc_handler.is_dir(my_path):
|
|
txc_handler.makedirs(my_path)
|
|
|
|
crippled_uri = EntropyTransceiver.get_uri_name(uri)
|
|
lock_string = ''
|
|
|
|
if dblock:
|
|
self._entropy._create_local_database_lockfile(repo)
|
|
lock_file = self._entropy._get_database_lockfile(repo)
|
|
else:
|
|
# locking/unlocking mirror1 for download
|
|
lock_string = _('for download')
|
|
self._entropy._create_local_database_download_lockfile(repo)
|
|
lock_file = self._entropy._get_database_download_lockfile(repo)
|
|
|
|
remote_path = os.path.join(my_path, os.path.basename(lock_file))
|
|
|
|
rc_upload = txc_handler.upload(lock_file, remote_path)
|
|
if rc_upload:
|
|
self._entropy.output(
|
|
"[repo:%s|%s] %s %s" % (
|
|
blue(repo),
|
|
red(crippled_uri),
|
|
blue(_("mirror successfully locked")),
|
|
blue(lock_string),
|
|
),
|
|
importance = 1,
|
|
level = "info",
|
|
header = red(" @@ ")
|
|
)
|
|
else:
|
|
self._entropy.output(
|
|
"[repo:%s|%s] %s: %s - %s %s" % (
|
|
blue(repo),
|
|
red(crippled_uri),
|
|
blue("lock error"),
|
|
rc_upload,
|
|
blue(_("mirror not locked")),
|
|
blue(lock_string),
|
|
),
|
|
importance = 1,
|
|
level = "error",
|
|
header = darkred(" * ")
|
|
)
|
|
self._entropy._remove_local_database_lockfile(repo)
|
|
|
|
return rc_upload
|
|
|
|
|
|
def _do_mirror_unlock(self, uri, txc_handler, dblock = True, repo = None):
|
|
|
|
if repo is None:
|
|
repo = self._entropy.default_repository
|
|
|
|
my_path = os.path.join(
|
|
self._entropy._get_remote_database_relative_path(repo),
|
|
self._settings['repositories']['branch'])
|
|
|
|
crippled_uri = EntropyTransceiver.get_uri_name(uri)
|
|
|
|
if dblock:
|
|
dbfile = etpConst['etpdatabaselockfile']
|
|
else:
|
|
dbfile = etpConst['etpdatabasedownloadlockfile']
|
|
|
|
# make sure
|
|
remote_path = os.path.join(my_path, os.path.basename(dbfile))
|
|
|
|
rc_delete = txc_handler.delete(remote_path)
|
|
if rc_delete:
|
|
self._entropy.output(
|
|
"[repo:%s|%s] %s" % (
|
|
blue(repo),
|
|
red(crippled_uri),
|
|
blue(_("mirror successfully unlocked")),
|
|
),
|
|
importance = 1,
|
|
level = "info",
|
|
header = darkgreen(" * ")
|
|
)
|
|
if dblock:
|
|
self._entropy._remove_local_database_lockfile(repo)
|
|
else:
|
|
self._entropy._remove_local_database_download_lockfile(repo)
|
|
else:
|
|
self._entropy.output(
|
|
"[repo:%s|%s] %s: %s - %s" % (
|
|
blue(repo),
|
|
red(crippled_uri),
|
|
blue(_("unlock error")),
|
|
rc_delete,
|
|
blue(_("mirror not unlocked")),
|
|
),
|
|
importance = 1,
|
|
level = "error",
|
|
header = darkred(" * ")
|
|
)
|
|
|
|
return rc_delete
|
|
|
|
def download_package(self, uri, pkg_relative_path, repo = None):
|
|
|
|
if repo is None:
|
|
repo = self._entropy.default_repository
|
|
|
|
pkgfile = os.path.basename(pkg_relative_path)
|
|
crippled_uri = EntropyTransceiver.get_uri_name(uri)
|
|
|
|
tries = 0
|
|
while tries < 5:
|
|
|
|
tries += 1
|
|
txc = self._entropy.Transceiver(uri)
|
|
with txc as handler:
|
|
|
|
self._entropy.output(
|
|
"[repo:%s|%s|#%s] %s: %s" % (
|
|
brown(repo),
|
|
darkgreen(crippled_uri),
|
|
brown(str(tries)),
|
|
blue(_("connecting to download package")),
|
|
darkgreen(pkg_relative_path),
|
|
),
|
|
importance = 1,
|
|
level = "info",
|
|
header = darkgreen(" * "),
|
|
back = True
|
|
)
|
|
|
|
remote_path = \
|
|
self._entropy.complete_remote_package_relative_path(
|
|
pkg_relative_path, repo = repo)
|
|
download_path = self._entropy.complete_local_package_path(
|
|
pkg_relative_path, repo = repo)
|
|
|
|
download_dir = os.path.dirname(download_path)
|
|
|
|
self._entropy.output(
|
|
"[repo:%s|%s|#%s] %s: %s" % (
|
|
brown(repo),
|
|
darkgreen(crippled_uri),
|
|
brown(str(tries)),
|
|
blue(_("downloading package")),
|
|
darkgreen(remote_path),
|
|
),
|
|
importance = 1,
|
|
level = "info",
|
|
header = darkgreen(" * ")
|
|
)
|
|
|
|
if (not os.path.isdir(download_dir)) and \
|
|
(not os.access(download_dir, os.R_OK)):
|
|
self._entropy._ensure_dir_path(download_dir)
|
|
|
|
rc_download = handler.download(remote_path, download_path)
|
|
if not rc_download:
|
|
self._entropy.output(
|
|
"[repo:%s|%s|#%s] %s: %s %s" % (
|
|
brown(repo),
|
|
darkgreen(crippled_uri),
|
|
brown(str(tries)),
|
|
blue(_("package")),
|
|
darkgreen(pkg_relative_path),
|
|
blue(_("does not exist")),
|
|
),
|
|
importance = 1,
|
|
level = "error",
|
|
header = darkred(" !!! ")
|
|
)
|
|
return rc_download
|
|
|
|
dbconn = self._entropy.open_server_repository(read_only = True,
|
|
no_upload = True, repo = repo)
|
|
idpackage = dbconn.getIdpackageFromDownload(pkg_relative_path)
|
|
if idpackage == -1:
|
|
self._entropy.output(
|
|
"[repo:%s|%s|#%s] %s: %s %s" % (
|
|
brown(repo),
|
|
darkgreen(crippled_uri),
|
|
brown(str(tries)),
|
|
blue(_("package")),
|
|
darkgreen(pkg_relative_path),
|
|
blue(_("is not listed in the repository !")),
|
|
),
|
|
importance = 1,
|
|
level = "error",
|
|
header = darkred(" !!! ")
|
|
)
|
|
return False
|
|
|
|
storedmd5 = dbconn.retrieveDigest(idpackage)
|
|
self._entropy.output(
|
|
"[repo:%s|%s|#%s] %s: %s" % (
|
|
brown(repo),
|
|
darkgreen(crippled_uri),
|
|
brown(str(tries)),
|
|
blue(_("verifying checksum of package")),
|
|
darkgreen(pkg_relative_path),
|
|
),
|
|
importance = 1,
|
|
level = "info",
|
|
header = darkgreen(" * "),
|
|
back = True
|
|
)
|
|
|
|
md5check = entropy.tools.compare_md5(download_path, storedmd5)
|
|
if md5check:
|
|
self._entropy.output(
|
|
"[repo:%s|%s|#%s] %s: %s %s" % (
|
|
brown(repo),
|
|
darkgreen(crippled_uri),
|
|
brown(str(tries)),
|
|
blue(_("package")),
|
|
darkgreen(pkg_relative_path),
|
|
blue(_("downloaded successfully")),
|
|
),
|
|
importance = 1,
|
|
level = "info",
|
|
header = darkgreen(" * ")
|
|
)
|
|
return True
|
|
else:
|
|
self._entropy.output(
|
|
"[repo:%s|%s|#%s] %s: %s %s" % (
|
|
brown(repo),
|
|
darkgreen(crippled_uri),
|
|
brown(str(tries)),
|
|
blue(_("package")),
|
|
darkgreen(pkg_relative_path),
|
|
blue(_("checksum does not match. re-downloading...")),
|
|
),
|
|
importance = 1,
|
|
level = "warning",
|
|
header = darkred(" * ")
|
|
)
|
|
if os.path.isfile(download_path):
|
|
os.remove(download_path)
|
|
|
|
# if we get here it means the files hasn't been downloaded properly
|
|
self._entropy.output(
|
|
"[repo:%s|%s|#%s] %s: %s %s" % (
|
|
brown(repo),
|
|
darkgreen(crippled_uri),
|
|
brown(str(tries)),
|
|
blue(_("package")),
|
|
darkgreen(pkg_relative_path),
|
|
blue(_("seems broken. Consider to re-package it. Giving up!")),
|
|
),
|
|
importance = 1,
|
|
level = "error",
|
|
header = darkred(" !!! ")
|
|
)
|
|
return False
|
|
|
|
def _get_remote_db_status(self, uri, repo):
|
|
|
|
sys_set = self._settings[self.sys_settings_plugin_id]['server']
|
|
db_format = sys_set['database_file_format']
|
|
cmethod = etpConst['etpdatabasecompressclasses'].get(db_format)
|
|
if cmethod is None:
|
|
raise AttributeError("Wrong database compression method passed")
|
|
remote_dir = os.path.join(
|
|
self._entropy._get_remote_database_relative_path(repo),
|
|
self._settings['repositories']['branch'])
|
|
|
|
# let raise exception if connection is impossible
|
|
txc = self._entropy.Transceiver(uri)
|
|
with txc as handler:
|
|
|
|
compressedfile = etpConst[cmethod[2]]
|
|
rc1 = handler.is_file(os.path.join(remote_dir, compressedfile))
|
|
|
|
rev_file = self._entropy._get_local_database_revision_file(repo)
|
|
revfilename = os.path.basename(rev_file)
|
|
rc2 = handler.is_file(os.path.join(remote_dir, revfilename))
|
|
|
|
revision = 0
|
|
if not (rc1 and rc2):
|
|
return [uri, revision]
|
|
|
|
tmp_fd, rev_tmp_path = tempfile.mkstemp()
|
|
|
|
dlcount = 5
|
|
dled = False
|
|
while dlcount:
|
|
remote_rev_path = os.path.join(remote_dir, revfilename)
|
|
dled = handler.download(remote_rev_path, rev_tmp_path)
|
|
if dled:
|
|
break
|
|
dlcount -= 1
|
|
os.close(tmp_fd)
|
|
|
|
crippled_uri = EntropyTransceiver.get_uri_name(uri)
|
|
|
|
if os.access(rev_tmp_path, os.R_OK) and \
|
|
os.path.isfile(rev_tmp_path):
|
|
|
|
f_rev = open(rev_tmp_path, "r")
|
|
try:
|
|
revision = int(f_rev.readline().strip())
|
|
except ValueError:
|
|
mytxt = _("mirror hasn't valid database revision file")
|
|
self._entropy.output(
|
|
"[repo:%s|%s] %s: %s" % (
|
|
brown(repo),
|
|
darkgreen(crippled_uri),
|
|
blue(mytxt),
|
|
bold(revision),
|
|
),
|
|
importance = 1,
|
|
level = "error",
|
|
header = darkred(" !!! ")
|
|
)
|
|
revision = 0
|
|
f_rev.close()
|
|
|
|
elif dlcount == 0:
|
|
self._entropy.output(
|
|
"[repo:%s|%s] %s: %s" % (
|
|
brown(repo),
|
|
darkgreen(crippled_uri),
|
|
blue(_("unable to download repository revision")),
|
|
bold(revision),
|
|
),
|
|
importance = 1,
|
|
level = "error",
|
|
header = darkred(" !!! ")
|
|
)
|
|
revision = 0
|
|
|
|
else:
|
|
self._entropy.output(
|
|
"[repo:%s|%s] %s: %s" % (
|
|
brown(repo),
|
|
darkgreen(crippled_uri),
|
|
blue(_("mirror doesn't have valid revision file")),
|
|
bold(revision),
|
|
),
|
|
importance = 1,
|
|
level = "error",
|
|
header = darkred(" !!! ")
|
|
)
|
|
revision = 0
|
|
|
|
os.remove(rev_tmp_path)
|
|
return [uri, revision]
|
|
|
|
def get_remote_repositories_status(self, repo = None, mirrors = None):
|
|
|
|
if repo is None:
|
|
repo = self._entropy.default_repository
|
|
if not mirrors:
|
|
mirrors = self._entropy.get_remote_mirrors(repo)
|
|
|
|
data = []
|
|
for uri in mirrors:
|
|
data.append(self._get_remote_db_status(uri, repo))
|
|
|
|
return data
|
|
|
|
def _is_local_repository_locked(self, repo = None):
|
|
local_repo = repo
|
|
if local_repo is None:
|
|
local_repo = self._entropy.default_repository
|
|
lock_file = self._entropy._get_database_lockfile(local_repo)
|
|
return os.path.isfile(lock_file)
|
|
|
|
def _get_mirrors_lock(self, repo = None):
|
|
|
|
dbstatus = []
|
|
remote_dir = os.path.join(
|
|
self._entropy._get_remote_database_relative_path(repo),
|
|
self._settings['repositories']['branch'])
|
|
lock_file = os.path.join(remote_dir, etpConst['etpdatabaselockfile'])
|
|
down_lock_file = os.path.join(remote_dir,
|
|
etpConst['etpdatabasedownloadlockfile'])
|
|
|
|
for uri in self._entropy.get_remote_mirrors(repo):
|
|
data = [uri, False, False]
|
|
|
|
# let raise exception if connection is impossible
|
|
txc = self._entropy.Transceiver(uri)
|
|
with txc as handler:
|
|
if handler.is_file(lock_file):
|
|
# upload locked
|
|
data[1] = True
|
|
if handler.is_file(down_lock_file):
|
|
# download locked
|
|
data[2] = True
|
|
dbstatus.append(data)
|
|
|
|
return dbstatus
|
|
|
|
def _update_rss_feed(self, repo = None):
|
|
|
|
if repo is None:
|
|
repo = self._entropy.default_repository
|
|
|
|
product = self._settings['repositories']['product']
|
|
#db_dir = self._entropy._get_local_database_dir(repo)
|
|
rss_path = self._entropy._get_local_database_rss_file(repo)
|
|
rss_light_path = self._entropy._get_local_database_rsslight_file(repo)
|
|
rss_dump_name = repo + etpConst['rss-dump-name']
|
|
db_revision_path = self._entropy._get_local_database_revision_file(repo)
|
|
|
|
rss_title = "%s Online Repository Status" % (
|
|
self._settings['system']['name'],)
|
|
rss_description = \
|
|
"Keep you updated on what's going on in the %s Repository." % (
|
|
self._settings['system']['name'],)
|
|
|
|
srv_set = self._settings[self.sys_settings_plugin_id]['server']
|
|
|
|
rss_main = RSS(rss_path, rss_title, rss_description,
|
|
maxentries = srv_set['rss']['max_entries'])
|
|
# load dump
|
|
db_actions = self.Cacher.pop(rss_dump_name,
|
|
cache_dir = self._entropy.CACHE_DIR)
|
|
if db_actions:
|
|
try:
|
|
f_rev = open(db_revision_path)
|
|
revision = f_rev.readline().strip()
|
|
f_rev.close()
|
|
except (IOError, OSError):
|
|
revision = "N/A"
|
|
commitmessage = ''
|
|
if ServerRssMetadata()['commitmessage']:
|
|
commitmessage = ' :: ' + \
|
|
ServerRssMetadata()['commitmessage']
|
|
|
|
title = ": " + self._settings['system']['name'] + " " + \
|
|
product[0].upper() + product[1:] + " " + \
|
|
self._settings['repositories']['branch'] + \
|
|
" :: Revision: " + revision + commitmessage
|
|
|
|
link = srv_set['rss']['base_url']
|
|
# create description
|
|
added_items = db_actions.get("added")
|
|
|
|
if added_items:
|
|
for atom in sorted(added_items):
|
|
mylink = link + "?search=" + atom.split("~")[0] + \
|
|
"&arch=" + etpConst['currentarch'] + "&product="+product
|
|
description = atom + ": " + added_items[atom]['description']
|
|
rss_main.add_item(title = "Added/Updated" + title,
|
|
link = mylink, description = description)
|
|
removed_items = db_actions.get("removed")
|
|
|
|
if removed_items:
|
|
for atom in sorted(removed_items):
|
|
description = atom + ": " + \
|
|
removed_items[atom]['description']
|
|
rss_main.add_item(title = "Removed" + title, link = link,
|
|
description = description)
|
|
|
|
light_items = db_actions.get('light')
|
|
if light_items:
|
|
rss_light = RSS(rss_light_path, rss_title, rss_description,
|
|
maxentries = srv_set['rss']['light_max_entries'])
|
|
for atom in sorted(light_items):
|
|
mylink = link + "?search=" + atom.split("~")[0] + \
|
|
"&arch=" + etpConst['currentarch'] + "&product=" + \
|
|
product
|
|
description = light_items[atom]['description']
|
|
rss_light.add_item(title = "[" + revision + "] " + atom,
|
|
link = mylink, description = description)
|
|
rss_light.write_changes()
|
|
|
|
rss_main.write_changes()
|
|
ServerRssMetadata().clear()
|
|
EntropyCacher.clear_cache_item(rss_dump_name,
|
|
cache_dir = self._entropy.CACHE_DIR)
|
|
|
|
|
|
def _dump_database_to_file(self, db_path, destination_path, opener,
|
|
exclude_tables = None, repo = None):
|
|
|
|
if not exclude_tables:
|
|
exclude_tables = []
|
|
|
|
f_out = opener(destination_path, "wb")
|
|
dbconn = self._entropy.open_server_repository(db_path,
|
|
just_reading = True, repo = repo, do_treeupdates = False)
|
|
dbconn.doDatabaseExport(f_out, exclude_tables = exclude_tables)
|
|
self._entropy.close_repository(dbconn)
|
|
f_out.close()
|
|
|
|
def _create_file_checksum(self, file_path, checksum_path):
|
|
mydigest = entropy.tools.md5sum(file_path)
|
|
f_ck = open(checksum_path, "w")
|
|
mystring = "%s %s\n" % (mydigest, os.path.basename(file_path),)
|
|
f_ck.write(mystring)
|
|
f_ck.flush()
|
|
f_ck.close()
|
|
|
|
def _compress_file(self, file_path, destination_path, opener):
|
|
f_out = opener(destination_path, "wb")
|
|
f_in = open(file_path, "rb")
|
|
data = f_in.read(8192)
|
|
while data:
|
|
f_out.write(data)
|
|
data = f_in.read(8192)
|
|
f_in.close()
|
|
if hasattr(f_out, 'flush'):
|
|
f_out.flush()
|
|
f_out.close()
|
|
|
|
def _get_files_to_sync(self, cmethod, download = False, repo = None,
|
|
disabled_eapis = None):
|
|
|
|
if repo is None:
|
|
repo = self._entropy.default_repository
|
|
|
|
if disabled_eapis is None:
|
|
disabled_eapis = []
|
|
|
|
critical = []
|
|
extra_text_files = []
|
|
gpg_signed_files = []
|
|
data = {}
|
|
db_rev_file = self._entropy._get_local_database_revision_file(repo)
|
|
# adding ~ at the beginning makes this file to be appended at the end
|
|
# of the upload queue
|
|
data['~database_revision_file'] = db_rev_file
|
|
extra_text_files.append(db_rev_file)
|
|
critical.append(db_rev_file)
|
|
|
|
# branch migration support scripts
|
|
post_branch_mig_file = self._entropy._get_local_post_branch_mig_script(
|
|
repo)
|
|
if os.path.isfile(post_branch_mig_file) or download:
|
|
if download:
|
|
data['database_post_branch_hop_script'] = post_branch_mig_file
|
|
extra_text_files.append(post_branch_mig_file)
|
|
|
|
post_branch_upg_file = self._entropy._get_local_post_branch_upg_script(
|
|
repo)
|
|
if os.path.isfile(post_branch_upg_file) or download:
|
|
if download:
|
|
data['database_post_branch_upgrade_script'] = \
|
|
post_branch_upg_file
|
|
extra_text_files.append(post_branch_upg_file)
|
|
|
|
post_repo_update_file = self._entropy._get_local_post_repo_update_script(
|
|
repo)
|
|
if os.path.isfile(post_repo_update_file) or download:
|
|
if download:
|
|
data['database_post_repo_update_script'] = post_repo_update_file
|
|
extra_text_files.append(post_repo_update_file)
|
|
|
|
database_ts_file = self._entropy._get_local_database_timestamp_file(repo)
|
|
if os.path.isfile(database_ts_file) or download:
|
|
data['database_timestamp_file'] = database_ts_file
|
|
if not download:
|
|
critical.append(database_ts_file)
|
|
|
|
database_package_mask_file = \
|
|
self._entropy._get_local_database_mask_file(repo)
|
|
if os.path.isfile(database_package_mask_file) or download:
|
|
if download:
|
|
data['database_package_mask_file'] = database_package_mask_file
|
|
extra_text_files.append(database_package_mask_file)
|
|
|
|
database_package_system_mask_file = \
|
|
self._entropy._get_local_database_system_mask_file(repo)
|
|
if os.path.isfile(database_package_system_mask_file) or download:
|
|
if download:
|
|
data['database_package_system_mask_file'] = \
|
|
database_package_system_mask_file
|
|
extra_text_files.append(database_package_system_mask_file)
|
|
|
|
database_package_confl_tagged_file = \
|
|
self._entropy._get_local_database_confl_tagged_file(repo)
|
|
if os.path.isfile(database_package_confl_tagged_file) or download:
|
|
if download:
|
|
data['database_package_confl_tagged_file'] = \
|
|
database_package_confl_tagged_file
|
|
extra_text_files.append(database_package_confl_tagged_file)
|
|
|
|
database_license_whitelist_file = \
|
|
self._entropy._get_local_database_licensewhitelist_file(repo)
|
|
if os.path.isfile(database_license_whitelist_file) or download:
|
|
if download:
|
|
data['database_license_whitelist_file'] = \
|
|
database_license_whitelist_file
|
|
extra_text_files.append(database_license_whitelist_file)
|
|
|
|
exp_based_pkgs_removal_file = \
|
|
self._entropy._get_local_exp_based_pkgs_rm_whitelist_file(repo)
|
|
if os.path.isfile(exp_based_pkgs_removal_file) or download:
|
|
if download:
|
|
data['exp_based_pkgs_removal_file'] = \
|
|
exp_based_pkgs_removal_file
|
|
extra_text_files.append(exp_based_pkgs_removal_file)
|
|
|
|
database_rss_file = self._entropy._get_local_database_rss_file(repo)
|
|
if os.path.isfile(database_rss_file) or download:
|
|
data['database_rss_file'] = database_rss_file
|
|
if not download:
|
|
critical.append(data['database_rss_file'])
|
|
database_rss_light_file = \
|
|
self._entropy._get_local_database_rsslight_file(repo)
|
|
|
|
if os.path.isfile(database_rss_light_file) or download:
|
|
data['database_rss_light_file'] = database_rss_light_file
|
|
if not download:
|
|
critical.append(data['database_rss_light_file'])
|
|
|
|
pkglist_file = self._entropy._get_local_pkglist_file(repo)
|
|
data['pkglist_file'] = pkglist_file
|
|
if not download:
|
|
critical.append(data['pkglist_file'])
|
|
|
|
critical_updates_file = self._entropy._get_local_critical_updates_file(
|
|
repo)
|
|
if os.path.isfile(critical_updates_file) or download:
|
|
if download:
|
|
data['critical_updates_file'] = critical_updates_file
|
|
extra_text_files.append(critical_updates_file)
|
|
|
|
restricted_file = self._entropy._get_local_restricted_file(repo)
|
|
if os.path.isfile(restricted_file) or download:
|
|
if download:
|
|
data['restricted_file'] = restricted_file
|
|
extra_text_files.append(restricted_file)
|
|
|
|
keywords_file = self._entropy._get_local_database_keywords_file(
|
|
repo)
|
|
if os.path.isfile(keywords_file) or download:
|
|
if download:
|
|
data['keywords_file'] = keywords_file
|
|
extra_text_files.append(keywords_file)
|
|
|
|
gpg_file = self._entropy._get_local_database_gpg_signature_file(repo)
|
|
if os.path.isfile(gpg_file) or download:
|
|
data['gpg_file'] = gpg_file
|
|
# no need to add to extra_text_files, it will be added
|
|
# afterwards
|
|
gpg_signed_files.append(gpg_file)
|
|
|
|
# EAPI 2,3
|
|
if not download: # we don't need to get the dump
|
|
|
|
# always push metafiles file, it's cheap
|
|
data['metafiles_path'] = \
|
|
self._entropy._get_local_database_compressed_metafiles_file(repo)
|
|
critical.append(data['metafiles_path'])
|
|
gpg_signed_files.append(data['metafiles_path'])
|
|
|
|
if 2 not in disabled_eapis:
|
|
|
|
data['dump_path_light'] = os.path.join(
|
|
self._entropy._get_local_database_dir(repo),
|
|
etpConst[cmethod[5]])
|
|
critical.append(data['dump_path_light'])
|
|
gpg_signed_files.append(data['dump_path_light'])
|
|
|
|
data['dump_path_digest_light'] = os.path.join(
|
|
self._entropy._get_local_database_dir(repo),
|
|
etpConst[cmethod[6]])
|
|
critical.append(data['dump_path_digest_light'])
|
|
gpg_signed_files.append(data['dump_path_digest_light'])
|
|
|
|
# EAPI 1
|
|
if 1 not in disabled_eapis:
|
|
|
|
data['compressed_database_path'] = os.path.join(
|
|
self._entropy._get_local_database_dir(repo), etpConst[cmethod[2]])
|
|
critical.append(data['compressed_database_path'])
|
|
gpg_signed_files.append(data['compressed_database_path'])
|
|
|
|
data['compressed_database_path_light'] = os.path.join(
|
|
self._entropy._get_local_database_dir(repo), etpConst[cmethod[7]])
|
|
critical.append(data['compressed_database_path_light'])
|
|
gpg_signed_files.append(data['compressed_database_path_light'])
|
|
|
|
data['database_path_digest'] = os.path.join(
|
|
self._entropy._get_local_database_dir(repo),
|
|
etpConst['etpdatabasehashfile']
|
|
)
|
|
critical.append(data['database_path_digest'])
|
|
gpg_signed_files.append(data['database_path_digest'])
|
|
|
|
data['compressed_database_path_digest'] = os.path.join(
|
|
self._entropy._get_local_database_dir(repo),
|
|
etpConst[cmethod[2]] + etpConst['packagesmd5fileext']
|
|
)
|
|
critical.append(data['compressed_database_path_digest'])
|
|
gpg_signed_files.append(data['compressed_database_path_digest'])
|
|
|
|
data['compressed_database_path_digest_light'] = os.path.join(
|
|
self._entropy._get_local_database_dir(repo),
|
|
etpConst[cmethod[8]]
|
|
)
|
|
critical.append(data['compressed_database_path_digest_light'])
|
|
gpg_signed_files.append(
|
|
data['compressed_database_path_digest_light'])
|
|
|
|
|
|
# SSL cert file, just for reference
|
|
ssl_ca_cert = self._entropy._get_local_database_ca_cert_file()
|
|
if os.path.isfile(ssl_ca_cert):
|
|
if download:
|
|
data['ssl_ca_cert_file'] = ssl_ca_cert
|
|
extra_text_files.append(ssl_ca_cert)
|
|
|
|
ssl_server_cert = self._entropy._get_local_database_server_cert_file()
|
|
if os.path.isfile(ssl_server_cert):
|
|
if download:
|
|
data['ssl_server_cert_file'] = ssl_server_cert
|
|
extra_text_files.append(ssl_server_cert)
|
|
|
|
# Some information regarding how packages are built
|
|
spm_files_map = self._entropy.Spm_class().config_files_map()
|
|
spm_syms = {}
|
|
for myname, myfile in spm_files_map.items():
|
|
if os.path.islink(myfile):
|
|
spm_syms[myname] = myfile
|
|
continue # we don't want symlinks
|
|
if os.path.isfile(myfile) and os.access(myfile, os.R_OK):
|
|
if download:
|
|
data[myname] = myfile
|
|
extra_text_files.append(myfile)
|
|
|
|
# XXX/FIXME: for symlinks, we read their link and send a file with that
|
|
# content. This is the default behaviour for now and allows to send
|
|
# /etc/make.profile link pointer correctly.
|
|
for symname, symfile in spm_syms.items():
|
|
|
|
tmp_file = entropy.tools.get_random_temp_file()
|
|
mytmpdir = os.path.dirname(tmp_file)
|
|
mytmpfile = os.path.join(mytmpdir, os.path.basename(symfile))
|
|
mylink = os.readlink(symfile)
|
|
f_mkp = open(mytmpfile, "w")
|
|
f_mkp.write(mylink)
|
|
f_mkp.flush()
|
|
f_mkp.close()
|
|
|
|
if download:
|
|
data[symname] = mytmpfile
|
|
extra_text_files.append(mytmpfile)
|
|
|
|
return data, critical, extra_text_files, gpg_signed_files
|
|
|
|
def _show_package_sets_messages(self, repo):
|
|
|
|
self._entropy.output(
|
|
"[repo:%s] %s:" % (
|
|
brown(repo),
|
|
blue(_("configured package sets")),
|
|
),
|
|
importance = 0,
|
|
level = "info",
|
|
header = darkgreen(" * ")
|
|
)
|
|
sets_data = self._entropy.sets_available(matchRepo = [repo])
|
|
if not sets_data:
|
|
self._entropy.output(
|
|
"%s" % (_("None configured"),),
|
|
importance = 0,
|
|
level = "info",
|
|
header = brown(" # ")
|
|
)
|
|
return
|
|
for s_repo, s_name, s_sets in sets_data:
|
|
self._entropy.output(
|
|
blue("%s" % (s_name,)),
|
|
importance = 0,
|
|
level = "info",
|
|
header = brown(" # ")
|
|
)
|
|
|
|
def _show_eapi3_upload_messages(self, crippled_uri, database_path, repo):
|
|
|
|
self._entropy.output(
|
|
"[repo:%s|%s|%s:%s] %s" % (
|
|
brown(repo),
|
|
darkgreen(crippled_uri),
|
|
red("EAPI"),
|
|
bold("3"),
|
|
blue(_("preparing uncompressed database for the upload")),
|
|
),
|
|
importance = 0,
|
|
level = "info",
|
|
header = darkgreen(" * ")
|
|
)
|
|
self._entropy.output(
|
|
"%s: %s" % (_("database path"), blue(database_path),),
|
|
importance = 0,
|
|
level = "info",
|
|
header = brown(" # ")
|
|
)
|
|
|
|
def _show_eapi2_upload_messages(self, crippled_uri, database_path,
|
|
upload_data, cmethod, repo):
|
|
|
|
if repo is None:
|
|
repo = self._entropy.default_repository
|
|
|
|
self._entropy.output(
|
|
"[repo:%s|%s|%s:%s] %s" % (
|
|
brown(repo),
|
|
darkgreen(crippled_uri),
|
|
red("EAPI"),
|
|
bold("2"),
|
|
blue(_("creating compressed database dump + checksum")),
|
|
),
|
|
importance = 0,
|
|
level = "info",
|
|
header = darkgreen(" * ")
|
|
)
|
|
self._entropy.output(
|
|
"%s: %s" % (_("database path"), blue(database_path),),
|
|
importance = 0,
|
|
level = "info",
|
|
header = brown(" # ")
|
|
)
|
|
self._entropy.output(
|
|
"%s: %s" % (
|
|
_("dump light"),
|
|
blue(upload_data['dump_path_light']),
|
|
),
|
|
importance = 0,
|
|
level = "info",
|
|
header = brown(" # ")
|
|
)
|
|
self._entropy.output(
|
|
"%s: %s" % (
|
|
_("dump light checksum"),
|
|
blue(upload_data['dump_path_digest_light']),
|
|
),
|
|
importance = 0,
|
|
level = "info",
|
|
header = brown(" # ")
|
|
)
|
|
|
|
self._entropy.output(
|
|
"%s: %s" % (_("opener"), blue(str(cmethod[0])),),
|
|
importance = 0,
|
|
level = "info",
|
|
header = brown(" # ")
|
|
)
|
|
|
|
def _show_eapi1_upload_messages(self, crippled_uri, database_path,
|
|
upload_data, cmethod, repo):
|
|
|
|
self._entropy.output(
|
|
"[repo:%s|%s|%s:%s] %s" % (
|
|
brown(repo),
|
|
darkgreen(crippled_uri),
|
|
red("EAPI"),
|
|
bold("1"),
|
|
blue(_("compressing database + checksum")),
|
|
),
|
|
importance = 0,
|
|
level = "info",
|
|
header = darkgreen(" * "),
|
|
back = True
|
|
)
|
|
self._entropy.output(
|
|
"%s: %s" % (_("database path"), blue(database_path),),
|
|
importance = 0,
|
|
level = "info",
|
|
header = brown(" # ")
|
|
)
|
|
self._entropy.output(
|
|
"%s: %s" % (
|
|
_("compressed database path"),
|
|
blue(upload_data['compressed_database_path']),
|
|
),
|
|
importance = 0,
|
|
level = "info",
|
|
header = brown(" # ")
|
|
)
|
|
self._entropy.output(
|
|
"%s: %s" % (
|
|
_("database checksum"),
|
|
blue(upload_data['database_path_digest']),
|
|
),
|
|
importance = 0,
|
|
level = "info",
|
|
header = brown(" # ")
|
|
)
|
|
self._entropy.output(
|
|
"%s: %s" % (
|
|
_("compressed checksum"),
|
|
blue(upload_data['compressed_database_path_digest']),
|
|
),
|
|
importance = 0,
|
|
level = "info",
|
|
header = brown(" # ")
|
|
)
|
|
self._entropy.output(
|
|
"%s: %s" % (_("opener"), blue(str(cmethod[0])),),
|
|
importance = 0,
|
|
level = "info",
|
|
header = brown(" # ")
|
|
)
|
|
|
|
def __get_repo_security_intf(self, repo):
|
|
try:
|
|
repo_sec = RepositorySecurity()
|
|
if not repo_sec.is_keypair_available(repo):
|
|
raise KeyError("no key avail")
|
|
except RepositorySecurity.KeyExpired:
|
|
self._entropy.output("%s: %s" % (
|
|
darkred(_("Keys for repository are expired")),
|
|
bold(repo),
|
|
),
|
|
level = "warning",
|
|
header = bold(" !!! ")
|
|
)
|
|
except RepositorySecurity.GPGError:
|
|
return
|
|
except KeyError:
|
|
return
|
|
return repo_sec
|
|
|
|
def __write_gpg_pubkey(self, repo_sec, repo):
|
|
pubkey = repo_sec.get_pubkey(repo)
|
|
# write pubkey to file and add to data upload
|
|
gpg_path = self._entropy._get_local_database_gpg_signature_file(repo)
|
|
with open(gpg_path, "w") as gpg_f:
|
|
gpg_f.write(pubkey)
|
|
gpg_f.flush()
|
|
return gpg_path
|
|
|
|
def _create_metafiles_file(self, compressed_dest_path, file_list, repo):
|
|
|
|
found_file_list = [x for x in file_list if os.path.isfile(x) and \
|
|
os.path.isfile(x) and os.access(x, os.R_OK)]
|
|
|
|
not_found_file_list = ["%s\n" % (os.path.basename(x),) for x in \
|
|
file_list if x not in found_file_list]
|
|
|
|
# GPG, also pack signature.asc inside
|
|
repo_sec = self.__get_repo_security_intf(repo)
|
|
if repo_sec is not None:
|
|
gpg_path = self.__write_gpg_pubkey(repo_sec, repo)
|
|
if gpg_path is not None:
|
|
found_file_list.append(gpg_path)
|
|
else:
|
|
gpg_path = \
|
|
self._entropy._get_local_database_gpg_signature_file(repo)
|
|
not_found_file_list.append(gpg_path) # not found
|
|
|
|
metafile_not_found_file = \
|
|
self._entropy._get_local_database_metafiles_not_found_file(repo)
|
|
f_meta = open(metafile_not_found_file, "w")
|
|
f_meta.writelines(not_found_file_list)
|
|
f_meta.flush()
|
|
f_meta.close()
|
|
found_file_list.append(metafile_not_found_file)
|
|
if os.path.isfile(compressed_dest_path):
|
|
os.remove(compressed_dest_path)
|
|
|
|
entropy.tools.compress_files(compressed_dest_path, found_file_list)
|
|
|
|
def _create_upload_gpg_signatures(self, upload_data, to_sign_files, repo):
|
|
"""
|
|
This method creates .asc files for every path that is going to be
|
|
uploaded. upload_data directly comes from _upload_database()
|
|
"""
|
|
repo_sec = self.__get_repo_security_intf(repo)
|
|
if repo_sec is None:
|
|
return
|
|
|
|
# for every item in upload_data, create a gpg signature
|
|
gpg_upload_data = {}
|
|
for item_id, item_path in upload_data.items():
|
|
if item_path not in to_sign_files:
|
|
continue
|
|
if os.path.isfile(item_path) and os.access(item_path, os.R_OK):
|
|
gpg_item_id = item_id + "_gpg_sign_part"
|
|
if gpg_item_id in upload_data:
|
|
raise KeyError("wtf!")
|
|
sign_path = repo_sec.sign_file(repo, item_path)
|
|
gpg_upload_data[gpg_item_id] = sign_path
|
|
upload_data.update(gpg_upload_data)
|
|
|
|
def _mirror_lock_check(self, uri, repo = None):
|
|
"""
|
|
Return whether mirror is locked.
|
|
"""
|
|
|
|
if repo is None:
|
|
repo = self._entropy.default_repository
|
|
gave_up = False
|
|
|
|
lock_file = self._entropy._get_database_lockfile(repo)
|
|
lock_filename = os.path.basename(lock_file)
|
|
|
|
remote_dir = os.path.join(
|
|
self._entropy._get_remote_database_relative_path(repo),
|
|
self._settings['repositories']['branch'])
|
|
remote_lock_file = os.path.join(remote_dir, lock_filename)
|
|
|
|
txc = self._entropy.Transceiver(uri)
|
|
with txc as handler:
|
|
|
|
if not os.path.isfile(lock_file) and \
|
|
handler.is_file(remote_lock_file):
|
|
|
|
crippled_uri = EntropyTransceiver.get_uri_name(uri)
|
|
self._entropy.output(
|
|
"[repo:%s|%s|%s] %s, %s" % (
|
|
brown(str(repo)),
|
|
darkgreen(crippled_uri),
|
|
red(_("locking")),
|
|
darkblue(_("mirror already locked")),
|
|
blue(_("waiting up to 2 minutes before giving up")),
|
|
),
|
|
importance = 1,
|
|
level = "warning",
|
|
header = brown(" * "),
|
|
back = True
|
|
)
|
|
|
|
unlocked = False
|
|
count = 0
|
|
while count < 120:
|
|
count += 1
|
|
time.sleep(1)
|
|
if not handler.is_file(remote_lock_file):
|
|
self._entropy.output(
|
|
red("[repo:%s|%s|%s] %s !" % (
|
|
repo,
|
|
crippled_uri,
|
|
_("locking"),
|
|
_("mirror unlocked"),
|
|
)
|
|
),
|
|
importance = 1,
|
|
level = "info",
|
|
header = darkgreen(" * ")
|
|
)
|
|
unlocked = True
|
|
break
|
|
|
|
if not unlocked:
|
|
gave_up = True
|
|
|
|
return gave_up
|
|
|
|
def _shrink_database_and_close(self, repo = None):
|
|
dbconn = self._entropy.open_server_repository(read_only = False,
|
|
no_upload = True, repo = repo, indexing = False,
|
|
do_treeupdates = False)
|
|
dbconn.doCleanups()
|
|
dbconn.dropAllIndexes()
|
|
dbconn.vacuum()
|
|
dbconn.vacuum()
|
|
dbconn.commitChanges()
|
|
self._entropy.close_repository(dbconn)
|
|
|
|
def _get_current_timestamp(self):
|
|
from datetime import datetime
|
|
return "%s" % (datetime.fromtimestamp(time.time()),)
|
|
|
|
def _update_repository_timestamp(self, repo = None):
|
|
if repo is None:
|
|
repo = self._entropy.default_repository
|
|
ts_file = self._entropy._get_local_database_timestamp_file(repo)
|
|
current_ts = self._get_current_timestamp()
|
|
ts_f = open(ts_file, "w")
|
|
ts_f.write(current_ts)
|
|
ts_f.flush()
|
|
ts_f.close()
|
|
|
|
def _sync_database_treeupdates(self, repo = None):
|
|
|
|
if repo is None:
|
|
repo = self._entropy.default_repository
|
|
dbconn = self._entropy.open_server_repository(read_only = False,
|
|
no_upload = True, repo = repo, do_treeupdates = False)
|
|
# grab treeupdates from other databases and inject
|
|
srv_set = self._settings[self.sys_settings_plugin_id]['server']
|
|
server_repos = list(srv_set['repositories'].keys())
|
|
all_actions = set()
|
|
for myrepo in server_repos:
|
|
|
|
# avoid __default__
|
|
if myrepo == etpConst['clientserverrepoid']:
|
|
continue
|
|
|
|
mydbc = self._entropy.open_server_repository(just_reading = True,
|
|
repo = myrepo)
|
|
actions = mydbc.listAllTreeUpdatesActions(no_ids_repos = True)
|
|
for data in actions:
|
|
all_actions.add(data)
|
|
if not actions:
|
|
continue
|
|
backed_up_entries = dbconn.listAllTreeUpdatesActions()
|
|
try:
|
|
# clear first
|
|
dbconn.removeTreeUpdatesActions(repo)
|
|
dbconn.insertTreeUpdatesActions(all_actions, repo)
|
|
except Error as err:
|
|
entropy.tools.print_traceback()
|
|
mytxt = "%s, %s: %s. %s" % (
|
|
_("Troubles with treeupdates"),
|
|
_("error"),
|
|
err,
|
|
_("Bumping old data back"),
|
|
)
|
|
self._entropy.output(
|
|
mytxt,
|
|
importance = 1,
|
|
level = "warning"
|
|
)
|
|
# restore previous data
|
|
dbconn.bumpTreeUpdatesActions(backed_up_entries)
|
|
|
|
dbconn.commitChanges()
|
|
|
|
def _create_repository_pkglist(self, repo = None, branch = None):
|
|
pkglist_file = self._entropy._get_local_pkglist_file(repo = repo,
|
|
branch = branch)
|
|
|
|
tmp_pkglist_file = pkglist_file + ".tmp"
|
|
dbconn = self._entropy.open_server_repository(repo = repo,
|
|
just_reading = True, do_treeupdates = False)
|
|
pkglist = dbconn.listAllDownloads(do_sort = True, full_path = True)
|
|
|
|
with open(tmp_pkglist_file, "w") as pkg_f:
|
|
for pkg in pkglist:
|
|
pkg_f.write(pkg + "\n")
|
|
pkg_f.flush()
|
|
|
|
os.rename(tmp_pkglist_file, pkglist_file)
|
|
|
|
def _upload_database(self, uris, lock_check = False, pretend = False,
|
|
repo = None):
|
|
|
|
if repo is None:
|
|
repo = self._entropy.default_repository
|
|
|
|
srv_set = self._settings[self.sys_settings_plugin_id]['server']
|
|
if srv_set['rss']['enabled']:
|
|
self._update_rss_feed(repo = repo)
|
|
|
|
upload_errors = False
|
|
broken_uris = set()
|
|
fine_uris = set()
|
|
|
|
disabled_eapis = sorted(srv_set['disabled_eapis'])
|
|
|
|
for uri in uris:
|
|
|
|
db_format = srv_set['database_file_format']
|
|
cmethod = etpConst['etpdatabasecompressclasses'].get(db_format)
|
|
if cmethod is None:
|
|
raise AttributeError("wrong database compression method passed")
|
|
|
|
crippled_uri = EntropyTransceiver.get_uri_name(uri)
|
|
database_path = self._entropy._get_local_database_file(repo)
|
|
|
|
if disabled_eapis:
|
|
self._entropy.output(
|
|
"[repo:%s|%s|%s] %s: %s" % (
|
|
blue(repo),
|
|
red(crippled_uri),
|
|
darkgreen(_("upload")),
|
|
darkred(_("disabled EAPI")),
|
|
bold(', '.join([str(x) for x in disabled_eapis])),
|
|
),
|
|
importance = 1,
|
|
level = "warning",
|
|
header = darkgreen(" * ")
|
|
)
|
|
|
|
# create/update timestamp file
|
|
self._update_repository_timestamp(repo)
|
|
# create pkglist service file
|
|
self._create_repository_pkglist(repo)
|
|
|
|
upload_data, critical, text_files, gpg_to_sign_files = \
|
|
self._get_files_to_sync(cmethod, repo = repo,
|
|
disabled_eapis = disabled_eapis)
|
|
|
|
if lock_check:
|
|
given_up = self._mirror_lock_check(uri, repo = repo)
|
|
if given_up:
|
|
upload_errors = True
|
|
broken_uris.add(uri)
|
|
continue
|
|
|
|
self.lock_mirrors_for_download(True, [uri], repo = repo)
|
|
|
|
self._entropy.output(
|
|
"[repo:%s|%s|%s] %s" % (
|
|
blue(repo),
|
|
red(crippled_uri),
|
|
darkgreen(_("upload")),
|
|
darkgreen(_("preparing to upload database to mirror")),
|
|
),
|
|
importance = 1,
|
|
level = "info",
|
|
header = darkgreen(" * ")
|
|
)
|
|
|
|
# Package Sets info
|
|
self._show_package_sets_messages(repo)
|
|
|
|
self._sync_database_treeupdates(repo)
|
|
self._entropy._update_database_package_sets(repo)
|
|
dbconn = self._entropy.open_server_repository(
|
|
read_only = False, no_upload = True, repo = repo,
|
|
do_treeupdates = False)
|
|
dbconn.commitChanges()
|
|
# now we can safely copy it
|
|
|
|
# backup current database to avoid re-indexing
|
|
old_dbpath = self._entropy._get_local_database_file(repo)
|
|
backup_dbpath = old_dbpath + ".up_backup"
|
|
copy_back = False
|
|
if not pretend:
|
|
try:
|
|
if os.access(backup_dbpath, os.R_OK) and \
|
|
os.path.isfile(backup_dbpath):
|
|
os.remove(backup_dbpath)
|
|
|
|
shutil.copy2(old_dbpath, backup_dbpath)
|
|
copy_back = True
|
|
except shutil.Error:
|
|
copy_back = False
|
|
|
|
self._shrink_database_and_close(repo)
|
|
|
|
# EAPI 3
|
|
if 3 not in disabled_eapis:
|
|
self._show_eapi3_upload_messages(crippled_uri, database_path,
|
|
repo)
|
|
|
|
# EAPI 2
|
|
if 2 not in disabled_eapis:
|
|
self._show_eapi2_upload_messages(crippled_uri, database_path,
|
|
upload_data, cmethod, repo)
|
|
|
|
# create compressed dump + checksum
|
|
self._dump_database_to_file(database_path,
|
|
upload_data['dump_path_light'], cmethod[0],
|
|
exclude_tables = ["content", "packagechangelogs"],
|
|
repo = repo)
|
|
self._create_file_checksum(upload_data['dump_path_light'],
|
|
upload_data['dump_path_digest_light'])
|
|
|
|
|
|
# EAPI 1
|
|
if 1 not in disabled_eapis:
|
|
self._show_eapi1_upload_messages(crippled_uri, database_path,
|
|
upload_data, cmethod, repo)
|
|
|
|
# compress the database and create uncompressed
|
|
# database checksum -- DEPRECATED
|
|
self._compress_file(database_path,
|
|
upload_data['compressed_database_path'], cmethod[0])
|
|
self._create_file_checksum(database_path,
|
|
upload_data['database_path_digest'])
|
|
|
|
# create compressed database checksum
|
|
self._create_file_checksum(
|
|
upload_data['compressed_database_path'],
|
|
upload_data['compressed_database_path_digest'])
|
|
|
|
# create light version of the compressed db
|
|
eapi1_dbfile = self._entropy._get_local_database_file(repo)
|
|
temp_eapi1_dbfile = eapi1_dbfile+".light"
|
|
shutil.copy2(eapi1_dbfile, temp_eapi1_dbfile)
|
|
# open and remove content table
|
|
eapi1_tmp_dbconn = \
|
|
self._entropy.open_generic_repository(
|
|
temp_eapi1_dbfile, indexing_override = False,
|
|
xcache = False)
|
|
eapi1_tmp_dbconn.dropContent()
|
|
eapi1_tmp_dbconn.dropChangelog()
|
|
eapi1_tmp_dbconn.commitChanges()
|
|
eapi1_tmp_dbconn.vacuum()
|
|
eapi1_tmp_dbconn.closeDB()
|
|
|
|
# compress
|
|
self._compress_file(temp_eapi1_dbfile,
|
|
upload_data['compressed_database_path_light'], cmethod[0])
|
|
# go away, we don't need you anymore
|
|
os.remove(temp_eapi1_dbfile)
|
|
# create compressed light database checksum
|
|
self._create_file_checksum(
|
|
upload_data['compressed_database_path_light'],
|
|
upload_data['compressed_database_path_digest_light'])
|
|
|
|
# always upload metafile, it's cheap and also used by EAPI1,2
|
|
self._create_metafiles_file(upload_data['metafiles_path'],
|
|
text_files, repo)
|
|
# Setup GPG signatures for files that are going to be uploaded
|
|
self._create_upload_gpg_signatures(upload_data, gpg_to_sign_files,
|
|
repo)
|
|
|
|
if not pretend:
|
|
# upload
|
|
uploader = self.TransceiverServerHandler(
|
|
self._entropy,
|
|
[uri],
|
|
[upload_data[x] for x in sorted(upload_data)],
|
|
critical_files = critical,
|
|
repo = repo
|
|
)
|
|
errors, m_fine_uris, m_broken_uris = uploader.go()
|
|
if errors:
|
|
my_broken_uris = sorted([
|
|
(EntropyTransceiver.get_uri_name(x[0]),
|
|
x[1]) for x in m_broken_uris])
|
|
self._entropy.output(
|
|
"[repo:%s|%s|%s] %s" % (
|
|
repo,
|
|
crippled_uri,
|
|
_("errors"),
|
|
_("upload failed, not unlocking and continuing"),
|
|
),
|
|
importance = 0,
|
|
level = "error",
|
|
header = darkred(" !!! ")
|
|
)
|
|
# get reason
|
|
reason = my_broken_uris[0][1]
|
|
self._entropy.output(
|
|
blue("%s: %s" % (_("reason"), reason,)),
|
|
importance = 0,
|
|
level = "error",
|
|
header = blue(" # ")
|
|
)
|
|
upload_errors = True
|
|
broken_uris |= m_broken_uris
|
|
continue
|
|
|
|
# copy db back
|
|
if copy_back and os.path.isfile(backup_dbpath):
|
|
self._entropy.close_repositories()
|
|
further_backup_dbpath = old_dbpath+".security_backup"
|
|
if os.path.isfile(further_backup_dbpath):
|
|
os.remove(further_backup_dbpath)
|
|
shutil.copy2(old_dbpath, further_backup_dbpath)
|
|
shutil.move(backup_dbpath, old_dbpath)
|
|
|
|
# unlock
|
|
self.lock_mirrors_for_download(False, [uri], repo = repo)
|
|
fine_uris |= m_fine_uris
|
|
|
|
if not fine_uris:
|
|
upload_errors = True
|
|
return upload_errors, broken_uris, fine_uris
|
|
|
|
|
|
def _download_database(self, uris, lock_check = False, pretend = False,
|
|
repo = None):
|
|
|
|
if repo is None:
|
|
repo = self._entropy.default_repository
|
|
|
|
download_errors = False
|
|
broken_uris = set()
|
|
fine_uris = set()
|
|
srv_set = self._settings[self.sys_settings_plugin_id]['server']
|
|
disabled_eapis = sorted(srv_set['disabled_eapis'])
|
|
|
|
for uri in uris:
|
|
|
|
db_format = srv_set['database_file_format']
|
|
cmethod = etpConst['etpdatabasecompressclasses'].get(db_format)
|
|
if cmethod is None:
|
|
raise AttributeError("wrong database compression method passed")
|
|
|
|
crippled_uri = EntropyTransceiver.get_uri_name(uri)
|
|
database_path = self._entropy._get_local_database_file(repo)
|
|
database_dir_path = os.path.dirname(
|
|
self._entropy._get_local_database_file(repo))
|
|
download_data, critical, text_files, gpg_to_verify_files = \
|
|
self._get_files_to_sync(cmethod, download = True,
|
|
repo = repo, disabled_eapis = disabled_eapis)
|
|
mytmpdir = tempfile.mkdtemp()
|
|
|
|
self._entropy.output(
|
|
"[repo:%s|%s|%s] %s" % (
|
|
brown(repo),
|
|
darkgreen(crippled_uri),
|
|
red(_("download")),
|
|
blue(_("preparing to download database from mirror")),
|
|
),
|
|
importance = 1,
|
|
level = "info",
|
|
header = darkgreen(" * ")
|
|
)
|
|
files_to_sync = sorted(download_data.keys())
|
|
for myfile in files_to_sync:
|
|
self._entropy.output(
|
|
"%s: %s" % (
|
|
blue(_("download path")),
|
|
brown(download_data[myfile]),
|
|
),
|
|
importance = 0,
|
|
level = "info",
|
|
header = brown(" # ")
|
|
)
|
|
|
|
if lock_check:
|
|
given_up = self._mirror_lock_check(uri, repo = repo)
|
|
if given_up:
|
|
download_errors = True
|
|
broken_uris.add(uri)
|
|
continue
|
|
|
|
# avoid having others messing while we're downloading
|
|
self.lock_mirrors(True, [uri], repo = repo)
|
|
|
|
if not pretend:
|
|
# download
|
|
downloader = self.TransceiverServerHandler(
|
|
self._entropy, [uri],
|
|
[download_data[x] for x in download_data], download = True,
|
|
local_basedir = mytmpdir, critical_files = critical,
|
|
repo = repo
|
|
)
|
|
errors, m_fine_uris, m_broken_uris = downloader.go()
|
|
if errors:
|
|
my_broken_uris = sorted([
|
|
(EntropyTransceiver.get_uri_name(x[0]),
|
|
x[1]) for x in m_broken_uris])
|
|
self._entropy.output(
|
|
"[repo:%s|%s|%s] %s" % (
|
|
brown(repo),
|
|
darkgreen(crippled_uri),
|
|
red(_("errors")),
|
|
blue(_("failed to download from mirror")),
|
|
),
|
|
importance = 0,
|
|
level = "error",
|
|
header = darkred(" !!! ")
|
|
)
|
|
# get reason
|
|
reason = my_broken_uris[0][1]
|
|
self._entropy.output(
|
|
blue("%s: %s" % (_("reason"), reason,)),
|
|
importance = 0,
|
|
level = "error",
|
|
header = blue(" # ")
|
|
)
|
|
download_errors = True
|
|
broken_uris |= m_broken_uris
|
|
self.lock_mirrors(False, [uri], repo = repo)
|
|
continue
|
|
|
|
# all fine then, we need to move data from mytmpdir
|
|
# to database_dir_path
|
|
|
|
# EAPI 1 -- unpack database
|
|
if 1 not in disabled_eapis:
|
|
compressed_db_filename = os.path.basename(
|
|
download_data['compressed_database_path'])
|
|
uncompressed_db_filename = os.path.basename(database_path)
|
|
compressed_file = os.path.join(mytmpdir,
|
|
compressed_db_filename)
|
|
uncompressed_file = os.path.join(mytmpdir,
|
|
uncompressed_db_filename)
|
|
entropy.tools.uncompress_file(compressed_file,
|
|
uncompressed_file, cmethod[0])
|
|
|
|
# now move
|
|
for myfile in os.listdir(mytmpdir):
|
|
fromfile = os.path.join(mytmpdir, myfile)
|
|
tofile = os.path.join(database_dir_path, myfile)
|
|
shutil.move(fromfile, tofile)
|
|
self._entropy.setup_file_permissions(tofile)
|
|
|
|
if os.path.isdir(mytmpdir):
|
|
shutil.rmtree(mytmpdir)
|
|
if os.path.isdir(mytmpdir):
|
|
os.rmdir(mytmpdir)
|
|
|
|
|
|
fine_uris.add(uri)
|
|
self.lock_mirrors(False, [uri], repo = repo)
|
|
|
|
return download_errors, fine_uris, broken_uris
|
|
|
|
def _calculate_database_sync_queues(self, repo = None):
|
|
|
|
if repo is None:
|
|
repo = self._entropy.default_repository
|
|
|
|
remote_status = self.get_remote_repositories_status(repo)
|
|
local_revision = self._entropy.get_local_repository_revision(repo)
|
|
upload_queue = []
|
|
download_latest = ()
|
|
|
|
# all mirrors are empty ? I rule
|
|
if not [x for x in remote_status if x[1]]:
|
|
upload_queue = remote_status[:]
|
|
else:
|
|
highest_remote_revision = max([x[1] for x in remote_status])
|
|
|
|
if local_revision < highest_remote_revision:
|
|
for remote_item in remote_status:
|
|
if remote_item[1] == highest_remote_revision:
|
|
download_latest = x
|
|
break
|
|
|
|
if download_latest:
|
|
upload_queue = [x for x in remote_status if \
|
|
(x[1] < highest_remote_revision)]
|
|
else:
|
|
upload_queue = [x for x in remote_status if \
|
|
(x[1] < local_revision)]
|
|
|
|
return download_latest, upload_queue
|
|
|
|
def sync_repositories(self, no_upload = False, unlock_mirrors = False,
|
|
repo = None):
|
|
|
|
if repo is None:
|
|
repo = self._entropy.default_repository
|
|
|
|
while True:
|
|
|
|
db_locked = False
|
|
if self._is_local_repository_locked(repo):
|
|
db_locked = True
|
|
|
|
lock_data = self._get_mirrors_lock(repo)
|
|
mirrors_locked = [x for x in lock_data if x[1]]
|
|
|
|
if not mirrors_locked and db_locked:
|
|
# mirrors not locked remotely but only locally
|
|
mylock_file = self._entropy._get_database_lockfile(repo)
|
|
if os.access(mylock_file, os.W_OK) and \
|
|
os.path.isfile(mylock_file):
|
|
|
|
os.remove(mylock_file)
|
|
continue
|
|
|
|
break
|
|
|
|
if mirrors_locked and not db_locked:
|
|
mytxt = "%s, %s %s" % (
|
|
_("Mirrors are locked, someone is working on the repository"),
|
|
_("try again later"),
|
|
"...",
|
|
)
|
|
raise OnlineMirrorError("OnlineMirrorError: %s" % (mytxt,))
|
|
|
|
download_latest, upload_queue = self._calculate_database_sync_queues(
|
|
repo)
|
|
|
|
if not download_latest and not upload_queue:
|
|
self._entropy.output(
|
|
"[repo:%s|%s] %s" % (
|
|
brown(repo),
|
|
red(_("sync")), # something short please
|
|
blue(_("database already in sync")),
|
|
),
|
|
importance = 1,
|
|
level = "info",
|
|
header = blue(" @@ ")
|
|
)
|
|
return 0, set(), set()
|
|
|
|
if download_latest:
|
|
# close all the currently open repos
|
|
self._entropy.close_repositories()
|
|
download_uri = download_latest[0]
|
|
download_errors, fine_uris, broken_uris = self._download_database(
|
|
[download_uri], repo = repo)
|
|
if download_errors:
|
|
self._entropy.output(
|
|
"[repo:%s|%s] %s: %s" % (
|
|
brown(repo),
|
|
red(_("sync")),
|
|
blue(_("database sync failed")),
|
|
red(_("download issues")),
|
|
),
|
|
importance = 1,
|
|
level = "error",
|
|
header = darkred(" !!! ")
|
|
)
|
|
return 1, fine_uris, broken_uris
|
|
|
|
if upload_queue and not no_upload:
|
|
|
|
# XXX QA checks,
|
|
# please group them into entropy.qa
|
|
|
|
srv_set = self._settings[self.sys_settings_plugin_id]['server']
|
|
base_repo = srv_set['base_repository_id']
|
|
if base_repo is None:
|
|
base_repo = repo
|
|
|
|
base_deps_not_found = set()
|
|
if base_repo != repo:
|
|
base_deps_not_found = self._entropy.dependencies_test(
|
|
repo = base_repo)
|
|
|
|
deps_not_found = self._entropy.dependencies_test(repo = repo)
|
|
if (deps_not_found or base_deps_not_found) \
|
|
and not self._entropy.community_repo:
|
|
|
|
self._entropy.output(
|
|
"[repo:%s|%s] %s: %s" % (
|
|
brown(repo),
|
|
red(_("sync")),
|
|
blue(_("database sync forbidden")),
|
|
red(_("dependencies_test() reported errors")),
|
|
),
|
|
importance = 1,
|
|
level = "error",
|
|
header = darkred(" !!! ")
|
|
)
|
|
return 3, set(), set()
|
|
|
|
problems = self._entropy._check_config_file_updates()
|
|
if problems:
|
|
return 4, set(), set()
|
|
|
|
self._entropy.output(
|
|
"[repo:%s|%s] %s" % (
|
|
brown(repo),
|
|
red(_("config files")), # something short please
|
|
blue(_("no configuration files to commit. All fine.")),
|
|
),
|
|
importance = 1,
|
|
level = "info",
|
|
header = blue(" @@ "),
|
|
back = True
|
|
)
|
|
|
|
uris = [x[0] for x in upload_queue]
|
|
errors, fine_uris, broken_uris = self._upload_database(uris,
|
|
repo = repo)
|
|
if errors:
|
|
self._entropy.output(
|
|
"[repo:%s|%s] %s: %s" % (
|
|
brown(repo),
|
|
red(_("sync")),
|
|
blue(_("database sync failed")),
|
|
red(_("upload issues")),
|
|
),
|
|
importance = 1,
|
|
level = "error",
|
|
header = darkred(" !!! ")
|
|
)
|
|
return 2, fine_uris, broken_uris
|
|
|
|
|
|
self._entropy.output(
|
|
"[repo:%s|%s] %s" % (
|
|
brown(repo),
|
|
red(_("sync")),
|
|
blue(_("database sync completed successfully")),
|
|
),
|
|
importance = 1,
|
|
level = "info",
|
|
header = darkgreen(" * ")
|
|
)
|
|
|
|
if unlock_mirrors:
|
|
self.lock_mirrors(False, repo = repo)
|
|
return 0, set(), set()
|
|
|
|
def _calculate_local_upload_files(self, repo = None):
|
|
upload_files = 0
|
|
upload_packages = set()
|
|
upload_dir = self._entropy._get_local_upload_directory(repo = repo)
|
|
|
|
# check if it exists
|
|
if not os.path.isdir(upload_dir):
|
|
return upload_files, upload_packages
|
|
|
|
branch = self._settings['repositories']['branch']
|
|
upload_pkgs = self._entropy._get_basedir_pkg_listing(upload_dir,
|
|
repo = repo, branch = branch)
|
|
|
|
pkg_ext = etpConst['packagesext']
|
|
pkg_md5_ext = etpConst['packagesmd5fileext']
|
|
for package in upload_pkgs:
|
|
if package.endswith(pkg_ext) or package.endswith(pkg_md5_ext):
|
|
upload_packages.add(package)
|
|
if package.endswith(pkg_ext):
|
|
upload_files += 1
|
|
|
|
return upload_files, upload_packages
|
|
|
|
def _calculate_local_package_files(self, repo = None):
|
|
local_files = 0
|
|
local_packages = set()
|
|
base_dir = self._entropy._get_local_repository_base_directory(
|
|
repo = repo)
|
|
|
|
# check if it exists
|
|
if not os.path.isdir(base_dir):
|
|
return local_files, local_packages
|
|
|
|
branch = self._settings['repositories']['branch']
|
|
pkg_files = self._entropy._get_basedir_pkg_listing(base_dir,
|
|
repo = repo, branch = branch)
|
|
|
|
pkg_ext = etpConst['packagesext']
|
|
pkg_md5_ext = etpConst['packagesmd5fileext']
|
|
for package in pkg_files:
|
|
if package.endswith(pkg_ext) or package.endswith(pkg_md5_ext):
|
|
local_packages.add(package)
|
|
if package.endswith(pkg_ext):
|
|
local_files += 1
|
|
|
|
return local_files, local_packages
|
|
|
|
|
|
def _show_local_sync_stats(self, upload_files, local_files):
|
|
self._entropy.output(
|
|
"%s:" % (
|
|
blue(_("Local statistics")),
|
|
),
|
|
importance = 1,
|
|
level = "info",
|
|
header = red(" @@ ")
|
|
)
|
|
self._entropy.output(
|
|
red("%s: %s %s" % (
|
|
blue(_("upload directory")),
|
|
bold(str(upload_files)),
|
|
red(_("files ready")),
|
|
)
|
|
),
|
|
importance = 0,
|
|
level = "info",
|
|
header = red(" @@ ")
|
|
)
|
|
self._entropy.output(
|
|
red("%s: %s %s" % (
|
|
blue(_("packages directory")),
|
|
bold(str(local_files)),
|
|
red(_("files ready")),
|
|
)
|
|
),
|
|
importance = 0,
|
|
level = "info",
|
|
header = red(" @@ ")
|
|
)
|
|
|
|
def _show_sync_queues(self, upload, download, removal, copy, metainfo):
|
|
|
|
branch = self._settings['repositories']['branch']
|
|
|
|
# show stats
|
|
for package, rel_pkg, size in upload:
|
|
package = darkgreen(rel_pkg)
|
|
size = blue(entropy.tools.bytes_into_human(size))
|
|
self._entropy.output(
|
|
"[branch:%s|%s] %s [%s]" % (
|
|
brown(branch),
|
|
blue(_("upload")),
|
|
darkgreen(package),
|
|
size,
|
|
),
|
|
importance = 0,
|
|
level = "info",
|
|
header = red(" # ")
|
|
)
|
|
for package, rel_pkg, size in download:
|
|
package = darkred(rel_pkg)
|
|
size = blue(entropy.tools.bytes_into_human(size))
|
|
self._entropy.output(
|
|
"[branch:%s|%s] %s [%s]" % (
|
|
brown(branch),
|
|
darkred(_("download")),
|
|
blue(package),
|
|
size,
|
|
),
|
|
importance = 0,
|
|
level = "info",
|
|
header = red(" # ")
|
|
)
|
|
for package, rel_pkg, size in copy:
|
|
package = darkblue(rel_pkg)
|
|
size = blue(entropy.tools.bytes_into_human(size))
|
|
self._entropy.output(
|
|
"[branch:%s|%s] %s [%s]" % (
|
|
brown(branch),
|
|
darkgreen(_("copy")),
|
|
brown(package),
|
|
size,
|
|
),
|
|
importance = 0,
|
|
level = "info",
|
|
header = red(" # ")
|
|
)
|
|
for package, rel_pkg, size in removal:
|
|
package = brown(rel_pkg)
|
|
size = blue(entropy.tools.bytes_into_human(size))
|
|
self._entropy.output(
|
|
"[branch:%s|%s] %s [%s]" % (
|
|
brown(branch),
|
|
red(_("remove")),
|
|
red(package),
|
|
size,
|
|
),
|
|
importance = 0,
|
|
level = "info",
|
|
header = red(" # ")
|
|
)
|
|
|
|
self._entropy.output(
|
|
"%s: %s" % (
|
|
blue(_("Packages to be removed")),
|
|
darkred(str(len(removal))),
|
|
),
|
|
importance = 0,
|
|
level = "info",
|
|
header = blue(" @@ ")
|
|
)
|
|
self._entropy.output(
|
|
"%s: %s" % (
|
|
darkgreen(_("Packages to be moved locally")),
|
|
darkgreen(str(len(copy))),
|
|
),
|
|
importance = 0,
|
|
level = "info",
|
|
header = blue(" @@ ")
|
|
)
|
|
self._entropy.output(
|
|
"%s: %s" % (
|
|
bold(_("Packages to be uploaded")),
|
|
bold(str(len(upload))),
|
|
),
|
|
importance = 0,
|
|
level = "info",
|
|
header = blue(" @@ ")
|
|
)
|
|
|
|
self._entropy.output(
|
|
"%s: %s" % (
|
|
darkred(_("Total removal size")),
|
|
darkred(
|
|
entropy.tools.bytes_into_human(metainfo['removal'])
|
|
),
|
|
),
|
|
importance = 0,
|
|
level = "info",
|
|
header = blue(" @@ ")
|
|
)
|
|
|
|
self._entropy.output(
|
|
"%s: %s" % (
|
|
blue(_("Total upload size")),
|
|
blue(entropy.tools.bytes_into_human(metainfo['upload'])),
|
|
),
|
|
importance = 0,
|
|
level = "info",
|
|
header = blue(" @@ ")
|
|
)
|
|
self._entropy.output(
|
|
"%s: %s" % (
|
|
brown(_("Total download size")),
|
|
brown(entropy.tools.bytes_into_human(metainfo['download'])),
|
|
),
|
|
importance = 0,
|
|
level = "info",
|
|
header = blue(" @@ ")
|
|
)
|
|
|
|
def _calculate_remote_package_files(self, uri, txc_handler, repo = None):
|
|
|
|
remote_files = 0
|
|
remote_packages_data = {}
|
|
remote_packages = []
|
|
branch = self._settings['repositories']['branch']
|
|
|
|
pkgs_dir_types = self._entropy._get_pkg_dir_names()
|
|
for pkg_dir_type in pkgs_dir_types:
|
|
|
|
remote_dir = self._entropy.complete_remote_package_relative_path(
|
|
pkg_dir_type, repo = repo)
|
|
remote_dir = os.path.join(remote_dir, etpConst['currentarch'],
|
|
branch)
|
|
only_dir = self._entropy.complete_remote_package_relative_path("",
|
|
repo = repo)
|
|
db_url_dir = remote_dir[len(only_dir):]
|
|
|
|
# create path to lock file if it doesn't exist
|
|
if not txc_handler.is_dir(remote_dir):
|
|
txc_handler.makedirs(remote_dir)
|
|
|
|
remote_packages_info = txc_handler.list_content_metadata(remote_dir)
|
|
remote_packages += [os.path.join(db_url_dir, x[0]) for x \
|
|
in remote_packages_info]
|
|
|
|
for pkg in remote_packages:
|
|
if pkg.endswith(etpConst['packagesext']):
|
|
remote_files += 1
|
|
|
|
my_remote_pkg_data = dict((os.path.join(db_url_dir, x[0]),
|
|
int(x[1])) for x in remote_packages_info)
|
|
remote_packages_data.update(my_remote_pkg_data)
|
|
|
|
return remote_files, remote_packages, remote_packages_data
|
|
|
|
def calculate_packages_to_sync(self, uri, repo = None):
|
|
|
|
if repo is None:
|
|
repo = self._entropy.default_repository
|
|
|
|
crippled_uri = EntropyTransceiver.get_uri_name(uri)
|
|
upload_files, upload_packages = self._calculate_local_upload_files(repo)
|
|
local_files, local_packages = self._calculate_local_package_files(repo)
|
|
self._show_local_sync_stats(upload_files, local_files)
|
|
|
|
self._entropy.output(
|
|
"%s: %s" % (blue(_("Remote statistics for")), red(crippled_uri),),
|
|
importance = 1,
|
|
level = "info",
|
|
header = red(" @@ ")
|
|
)
|
|
|
|
txc = self._entropy.Transceiver(uri)
|
|
with txc as handler:
|
|
remote_files, remote_packages, remote_packages_data = \
|
|
self._calculate_remote_package_files(uri, handler, repo = repo)
|
|
|
|
self._entropy.output(
|
|
"%s: %s %s" % (
|
|
blue(_("remote packages")),
|
|
bold(str(remote_files)),
|
|
red(_("files stored")),
|
|
),
|
|
importance = 0,
|
|
level = "info",
|
|
header = red(" @@ ")
|
|
)
|
|
|
|
mytxt = blue("%s ...") % (
|
|
_("Calculating queues"),
|
|
)
|
|
self._entropy.output(
|
|
mytxt,
|
|
importance = 1,
|
|
level = "info",
|
|
header = red(" @@ ")
|
|
)
|
|
|
|
upload_queue, download_queue, removal_queue, fine_queue = \
|
|
self._calculate_sync_queues(upload_packages, local_packages,
|
|
remote_packages, remote_packages_data, repo)
|
|
return upload_queue, download_queue, removal_queue, fine_queue, \
|
|
remote_packages_data
|
|
|
|
def _calculate_sync_queues(self, upload_packages, local_packages,
|
|
remote_packages, remote_packages_data, repo = None):
|
|
|
|
upload_queue = set()
|
|
download_queue = set()
|
|
removal_queue = set()
|
|
fine_queue = set()
|
|
branch = self._settings['repositories']['branch']
|
|
|
|
for local_package in upload_packages:
|
|
|
|
if not local_package.endswith(etpConst['packagesext']):
|
|
continue
|
|
|
|
if local_package in remote_packages:
|
|
|
|
local_filepath = \
|
|
self._entropy.complete_local_upload_package_path(
|
|
local_package, repo = repo)
|
|
|
|
local_size = entropy.tools.get_file_size(local_filepath)
|
|
remote_size = remote_packages_data.get(local_package)
|
|
if remote_size is None:
|
|
remote_size = 0
|
|
if local_size != remote_size:
|
|
# size does not match, adding to the upload queue
|
|
upload_queue.add(local_package)
|
|
else:
|
|
# just move from upload to packages
|
|
fine_queue.add(local_package)
|
|
|
|
else:
|
|
# always force upload of packages in uploaddir
|
|
upload_queue.add(local_package)
|
|
|
|
# if a package is in the packages directory but not online,
|
|
# we have to upload it we have local_packages and remote_packages
|
|
for local_package in local_packages:
|
|
|
|
if not local_package.endswith(etpConst['packagesext']):
|
|
continue
|
|
|
|
if local_package in remote_packages:
|
|
local_filepath = self._entropy.complete_local_package_path(
|
|
local_package, repo = repo)
|
|
local_size = entropy.tools.get_file_size(local_filepath)
|
|
remote_size = remote_packages_data.get(local_package)
|
|
if remote_size is None:
|
|
remote_size = 0
|
|
if (local_size != remote_size) and (local_size != 0):
|
|
# size does not match, adding to the upload queue
|
|
if local_package not in fine_queue:
|
|
upload_queue.add(local_package)
|
|
else:
|
|
# this means that the local package does not exist
|
|
# so, we need to download it
|
|
upload_queue.add(local_package)
|
|
|
|
# Fill download_queue and removal_queue
|
|
for remote_package in remote_packages:
|
|
|
|
if not remote_package.endswith(etpConst['packagesext']):
|
|
continue
|
|
|
|
if remote_package in local_packages:
|
|
local_filepath = self._entropy.complete_local_package_path(
|
|
remote_package, repo = repo)
|
|
local_size = entropy.tools.get_file_size(local_filepath)
|
|
remote_size = remote_packages_data.get(remote_package)
|
|
if remote_size is None:
|
|
remote_size = 0
|
|
if (local_size != remote_size) and (local_size != 0):
|
|
# size does not match, remove first
|
|
# do it only if the package has not been
|
|
# added to the upload_queue
|
|
if remote_package not in upload_queue:
|
|
# remotePackage == localPackage
|
|
# just remove something that differs
|
|
# from the content of the mirror
|
|
removal_queue.add(remote_package)
|
|
# then add to the download queue
|
|
download_queue.add(remote_package)
|
|
else:
|
|
# this means that the local package does not exist
|
|
# so, we need to download it
|
|
# ignore .tmp files
|
|
if not remote_package.endswith(
|
|
EntropyUriHandler.TMP_TXC_FILE_EXT):
|
|
download_queue.add(remote_package)
|
|
|
|
# Collect packages that don't exist anymore in the database
|
|
# so we can filter them out from the download queue
|
|
dbconn = self._entropy.open_server_repository(just_reading = True,
|
|
repo = repo)
|
|
db_files = dbconn.listAllDownloads(do_sort = False,
|
|
full_path = True)
|
|
db_files = set([x for x in db_files if \
|
|
(self._entropy._get_branch_from_download_relative_uri(x) == branch)])
|
|
|
|
"""
|
|
### actually do not exclude files not available locally. This makes
|
|
### possible to repair broken tidy runs, downloading a pkg again
|
|
### makes it get flagged as expired afterwards
|
|
exclude = set()
|
|
for myfile in download_queue:
|
|
if myfile.endswith(etpConst['packagesext']):
|
|
if myfile not in db_files:
|
|
exclude.add(myfile)
|
|
download_queue -= exclude
|
|
"""
|
|
|
|
# filter out packages not in our repository
|
|
upload_queue = set([x for x in upload_queue if x in db_files])
|
|
# filter out weird moves, packages set for upload should not
|
|
# be downloaded
|
|
download_queue = set([x for x in download_queue if x not in \
|
|
upload_queue])
|
|
|
|
return upload_queue, download_queue, removal_queue, fine_queue
|
|
|
|
def _expand_queues(self, upload_queue, download_queue, removal_queue,
|
|
remote_packages_data, repo):
|
|
|
|
metainfo = {
|
|
'removal': 0,
|
|
'download': 0,
|
|
'upload': 0,
|
|
}
|
|
removal = []
|
|
download = []
|
|
do_copy = []
|
|
upload = []
|
|
|
|
for item in removal_queue:
|
|
local_filepath = self._entropy.complete_local_package_path(
|
|
item, repo = repo)
|
|
size = entropy.tools.get_file_size(local_filepath)
|
|
metainfo['removal'] += size
|
|
removal.append((local_filepath, item, size))
|
|
|
|
for item in download_queue:
|
|
|
|
local_filepath = self._entropy.complete_local_upload_package_path(
|
|
item, repo = repo)
|
|
if not os.path.isfile(local_filepath):
|
|
size = remote_packages_data.get(item)
|
|
if size is None:
|
|
size = 0
|
|
size = int(size)
|
|
metainfo['removal'] += size
|
|
download.append((local_filepath, item, size))
|
|
else:
|
|
size = entropy.tools.get_file_size(local_filepath)
|
|
do_copy.append((local_filepath, item, size))
|
|
|
|
for item in upload_queue:
|
|
|
|
local_filepath = self._entropy.complete_local_upload_package_path(
|
|
item, repo = repo)
|
|
|
|
local_filepath_pkgs = self._entropy.complete_local_package_path(
|
|
item, repo = repo)
|
|
if os.path.isfile(local_filepath):
|
|
size = entropy.tools.get_file_size(local_filepath)
|
|
upload.append((local_filepath, item, size))
|
|
else:
|
|
size = entropy.tools.get_file_size(local_filepath_pkgs)
|
|
upload.append((local_filepath_pkgs, item, size))
|
|
metainfo['upload'] += size
|
|
|
|
return upload, download, removal, do_copy, metainfo
|
|
|
|
def _sync_run_removal_queue(self, removal_queue, repo = None):
|
|
|
|
if repo is None:
|
|
repo = self._entropy.default_repository
|
|
branch = self._settings['repositories']['branch']
|
|
|
|
for remove_filepath, rel_path, size in removal_queue:
|
|
|
|
remove_filename = os.path.basename(remove_filepath)
|
|
remove_filepath_hash = remove_filepath + \
|
|
etpConst['packagesmd5fileext']
|
|
remove_filepath_exp = remove_filepath + \
|
|
etpConst['packagesexpirationfileext']
|
|
|
|
self._entropy.output(
|
|
"[repo:%s|%s|%s] %s: %s [%s]" % (
|
|
brown(repo),
|
|
red("sync"),
|
|
brown(branch),
|
|
blue(_("removing package+hash")),
|
|
darkgreen(remove_filename),
|
|
blue(entropy.tools.bytes_into_human(size)),
|
|
),
|
|
importance = 0,
|
|
level = "info",
|
|
header = darkred(" * ")
|
|
)
|
|
|
|
if os.path.isfile(remove_filepath):
|
|
os.remove(remove_filepath)
|
|
if os.path.isfile(remove_filepath_hash):
|
|
os.remove(remove_filepath_hash)
|
|
if os.path.isfile(remove_filepath_exp):
|
|
os.remove(remove_filepath_exp)
|
|
|
|
self._entropy.output(
|
|
"[repo:%s|%s|%s] %s" % (
|
|
brown(repo),
|
|
red(_("sync")),
|
|
brown(branch),
|
|
blue(_("removal complete")),
|
|
),
|
|
importance = 0,
|
|
level = "info",
|
|
header = darkred(" * ")
|
|
)
|
|
|
|
|
|
def _sync_run_copy_queue(self, copy_queue, repo = None):
|
|
|
|
if repo is None:
|
|
repo = self._entropy.default_repository
|
|
branch = self._settings['repositories']['branch']
|
|
|
|
for from_file, rel_file, size in copy_queue:
|
|
from_file_hash = from_file + etpConst['packagesmd5fileext']
|
|
|
|
to_file = self._entropy.complete_local_package_path(rel_file)
|
|
to_file_hash = to_file+etpConst['packagesmd5fileext']
|
|
expiration_file = to_file+etpConst['packagesexpirationfileext']
|
|
|
|
self._entropy.output(
|
|
"[repo:%s|%s|%s] %s: %s" % (
|
|
brown(repo),
|
|
red("sync"),
|
|
brown(branch),
|
|
blue(_("copying file+hash to repository")),
|
|
darkgreen(from_file),
|
|
),
|
|
importance = 0,
|
|
level = "info",
|
|
header = darkred(" * ")
|
|
)
|
|
self._entropy._ensure_dir_path(os.path.dirname(to_file))
|
|
|
|
shutil.copy2(from_file, to_file)
|
|
self._create_file_checksum(from_file, from_file_hash)
|
|
shutil.copy2(from_file_hash, to_file_hash)
|
|
|
|
# clear expiration file
|
|
if os.path.isfile(expiration_file):
|
|
os.remove(expiration_file)
|
|
|
|
|
|
def _sync_run_upload_queue(self, uri, upload_queue, repo = None):
|
|
|
|
if repo is None:
|
|
repo = self._entropy.default_repository
|
|
branch = self._settings['repositories']['branch']
|
|
|
|
crippled_uri = EntropyTransceiver.get_uri_name(uri)
|
|
queue_map = {}
|
|
|
|
for upload_path, rel_path, size in upload_queue:
|
|
|
|
hash_file = upload_path + etpConst['packagesmd5fileext']
|
|
if not os.path.isfile(hash_file):
|
|
entropy.tools.create_md5_file(upload_path)
|
|
|
|
rel_dir = os.path.dirname(rel_path)
|
|
obj = queue_map.setdefault(rel_dir, [])
|
|
obj.append(hash_file)
|
|
obj.append(upload_path)
|
|
|
|
errors = False
|
|
m_fine_uris = set()
|
|
m_broken_uris = set()
|
|
for rel_path, myqueue in queue_map.items():
|
|
|
|
remote_dir = self._entropy.complete_remote_package_relative_path(
|
|
rel_path, repo = repo)
|
|
|
|
uploader = self.TransceiverServerHandler(self._entropy, [uri],
|
|
myqueue, critical_files = myqueue,
|
|
txc_basedir = remote_dir,
|
|
handlers_data = {'branch': branch }, repo = repo)
|
|
|
|
xerrors, xm_fine_uris, xm_broken_uris = uploader.go()
|
|
if xerrors:
|
|
errors = True
|
|
m_fine_uris.update(xm_fine_uris)
|
|
m_broken_uris.update(xm_broken_uris)
|
|
|
|
if errors:
|
|
my_broken_uris = [
|
|
(EntropyTransceiver.get_uri_name(x[0]), x[1]) for \
|
|
x in m_broken_uris]
|
|
reason = my_broken_uris[0][1]
|
|
self._entropy.output(
|
|
"[branch:%s] %s: %s, %s: %s" % (
|
|
brown(branch),
|
|
blue(_("upload errors")),
|
|
red(crippled_uri),
|
|
blue(_("reason")),
|
|
darkgreen(repr(reason)),
|
|
),
|
|
importance = 1,
|
|
level = "error",
|
|
header = darkred(" !!! ")
|
|
)
|
|
return errors, m_fine_uris, m_broken_uris
|
|
|
|
self._entropy.output(
|
|
"[branch:%s] %s: %s" % (
|
|
brown(branch),
|
|
blue(_("upload completed successfully")),
|
|
red(crippled_uri),
|
|
),
|
|
importance = 1,
|
|
level = "info",
|
|
header = blue(" @@ ")
|
|
)
|
|
return errors, m_fine_uris, m_broken_uris
|
|
|
|
|
|
def _sync_run_download_queue(self, uri, download_queue, repo = None):
|
|
|
|
if repo is None:
|
|
repo = self._entropy.default_repository
|
|
branch = self._settings['repositories']['branch']
|
|
|
|
crippled_uri = EntropyTransceiver.get_uri_name(uri)
|
|
|
|
queue_map = {}
|
|
|
|
for download_path, rel_path, size in download_queue:
|
|
hash_file = download_path + etpConst['packagesmd5fileext']
|
|
|
|
rel_dir = os.path.dirname(rel_path)
|
|
obj = queue_map.setdefault(rel_dir, [])
|
|
obj.append(hash_file)
|
|
obj.append(download_path)
|
|
|
|
errors = False
|
|
m_fine_uris = set()
|
|
m_broken_uris = set()
|
|
for rel_path, myqueue in queue_map.items():
|
|
|
|
remote_dir = self._entropy.complete_remote_package_relative_path(
|
|
rel_path, repo = repo)
|
|
|
|
local_basedir = self._entropy.complete_local_package_path(rel_path,
|
|
repo = repo)
|
|
if not os.path.isdir(local_basedir):
|
|
self._entropy._ensure_dir_path(local_basedir)
|
|
|
|
downloader = self.TransceiverServerHandler(
|
|
self._entropy, [uri], myqueue,
|
|
critical_files = myqueue,
|
|
txc_basedir = remote_dir, local_basedir = local_basedir,
|
|
handlers_data = {'branch': branch }, download = True,
|
|
repo = repo)
|
|
|
|
xerrors, xm_fine_uris, xm_broken_uris = downloader.go()
|
|
if xerrors:
|
|
errors = True
|
|
m_fine_uris.update(xm_fine_uris)
|
|
m_broken_uris.update(xm_broken_uris)
|
|
|
|
if errors:
|
|
my_broken_uris = [
|
|
(EntropyTransceiver.get_uri_name(x), y,) \
|
|
for x, y in m_broken_uris]
|
|
reason = my_broken_uris[0][1]
|
|
self._entropy.output(
|
|
"[repo:%s|%s|%s] %s: %s, %s: %s" % (
|
|
brown(repo),
|
|
red(_("sync")),
|
|
brown(branch),
|
|
blue(_("download errors")),
|
|
darkgreen(crippled_uri),
|
|
blue(_("reason")),
|
|
reason,
|
|
),
|
|
importance = 1,
|
|
level = "error",
|
|
header = darkred(" !!! ")
|
|
)
|
|
return errors, m_fine_uris, m_broken_uris
|
|
|
|
self._entropy.output(
|
|
"[repo:%s|%s|%s] %s: %s" % (
|
|
brown(repo),
|
|
red(_("sync")),
|
|
brown(branch),
|
|
blue(_("download completed successfully")),
|
|
darkgreen(crippled_uri),
|
|
),
|
|
importance = 1,
|
|
level = "info",
|
|
header = darkgreen(" * ")
|
|
)
|
|
return errors, m_fine_uris, m_broken_uris
|
|
|
|
def _run_package_files_qa_checks(self, packages_list, repo = None):
|
|
|
|
if repo is None:
|
|
repo = self._entropy.default_repository
|
|
|
|
my_qa = self._entropy.QA()
|
|
|
|
qa_total = len(packages_list)
|
|
qa_count = 0
|
|
qa_some_faulty = []
|
|
|
|
for upload_package in packages_list:
|
|
qa_count += 1
|
|
|
|
self._entropy.output(
|
|
"%s: %s" % (
|
|
purple(_("QA checking package file")),
|
|
darkgreen(os.path.basename(upload_package)),
|
|
),
|
|
importance = 0,
|
|
level = "info",
|
|
header = purple(" @@ "),
|
|
back = True,
|
|
count = (qa_count, qa_total,)
|
|
)
|
|
|
|
result = my_qa.entropy_package_checks(upload_package)
|
|
if not result:
|
|
# call wolfman-911
|
|
qa_some_faulty.append(os.path.basename(upload_package))
|
|
|
|
if qa_some_faulty:
|
|
|
|
for qa_faulty_pkg in qa_some_faulty:
|
|
self._entropy.output(
|
|
"[repo:%s|branch:%s] %s: %s" % (
|
|
brown(repo),
|
|
self._settings['repositories']['branch'],
|
|
red(_("faulty package file, please fix")),
|
|
blue(os.path.basename(qa_faulty_pkg)),
|
|
),
|
|
importance = 1,
|
|
level = "error",
|
|
header = darkred(" @@ ")
|
|
)
|
|
raise EntropyPackageException(
|
|
'EntropyPackageException: cannot continue')
|
|
|
|
|
|
def sync_packages(self, ask = True, pretend = False, packages_check = False,
|
|
repo = None):
|
|
|
|
if repo is None:
|
|
repo = self._entropy.default_repository
|
|
|
|
self._entropy.output(
|
|
"[repo:%s|%s] %s" % (
|
|
repo,
|
|
red(_("sync")),
|
|
darkgreen(_("starting packages sync")),
|
|
),
|
|
importance = 1,
|
|
level = "info",
|
|
header = red(" @@ "),
|
|
back = True
|
|
)
|
|
|
|
|
|
successfull_mirrors = set()
|
|
broken_mirrors = set()
|
|
check_data = ()
|
|
upload_queue_qa_checked = set()
|
|
mirrors_tainted = False
|
|
mirror_errors = False
|
|
mirrors_errors = False
|
|
|
|
for uri in self._entropy.get_remote_mirrors(repo):
|
|
|
|
crippled_uri = EntropyTransceiver.get_uri_name(uri)
|
|
mirror_errors = False
|
|
|
|
self._entropy.output(
|
|
"[repo:%s|%s|branch:%s] %s: %s" % (
|
|
repo,
|
|
red(_("sync")),
|
|
brown(self._settings['repositories']['branch']),
|
|
blue(_("packages sync")),
|
|
bold(crippled_uri),
|
|
),
|
|
importance = 1,
|
|
level = "info",
|
|
header = red(" @@ ")
|
|
)
|
|
|
|
try:
|
|
upload_queue, download_queue, removal_queue, fine_queue, \
|
|
remote_packages_data = self.calculate_packages_to_sync(uri,
|
|
repo)
|
|
except self.socket.error as err:
|
|
self._entropy.output(
|
|
"[repo:%s|%s|branch:%s] %s: %s, %s %s" % (
|
|
repo,
|
|
red(_("sync")),
|
|
self._settings['repositories']['branch'],
|
|
darkred(_("socket error")),
|
|
err,
|
|
darkred(_("on")),
|
|
crippled_uri,
|
|
),
|
|
importance = 1,
|
|
level = "error",
|
|
header = darkgreen(" * ")
|
|
)
|
|
continue
|
|
|
|
if (not upload_queue) and (not download_queue) and \
|
|
(not removal_queue):
|
|
self._entropy.output(
|
|
"[repo:%s|%s|branch:%s] %s: %s" % (
|
|
repo,
|
|
red(_("sync")),
|
|
self._settings['repositories']['branch'],
|
|
darkgreen(_("nothing to do on")),
|
|
crippled_uri,
|
|
),
|
|
importance = 1,
|
|
level = "info",
|
|
header = darkgreen(" * ")
|
|
)
|
|
successfull_mirrors.add(uri)
|
|
continue
|
|
|
|
self._entropy.output(
|
|
"%s:" % (blue(_("Expanding queues")),),
|
|
importance = 1,
|
|
level = "info",
|
|
header = red(" ** ")
|
|
)
|
|
|
|
upload, download, removal, copy_q, metainfo = self._expand_queues(
|
|
upload_queue, download_queue, removal_queue,
|
|
remote_packages_data, repo)
|
|
del upload_queue, download_queue, removal_queue, \
|
|
remote_packages_data
|
|
self._show_sync_queues(upload, download, removal, copy_q, metainfo)
|
|
|
|
if not len(upload)+len(download)+len(removal)+len(copy_q):
|
|
|
|
self._entropy.output(
|
|
"[repo:%s|%s|branch:%s] %s %s" % (
|
|
self._entropy.default_repository,
|
|
red(_("sync")),
|
|
self._settings['repositories']['branch'],
|
|
blue(_("nothing to sync for")),
|
|
crippled_uri,
|
|
),
|
|
importance = 1,
|
|
level = "info",
|
|
header = darkgreen(" @@ ")
|
|
)
|
|
|
|
successfull_mirrors.add(uri)
|
|
continue
|
|
|
|
if pretend:
|
|
successfull_mirrors.add(uri)
|
|
continue
|
|
|
|
if ask:
|
|
rc_sync = self._entropy.ask_question(
|
|
_("Would you like to run the steps above ?"))
|
|
if rc_sync == _("No"):
|
|
continue
|
|
|
|
try:
|
|
|
|
# QA checks
|
|
qa_package_files = [x[0] for x in upload if x[0] \
|
|
not in upload_queue_qa_checked]
|
|
upload_queue_qa_checked |= set(qa_package_files)
|
|
|
|
self._run_package_files_qa_checks(qa_package_files,
|
|
repo = repo)
|
|
|
|
if removal:
|
|
self._sync_run_removal_queue(removal, repo)
|
|
|
|
if copy_q:
|
|
self._sync_run_copy_queue(copy_q, repo)
|
|
|
|
if upload:
|
|
mirrors_tainted = True
|
|
|
|
if upload:
|
|
d_errors, m_fine_uris, \
|
|
m_broken_uris = self._sync_run_upload_queue(uri,
|
|
upload, repo)
|
|
|
|
if d_errors:
|
|
mirror_errors = True
|
|
|
|
if download:
|
|
d_errors, m_fine_uris, \
|
|
m_broken_uris = self._sync_run_download_queue(uri,
|
|
download, repo)
|
|
|
|
if d_errors:
|
|
mirror_errors = True
|
|
if not mirror_errors:
|
|
successfull_mirrors.add(uri)
|
|
else:
|
|
mirrors_errors = True
|
|
|
|
except KeyboardInterrupt:
|
|
self._entropy.output(
|
|
"[repo:%s|%s|branch:%s] %s" % (
|
|
repo,
|
|
red(_("sync")),
|
|
self._settings['repositories']['branch'],
|
|
darkgreen(_("keyboard interrupt !")),
|
|
),
|
|
importance = 1,
|
|
level = "info",
|
|
header = darkgreen(" * ")
|
|
)
|
|
continue
|
|
|
|
except EntropyPackageException as err:
|
|
|
|
mirrors_errors = True
|
|
broken_mirrors.add(uri)
|
|
successfull_mirrors.clear()
|
|
# so that people will realize this is a very bad thing
|
|
self._entropy.output(
|
|
"[repo:%s|%s|branch:%s] %s: %s, %s: %s" % (
|
|
repo,
|
|
red(_("sync")),
|
|
self._settings['repositories']['branch'],
|
|
darkred(_("you must package them again")),
|
|
EntropyPackageException,
|
|
_("error"),
|
|
err,
|
|
),
|
|
importance = 1,
|
|
level = "error",
|
|
header = darkred(" !!! ")
|
|
)
|
|
return mirrors_tainted, mirrors_errors, successfull_mirrors, \
|
|
broken_mirrors, check_data
|
|
|
|
except Exception as err:
|
|
|
|
entropy.tools.print_traceback()
|
|
mirrors_errors = True
|
|
broken_mirrors.add(uri)
|
|
self._entropy.output(
|
|
"[repo:%s|%s|branch:%s] %s: %s, %s: %s" % (
|
|
repo,
|
|
red(_("sync")),
|
|
self._settings['repositories']['branch'],
|
|
darkred(_("exception caught")),
|
|
Exception,
|
|
_("error"),
|
|
err,
|
|
),
|
|
importance = 1,
|
|
level = "error",
|
|
header = darkred(" !!! ")
|
|
)
|
|
|
|
exc_txt = entropy.tools.print_exception(
|
|
returndata = True)
|
|
for line in exc_txt:
|
|
self._entropy.output(
|
|
repr(line),
|
|
importance = 1,
|
|
level = "error",
|
|
header = darkred(": ")
|
|
)
|
|
|
|
if len(successfull_mirrors) > 0:
|
|
self._entropy.output(
|
|
"[repo:%s|%s|branch:%s] %s" % (
|
|
repo,
|
|
red(_("sync")),
|
|
self._settings['repositories']['branch'],
|
|
darkred(
|
|
_("at least one mirror synced properly!")),
|
|
),
|
|
importance = 1,
|
|
level = "error",
|
|
header = darkred(" !!! ")
|
|
)
|
|
continue
|
|
|
|
# if at least one server has been synced successfully, move files
|
|
if (len(successfull_mirrors) > 0) and not pretend:
|
|
self._move_files_over_from_upload(repo)
|
|
|
|
if packages_check:
|
|
check_data = self._entropy.verify_local_packages([], ask = ask,
|
|
repo = repo)
|
|
|
|
return mirrors_tainted, mirrors_errors, successfull_mirrors, \
|
|
broken_mirrors, check_data
|
|
|
|
def _move_files_over_from_upload(self, repo = None):
|
|
|
|
if repo is None:
|
|
repo = self._entropy.default_repository
|
|
|
|
upload_dir = self._entropy._get_local_upload_directory(repo = repo)
|
|
basedir_list = self._entropy._get_basedir_pkg_listing(upload_dir,
|
|
repo = repo)
|
|
|
|
for pkg_rel in basedir_list:
|
|
|
|
source_pkg = self._entropy.complete_local_upload_package_path(
|
|
pkg_rel, repo = repo)
|
|
dest_pkg = self._entropy.complete_local_package_path(pkg_rel,
|
|
repo = repo)
|
|
dest_pkg_dir = os.path.dirname(dest_pkg)
|
|
self._entropy._ensure_dir_path(dest_pkg_dir)
|
|
|
|
try:
|
|
os.rename(source_pkg, dest_pkg)
|
|
except OSError: # on different hard drives?
|
|
shutil.move(source_pkg, dest_pkg)
|
|
|
|
# clear expiration file
|
|
dest_expiration = dest_pkg + etpConst['packagesexpirationfileext']
|
|
if os.path.isfile(dest_expiration):
|
|
os.remove(dest_expiration)
|
|
|
|
def _is_package_expired(self, package_rel, repo = None):
|
|
|
|
pkg_path = self._entropy.complete_local_package_path(package_rel,
|
|
repo = repo)
|
|
pkg_path += etpConst['packagesexpirationfileext']
|
|
if not os.path.isfile(pkg_path):
|
|
return False
|
|
|
|
srv_set = self._settings[self.sys_settings_plugin_id]['server']
|
|
mtime = os.path.getmtime(pkg_path)
|
|
days = srv_set['packages_expiration_days']
|
|
delta = int(days)*24*3600
|
|
currmtime = time.time()
|
|
file_delta = currmtime - mtime
|
|
|
|
if file_delta > delta:
|
|
return True
|
|
return False
|
|
|
|
def _create_expiration_file(self, package_rel, repo = None, gentle = False):
|
|
|
|
pkg_path = self._entropy.complete_local_package_path(package_rel,
|
|
repo = repo)
|
|
pkg_path += etpConst['packagesexpirationfileext']
|
|
if gentle and os.path.isfile(pkg_path):
|
|
return
|
|
f_exp = open(pkg_path, "w")
|
|
f_exp.flush()
|
|
f_exp.close()
|
|
|
|
def _collect_expiring_packages(self, branch, repo = None):
|
|
|
|
dbconn = self._entropy.open_server_repository(just_reading = True,
|
|
repo = repo)
|
|
|
|
database_bins = set(dbconn.listAllDownloads(do_sort = False,
|
|
full_path = True))
|
|
|
|
repo_basedir = self._entropy._get_local_repository_base_directory(
|
|
repo = repo)
|
|
|
|
repo_bins = self._entropy._get_basedir_pkg_listing(repo_basedir,
|
|
repo = repo, branch = branch)
|
|
|
|
# convert to set, so that we can do fast thingszzsd
|
|
repo_bins = set(repo_bins)
|
|
repo_bins -= database_bins
|
|
return repo_bins
|
|
|
|
|
|
def tidy_mirrors(self, ask = True, pretend = False, repo = None):
|
|
|
|
if repo is None:
|
|
repo = self._entropy.default_repository
|
|
|
|
self._entropy.output(
|
|
"[repo:%s|%s|branch:%s] %s" % (
|
|
brown(repo),
|
|
red(_("tidy")),
|
|
blue(self._settings['repositories']['branch']),
|
|
blue(_("collecting expired packages")),
|
|
),
|
|
importance = 1,
|
|
level = "info",
|
|
header = red(" @@ ")
|
|
)
|
|
|
|
branch_data = {}
|
|
errors = False
|
|
branch_data['errors'] = False
|
|
branch = self._settings['repositories']['branch']
|
|
|
|
self._entropy.output(
|
|
"[branch:%s] %s" % (
|
|
brown(branch),
|
|
blue(_("collecting expired packages in the selected branches")),
|
|
),
|
|
importance = 1,
|
|
level = "info",
|
|
header = blue(" @@ ")
|
|
)
|
|
|
|
# collect removed packages
|
|
expiring_packages = self._collect_expiring_packages(branch, repo)
|
|
if expiring_packages:
|
|
|
|
# filter expired packages used by other branches
|
|
# this is done for the sake of consistency
|
|
# --- read packages.db.pkglist, make sure your repository
|
|
# has been ported to latest Entropy
|
|
|
|
branch_pkglist_data = self._read_remote_file_in_branches(
|
|
etpConst['etpdatabasepkglist'], repo = repo,
|
|
excluded_branches = [branch])
|
|
# format data
|
|
for key, val in branch_pkglist_data.items():
|
|
branch_pkglist_data[key] = val.split("\n")
|
|
|
|
for other_branch in branch_pkglist_data:
|
|
branch_pkglist = set(branch_pkglist_data[other_branch])
|
|
expiring_packages -= branch_pkglist
|
|
|
|
removal = []
|
|
for package_rel in expiring_packages:
|
|
expired = self._is_package_expired(package_rel, repo)
|
|
if expired:
|
|
removal.append(package_rel)
|
|
else:
|
|
self._create_expiration_file(package_rel, repo, gentle = True)
|
|
|
|
# fill returning data
|
|
branch_data['removal'] = removal[:]
|
|
|
|
if not removal:
|
|
self._entropy.output(
|
|
"[branch:%s] %s" % (
|
|
brown(branch),
|
|
blue(_("nothing to remove on this branch")),
|
|
),
|
|
importance = 1,
|
|
level = "info",
|
|
header = blue(" @@ ")
|
|
)
|
|
return errors, branch_data
|
|
else:
|
|
self._entropy.output(
|
|
"[branch:%s] %s:" % (
|
|
brown(branch),
|
|
blue(_("these are the expired packages")),
|
|
),
|
|
importance = 1,
|
|
level = "info",
|
|
header = blue(" @@ ")
|
|
)
|
|
for package in removal:
|
|
self._entropy.output(
|
|
"[branch:%s] %s: %s" % (
|
|
brown(branch),
|
|
blue(_("remove")),
|
|
darkgreen(package),
|
|
),
|
|
importance = 1,
|
|
level = "info",
|
|
header = brown(" # ")
|
|
)
|
|
|
|
if pretend:
|
|
return errors, branch_data
|
|
|
|
if ask:
|
|
rc_question = self._entropy.ask_question(
|
|
_("Would you like to continue ?"))
|
|
if rc_question == _("No"):
|
|
return errors, branch_data
|
|
|
|
# split queue by remote directories to work on
|
|
removal_map = {}
|
|
for package_rel in removal:
|
|
rel_path = self._entropy.complete_remote_package_relative_path(
|
|
package_rel, repo = repo)
|
|
rel_dir = os.path.dirname(rel_path)
|
|
obj = removal_map.setdefault(rel_dir, [])
|
|
base_pkg = os.path.basename(package_rel)
|
|
obj.append(base_pkg)
|
|
obj.append(base_pkg+etpConst['packagesmd5fileext'])
|
|
|
|
for uri in self._entropy.get_remote_mirrors(repo):
|
|
|
|
##
|
|
# remove remotely
|
|
##
|
|
|
|
errors = False
|
|
m_fine_uris = set()
|
|
m_broken_uris = set()
|
|
for remote_dir, myqueue in removal_map.items():
|
|
|
|
self._entropy.output(
|
|
"[branch:%s] %s..." % (
|
|
brown(branch),
|
|
blue(_("removing packages remotely")),
|
|
),
|
|
importance = 1,
|
|
level = "info",
|
|
header = blue(" @@ ")
|
|
)
|
|
|
|
destroyer = self.TransceiverServerHandler(
|
|
self._entropy,
|
|
[uri],
|
|
myqueue,
|
|
critical_files = [],
|
|
txc_basedir = remote_dir,
|
|
remove = True,
|
|
repo = repo
|
|
)
|
|
xerrors, xm_fine_uris, xm_broken_uris = destroyer.go()
|
|
if xerrors:
|
|
errors = True
|
|
m_fine_uris.update(xm_fine_uris)
|
|
m_broken_uris.update(xm_broken_uris)
|
|
|
|
if errors:
|
|
my_broken_uris = [
|
|
(EntropyTransceiver.get_uri_name(x[0]), x[1]) \
|
|
for x in m_broken_uris]
|
|
|
|
reason = my_broken_uris[0][1]
|
|
crippled_uri = EntropyTransceiver.get_uri_name(uri)
|
|
self._entropy.output(
|
|
"[branch:%s] %s: %s, %s: %s" % (
|
|
brown(branch),
|
|
blue(_("remove errors")),
|
|
red(crippled_uri),
|
|
blue(_("reason")),
|
|
reason,
|
|
),
|
|
importance = 1,
|
|
level = "warning",
|
|
header = brown(" !!! ")
|
|
)
|
|
branch_data['errors'] = True
|
|
errors = True
|
|
|
|
self._entropy.output(
|
|
"[branch:%s] %s..." % (
|
|
brown(branch),
|
|
blue(_("removing packages locally")),
|
|
),
|
|
importance = 1,
|
|
level = "info",
|
|
header = blue(" @@ ")
|
|
)
|
|
|
|
##
|
|
# remove locally
|
|
##
|
|
|
|
branch_data['removed'] = set()
|
|
for package_rel in removal:
|
|
|
|
package_path = self._entropy.complete_local_package_path(
|
|
package_rel, repo = repo)
|
|
|
|
package_path_hash = package_path + \
|
|
etpConst['packagesmd5fileext']
|
|
package_path_expired = package_path + \
|
|
etpConst['packagesexpirationfileext']
|
|
|
|
my_rm_list = (package_path_hash, package_path,
|
|
package_path_expired)
|
|
for myfile in my_rm_list:
|
|
if os.path.isfile(myfile):
|
|
self._entropy.output(
|
|
"[branch:%s] %s: %s" % (
|
|
brown(branch),
|
|
blue(_("removing")),
|
|
darkgreen(myfile),
|
|
),
|
|
importance = 1,
|
|
level = "info",
|
|
header = brown(" @@ ")
|
|
)
|
|
os.remove(myfile)
|
|
branch_data['removed'].add(myfile)
|
|
|
|
return errors, branch_data
|