Source code for lago.cmd

#!/usr/bin/python
#
# Copyright 2014 Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
#
# Refer to the README and COPYING files for full details of the license
#

import argparse
import functools
import grp
import json
import logging
import os
import shutil
import sys

import lago
import lago.config
import lago.plugins
import lago.plugins.cli
import lago.templates
from lago import log_utils

CLI_PREFIX = 'lagocli-'
LOGGER = logging.getLogger('cli')


@lago.plugins.cli.cli_plugin(
    help='Initialize a directory for framework deployment'
)
@lago.plugins.cli.cli_plugin_add_argument(
    'virt_config',
    help='Configuration of resources to deploy',
    metavar='VIRT_CONFIG',
    type=os.path.abspath,
)
@lago.plugins.cli.cli_plugin_add_argument(
    'prefix',
    help='Prefix directory of the deployment',
    metavar='PREFIX',
    type=os.path.abspath,
)
@lago.plugins.cli.cli_plugin_add_argument(
    '--template-repo-path',
    help='Repo file describing the templates',
)
@lago.plugins.cli.cli_plugin_add_argument(
    '--template-repo-name',
    help='Name of the repo from the template repos dir',
)
@lago.plugins.cli.cli_plugin_add_argument(
    '--template-store',
    help='Location to store templates at',
    type=os.path.abspath,
)
@log_utils.log_task('Initialize and populate prefix', LOGGER)
def do_init(
    prefix,
    virt_config,
    template_repo_path=None,
    template_repo_name=None,
    template_store=None,
    **kwargs
):
    prefix = lago.Prefix(prefix)
    prefix.initialize()

    with open(virt_config, 'r') as f:
        virt_conf = json.load(f)

    log_utils.setup_prefix_logging(prefix.paths.logs())

    try:
        if template_repo_path:
            repo = lago.templates.TemplateRepository.from_url(
                template_repo_path
            )
        else:
            try:
                repo_name = (
                    template_repo_name
                    or lago.config.get('template_default_repo')
                )
            except KeyError:
                raise RuntimeError(
                    'No template repo was configured or specified'
                )

            repo = lago.templates.find_repo_by_name(repo_name)

        template_store_path = (
            template_store or lago.config.get(
                'template_store',
                default=None
            )
        )
        store = lago.templates.TemplateStore(template_store_path)

        prefix.virt_conf(virt_conf, repo, store)
    except:
        shutil.rmtree(prefix.paths.prefixed(''))
        raise


[docs]def in_prefix(func): @functools.wraps(func) def wrapper(*args, **kwargs): if not os.path.exists('.lago'): raise RuntimeError('Not inside prefix') return func(*args, prefix=lago.Prefix(os.getcwd()), **kwargs) return wrapper
[docs]def with_logging(func): @functools.wraps(func) def wrapper(prefix, *args, **kwargs): log_utils.setup_prefix_logging(prefix.paths.logs()) return func(*args, prefix=prefix, **kwargs) return wrapper
@lago.plugins.cli.cli_plugin(help='Clean up deployed resources') @in_prefix @with_logging def do_cleanup(prefix, **kwargs): prefix.cleanup() @lago.plugins.cli.cli_plugin(help='Deploy lago resources') @lago.plugins.cli.cli_plugin_add_argument( 'vm_names', help='Name of the vm to start', metavar='VM_NAME', nargs='*', ) @in_prefix @with_logging def do_start(prefix, vm_names=None, **kwargs): prefix.start(vm_names=vm_names) @lago.plugins.cli.cli_plugin(help='Destroy lago resources') @lago.plugins.cli.cli_plugin_add_argument( 'vm_names', help='Name of the vm to stop', metavar='VM_NAME', nargs='*', ) @in_prefix @with_logging def do_stop(prefix, vm_names, **kwargs): prefix.stop(vm_names=vm_names) @lago.plugins.cli.cli_plugin( help='Create snapshots for all deployed resources' ) @lago.plugins.cli.cli_plugin_add_argument( 'snapshot_name', help='Name of the snapshot to create', metavar='SNAPSHOT_NAME' ) @in_prefix @with_logging def do_snapshot(prefix, snapshot_name, **kwargs): prefix.create_snapshots(snapshot_name) @lago.plugins.cli.cli_plugin(help='Revert resources to a snapshot') @lago.plugins.cli.cli_plugin_add_argument( 'snapshot_name', help='Name of the snapshot to revert to', metavar='SNAPSHOT_NAME', ) @in_prefix @with_logging def do_revert(prefix, snapshot_name, **kwargs): prefix.revert_snapshots(snapshot_name) @lago.plugins.cli.cli_plugin( help='Open shell on the domain or run as script/command', prefix_chars='\x00', ) @lago.plugins.cli.cli_plugin_add_argument( 'args', help=( 'If none provided, an interactive shell will be started.\n' 'If arguments start with -c, what follows will be ' 'executes as a command.\n' 'Otherwise, if a single provided, it will be ran as script' ' on the domain.' ), nargs='*', ) @lago.plugins.cli.cli_plugin_add_argument( 'host', help='Host to connect to', metavar='HOST', ) @in_prefix @with_logging def do_shell(prefix, host, args=None, **kwargs): args = args or [] try: host = prefix.virt_env.get_vm(host) except KeyError: LOGGER.error('Unable to find VM %s', host) LOGGER.info( 'Available VMs:\n\t' + '\n\t'.join( prefix.virt_env.get_vms().keys( ) ) ) raise if not host.alive(): raise RuntimeError('Host %s is not running' % host.name()) host.wait_for_ssh() if len(args) == 0: result = host.interactive_ssh(['bash']) elif len(args) == 1 and os.path.isfile(args[0]): result = host.ssh_script(args[0]) else: if args[0] == '-c': args = args[1:] result = host.interactive_ssh(args) sys.exit(result.code) @lago.plugins.cli.cli_plugin(help='Open serial console to the domain', ) @lago.plugins.cli.cli_plugin_add_argument( 'host', help='Host to connect to', metavar='HOST', ) @in_prefix @with_logging def do_console(prefix, host, **kwargs): try: host = prefix.virt_env.get_vm(host) except KeyError: LOGGER.error('Unable to find VM %s', host) LOGGER.info( 'Available VMs:\n\t' + '\n\t'.join( prefix.virt_env.get_vms().keys( ) ) ) raise result = host.interactive_console() sys.exit(result.code) @lago.plugins.cli.cli_plugin( help='Show status of the deployed virtual resources' ) @in_prefix @with_logging def do_status(prefix, **kwargs): print '[Prefix]:' print '\tBase directory:', prefix.paths.prefix() with open(prefix.paths.uuid()) as f: print '\tUUID:', f.read() net = prefix.virt_env.get_net() print '[Networks]:' for net in prefix.virt_env.get_nets().values(): print '\t[%s]' % net.name() print '\t\tGateway:', net.gw() print '\t\tStatus:', net.alive() and 'up' or 'down' print '\t\tManagement:', net.is_management() print '[VMs]:' for vm in prefix.virt_env.get_vms().values(): print '\t[%s]' % vm.name() print '\t\tDistro:', vm.distro() print '\t\tRoot password:', vm.root_password() print '\t\tStatus:', vm.alive() and 'up' or 'down' print '\t\tSnapshots:', ', '.join(vm._spec['snapshots'].keys()) if vm.alive(): print '\t\tVNC port:', vm.vnc_port() if vm.metadata: print '\t\tMetadata:' for k, v in vm.metadata.items(): print '\t\t\t%s: %s' % (k, v) print '\t\tNICs:' for i, nic in enumerate(vm.nics()): print '\t\t\t[eth%d]' % i print '\t\t\t\tNetwork:', nic['net'] print '\t\t\t\tIP', nic.get('ip', 'N/A') @lago.plugins.cli.cli_plugin( help='Copy file from a virtual machine to local machine' ) @lago.plugins.cli.cli_plugin_add_argument( 'local_path', help='Path on the local host to copy the file/dir to', metavar='LOCAL_PATH', ) @lago.plugins.cli.cli_plugin_add_argument( 'remote_path', help='Path of the file/dir to copy from the host', metavar='REMOTE_PATH', ) @lago.plugins.cli.cli_plugin_add_argument( 'host', help='Host to copy files from', metavar='HOST', ) @in_prefix @with_logging def do_copy_from_vm(prefix, host, remote_path, local_path, **kwargs): try: host = prefix.virt_env.get_vm(host) except KeyError: LOGGER.error('Unable to find VM %s', host) LOGGER.info( 'Available VMs:\n\t' + '\n\t'.join( prefix.virt_env.get_vms().keys( ) ) ) raise if not host.alive(): raise RuntimeError('Host %s is not running' % host.name()) host.wait_for_ssh() host.copy_from(remote_path, local_path) @lago.plugins.cli.cli_plugin( help='Copy file/dir to a virtual machine from the local host' ) @lago.plugins.cli.cli_plugin_add_argument( 'remote_path', help='Local path to copy the file/dir to', metavar='REMOTE_PATH', ) @lago.plugins.cli.cli_plugin_add_argument( 'local_path', help='Path of the file/dir to copy from the host', metavar='LOCAL_PATH', ) @lago.plugins.cli.cli_plugin_add_argument( 'host', help='Host to copy files to', metavar='HOST', ) @in_prefix @with_logging def do_copy_to_vm(prefix, host, remote_path, local_path, **kwargs): try: host = prefix.virt_env.get_vm(host) except KeyError: LOGGER.error('Unable to find VM %s', host) LOGGER.info( 'Available VMs:\n\t' + '\n\t'.join( prefix.virt_env.get_vms().keys( ) ) ) raise if not host.alive(): raise RuntimeError('Host %s is not running' % host.name()) host.wait_for_ssh() host.copy_to(local_path, remote_path)
[docs]def create_parser(cli_plugins): parser = argparse.ArgumentParser( description='Command line interface to oVirt testing framework.' ) parser.add_argument( '-l', '--loglevel', default='info', choices=['info', 'debug', 'error', 'warning'], help='Log level to use, by default %(default)s' ) parser.add_argument( '--logdepth', default=3, type=int, help='How many task levels to show, by default %(default)s' ) verbs_parser = parser.add_subparsers(dest='verb', metavar='VERB') for cli_plugin_name, cli_plugin in cli_plugins.items(): plugin_parser = verbs_parser.add_parser( cli_plugin_name, **cli_plugin.init_args ) cli_plugin.populate_parser(plugin_parser) return parser
[docs]def check_group_membership(): if 'lago' not in [grp.getgrgid(gid).gr_name for gid in os.getgroups()]: LOGGER.warning('current session does not belong to lago group.')
[docs]def main(): cli_plugins = lago.plugins.load_plugins( lago.plugins.PLUGIN_ENTRY_POINTS['cli'] ) parser = create_parser(cli_plugins) args = parser.parse_args() logging.basicConfig(level=logging.DEBUG), logging.root.handlers = [ log_utils.TaskHandler( task_tree_depth=args.logdepth, level=getattr(logging, args.loglevel.upper()), dump_level=logging.ERROR, formatter=log_utils.ColorFormatter( fmt='%(msg)s', ) ) ] check_group_membership() try: cli_plugins[args.verb].do_run(args) except Exception: LOGGER.exception('Error occured, aborting') sys.exit(1)
if __name__ == '__main__': main()