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 logging
import os
import pkg_resources
import shutil
import sys

import lago
import lago.config
import lago.plugins
import lago.plugins.cli
import lago.templates
from lago import log_utils, prefix as lago_prefix

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, json and yaml file formats '
        'are supported'
    ),
    metavar='VIRT_CONFIG',
    type=os.path.abspath,
)
@lago.plugins.cli.cli_plugin_add_argument(
    'prefix',
    help=(
        'Prefix directory of the deployment, if none passed, it will use '
        '$PWD/.lago'
    ),
    metavar='PREFIX',
    type=os.path.abspath,
    nargs='?',
    default=os.path.join(os.path.curdir, '.lago'),
)
@lago.plugins.cli.cli_plugin_add_argument(
    '--template-repo-path',
    help='Repo file describing the templates',
    default='http://templates.ovirt.org/repo/repo.metadata',
)
@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)
    prefix.initialize()
    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)

        with open(virt_config, 'r') as virt_fd:
            prefix.virt_conf_from_stream(virt_fd, repo, store)
    except:
        shutil.rmtree(prefix.paths.prefixed(''))
        raise


[docs]def in_prefix(func): @functools.wraps(func) def wrapper(*args, **kwargs): prefix_path = kwargs.get('prefix_path', 'auto') if prefix_path == 'auto': prefix_path = lago_prefix.resolve_prefix_path() return func(*args, prefix=lago_prefix.Prefix(prefix_path), **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='Cleanup and remove the whole prefix and any files in it' ) @lago.plugins.cli.cli_plugin_add_argument( '-y', '--yes', help="Don't ask for confirmation, assume yes", action='store_true', ) @in_prefix @with_logging def do_destroy(prefix, yes, **kwargs): prefix_path = prefix.paths.prefix if not yes: response = raw_input( 'Do you really want to destroy %s? [Yn] ' % prefix_path ) if response and response[0] not in 'Yy': LOGGER.info('Aborting on user input') return prefix.cleanup() if os.path.islink(prefix_path): os.unlink(prefix_path) else: shutil.rmtree(prefix_path) @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( '--list', '-l', dest='list_only', help='List current available snapshots', action='store_true', ) @lago.plugins.cli.cli_plugin_add_argument( 'snapshot_name', help='Name of the snapshot to create', metavar='SNAPSHOT_NAME', nargs='?', default=None, ) @in_prefix @with_logging def do_snapshot(prefix, list_only, snapshot_name, out_format, **kwargs): if list_only: snapshots = prefix.get_snapshots() print out_format.format(snapshots) elif snapshot_name: prefix.create_snapshots(snapshot_name) else: raise RuntimeError('No snapshot name provided') @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, out_format, **kwargs): with open(prefix.paths.uuid()) as f: uuid = f.read() info_dict = { 'Prefix': { 'Base directory': prefix.paths.prefix, 'UUID': uuid, 'Networks': dict( ( net.name(), { 'gateway': net.gw(), 'status': net.alive() and 'up' or 'down', 'management': net.is_management(), } ) for net in prefix.virt_env.get_nets().values() ), 'VMs': dict( ( vm.name(), { 'distro': vm.distro(), 'root password': vm.root_password(), 'status': vm.alive() and 'up' or 'down', 'snapshots': ', '.join(vm._spec['snapshots'].keys()), 'VNC port': vm.vnc_port() if vm.alive() else None, 'metadata': vm.metadata, 'NICs': dict( ( 'eth%d' % i, { 'network': nic['net'], 'ip': nic.get('ip', 'N/A'), } ) for i, nic in enumerate(vm.nics()) ), } ) for vm in prefix.virt_env.get_vms().values() ), }, } print out_format.format(info_dict) @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, out_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' ) pkg_info = pkg_resources.require("lago")[0] parser.add_argument( '--version', action='version', version='%(prog)s ' + pkg_info.version, ) parser.add_argument( '--out-format', '-f', action='store', default='default', choices=out_plugins.keys(), ) parser.add_argument( '--prefix-path', '-p', action='store', default='auto', ) 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'] ) out_plugins = lago.plugins.load_plugins( lago.plugins.PLUGIN_ENTRY_POINTS['out'] ) parser = create_parser(cli_plugins=cli_plugins, out_plugins=out_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() args.out_format = out_plugins[args.out_format] try: cli_plugins[args.verb].do_run(args) except Exception: LOGGER.exception('Error occured, aborting') sys.exit(1)
if __name__ == '__main__': main()