Skip to content
is 18.1 KiB
Newer Older
#!/usr/bin/python2.7
Seblu's avatar
Seblu committed
# -*- python -*-
Seblu's avatar
Seblu committed
# -*- coding: utf-8 -*-
# Started 30/06/2011 by Seblu <seblu@seblu.net>

'''
InstallSystems Command line Tool
'''

import os
import time
import datetime
import argparse
import re
import fnmatch
Seblu's avatar
Seblu committed
import installsystems
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

def load_repositories(args):
    '''
    Load repositories on a repository manager
    '''
    # remove cache is asked
    if args.no_cache:
        args.cache = None
    # init repo cache object
    repoman = RepositoryManager(args.cache, timeout=args.timeout, filter=args.repo_filter)
    # register repositories (order matter)
    # load repo configs from command line
    if args.repo_path is not None:
        repoman.register(RepositoryConfig(istools.smd5sum(args.repo_path)[:8],
                                          path=args.repo_path))
    # load repo configs from config
    for repoconf in RepoConfigFile(args.repo_config).repos:
        repoman.register(repoconf)
def select_image(name, repoman, best=False):
    '''
    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
    '''
    if istools.isfile(name) and os.path.isfile(name):
        return PackageImage(name), None
    else:
Seblu's avatar
Seblu committed
        x = re.match("^(([-_\w]+)/)?([-_\w]+)(:v?(\d+))?$", name)
        if x is None:
            raise Exception("%s is not a valid image" % name)
        (repo, image, version) = x.group(2,3,5)
        debug("Selected image is %s v%s in %s" % (image, version, repo))
        if repo is None:
            return repoman.get(image, version, best)
        else:
            return repoman[repo].get(image, version), repoman[repo]

def c_add(parser, args):
Seblu's avatar
Seblu committed
    '''
    Add an image package into a repository
Seblu's avatar
Seblu committed
    '''
    repoman = load_repositories(args)
    repo = repoman[args.repository]
    for image in args.path:
        pkg = PackageImage(image)
        repo.add(pkg, delete=not args.preserve)
def c_build(parser, args):
Seblu's avatar
Seblu committed
    '''
    Build an image source
    '''
Seblu's avatar
Seblu committed
    # build start time
    t0 = time.time()
    # load source image
    simg = SourceImage(args.path)
    # do the job
    simg.build(force=args.force, check=not args.no_check)
    # compute building time
    t1 = time.time()
    dt = int(t1 - t0)
    arrow("Build time: %s" % datetime.timedelta(seconds=dt))
def c_cat(parser, args):
Seblu's avatar
Seblu committed
    Display image's file
    # looks if arguments is a file or image name
Seblu's avatar
Seblu committed
    repoman = load_repositories(args)
    img, repo = select_image(args.image, repoman, args.best)
Seblu's avatar
Seblu committed
    for filename in args.file:
        img.cat(filename)
Matthieu Gonnet's avatar
Matthieu Gonnet committed
def c_check(parser, args):
    '''
    Check for unreferenced and missing files inside a repository
    '''
    repoman = load_repositories(args)
    for reponame in args.repository:
        repoman[reponame].check()

def c_clean(parser, args):
Seblu's avatar
Seblu committed
    '''
Seblu's avatar
Seblu committed
    Remove unreferenced files from repoistory
Seblu's avatar
Seblu committed
    '''
Matthieu Gonnet's avatar
Matthieu Gonnet committed
    repoman = load_repositories(args)
    for reponame in args.repository:
        repoman[reponame].clean()

def c_copy(parser, args):
    '''
    Copy an image from a repository to another one
    '''
Seblu's avatar
Seblu committed
    repoman = load_repositories(args)
Seblu's avatar
Seblu committed
    dstrepo = repoman[args.repository]
    for image in args.image:
        srcimg, srcrepo = select_image(image, repoman, args.best)
Seblu's avatar
Seblu committed
        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)
def c_del(parser, args):
Seblu's avatar
Seblu committed
    '''
    Remove an image package from a repository
    '''
Seblu's avatar
Seblu committed
    repoman = load_repositories(args)
    for image in args.image:
        img, repo = select_image(image, repoman, args.best)
        if repo is None:
            raise Exception("You cannot delete an image outside a repository")
Seblu's avatar
Seblu committed
        if not args.force:
            warn("The following opereation cannot be reversed!")
            out("You will delete %s v%s in repository %s" % (img.name,
                                                             img.version,
                                                             repo.config.name))
            if not confirm():
                raise Exception("Aborted!")
Seblu's avatar
Seblu committed
        repo.delete(img.name, img.version)
Seblu's avatar
Seblu committed
def c_diff(parser, args):
    '''
    Show diff between two repositories or 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:
        img1, repo1 = select_image(args.object[0], repoman, args.best)
        img2, repo2 = select_image(args.object[1], repoman, args.best)
        PackageImage.diff(img1, img2)

Seblu's avatar
Seblu committed
def c_extract(parser, args):
    '''
    Extract an image package inside a directory
    '''
    repoman = load_repositories(args)
    img, repo = select_image(args.image, repoman, args.best)
    img.extract(args.path, payload=args.payload, force=args.force)
Seblu's avatar
Seblu committed

def c_get(parser, args):
    '''
Seblu's avatar
Seblu committed
    Download a remote image in current directory
Seblu's avatar
Seblu committed
    repoman = load_repositories(args)
    for image in args.image:
        img, repo = select_image(image, repoman, args.best)
Seblu's avatar
Seblu committed
        img.download(".", payload=args.payload, force=args.force)

def c_help(parser, args):
    '''
    Show help
    '''
    if args.command not in args.subparser.choices:
        parser.print_help()
    else:
        args.subparser.choices[args.command].print_help()

def c_init(parser, args):
    '''
    Create an empty repository
    '''
    repoman = load_repositories(args)
    for reponame in args.repository:
        repoman[reponame].init()

def c_install(parser, args):
Seblu's avatar
Seblu committed
    '''
    Install an image
    '''
    # select image to install
    repoman = load_repositories(args)
    img = select_image(args.image, repoman, args.best)[0]
    # add default show help options
    args.subparser.add_argument("-h", "--help", action="help", default=argparse.SUPPRESS,
                                help="show program's version number and exit")
Seblu's avatar
Seblu committed
    # Print setup information
    arrow("Installing %s v%s" % (img.name, img.version))
Seblu's avatar
Seblu committed
    # install start time
    t0 = time.time()
    # run parser scripts with parser parser argument
    img.run_parser(parser=args.subparser)
Seblu's avatar
Seblu committed
    # call parser again, with extended attributes
    arrow("Parsing arguments")
    args = parser.parse_args(namespace=args)
Seblu's avatar
Seblu committed
    # run setup scripts
    img.run_setup(namespace=args)
Seblu's avatar
Seblu committed
    # compute building time
    t1 = time.time()
    dt = int(t1 - t0)
    arrow("Install time: %s" % datetime.timedelta(seconds=dt))

Aurélien Dunand's avatar
Aurélien Dunand committed
def c_list(parser, args):
    '''
    List images in repository or image content
    '''
Seblu's avatar
Seblu committed
    # List available repositories
Seblu's avatar
Seblu committed
    repoman = load_repositories(args)
    if len(args.object) == 0:
        arrow("Repositories")
        repoman.show(verbose=args.verbose)
    else:
Seblu's avatar
Seblu committed
        for o in args.object:
            repos = fnmatch.filter(repoman.onlines, o)
            if len(repos) > 0:
                for reponame in repos:
                    arrow(reponame)
                    repoman[reponame].show(verbose=args.verbose)
Seblu's avatar
Seblu committed
            else:
                img, repo = select_image(o, repoman, args.best)
                img.show(verbose=args.verbose)
Aurélien Dunand's avatar
Aurélien Dunand committed

Seblu's avatar
Seblu committed
def c_move(parser, args):
    '''
    Move an image from a repository to another one
    '''
    repoman = load_repositories(args)
Seblu's avatar
Seblu committed
    dstrepo = repoman[args.repository]
    for image in args.image:
        srcimg, srcrepo = select_image(image, repoman, args.best)
Seblu's avatar
Seblu committed
        if not args.force:
            out("You will move %s v%s from %s to %s" % (srcimg.name,
                                                        srcimg.version,
                                                        srcrepo.config.name,
                                                        dstrepo.config.name))
            if not confirm():
                raise Exception("Aborted!")
Seblu's avatar
Seblu committed
        arrow("Moving %s v%s from repository %s to %s" % (srcimg.name,
                                                          srcimg.version,
                                                          srcrepo.config.name,
                                                          dstrepo.config.name))
Seblu's avatar
Seblu committed
        arrowlevel(1)
        dstrepo.add(srcimg)
        srcrepo.delete(srcimg.name, srcimg.version)
        arrowlevel(-1)
Seblu's avatar
Seblu committed

def c_new(parser, args):
Seblu's avatar
Seblu committed
    '''
    Create a new source image
Seblu's avatar
Seblu committed
    '''
    SourceImage.create(args.path)

def c_search(parser, args):
    '''
    Search in repository
    '''
    repoman = load_repositories(args)
    repoman.search(args.pattern)
Seblu's avatar
Seblu committed
def c_version(parser, args):
    '''
    Print installsystems version
    '''
    out(installsystems.version)

Seblu's avatar
Seblu committed
# Top level argument parsing
p_main = argparse.ArgumentParser()
p_main.add_argument("-V", "--version", action="version",
                    version=installsystems.version,
                    help="show installsystems version")
# exclusive group on debug/quiet
ex_group = p_main.add_mutually_exclusive_group()
ex_group.add_argument("-d", "--debug", action="store_true",
Seblu's avatar
Seblu committed
                      help="active debug mode")
ex_group.add_argument("-q", "--quiet", action="store_true",
Seblu's avatar
Seblu committed
                      help="active quiet mode")
# common options
p_main.add_argument("-c", "--config", default="installsystems",
Seblu's avatar
Seblu committed
                    help="config file path")
p_main.add_argument("-R", "--repo-config", default="repository",
                    help="repository config file path")
Seblu's avatar
Seblu committed
p_main.add_argument("-f", "--repo-filter", default=None,
                    help="select repository by name in config files")
p_main.add_argument("-r", "--repo-path", default=None,
Seblu's avatar
Seblu committed
                    help="repository path")
p_main.add_argument("-C", "--cache", default=None,
                    help="path of the repository cache")
p_main.add_argument("--no-cache", action="store_true", default=False,
                    help="not use persistent db caching")
p_main.add_argument("-t", "--timeout", dest="timeout", type=int, default=3,
                    help="download timeout (default 3)")

Seblu's avatar
Seblu committed
# create a subparsers for each command
subparsers = p_main.add_subparsers()

# add command parser
p_add =  subparsers.add_parser("add", help=c_add.__doc__.lower())
p_add.add_argument("-p", "--preserve", action="store_true", default=False,
                   help="don't remove image after adding to database")
p_add.add_argument("repository", help="repository where images will be added")
p_add.add_argument("path", nargs="+", help="image path")
p_add.set_defaults(func=c_add)
Seblu's avatar
Seblu committed

# build command parser
p_build = subparsers.add_parser("build", help=c_build.__doc__.lower())
p_build.add_argument("-f", "--force", action="store_true", default=False,
Seblu's avatar
Seblu committed
                     help="overwrite existing image")
p_build.add_argument("-c", "--no-check", action="store_true", default=False,
Seblu's avatar
Seblu committed
                     help="do not check compilation before adding scripts")
p_build.add_argument("path", nargs="?", default=".")
p_build.set_defaults(func=c_build)

# cat command parser
p_cat = subparsers.add_parser("cat", help=c_cat.__doc__.lower())
p_cat.add_argument("-b", "--best", action="store_true", default=False,
                   help="in best mode, image is the most recent in all repositories")
p_cat.add_argument("image", help="<path|[repository/]image[:version]>")
p_cat.add_argument("file", nargs="+",
                   help="file inside image to cat (globbing allowed)")
p_cat.set_defaults(func=c_cat)
Matthieu Gonnet's avatar
Matthieu Gonnet committed
# check command parser
p_check = subparsers.add_parser("check", help=c_check.__doc__.lower())
p_check.add_argument("repository", nargs="+", help="repositories to check")
p_check.set_defaults(func=c_check)

# clean command parser
p_clean = subparsers.add_parser("clean", help=c_clean.__doc__.lower())
Matthieu Gonnet's avatar
Matthieu Gonnet committed
p_clean.add_argument("repository", nargs="+", help="repositories to clean")
p_clean.set_defaults(func=c_clean)

# copy command parser
p_copy = subparsers.add_parser("copy", help=c_copy.__doc__.lower())
p_copy.add_argument("-b", "--best", action="store_true", default=False,
                   help="in best mode, image is the most recent in all repositories")
p_copy.add_argument("image", nargs="+",
                    help="image syntax is <path|[repository/]image[:version]>")
p_copy.add_argument("repository", help="destination repository")
p_copy.set_defaults(func=c_copy)
Seblu's avatar
Seblu committed

# del command parser
p_del =  subparsers.add_parser("del", help=c_del.__doc__.lower())
p_del.add_argument("image", nargs="+",
                   help="image syntax is <path|[repository/]image[:version]>")
p_del.add_argument("-b", "--best", action="store_true", default=False,
                   help="in best mode, image is the most recent in all repositories")
p_del.add_argument("-f", "--force", action="store_true", default=False,
                   help="delete image without confirmation")
Seblu's avatar
Seblu committed
p_del.set_defaults(func=c_del)

Seblu's avatar
Seblu committed
# diff command parser
p_diff = subparsers.add_parser("diff", help=c_diff.__doc__.lower())
p_diff.add_argument("object", nargs=2,
                    help="object syntax is <path|repository|[repository/]image[:version]>")
p_diff.add_argument("-b", "--best", action="store_true", default=False,
                    help="in best mode, image is the most recent in all repositories")
p_diff.set_defaults(func=c_diff)

Seblu's avatar
Seblu committed
# extract command parser
p_extract = subparsers.add_parser("extract", help=c_extract.__doc__.lower())
p_extract.add_argument("-p", action="store_true", dest="payload", default=False,
                       help="extract payloads")
Seblu's avatar
Seblu committed
p_extract.add_argument("-f", "--force", action="store_true", default=False,
                       help="overwrite existing destinations")
p_extract.add_argument("-b", "--best", action="store_true", default=False,
                       help="in best mode, image is the most recent in all repositories")
p_extract.add_argument("image",
                       help="image syntax is <path|[repository/]image[:version]>")
p_extract.add_argument("path", help="image will be extracted in path")
Seblu's avatar
Seblu committed
p_extract.set_defaults(func=c_extract)

# get command parser
p_get = subparsers.add_parser("get", help=c_get.__doc__.lower())
Seblu's avatar
Seblu committed
p_get.add_argument("-p", action="store_true", dest="payload", default=False,
                   help="get payloads")
Seblu's avatar
Seblu committed
p_get.add_argument("-f", "--force", action="store_true", default=False,
                   help="overwrite existing destinations")
p_get.add_argument("-b", "--best", action="store_true", default=False,
                   help="in best mode, image is the most recent in all repositories")
p_get.add_argument("image", nargs="+",
                   help="image syntax is <path|[repository/]image[:version]>")
p_get.set_defaults(func=c_get)

# help command parser
p_help = subparsers.add_parser("help", help=c_help.__doc__.lower())
p_help.add_argument("command", nargs="?", help="command name")
p_help.set_defaults(func=c_help, subparser=subparsers)

# init command parser
p_init = subparsers.add_parser("init", help=c_init.__doc__.lower())
p_init.add_argument("repository", nargs="+",
                    help="repository to initialize")
p_init.set_defaults(func=c_init)

Seblu's avatar
Seblu committed
# install command parser
p_install = subparsers.add_parser("install", add_help=False,
                                  help=c_install.__doc__.lower())
p_install.add_argument("-b", "--best", action="store_true", default=False,
                       help="in best mode, image is the most recent in all repositories")
p_install.add_argument("image",
                       help="image syntax is <path|[repository/]image[:version]>")
p_install.set_defaults(func=c_install, subparser=p_install)
# list command parser
p_list = subparsers.add_parser("list", help=c_list.__doc__.lower())
p_list.add_argument("-v", action="store_true", dest="verbose", default=False,
                    help="verbose output")
p_list.add_argument("-b", "--best", action="store_true", default=False,
                    help="in best mode, image is the most recent in all repositories")
Seblu's avatar
Seblu committed
p_list.add_argument("object", nargs="*",
                    help="object syntax is <path|repository|[repository/]image[:version]>")
p_list.set_defaults(func=c_list, subparser=p_list)

Seblu's avatar
Seblu committed
# move command parser
p_move = subparsers.add_parser("move", help=c_move.__doc__.lower())
Seblu's avatar
Seblu committed
p_move.add_argument("-f", "--force", action="store_true", default=False,
                    help="move image without confirmation")
p_move.add_argument("-b", "--best", action="store_true", default=False,
                    help="in best mode, image is the most recent in all repositories")
p_move.add_argument("image", nargs="+",
                    help="image syntax is <path|[repository/]image[:version]>")
p_move.add_argument("repository", help="destination repository")
Seblu's avatar
Seblu committed
p_move.set_defaults(func=c_move)

# new command parser
p_new = subparsers.add_parser("new", help=c_new.__doc__.lower())
p_new.add_argument("path", help="new image directory path")
p_new.set_defaults(func=c_new)

# search command parser
p_search = subparsers.add_parser("search", help=c_search.__doc__.lower())
p_search.add_argument("pattern", help="pattern to search in repositories")
p_search.set_defaults(func=c_search)
Seblu's avatar
Seblu committed

Seblu's avatar
Seblu committed
# version command parser
p_version = subparsers.add_parser("version", help=c_version.__doc__.lower())
p_version.set_defaults(func=c_version)

Seblu's avatar
Seblu committed
try:
    # Parse and run
    args = p_main.parse_known_args()[0]
    # set debug and quiet mode before merge
    installsystems.debug = args.debug
    installsystems.quiet = args.quiet
    # load isinstall config
    config = MainConfigFile(args.config, "installsystems")
Seblu's avatar
Seblu committed
    config.merge(args)
    # set debug and quiet mode after merge
    installsystems.debug = args.debug
    installsystems.quiet = args.quiet
    if args.func is not c_install:
Seblu's avatar
Seblu committed
        args = p_main.parse_args(namespace=args)
    # let's go
    args.func(p_main, args)
Seblu's avatar
Seblu committed
except Exception as e:
    error(e)
except KeyboardInterrupt:
    warn("Keyboard Interrupted")