Esempio n. 1
0
 def test_openssh(self):
     openssh = util.find_openssh()
     if openssh is None:
         raise SkipTest('openssh is needed for this test')
     if sys.platform.startswith('win'):
         raise SkipTest('this test is not supported on Windows')
     try:
         import pexpect
     except ImportError:
         raise SkipTest('this test requires pexpect')
     # Fire up a new Python process using pexpect. Pexpect will assign a
     # PTY, and therefore the child will use openssh.
     for vm in self.vms:
         libdir = os.path.join(testenv.topdir, 'lib')
         env = os.environ.copy()
         env['PYTHONPATH'] = libdir
         args = ['-mtestmill.main']
         args += get_common_args()
         args += ['-m', 'platformtest.yml', 'ssh', self.appname, vm]
         child = pexpect.spawn(sys.executable, args, env=env)
         # Try to get some remote output. The interaction between Unix TTYs,
         # regular expressions, python string escapes, and shell expansions
         # make the 3 lines below the path to the Zen of Unix.
         child.expect('\$')  # escape '$' regex special
         child.send("echo 'Hello from remote!'\n")  # escape ! history expn
         # Skip echo, TTY changed '\n' to '\r\n', and literal \ + r and 
         # \ + n to match \r\n.
         child.expect(r'remote!\r\n')  # note: without ' so not the echo
         child.send('exit\n')
         child.expect([pexpect.EOF, pexpect.TIMEOUT])
Esempio n. 2
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