Newer
Older
# -*- coding: utf-8 -*-
# Started 30/06/2011 by Seblu <seblu@seblu.net>
'''
InstallSystems Command line Tool
'''
import os
import time
import datetime
import warnings
import installsystems.tools as istools
from installsystems.printer import *
from installsystems.repository import Repository
from installsystems.repository import RepositoryManager
from installsystems.repository import RepositoryConfig
from installsystems.image import PackageImage, SourceImage
from installsystems.config import MainConfigFile, RepoConfigFile
################################################################################
# Common functions
################################################################################
Load repositories on a repository manager
'''
# remove cache is asked
args.repo_filter = Repository.split_repository_list(args.repo_filter)
repoman = RepositoryManager(args.cache, timeout=args.timeout,
filter=args.repo_filter, search=args.repo_search)
# register repositories (order matter)
if args.repo_path != "":
repoman.register(RepositoryConfig(istools.smd5sum(args.repo_path)[:8],
Seblu
committed
for repoconf in RepoConfigFile(args.repo_config).repos:
repoman.register(repoconf, nosync=args.no_sync)
def get_images(patterns, repoman, local=True, min=None, max=None):
'''
Select and load a package image from a standard naming type
Allowed type are a direct filename on filesystem
or [repo/]image[:version]
Return the repository as second argument
'''
for pattern in patterns:
# check if image is a local file
if local and istools.isfile(pattern) and os.path.isfile(pattern):
ans.append((pattern, None))
else: # we need to find image in a repository
ans += sorted(repoman.select_images([pattern]).items())
# check selected images cound
if min is not None and len(ans) < min:
raise Exception("%s images found. Should be at least %s" % (
len(ans), min))
# check max selected images
if max is not None and len(ans) > max:
raise Exception("Too many selected images: %s. Max is %s" % (
", ".join([n[0] for n in ans]), max))
for item in ans:
if item[1] is None:
yield PackageImage(item[0]), None
yield repoman[r["repo"]].get(r["name"], r["version"]), repoman[r["repo"]]
################################################################################
# Commands functions
################################################################################
Add packaged images into a repository
repoman = load_repositories(args)
repo = repoman[args.repository]
for image in args.path:
pkg = PackageImage(image)
repo.add(pkg, delete=not args.preserve)
Build a source image in the current directory
# build start time
t0 = time.time()
# load source image
simg = SourceImage(args.path)
# do the job
simg.build(force=args.force, force_payload=args.payload,
check=not args.no_check, script=not args.no_script)
# compute building time
t1 = time.time()
dt = int(t1 - t0)
arrow("Build time: %s" % datetime.timedelta(seconds=dt))
Display files inside a packaged image
image, repo = next(get_images([args.pattern], repoman, min=1, max=1))
for image, repo in get_images(args.pattern, repoman, min=1):
image.changelog.show(int(image.version), args.all_version)
'''
repoman = load_repositories(args)
for reponame in args.repository:
repoman[reponame].check()
'''
Helper to go cleanly inside a chroot
'''
istools.chroot(args.path, shell=args.shell, mount=not args.no_mount)
repoman = load_repositories(args)
for reponame in args.repository:
repoman[reponame].clean(args.force)
'''
Copy an image from a repository to another one
'''
todo = list(get_images(args.pattern, repoman, local=False, min=1))
# check user really want to this
if not args.force:
out("You will copy the following images:")
for img, repo in todo:
out(" %s/%s:%s" % (repo.config.name, img.name, img.version))
out("Inside repository: #l##b#%s#R#" % dstrepo.config.name)
if not confirm():
raise Exception("Aborted!")
# copy it for real
for srcimg, srcrepo in todo:
arrow("Copying %s v%s from repository %s to %s" %
(srcimg.name, srcimg.version,
srcrepo.config.name, dstrepo.config.name))
arrowlevel(1)
dstrepo.add(srcimg)
arrowlevel(-1)
'''
Remove an image package from a repository
'''
todo = list(get_images(args.pattern, repoman, local=False, min=1))
# check all source repository are local (need by deletion)
for img, repo in todo:
if not repo.local:
raise Exception("Repository %s is not local. Unable to delete" %
repo.config.name)
# check user really want to this
if not args.force:
out("You will remove the following images:")
for img, repo in todo:
out(" %s/%s:%s" % (repo.config.name, img.name, img.version))
if not confirm():
raise Exception("Aborted!")
# delete it for real
for img, repo in todo:
arrow("Deleting %s v%s from repository %s" %
(img.name, img.version, repo.config.name))
arrowlevel(1)
repo.delete(img.name, img.version, payloads=not args.preserve)
arrowlevel(-1)
Show difference between two repositories or packaged images
'''
repoman = load_repositories(args)
if args.object[0] in repoman.onlines and args.object[1] in repoman.onlines:
Repository.diff(repoman[args.object[0]], repoman[args.object[1]])
else:
img = get_images(args.object, repoman, min=2, max=2)
img1, repo1 = next(img)
img2, repo2 = next(img)
Extract a packaged image inside a directory
for image, repo in get_images([args.pattern], repoman, min=1, max=1):
image.extract(args.path, payload=args.payload, force=args.force,
gendescription=args.gen_description)
Get packaged images from repository to current directory
for image, repo in get_images(args.pattern, repoman, local=False, min=1):
image.download(".", image=not args.no_image, payload=args.payload, force=args.force)
'''
Show help
'''
if args.command not in args.subparser.choices:
else:
args.subparser.choices[args.command].print_help()
for image, repo in get_images(args.pattern, repoman, min=1):
image.show(o_verbose=args.verbose, o_changelog=args.changelog,
o_json=args.json)
'''
repoman = load_repositories(args)
for reponame in args.repository:
repoman[reponame].init()
# remove old image args
args.install_parser._remove_action(
[d for d in args.install_parser._actions if d.dest == "pattern"][0])
# create a subparser for current image to have a sexy display of args
subparser = args.install_parser.add_subparsers().add_parser(args.pattern)
# select image to install
repoman = load_repositories(args)
image, repo = next(get_images([args.pattern], repoman, min=1, max=1))
arrow("Installing %s v%s" % (image.name, image.version))
# install start time
t0 = time.time()
# run parser scripts with parser parser argument
# call parser again, with extended attributes
arrow("Parsing arguments")
args = args.parser.parse_args()
if not args.dry_run:
image.run_setup(namespace=args)
# compute building time
t1 = time.time()
dt = int(t1 - t0)
arrow("Install time: %s" % datetime.timedelta(seconds=dt))
List packaged images in repositories
if len(args.pattern) == 0 and len(repoman.search) == 0:
args.pattern = ["*/*"]
elif len(args.pattern) == 0:
args.pattern = ["*"]
repoman.show_images(args.pattern, o_long=args.long, o_json=args.json,
o_md5=args.md5, o_date=args.date, o_author=args.author,
o_size=args.size, o_url=args.url,
o_description=args.description)
Move packaged image from a repository to another one
todo = list(get_images(args.pattern, repoman, local=False, min=1))
# check all source repository are local (need by deletion)
for img, repo in todo:
if not repo.local:
raise Exception("Repository %s is not local. Unable to move" %
repo.config.name)
# check user really want to this
if not args.force:
out("You will copy and remove the following images:")
for img, repo in todo:
out(" %s/%s:%s" % (repo.config.name, img.name, img.version))
out("Inside repository: #l##b#%s#R#" % dstrepo.config.name)
if not confirm():
raise Exception("Aborted!")
# move it for real
for srcimg, srcrepo in todo:
arrow("Moving %s v%s from repository %s to %s" %
(srcimg.name, srcimg.version,
srcrepo.config.name, dstrepo.config.name))
arrowlevel(1)
dstrepo.add(srcimg)
srcrepo.delete(srcimg.name, srcimg.version)
arrowlevel(-1)
SourceImage.create(args.path, args.force)
def c_payload(args):
'''
List payloads
'''
repoman = load_repositories(args)
repoman.show_payloads(args.payload, o_images=args.images, o_json=args.json)
Helper to prepare a path to be chrooted
'''
istools.prepare_chroot(args.path, mount=not args.no_mount)
# in cleaning mode we doesn't needs to sync repositories
repoman = load_repositories(args)
if args.purge:
repoman.purge_repositories(args.repository)
else:
repoman.show_repositories(args.repository,
online=args.online, local=args.local,
o_url=args.url, o_state=args.state,
o_json=args.json)
Search for packaged images in repositories
'''
repoman = load_repositories(args)
def c_unprepare_chroot(args):
Helper to remove chroot preparation of a path
'''
istools.unprepare_chroot(args.path, mount=not args.no_umount)
def arg_parser_init():
'''
Create command parser
'''
# Top level argument parsing
parser = argparse.ArgumentParser()
parser.add_argument("-V", "--version", action="version",
version=installsystems.version)
# exclusive group on verbosity
g = parser.add_mutually_exclusive_group()
g.add_argument("-v", "--verbosity", default=1,
type=int, choices=[0,1,2],
help="define verbosity level (0: quiet, 1:normal, 2:debug)")
g.add_argument("-d", "--debug", dest="verbosity",
action="store_const", const=2,
g.add_argument("-q", "--quiet", dest="verbosity",
action="store_const", const=0,
help="active quiet mode")
# common options
parser.add_argument("-c", "--config", default="installsystems",
metavar="PATH", help="config file path")
parser.add_argument("-R", "--repo-config", default="repository",
metavar="REPO", help="repository config file path")
parser.add_argument("-s", "--repo-search", default="",
metavar="REPO,REPO,...",
help="search for images inside those repositories")
parser.add_argument("-f", "--repo-filter", default="",
metavar="REPO,REPO,...",
help="filter repositories by name")
parser.add_argument("-r", "--repo-path", default="", metavar="PATH",
help="define a temporary repository")
parser.add_argument("-C", "--cache", default="", metavar="PATH",
help="path of repositories cache")
parser.add_argument("-t", "--timeout", dest="timeout", type=int, default=3,
metavar="SECONDS", help="download timeout (default 3)")
parser.add_argument("--no-cache", action="store_true",
help="not use persistent database caching")
parser.add_argument("--no-sync", action="store_true",
help="doesn't sync repository database cache")
parser.add_argument("--no-color", action="store_true",
help="dot not display colored output")
parser.add_argument("--nice", type=int, default=0,
help="nice of the process")
parser.add_argument("--ionice-class", choices=["none","rt", "be","idle"],
help="ionice class of the process (default: none)")
parser.add_argument("--ionice-level", type=int, default=None,
help="ionice class level of the process")
# create a subparser for commands
subparser = parser.add_subparsers()
# add command parser
p = subparser.add_parser("add", help=c_add.__doc__.lower())
p.add_argument("-p", "--preserve", action="store_true",
help="don't remove image after adding to database")
p.add_argument("repository", help="repository where images will be added")
p.add_argument("path", nargs="+", help="local packaged image path")
p.set_defaults(func=c_add)
# build command parser
p = subparser.add_parser("build", help=c_build.__doc__.lower())
p.add_argument("-c", "--no-check", action="store_true",
help="do not check compilation before adding scripts")
p.add_argument("-f", "--force", action="store_true",
help="rebuild image if already exists")
p.add_argument("-p", "--payload", action="store_true",
help="rebuild payloads if already exists")
p.add_argument("-s", "--no-script", action="store_true",
help="doesn't execute build script")
p.add_argument("path", nargs="?", default=".")
p.set_defaults(func=c_build)
# cat command parser
p = subparser.add_parser("cat", help=c_cat.__doc__.lower())
p.add_argument("pattern", help="path|[repository/]image[:version]")
p.add_argument("file", nargs="+",
help="file inside image to cat (globbing allowed)")
p.set_defaults(func=c_cat)
# changelog command parser
p = subparser.add_parser("changelog", help=c_changelog.__doc__.lower())
p.add_argument("-v", "--all-version", action="store_true",
help="display changelog for all versions")
p.add_argument("pattern", nargs="+", help="path|[repository/]image[:version]")
p.set_defaults(func=c_changelog)
# check command parser
p = subparser.add_parser("check", help=c_check.__doc__.lower())
p.add_argument("repository", nargs="+", help="repositories to check")
p.set_defaults(func=c_check)
# chroot command parser
p = subparser.add_parser("chroot", help=c_chroot.__doc__.lower())
p.add_argument("-m", "--no-mount", action="store_true",
help="disable mouting of /{proc,dev,sys} inside chroot")
p.add_argument("-s", "--shell", default="/bin/bash",
help="shell to call inside chroot")
p.add_argument("path")
p.set_defaults(func=c_chroot)
# clean command parser
p = subparser.add_parser("clean", help=c_clean.__doc__.lower())
p.add_argument("-f", "--force", action="store_true",
help="clean repository without confirmation")
p.add_argument("repository", nargs="+", help="repositories to clean")
p.set_defaults(func=c_clean)
# copy command parser
p = subparser.add_parser("copy", help=c_copy.__doc__.lower())
p.add_argument("-f", "--force", action="store_true",
help="copy image without confirmation")
p.add_argument("pattern", nargs="+",
help="[repository/]image[:version]")
p.add_argument("repository", help="destination repository")
p.set_defaults(func=c_copy)
# del command parser
p = subparser.add_parser("del", help=c_del.__doc__.lower())
p.add_argument("pattern", nargs="+",
help="[repository/]image[:version]")
p.add_argument("-f", "--force", action="store_true",
help="delete image without confirmation")
p.add_argument("-p", "--preserve", action="store_true",
help="preserve payloads. doesn't remove it from repository")
p.set_defaults(func=c_del)
# diff command parser
p = subparser.add_parser("diff", help=c_diff.__doc__.lower())
p.add_argument("object", nargs="+",
help="path|repository|[repository/]image[:version]")
p.set_defaults(func=c_diff)
# extract command parser
p = subparser.add_parser("extract", help=c_extract.__doc__.lower())
p.add_argument("-f", "--force", action="store_true",
help="overwrite existing destinations")
p.add_argument("-g", "--gen-description", action="store_true",
help="generate a description file from metadata")
p.add_argument("-p", "--payload", action="store_true",
p.add_argument("pattern",
help="path|[repository/]image[:version]")
p.add_argument("path", help="image will be extracted in path")
p.set_defaults(func=c_extract)
# get command parser
p = subparser.add_parser("get", help=c_get.__doc__.lower())
p.add_argument("-f", "--force", action="store_true",
help="overwrite existing destinations")
p.add_argument("-I", "--no-image", action="store_true",
p.add_argument("-p", "--payload", action="store_true",
p.add_argument("pattern", nargs="+",
help="[repository/]image[:version]")
p.set_defaults(func=c_get)
# help command parser
p = subparser.add_parser("help", help=c_help.__doc__.lower())
p.add_argument("command", nargs="?", help="command name")
p.set_defaults(func=c_help, parser=parser, subparser=subparser)
# info command parser
p = subparser.add_parser("info", help=c_info.__doc__.lower())
p.add_argument("-c", "--changelog", action="store_true",
help="display image changelog")
p.add_argument("-j", "--json", action="store_true",
help="output is formated in json")
p.add_argument("-v", "--verbose", action="store_true",
p.add_argument("pattern", nargs="+",
help="path|[repository/]image[:version]")
p.set_defaults(func=c_info)
# init command parser
p = subparser.add_parser("init", help=c_init.__doc__.lower())
p.add_argument("repository", nargs="+",
help="repository to initialize")
p.set_defaults(func=c_init)
# install command parser
p = subparser.add_parser("install", add_help=False,
help=c_install.__doc__.lower())
p.add_argument("--dry-run", action="store_true",
help="doesn't execute setup scripts")
p.add_argument("pattern", help="path|[repository/]image[:version]")
p.set_defaults(func=c_install, parser=parser, install_parser=p)
# list command parser
p = subparser.add_parser("list", help=c_list.__doc__.lower())
p.add_argument("-A", "--author", action="store_true",
p.add_argument("-d", "--date", action="store_true",
p.add_argument("-D", "--description", action="store_true",
help="display image description")
p.add_argument("-j", "--json", action="store_true",
help="output is formated in json")
p.add_argument("-l", "--long", action="store_true",
p.add_argument("-m", "--md5", action="store_true",
p.add_argument("-s", "--size", action="store_true",
p.add_argument("-u", "--url", action="store_true",
p.add_argument("pattern", nargs="*", default=[],
help="[repository/]image[:version]")
p.set_defaults(func=c_list)
# move command parser
p = subparser.add_parser("move", help=c_move.__doc__.lower())
p.add_argument("-f", "--force", action="store_true",
help="move image without confirmation")
p.add_argument("pattern", nargs="+",
help="[repository/]image[:version]")
p.add_argument("repository", help="destination repository")
p.set_defaults(func=c_move)
# new command parser
p = subparser.add_parser("new", help=c_new.__doc__.lower())
p.add_argument("-f", "--force", action="store_true",
help="overwrite existing source image")
p.add_argument("path", help="new image directory path")
p.set_defaults(func=c_new)
# payload command parser
p = subparser.add_parser("payload", help=c_payload.__doc__.lower())
p.add_argument("-j", "--json", action="store_true",
help="output is formated in json")
p.add_argument("-i", "--images", action="store_true",
help="list images using payload")
p.add_argument("payload", nargs='*', default=[""],
help="payload md5 pattern")
p.set_defaults(func=c_payload)
# prepare_chroot command parser
p = subparser.add_parser("prepare_chroot",
help=c_prepare_chroot.__doc__.lower())
p.add_argument("-m", "--no-mount", action="store_true",
help="disable mouting of /{proc,dev,sys}")
p.add_argument("path")
p.set_defaults(func=c_prepare_chroot)
# repo command parser
p = subparser.add_parser("repo", help=c_repo.__doc__.lower())
g = p.add_mutually_exclusive_group()
p.add_argument("-j", "--json", action="store_true",
help="output is formated in json")
g.add_argument("-l", "--local", action="store_true", default=None,
help="list local repository (filter)")
g.add_argument("-r", "--remote", action="store_false", dest="local",
help="list remote repository (filter)")
g = p.add_mutually_exclusive_group()
g.add_argument("-o", "--online", action="store_true", default=None,
help="list online repository (filter)")
g.add_argument("-O", "--offline", action="store_false", dest="online",
help="list offline repository (filter)")
p.add_argument("-s", "--state", action="store_true",
help="display repository state (online/offline/local/remote)")
p.add_argument("-u", "--url", action="store_true",
help="display repository url")
p.add_argument("--purge", action="store_true",
help="remove cache databases")
p.add_argument("repository", nargs='*', default=["*"], help="repository pattern")
p.set_defaults(func=c_repo)
# search command parser
p = subparser.add_parser("search", help=c_search.__doc__.lower())
p.add_argument("pattern", help="pattern to search in repositories")
p.set_defaults(func=c_search)
# unprepare_chroot command parser
p = subparser.add_parser("unprepare_chroot",
help=c_unprepare_chroot.__doc__.lower())
p.add_argument("-m", "--no-umount", action="store_true",
help="disable unmouting of /{proc,dev,sys}")
p.add_argument("path")
p.set_defaults(func=c_unprepare_chroot)
# version command parser
p = subparser.add_parser("version", help=c_version.__doc__.lower())
p.set_defaults(func=c_version)
# return main parser
return parser
def main():
'''
Program main
'''
try:
arg_parser = arg_parser_init()
# first partial parsing, to get early debug and config path
options = arg_parser.parse_known_args()[0]
# set early command line verbosity and color
installsystems.verbosity = options.verbosity
installsystems.printer.NOCOLOR = options.no_color
# load main config file options
config_parser = MainConfigFile(options.config, "installsystems")
options = config_parser.parse()
# second partial parsing, command line option overwrite config file
options = arg_parser.parse_known_args(namespace=options)[0]
# set verbosity and color
installsystems.verbosity = options.verbosity
installsystems.printer.NOCOLOR = options.no_color
# no warning if we are not in debug mode
if installsystems.verbosity < 2:
warnings.filterwarnings("ignore")
if options.nice is not None or options.ionice_class is not None:
proc = psutil.Process(os.getpid())
if options.nice is not None:
try: proc.nice = options.nice
except Exception:
warn("Unable to nice process to %s" % options.nice)
if options.ionice_class is not None:
try:
ioclassmap = {
"none": psutil.IOPRIO_CLASS_NONE,
"rt": psutil.IOPRIO_CLASS_RT,
"be": psutil.IOPRIO_CLASS_BE,
"idle": psutil.IOPRIO_CLASS_IDLE}
proc.set_ionice(ioclassmap[options.ionice_class], options.ionice_level)
warn("Unable to ionice process to %s" % options.ionice_class)
# except for install command we parse all args!
# install command is responsible of parsing
if options.func is not c_install:
options = arg_parser.parse_args(namespace=options)
options.func(options)
exit(0)
except Exception as e:
except KeyboardInterrupt:
warn("Keyboard Interrupted")
exit(1)
# Entry point
if __name__ == '__main__':
main()