diff --git a/server/eit b/server/eit deleted file mode 100755 index 6501faa24..000000000 --- a/server/eit +++ /dev/null @@ -1,389 +0,0 @@ -#!/usr/bin/python2 -O -# -*- coding: utf-8 -*- -""" - - @author: Fabio Erculiani - @contact: lxnay@sabayon.org - @copyright: Fabio Erculiani - @license: GPL-2 - - B{Entropy Package Manager Server Wrapper Tool}. - -""" -import os -import sys -import subprocess -sys.path.insert(0, '../libraries') -sys.path.insert(1, '../client') -sys.path.insert(2, '../server') -sys.path.insert(3, '/usr/lib/entropy/client') -sys.path.insert(4, '/usr/lib/entropy/libraries') -sys.path.insert(5, '/usr/lib/entropy/server') - -from entropy.i18n import _ -import entropy.tools -from text_tools import print_menu -from entropy.output import is_stdout_a_tty, nocolor, print_error, print_info, \ - print_warning, purple, teal -from entropy.const import etpConst, etpUi - -REAGENT_EXEC = "/usr/sbin/reagent" -ACTIVATOR_EXEC = "/usr/sbin/activator" - -help_opts = [ - None, - (0, " ~ eit ~ ", 1, - 'The Stupid Entropy Content Tracker (wrapper) - (C) %s' % ( - entropy.tools.get_year(),) ), - None, - (0, _('Options'), 0, None), - None, - (1, 'add ', 3, _('commit to current repository only the provided packages')), - (1, 'addto ', 1, _('commit to give repository only the provided packages')), - (1, 'bump []', 2, _('bump repository revision, this will force upload')), - (1, 'checkout ', 2, _('switch from a repository to another')), - (1, 'cleanup []', 2, _('clean expired packages from a repository')), - (1, 'commit []', 2, _('commit changes to repository')), - (1, 'cp ', 2, _('copy packages from a repository to another')), - (1, 'deps ', 3, _('edit package dependencies')), - (1, 'depsin ', 1, _('edit package dependencies in given repository')), - (1, 'deptest []', 2, _('edit package dependencies')), - (1, 'fit ', 2, _('inject package files into the current repository')), - (1, 'graph [--complete]', 1, _('show dependency graph of packages')), - (1, 'init ', 2, _('initialize repository (erasing all its content)')), - (1, 'libtest [--dump]', 2, _('look for missing libraries (scan system)')), - (1, 'linktest []', 2, _('look for missing libraries (scan repository metadata)')), - (1, 'list []', 2, _('list packages in repository')), - (1, 'log []', 2, _('show log for repository')), - (1, 'mv ', 2, _('move packages from a repository to another')), - (1, 'own ', 2, _('show owners of files (in current repository)')), - (1, 'push []', 2, _('push committed packages remotely')), - (1, 'pushas ', 1, _('push committed packages to given fake repository')), - (1, 'repack ', 3, _('re-package list of packages in default repository')), - (1, 'repackin ', 1, _('re-package list of packages in given repository')), - (1, 'repo', 4, _('show current repository')), - (1, 'reset []', 2, _('reset repository to remote status')), - (1, 'revgraph [--complete]', 1, _('show reverse dependency graph of packages')), - (1, 'rm ', 3, _('remove packages from current repository')), - (1, 'rmfrom ', 1, _('remove packages from given repository')), - (1, 'search ', 2, _('search for privided package dependencies')), - (1, 'show ', 2, _('show matches for privided package dependencies')), - (1, 'status []', 2, _('show current repositories status')), - (1, 'vacuum []', 2, _('clean unavailable packages from a repository')), - None, -] - -options = sys.argv[1:] - -import re -opt_r = re.compile("^(\\-)([a-z]+)$") -for n in range(len(options)): - if opt_r.match(options[n]): - x = options[n] - del options[n] - options.extend(["-%s" % (d,) for d in x[1:]]) - -# preliminary options parsing -_options = [] -no_color = False -force_color = False -for opt in options: - if opt in ["--nocolor", "-N"]: - nocolor() - no_color = True - elif opt in ["--color", "-C"]: - force_color = True - elif opt in ["--quiet", "-q"]: - etpUi['quiet'] = True - elif opt in ["--verbose", "-v"]: - etpUi['verbose'] = True - elif opt in ["--ask", "-a"]: - etpUi['ask'] = True - elif opt in ["--pretend", "-p"]: - etpUi['pretend'] = True - else: - _options.append(opt) -options = _options - -# Check if we need to disable colors -if (not force_color) and (not is_stdout_a_tty()): - nocolor() - -# print help -if not options or ("--help" in options) or ("-h" in options): - print_menu(help_opts) - if len(options) < 1: - print_error("not enough parameters") - raise SystemExit(1) - raise SystemExit(0) - -def _exec_args(args): - myargs = [] - if etpUi['quiet']: - myargs.append("--quiet") - if etpUi['verbose']: - myargs.append("--verbose") - if etpUi['ask']: - myargs.append("--ask") - if etpUi['pretend']: - myargs.append("--pretend") - if force_color: - myargs.append("--color") - if no_color: - myargs.append("--nocolor") - os.execvpe(args[0], args + myargs, os.environ) - -def get_entropy_server(quiet = False): - oldquiet = etpUi['quiet'] - try: - if quiet: - etpUi['quiet'] = True - from entropy.server.interfaces import Server - return Server(community_repo = etpConst['community']['mode']) - finally: - if quiet: - etpUi['quiet'] = oldquiet - -def _get_available_repositories(): - server = None - try: - server = get_entropy_server(quiet = True) - repository_ids = server.repositories() - return repository_ids - finally: - if server is not None: - server.shutdown() - -main_cmd = options.pop(0) - -if main_cmd == "status": - if options: - avail_repos = _get_available_repositories() - if options[0] in avail_repos: - os.environ['ETP_REPO'] = options.pop(0) - _exec_args([REAGENT_EXEC, "status"] + options) - -elif main_cmd == "add" and options: - _exec_args([REAGENT_EXEC, "update", "--atoms"] + options) - -elif main_cmd == "addto" and options and len(options) > 1: - avail_repos = _get_available_repositories() - if options[0] in avail_repos: - os.environ['ETP_REPO'] = options.pop(0) - _exec_args([REAGENT_EXEC, "update", "--atoms"] + options) - -elif main_cmd == "repack" and options: - _exec_args([REAGENT_EXEC, "update", "--atoms"] + options + \ - ["--repackage"] + options) - -elif main_cmd == "repackin" and options and len(options) > 1: - avail_repos = _get_available_repositories() - if options[0] in avail_repos: - os.environ['ETP_REPO'] = options.pop(0) - _exec_args([REAGENT_EXEC, "update", "--atoms"] + options + \ - ["--repackage"] + options) - -elif main_cmd == "bump": - if options: - avail_repos = _get_available_repositories() - if options[0] in avail_repos: - os.environ['ETP_REPO'] = options.pop(0) - _exec_args([REAGENT_EXEC, "repo", "bump"]) - -elif (main_cmd == "checkout") and options and len(options) < 2: - repository_id = options.pop(0) - _exec_args([REAGENT_EXEC, "repo", "default", repository_id]) - -elif main_cmd == "cleanup": - if options: - avail_repos = _get_available_repositories() - if options[0] in avail_repos: - os.environ['ETP_REPO'] = options.pop(0) - _exec_args([ACTIVATOR_EXEC, "tidy"]) - -elif main_cmd == "vacuum": - if options: - avail_repos = _get_available_repositories() - if options[0] in avail_repos: - os.environ['ETP_REPO'] = options.pop(0) - _exec_args([ACTIVATOR_EXEC, "repo", "vacuum"]) - -elif main_cmd == "commit": - if options: - avail_repos = _get_available_repositories() - if options[0] in avail_repos: - os.environ['ETP_REPO'] = options.pop(0) - _exec_args([REAGENT_EXEC, "update"] + options) - else: - _exec_args([REAGENT_EXEC, "update"] + options) - -elif (main_cmd == "fit") and options: - _exec_args([REAGENT_EXEC, "inject"] + options) - -elif main_cmd == "list": - myopts = [] - if options: - avail_repos = _get_available_repositories() - if options[0] in avail_repos: - myopts += [options[0]] - _exec_args([REAGENT_EXEC, "query", "list"] + myopts) - -elif main_cmd == "graph" and options and \ - not ((len(set(options)) == 1) and ("--complete" in options)): - _exec_args([REAGENT_EXEC, "query", "graph"] + options) - -elif main_cmd == "revgraph" and options and \ - not ((len(set(options)) == 1) and ("--complete" in options)): - _exec_args([REAGENT_EXEC, "query", "revgraph"] + options) - -elif (main_cmd == "init") and options and len(options) < 2: - repository_id = options.pop(0) - avail_repos = _get_available_repositories() - if repository_id in avail_repos: - os.environ['ETP_REPO'] = repository_id - _exec_args([REAGENT_EXEC, "repo", "init"]) - -elif main_cmd == "libtest": - lt_options = [] - if "--dump" in options: - lt_options.append("--dump") - _exec_args([REAGENT_EXEC, "libtest"] + lt_options) - -elif main_cmd == "linktest": - _exec_args([REAGENT_EXEC, "linktest"] + options) - -elif main_cmd == "deptest": - if options: - avail_repos = _get_available_repositories() - if options[0] in avail_repos: - os.environ['ETP_REPO'] = options.pop(0) - _exec_args([REAGENT_EXEC, "deptest"]) - -elif (main_cmd == "own") and options: - _exec_args([REAGENT_EXEC, "query", "belongs"] + options) - -elif main_cmd == "push": - if options: - avail_repos = _get_available_repositories() - if options[0] in avail_repos: - os.environ['ETP_REPO'] = options.pop(0) - _exec_args([ACTIVATOR_EXEC, "sync"] + options) - -elif main_cmd == "pushas" and len(options) == 2: - avail_repos = _get_available_repositories() - if options[0] in avail_repos: - os.environ['ETP_REPO'] = options.pop(0) - _exec_args([ACTIVATOR_EXEC, "syncas"] + options) - -elif main_cmd == "show" and options: - _exec_args([REAGENT_EXEC, "query", "match"] + options) - -elif main_cmd == "search" and options: - _exec_args([REAGENT_EXEC, "query", "search"] + options) - -elif main_cmd == "mv" and len(options) > 2: - from_repo = options.pop(0) - to_repo = options.pop(0) - _exec_args([REAGENT_EXEC, "repo", "move", from_repo, to_repo] + options) - -elif main_cmd == "cp" and len(options) > 2: - avail_repos = _get_available_repositories() - from_repo = options.pop(0) - to_repo = options.pop(0) - if (from_repo in avail_repos) and (to_repo in avail_repos): - _exec_args([REAGENT_EXEC, "repo", "copy", from_repo, to_repo] + options) - -elif main_cmd == "deps" and options: - server = None - repository_id = None - try: - server = get_entropy_server(quiet = True) - # get current repository id - repository_id = server.repository() - finally: - if server is not None: - server.shutdown() - - _exec_args([REAGENT_EXEC, "repo", "package-dep", repository_id] + options) - -elif main_cmd == "depsin" and len(options) > 1: - avail_repos = _get_available_repositories() - repository_id = options.pop(0) - if repository_id in avail_repos: - _exec_args([REAGENT_EXEC, "repo", "package-dep", repository_id] + options) - -elif main_cmd == "rm" and options: - _exec_args([REAGENT_EXEC, "repo", "remove"] + options) - -elif main_cmd == "rmfrom" and options and len(options) > 1: - avail_repos = _get_available_repositories() - if options[0] in avail_repos: - os.environ['ETP_REPO'] = options.pop(0) - _exec_args([REAGENT_EXEC, "repo", "remove"] + options) - -elif main_cmd == "repo": - server = None - try: - server = get_entropy_server(quiet = True) - repository_id = server.repository() - repository_ids = server.repositories() - for repo_id in sorted(repository_ids): - if repo_id == repository_id: - print_warning(purple(repo_id) + " *") - else: - print_info(teal(repo_id)) - raise SystemExit(0) - finally: - if server is not None: - server.shutdown() - -elif main_cmd == "reset": - if options: - avail_repos = _get_available_repositories() - if options[0] in avail_repos: - os.environ['ETP_REPO'] = options.pop(0) - - server = None - try: - server = get_entropy_server() - rev_path = server._get_local_repository_revision_file( - server.repository()) - try: - with open(rev_path, "w") as rev_f: - rev_f.write("0\n") - except (IOError, OSError) as err: - print_error("%s: %s" % (_("reset error"), err)) - raise SystemExit(1) - finally: - if server is not None: - server.shutdown() - - _exec_args([ACTIVATOR_EXEC, "repo", "sync"]) - -elif main_cmd == "log": - if options: - avail_repos = _get_available_repositories() - if options[0] in avail_repos: - os.environ['ETP_REPO'] = options.pop(0) - - server = None - try: - server = get_entropy_server() - changelog_path = server._get_local_repository_compressed_changelog_file( - server.repository()) - if os.path.isfile(changelog_path) and os.access(changelog_path, os.R_OK): - proc = subprocess.Popen( - "/bin/bzcat \"%s\" | ${PAGER:-/usr/bin/less}" % ( - changelog_path,), shell = True) - raise SystemExit(proc.wait()) - else: - print_error("log is not available") - raise SystemExit(1) - finally: - if server is not None: - server.shutdown() - -else: - print_menu(help_opts) - print_error("wrong parameters") - raise SystemExit(1) diff --git a/server/eit.py b/server/eit.py new file mode 100755 index 000000000..846b1c533 --- /dev/null +++ b/server/eit.py @@ -0,0 +1,14 @@ +#!/usr/bin/python2 +import sys + +# Entropy imports +sys.path.insert(0, "/usr/lib/entropy/libraries") +sys.path.insert(0, "/usr/lib/entropy/client") +sys.path.insert(0, "/usr/lib/entropy/server") +sys.path.insert(0, "../libraries") +sys.path.insert(0, "../server") +sys.path.insert(0, "../client") + +from eit.main import main +sys.argv[0] = "eit" +main() diff --git a/server/eit/__init__.py b/server/eit/__init__.py new file mode 100644 index 000000000..b0e064f6f --- /dev/null +++ b/server/eit/__init__.py @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- +""" + + @author: Fabio Erculiani + @contact: lxnay@sabayon.org + @copyright: Fabio Erculiani + @license: GPL-2 + + B{Entropy Infrastructure Toolkit}. + +""" diff --git a/server/eit/commands/__init__.py b/server/eit/commands/__init__.py new file mode 100644 index 000000000..b0e064f6f --- /dev/null +++ b/server/eit/commands/__init__.py @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- +""" + + @author: Fabio Erculiani + @contact: lxnay@sabayon.org + @copyright: Fabio Erculiani + @license: GPL-2 + + B{Entropy Infrastructure Toolkit}. + +""" diff --git a/server/eit/commands/command.py b/server/eit/commands/command.py new file mode 100644 index 000000000..29d07dbb2 --- /dev/null +++ b/server/eit/commands/command.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +""" + + @author: Fabio Erculiani + @contact: lxnay@sabayon.org + @copyright: Fabio Erculiani + @license: GPL-2 + + B{Entropy Infrastructure Toolkit}. + +""" +from entropy.const import etpConst +from entropy.server.interfaces import Server +from entropy.core.settings.base import SystemSettings + +class EitCommand(object): + """ + Base class for Eit commands + """ + + # Set this to the command name from where this object + # gets triggered (for eit help, "help" is the NAME + # that should be set). + NAME = None + + def __init__(self, args): + self._args = args + + def parse(self): + """ + Parse the actual arguments and return + the function that should be called and + its arguments. The function signature is: + int function([list of args]) + The return value represents the exit status + of the "command" + """ + raise NotImplementedError() + + def _entropy(self, *args, **kwargs): + """ + Return the Entropy Server object. + This method is not thread safe. + """ + if "community_repo" not in kwargs: + kwargs["community_repo"] = etpConst['community']['mode'] + return Server(*args, **kwargs) + + def _settings(self): + """ + Return a SystemSettings instance. + """ + return SystemSettings() diff --git a/server/eit/commands/commit.py b/server/eit/commands/commit.py new file mode 100644 index 000000000..4149e6527 --- /dev/null +++ b/server/eit/commands/commit.py @@ -0,0 +1,368 @@ +# -*- coding: utf-8 -*- +""" + + @author: Fabio Erculiani + @contact: lxnay@sabayon.org + @copyright: Fabio Erculiani + @license: GPL-2 + + B{Entropy Infrastructure Toolkit}. + +""" +import sys +import os +import argparse + +from entropy.const import etpConst +from entropy.i18n import _ +from entropy.exceptions import PermissionDenied +from entropy.output import print_info, print_warning, print_error, \ + darkgreen, teal, brown, darkred, bold, purple, blue, red, green + +from text_tools import print_table + +import entropy.tools + +from eit.commands.descriptor import EitCommandDescriptor +from eit.commands.command import EitCommand + + +class EitCommit(EitCommand): + """ + Main Eit commit command. + """ + + NAME = "commit" + + def __init__(self, args): + EitCommand.__init__(self, args) + # ask user before any critical operation + self._ask = True + # interactively ask for packages to be staged, etc + self._interactive = False + # list of package dependencies to re-package, if any + self._repackage = [] + # execute actions only for given atoms, if any + self._atoms = [] + + def parse(self): + descriptor = EitCommandDescriptor.obtain_descriptor( + EitCommit.NAME) + parser = argparse.ArgumentParser( + description=descriptor.get_description(), + formatter_class=argparse.RawDescriptionHelpFormatter, + prog="%s %s" % (sys.argv[0], EitCommit.NAME)) + + parser.add_argument("repo", nargs='?', default=None, + metavar="", help="repository id") + parser.add_argument("--interactive", action="store_true", + default=False, + help=_("selectively pick changes")) + parser.add_argument("--quick", action="store_true", + default=False, + help=_("no stupid questions")) + + try: + nsargs = parser.parse_args(self._args) + except IOError as err: + print_error("error: %s" % (err.strerror,)) + return parser.print_help, [] + + self._interactive = nsargs.interactive + if not self._interactive: + self._ask = not nsargs.quick + + return self._commit, [nsargs.repo] + + def _commit(self, repo): + """ + Commit command body. + """ + server = None + acquired = False + try: + try: + server = self._entropy(default_repository=repo) + except PermissionDenied as err: + print_error(err.value) + return 1 + acquired = entropy.tools.acquire_entropy_locks(server) + if not acquired: + print_error( + darkgreen(_("Another Entropy is currently running.")) + ) + return 1 + return self.__commit(server) + finally: + if server is not None: + if acquired: + entropy.tools.release_entropy_locks(server) + server.shutdown() + + def __commit(self, entropy_server): + to_be_added = set() + to_be_removed = set() + to_be_injected = set() + + key_sorter = lambda x: \ + entropy_server.open_repository(x[1]).retrieveAtom(x[0]) + repository_id = entropy_server.repository() + generated_packages = [] + + if self._repackage: + + packages = [] + dbconn = entropy_server.open_server_repository( + repository_id, read_only = True, + no_upload = True) + + spm = entropy_server.Spm() + for item in self._repackage: + match = dbconn.atomMatch(item) + if match[0] == -1: + print_warning(darkred(" !!! ") + \ + red(_("Cannot match"))+" "+bold(item)) + else: + cat = dbconn.retrieveCategory(match[0]) + name = dbconn.retrieveName(match[0]) + version = dbconn.retrieveVersion(match[0]) + spm_pkg = os.path.join(cat, name + "-" + version) + spm_build = \ + spm.get_installed_package_build_script_path( + spm_pkg) + spm_pkg_dir = os.path.dirname(spm_build) + if os.path.isdir(spm_pkg_dir): + packages.append((spm_pkg, 0)) + + if packages: + to_be_added |= set(packages) + else: + print_info(brown(" * ") + \ + red(_("No valid packages to repackage."))) + + # normal scanning + print_info(brown(" * ") + \ + red("%s..." % (_("Scanning database for differences"),) )) + try: + myadded, to_be_removed, to_be_injected = \ + entropy_server.scan_package_changes() + except KeyboardInterrupt: + return 1 + to_be_added |= myadded + + if self._atoms: + to_be_removed.clear() + to_be_injected.clear() + tba = dict(((x[0], x,) for x in to_be_added)) + tb_added_new = set() + for myatom in self._atoms: + if myatom in tba: + tb_added_new.add(tba.get(myatom)) + continue + try: + inst_myatom = entropy_server.Spm( + ).match_installed_package(myatom) + except KeyError: + print_warning(darkred(" !!! ") + \ + red(_("Invalid package")) + " " + bold(myatom)) + continue + if inst_myatom in tba: + tb_added_new.add(tba.get(inst_myatom)) + to_be_added = tb_added_new + + if not (len(to_be_removed)+len(to_be_added)+len(to_be_injected)): + print_info(brown(" * ") + \ + red("%s." % (_("Zarro thinggz totoo"),))) + return 0 + + if to_be_injected: + print_info(brown(" @@ ") + \ + blue("%s:" % ( + _("These would be marked as injected"),) )) + for idpackage, repoid in sorted(to_be_injected, + key = key_sorter): + dbconn = entropy_server.open_server_repository(repoid, + read_only = True, no_upload = True) + atom = dbconn.retrieveAtom(idpackage) + print_info(brown(" # ") + "["+blue(repoid) + "] " + \ + red(atom)) + if self._ask: + rc = entropy_server.ask_question( + ">> %s" % (_("Do it now ?"),)) + else: + rc = _("Yes") + + if rc == _("Yes"): + for idpackage, repoid in sorted(to_be_injected, + key = key_sorter): + dbconn = entropy_server.open_server_repository(repoid, + read_only = True, no_upload = True) + atom = dbconn.retrieveAtom(idpackage) + print_info(brown(" <> ") + \ + blue("%s: " % (_("Transforming"),) )+red(atom)) + entropy_server._transform_package_into_injected( + idpackage, repoid) + print_info(brown(" @@ ") + \ + blue("%s." % (_("Transform complete"),) )) + + def show_rm(idpackage, repoid): + dbconn = entropy_server.open_server_repository(repoid, + read_only = True, no_upload = True) + atom = dbconn.retrieveAtom(idpackage) + exp_string = '' + pkg_expired = entropy_server._is_match_expired( + (idpackage, repoid,)) + if pkg_expired: + exp_string = "|%s" % (purple(_("expired")),) + print_info(brown(" # ") + "["+blue(repoid) + \ + exp_string + "] " + red(atom)) + + if self._interactive and to_be_removed: + print_info(brown(" @@ ") + \ + blue(_("What packages do you want to remove ?"))) + new_to_be_removed = set() + for idpackage, repoid in sorted(to_be_removed, + key = key_sorter): + show_rm(idpackage, repoid) + rc = entropy_server.ask_question( + ">> %s" % (_("Remove this package?"),)) + if rc == _("Yes"): + new_to_be_removed.add((idpackage, repoid,)) + to_be_removed = new_to_be_removed + + if to_be_removed: + + print_info(brown(" @@ ") + blue("%s:" % ( + _("These would be removed from repository"),) )) + for idpackage, repoid in sorted(to_be_removed, + key = key_sorter): + show_rm(idpackage, repoid) + + if self._ask: + rc = entropy_server.ask_question( + ">> %s" % ( + _("Would you like to remove them now ?"),) ) + else: + rc = _("Yes") + + if rc == _("Yes"): + remdata = {} + for idpackage, repoid in to_be_removed: + if repoid not in remdata: + remdata[repoid] = set() + remdata[repoid].add(idpackage) + for repoid in remdata: + entropy_server.remove_packages(repoid, + remdata[repoid]) + + if self._interactive and to_be_added: + print_info(brown(" @@ ") + \ + blue(_("What packages do you want to add ?"))) + new_to_be_added = set() + for tb_atom, tb_counter in sorted(to_be_added, + key = lambda x: x[0]): + print_info(brown(" # ") + red(tb_atom)) + rc = entropy_server.ask_question( + ">> %s" % (_("Add this package?"),)) + if rc == _("Yes"): + new_to_be_added.add((tb_atom, tb_counter,)) + to_be_added = new_to_be_added + + if to_be_added: + + print_info(brown(" @@ ") + \ + blue("%s:" % ( + _("These would be added or updated"),) )) + items = sorted([x[0] for x in to_be_added]) + for item in items: + item_txt = purple(item) + + # this is a spm atom + spm_key = entropy.dep.dep_getkey(item) + try: + spm_slot = entropy_server.Spm( + ).get_installed_package_metadata(item, "SLOT") + spm_repo = entropy_server.Spm( + ).get_installed_package_metadata( + item, "repository") + except KeyError: + spm_slot = None + spm_repo = None + + # + # inform user about SPM repository sources moves !! + # + etp_repo = None + if spm_repo is not None: + pkg_id, repo_id = entropy_server.atom_match(spm_key, + match_slot = spm_slot) + if repo_id != 1: + repo_db = entropy_server.open_server_repository( + repo_id, just_reading = True) + etp_repo = repo_db.retrieveSpmRepository(pkg_id) + + if (etp_repo is not None) and \ + (etp_repo != spm_repo): + item_txt += ' [%s {%s=>%s}]' % ( + bold(_("warning")), + darkgreen(etp_repo), blue(spm_repo),) + + print_info(brown(" # ") + item_txt) + + if self._ask: + rc = entropy_server.ask_question(">> %s (%s %s)" % ( + _("Would you like to package them now ?"), + _("inside"), + repository_id, + ) + ) + if rc == _("No"): + return 0 + + problems = entropy_server._check_config_file_updates() + if problems: + return 1 + + # package them + print_info(brown(" @@ ") + \ + blue("%s..." % (_("Compressing packages"),) )) + store_dir = entropy_server._get_local_store_directory( + repository_id) + for x in sorted(to_be_added): + print_info(brown(" # ") + teal(x[0])) + try: + pkg_list = entropy_server.Spm().generate_package(x[0], + store_dir) + generated_packages.append(pkg_list) + except OSError: + entropy.tools.print_traceback() + print_info(brown(" !!! ")+bold("%s..." % ( + _("Ignoring broken Spm entry, please recompile it"),) ) + ) + + if not generated_packages: + print_info(brown(" * ")+red(_("Nothing to do, check later."))) + # then exit gracefully + return 0 + + etp_pkg_files = [(pkg_list, False) for pkg_list in \ + generated_packages] + idpackages = entropy_server.add_packages_to_repository( + repository_id, etp_pkg_files) + + if idpackages: + # checking dependencies and print issues + entropy_server.extended_dependencies_test([repository_id]) + entropy_server.close_repositories() + print_info(green(" * ") + red("%s: " % (_("Statistics"),) ) + \ + blue("%s: " % (_("Entries handled"),) ) + \ + bold(str(len(idpackages)))) + return 0 + + +EitCommandDescriptor.register( + EitCommandDescriptor( + EitCommit, + EitCommit.NAME, + _("commit changes to repository")) + ) diff --git a/server/eit/commands/descriptor.py b/server/eit/commands/descriptor.py new file mode 100644 index 000000000..3c3744fdc --- /dev/null +++ b/server/eit/commands/descriptor.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- +""" + + @author: Fabio Erculiani + @contact: lxnay@sabayon.org + @copyright: Fabio Erculiani + @license: GPL-2 + + B{Entropy Infrastructure Toolkit}. + Eit Command descriptor class. + +""" + +class EitCommandDescriptor(object): + """ + EitCommand descriptor object used for + help information purposes. + """ + + EIT_COMMANDS = [] + EIT_COMMANDS_MAP = {} + + @staticmethod + def register(descriptor): + """ + Register an EitCommandDescriptor object + """ + EitCommandDescriptor.EIT_COMMANDS.append(descriptor) + EitCommandDescriptor.EIT_COMMANDS_MAP[descriptor.get_name()] = \ + descriptor + + @staticmethod + def obtain(): + """ + Get the list of registered EitCommandDescriptor object + """ + return EitCommandDescriptor.EIT_COMMANDS[:] + + @staticmethod + def obtain_descriptor(name): + """ + Get a registered EitCommandDescritor object + through its name. + + @param name: name of the EitCommandDescriptor + @type name: string + @raise KeyError: if name isn't bound to any + EitCommandDescriptor object. + """ + return EitCommandDescriptor.EIT_COMMANDS_MAP[name] + + def __init__(self, klass, name, description): + self._klass = klass + self._name = name + self._description = description + + def get_class(self): + """ + Get EitCommand class bound to this command + """ + return self._klass + + def get_name(self): + """ + Get EitCommand name + """ + return self._name + + def get_description(self): + """ + Get EitCommand description + """ + return self._description diff --git a/server/eit/commands/help.py b/server/eit/commands/help.py new file mode 100644 index 000000000..2c8522a86 --- /dev/null +++ b/server/eit/commands/help.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +""" + + @author: Fabio Erculiani + @contact: lxnay@sabayon.org + @copyright: Fabio Erculiani + @license: GPL-2 + + B{Entropy Infrastructure Toolkit}. + +""" +import argparse + +from entropy.i18n import _ + +from eit.commands.descriptor import EitCommandDescriptor +from eit.commands.command import EitCommand + +class EitHelp(EitCommand): + """ + Main Eit help command. + """ + + NAME = "help" + + def parse(self): + """ + Parse help command + """ + return self._show_help, [] + + def _show_help(self, *args): + parser = argparse.ArgumentParser( + description=_("Entropy Infrastructure Toolkit"), + epilog="http://www.sabayon.org", + formatter_class=argparse.RawDescriptionHelpFormatter) + + descriptors = EitCommandDescriptor.obtain() + descriptors.sort(key = lambda x: x.get_name()) + group = parser.add_argument_group("command", "available commands") + for descriptor in descriptors: + group.add_argument(descriptor.get_name(), + help=descriptor.get_description(), + action="store_true") + parser.print_help() + if not self._args: + return 1 + return 0 + +EitCommandDescriptor.register( + EitCommandDescriptor( + EitHelp, + EitHelp.NAME, + _("this help")) + ) diff --git a/server/eit/commands/status.py b/server/eit/commands/status.py new file mode 100644 index 000000000..44ab7812c --- /dev/null +++ b/server/eit/commands/status.py @@ -0,0 +1,179 @@ +# -*- coding: utf-8 -*- +""" + + @author: Fabio Erculiani + @contact: lxnay@sabayon.org + @copyright: Fabio Erculiani + @license: GPL-2 + + B{Entropy Infrastructure Toolkit}. + +""" +import sys +import os +import argparse + +from entropy.const import etpConst +from entropy.i18n import _ +from entropy.exceptions import PermissionDenied +from entropy.output import print_info, print_error, darkgreen, \ + teal, brown, darkred, bold, purple, blue + +from text_tools import print_table + +import entropy.tools + +from eit.commands.descriptor import EitCommandDescriptor +from eit.commands.command import EitCommand + + +class EitStatus(EitCommand): + """ + Main Eit status command. + """ + + NAME = "status" + + def parse(self): + descriptor = EitCommandDescriptor.obtain_descriptor( + EitStatus.NAME) + parser = argparse.ArgumentParser( + description=descriptor.get_description(), + formatter_class=argparse.RawDescriptionHelpFormatter, + prog="%s %s" % (sys.argv[0], EitStatus.NAME)) + + parser.add_argument("repo", nargs='?', default=None, + metavar="", help="repository id") + + try: + nsargs = parser.parse_args(self._args) + except IOError as err: + print_error("error: %s" % (err.strerror,)) + return parser.print_help, [] + + return self._status, [nsargs.repo] + + def _status(self, repo): + """ + Status command body. + """ + server = None + acquired = False + try: + try: + server = self._entropy(default_repository=repo) + except PermissionDenied as err: + print_error(err.value) + return 1 + acquired = entropy.tools.acquire_entropy_locks(server) + if not acquired: + print_error( + darkgreen(_("Another Entropy is currently running.")) + ) + return 1 + return self.__status(server) + finally: + if server is not None: + if acquired: + entropy.tools.release_entropy_locks(server) + server.shutdown() + + def __status(self, entropy_server): + plugin_id = etpConst['system_settings_plugins_ids']['server_plugin'] + repos_data = self._settings()[plugin_id]['server']['repositories'] + repo_id = entropy_server.repository() + + repo_data = repos_data[repo_id] + repo_rev = entropy_server.local_repository_revision(repo_id) + store_dir = entropy_server._get_local_store_directory(repo_id) + upload_basedir = entropy_server._get_local_upload_directory(repo_id) + upload_files, upload_packages = \ + entropy_server.Mirrors._calculate_local_upload_files(repo_id) + key_sorter = lambda x: \ + entropy_server.open_repository(x[1]).retrieveAtom(x[0]) + + to_be_added, to_be_removed, to_be_injected = \ + entropy_server.scan_package_changes() + + to_be_added = [x[0] for x in to_be_added] + to_be_added.sort() + + toc = [] + + toc.append("[%s] %s" % (purple(repo_id), + brown(repo_data['description']),)) + toc.append((" %s:" % (blue(_("local revision")),), + str(repo_rev),)) + + store_pkgs = [] + if os.path.isdir(store_dir): + store_pkgs = os.listdir(store_dir) + + toc.append((" %s:" % (darkgreen(_("stored packages")),), + str(len(store_pkgs)),)) + for pkg_rel in sorted(store_pkgs): + toc.append((" ", brown(pkg_rel))) + + toc.append((" %s:" % (darkgreen(_("upload packages")),), + str(upload_files),)) + for pkg_rel in sorted(upload_packages): + toc.append((" ", brown(pkg_rel))) + + unstaged_len = len(to_be_added) + len(to_be_removed) + \ + len(to_be_injected) + toc.append((" %s:" % (darkgreen(_("unstaged packages")),), + str(unstaged_len),)) + + print_table(toc) + del toc[:] + print_info("") + + def _get_spm_slot_repo(pkg_atom): + try: + spm_slot = entropy_server.Spm( + ).get_installed_package_metadata(pkg_atom, "SLOT") + spm_repo = entropy_server.Spm( + ).get_installed_package_metadata(pkg_atom, + "repository") + except KeyError: + spm_repo = None + spm_slot = None + return spm_slot, spm_repo + + for pkg_atom in to_be_added: + spm_slot, spm_repo = _get_spm_slot_repo(pkg_atom) + + pkg_str = teal(pkg_atom) + if spm_repo is not None: + pkg_id, repo_id = entropy_server.atom_match(pkg_atom, + match_slot = spm_slot) + if pkg_id != -1: + etp_repo = entropy_server.open_repository( + repo_id).retrieveSpmRepository(pkg_id) + if etp_repo != spm_repo: + pkg_str += " [%s=>%s]" % ( + etp_repo, spm_repo,) + toc.append((" %s:" % (purple(_("add")),), teal(pkg_str))) + + for package_id, repo_id in sorted(to_be_removed, key = key_sorter): + pkg_atom = entropy_server.open_repository(repo_id + ).retrieveAtom(package_id) + toc.append((" %s:" % (darkred(_("remove")),), + brown(pkg_atom))) + + for package_id, repo_id in sorted(to_be_injected, + key = key_sorter): + pkg_atom = entropy_server.open_repository(repo_id + ).retrieveAtom( package_id) + toc.append((" %s:" % (bold(_("switch injected")),), + darkgreen(pkg_atom))) + + print_table(toc) + return 0 + +EitCommandDescriptor.register( + EitCommandDescriptor( + EitStatus, + EitStatus.NAME, + _("show repository status")) + ) diff --git a/server/eit/main.py b/server/eit/main.py new file mode 100644 index 000000000..5c6c26bd3 --- /dev/null +++ b/server/eit/main.py @@ -0,0 +1,82 @@ +# -*- coding: utf-8 -*- +""" + + @author: Fabio Erculiani + @contact: lxnay@sabayon.org + @copyright: Fabio Erculiani + @license: GPL-2 + + B{Entropy Infrastructure Toolkit}. + +""" +import os +import sys +import argparse + +from entropy.i18n import _ +from entropy.output import print_error +import entropy.tools + +from eit.commands.status import EitStatus +from eit.commands.help import EitHelp +from eit.commands.commit import EitCommit + + +def handle_exception(exc_class, exc_instance, exc_tb): + + # restore original exception handler, to avoid loops + uninstall_exception_handler() + entropy.tools.kill_threads() + + if exc_class is KeyboardInterrupt: + raise SystemExit(1) + + # always slap exception data (including stack content) + entropy.tools.print_exception(tb_data = exc_tb) + + raise exc_instance + +def install_exception_handler(): + sys.excepthook = handle_exception + +def uninstall_exception_handler(): + sys.excepthook = sys.__excepthook__ + +def main(): + + install_exception_handler() + + args_map = { + EitHelp.NAME: EitHelp, + "-h": EitHelp, + "--help": EitHelp, + EitStatus.NAME: EitStatus, + EitCommit.NAME: EitCommit, + } + + args = sys.argv[1:] + cmd = None + if args: + cmd = args[0] + args = args[1:] + cmd_class = args_map.get(cmd) + + if cmd_class is None: + cmd_class = args_map.get(EitHelp.NAME) + + # non-root users not allowed + allowed = True + if os.getuid() != 0 and \ + cmd_class is not EitHelp: + cmd_class = EitHelp + allowed = False + + cmd_obj = cmd_class(args) + func, func_args = cmd_obj.parse() + exit_st = func(*func_args) + if allowed: + raise SystemExit(exit_st) + else: + print_error(_("superuser access required")) + raise SystemExit(1) +