Beispiel #1
0
def main(argv=None):
    """The "ravtest" main entry point."""
    if sys.platform.startswith('win'):
        console.error('Windows is not currently supported by "ravtest".\n'
                      'Please use the Fabric front-end.')
        error.exit(1)
    parser = create_parser()
    args = parse_args(parser, argv)
    create_environment(args)
    setup_logging()
    command = subcommands[args.subcmd][0]
    try:
        ret = command(args, env)
    except KeyboardInterrupt:
        console.complete_partial_line()
        console.writeln('Exiting at user request.')
        ret = error.EX_INTERRUPTED
    except SystemExit as e:
        console.complete_partial_line()
        if env.debug:
            console.error('SystemExit caught')
            lines = traceback.format_exception(*sys.exc_info())
            console.writeln_err('Raised from:')
            console.writeln_err(''.join(lines))
        ret = e[0]
    except Exception as e:
        console.complete_partial_line()
        console.error(str(e))
        if env.debug:
            lines = ['An uncaught exception occurred:']
            lines += traceback.format_exception(*sys.exc_info())
            console.writeln_err()
            console.writeln_err(''.join(lines))
            console.writeln_err('Environment: {!r}'.format(env))
        ret = getattr(e, 'exitstatus', error.EX_SOFTWARE)
    return ret
Beispiel #2
0
def parse_args(parser, argv=None):
    """Parse aguments."""
    # Parse general arguments, extract the command, add command-specific
    # arguments, and then re-parse everything again.
    # The argparse module provides subparser support, and we could have
    # used that. However, there a serious usability issue with that, because
    # "command <subcmd> --foo" is an error if --foo is a valid argument
    # for command but not for the sub-command. Given that is very common
    # to mistakenly pass options out of order, we imlement our own solution
    # here that accepts the options in this case.
    try:
        args = parser.parse_args(argv)
    except argparse.ParseError as e:
        args = e.namespace
        if not (args.help or args.version) and not args.subcmd:
            console.write_err(parser.format_usage())
            console.error(str(e))
            error.exit(error.EX_USAGE)
    if args.help and not args.subcmd:
        console.write_err(parser.format_help())
        error.exit(error.EX_OK)
    if args.version and not args.subcmd:
        console.writeln_err(_version.version_string)
        error.exit(error.EX_OK)
    subcmd = args.subcmd
    if subcmd not in subcommands:
        console.error("Unknown command: '{0}'.", subcmd)
        error.exit(error.EX_USAGE)
    add_args = subcommands[subcmd][1]
    add_args(parser)
    try:
        args = parser.parse_args(argv)
    except argparse.ParseError as e:
        args = e.namespace
        if not args.help:
            console.write_err(parser.format_usage())
            console.error(str(e))
            error.exit(error.EX_USAGE)
    if args.help:
        console.write_err(parser.format_help())
        error.exit(error.EX_OK)
    return args
Beispiel #3
0
def do_ssh(args, env):
    """The "ravello ssh" command."""

    with env.let(quiet=True):
        login.default_login()
        keypair.default_keypair()

    if manifest.manifest_exists():
        with env.let(quiet=True):
            manif = manifest.default_manifest()
    else:
        manif = None

    parts = args.application.split(':')
    if len(parts) in (1, 2) and manif is None:
        error.raise_error('No manifest found ({0}).\n'
                          'Please specify the fully qualified app name.\n'
                          'Use `ravtest ps --all` for a list.',
                          manifest.manifest_name())
    if len(parts) in (1, 2):
        project = manif['project']['name']
        console.info('Project name is `{0}`.', project)
        defname = parts[0]
        instance = parts[1] if len(parts) == 2 else None
    elif len(parts) == 3:
        project, defname, instance = parts
    else:
        error.raise_error('Illegal application name: `{0}`.', appname)

    apps = cache.find_applications(project, defname, instance)
    if len(apps) == 0:
        error.raise_error('No instances of application `{0}` exist.',
                          defname)
    elif len(apps) > 1:
        error.raise_error('Multiple instances of `{0}` exist.\n'
                          'Use `ravtest ps` to list the instances and then\n'
                          'specify the application with its instance id.',
                          defname)
    app = cache.get_application(apps[0]['id'])
    appname = app['name']
    _, _, instance = appname.split(':')

    vmname = args.vm
    vm = application.get_vm(app, vmname)
    if vm is None:
        error.raise_error('Application `{0}:{1}` has no VM named `{2}`.\n'
                          'Use `ravtest ps --full` to see a list of VMs.',
                          defname, instance, vmname)
    console.info("Connecting to VM `{0}` of application `{1}:{2}`...",
                 vmname, defname, instance)

    # Start up the application and wait for it if we need to.

    state = application.get_application_state(app)
    if state not in ('PUBLISHING', 'STARTING', 'STOPPED', 'STARTED'):
        error.raise_error("VM `{0}` is in an unknown state.", vmname)

    userdata = vm.get('customVmConfigurationData', {})
    vmkey = userdata.get('keypair', {})

    if vmkey.get('id') != env.public_key['id']:
        error.raise_error("VM uses unknown public key `{0}`.",
                          vmkey.get('name'))

    application.start_application(app)
    application.wait_for_application(app, [vmname])

    # Now run ssh. Prefer openssh but fall back to using Fabric/Paramiko.

    host = 'ravello@{0}'.format(vm['dynamicMetadata']['externalIp'])
    command = '~/bin/run {0}'.format(args.testid)

    openssh = util.find_openssh()
    interactive = os.isatty(sys.stdin.fileno())

    if interactive and openssh:
        if not sys.platform.startswith('win'):
            # On Unix use execve(). This is the most efficient.
            argv = ['ssh', '-i', env.private_key_file,
                    '-o', 'UserKnownHostsFile=/dev/null',
                    '-o', 'StrictHostKeyChecking=no',
                    '-o', 'LogLevel=quiet',
                    '-t',  host, command]
            console.debug('Starting {0}', ' '.join(argv))
            os.execve(openssh, argv, os.environ)
        else:
            # Windows has execve() but for some reason it does not work
            # well with arguments with spaces in it. So use subprocess
            # instead.
            command = [openssh, '-i', env.private_key_file,
                       '-o', 'UserKnownHostsFile=NUL',
                       '-o', 'StrictHostKeyChecking=no',
                       '-o', 'LogLevel=quiet',
                       '-t', host, command]
            ssh = subprocess.Popen(command)
            ret = ssh.wait()
            error.exit(ret)

    # TODO: should also support PuTTY on Windows

    console.info(textwrap.dedent("""\
            Warning: no local openssh installation found.
            Falling back to Fabric/Paramiko for an interactive shell.
            However, please note:

            * CTRL-C and terminal resize signals may not work.
            * Output of programs that repaint the screen may
              be garbled (e.g. progress bars).
            """))

    fab.env.host_string = host
    fab.env.key_filename = env.private_key_file
    fab.env.disable_known_hosts = True
    fab.env.remote_interrupt = True
    fab.env.output_prefix = None
    fabric.state.output.running = None
    fabric.state.output.status = None

    ret = fab.run(command, warn_only=True)
    return ret.return_code