Esempio n. 1
0
def lookup(appname, *vms):
    """lookup(appname, *vms)

    Lookup the addresses for virtual machines in a Ravello application.

    The *appname* parameter must be the name of the application. After this
    parameter you may pass one or multiple positional arguments containing the
    VM names. If the VMs are not specified, all VMs in the application are
    selected. The first VM argument may also be a sequence, set or mapping
    containing VM names.

    The return value is a list of Fabric host strings, and may be directly
    assigned to *env.hosts*.
    """
    app = cache.get_application(name=appname)
    if app is None:
        error.raise_error("Application `{0}` does not exist.", appname)
    if isinstance(vms, compat.str):
        vms = [vms]
    app = cache.get_application(app['id'])
    hosts = []
    for vm in app.get('vms', []):
        if vms and vm['name'] not in vms:
            continue
        host = vm['dynamicMetadata']['externalIp']
        if fab.env.ravello_user:
            host = '{0}@{1}'.format(fab.env.ravello_user, host)
        hosts.append(host)
    return hosts
Esempio n. 2
0
def start_application(name, wait=True, show_progress=True, timeout=1200):
    """start_application(name, wait=True, show_progress=True, timeout=1200)

    Start up an application.

    The *name* argument must be the name of an application. If *wait* is
    nonzero, then this function will wait until the application is up and its
    VMs are accessible via ssh. If *show_progress* is nonzero then a progress
    bar is shown.  The *timeout* argument specifies the timeout in seconds.
    The default timeout is 20 minutes. Application startup times vary greatly
    between clouds, and whether or not the application has already been
    published.

    This method will start all VMs in the application that are in the 'STOPPED'
    state. If *wait* is nonzero, then all VMs must either be in the 'STOPPED'
    state (in which case they will get started), in the 'STARTED' state (in
    which case there is nothing to do), or in a state that will eventually
    transition to 'STARTED' state (currently 'STARTING' and 'PUBLISHING'). If a
    VM is in another state, then no action is taken and an exception is raised,
    because this call would just timeout without the ability to complete.

    This function has no return value, and raises an exception in case of an
    error.
    """
    app = cache.get_application(name=name)
    if app is None:
        error.raise_error("Application `{0}` does not exist.", name)
    app = application.start_application(app)
    if wait:
        state = application.get_application_state(app)
        if state not in application.vm_reuse_states:
            error.raise_error("Cannot wait for app in state '{0}'.", state)
        vms = set((vm['name'] for vm in app['vms']))
        with env.let(quiet=not show_progress):
            application.wait_for_application(app, vms, timeout)
Esempio n. 3
0
def create_blueprint(name, bpname=None, wait=True):
    """create_blueprint(name, bpname=None)

    Create a blueprint from an application.

    The *name* argument must be an application whose VMs are either all in
    the STOPPED or in the STARTED state.  The *bpname* argument is the name
    of the blueprint. If the blueprint name is not specified, a new unique name
    will be allocated.

    The return value of this function is the name of the blueprint that was
    created.
    """
    app = cache.get_application(name=name)
    if app is None:
        error.raise_error("Application `{0}` does not exist.", name)
    state = application.get_application_state(app)
    if state not in ('STOPPED', 'STARTED'):
        error.raise_error('Application `{0}` is currently in state {1}.\n'
                          'Can only save when STOPPED or STARTED.',
                          name, state)
    if bpname is None:
        bpname = new_blueprint_name('bp-{0}'.format(name))
    bp = application.create_blueprint(bpname, app)
    if wait:
        bp = application.wait_until_blueprint_is_in_state(bp, 'DONE')
    return application.appdef_from_app(bp)
Esempio n. 4
0
def stop_application(app):
    """Stop all started VMs in an application."""
    for vm in app['vms']:
        if vm['dynamicMetadata']['state'] == 'STARTED':
            env.api.stop_vm(app, vm)
    app = cache.get_application(app['id'], force_reload=True)
    return app
Esempio n. 5
0
def default_application(appname):
    """The default application loading function."""
    parts = appname.split(':')
    if len(parts) in (1, 2) and env.manifest is None:
        error.raise_error('No manifest found ({0}).\n'
                          'Please specify the fully qualified app name.\n'
                          "Use 'ravtest ps -a' for a list.",
                          manifest.manifest_name())
    if len(parts) in (1, 2):
        project = env.manifest['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'])
    return app
Esempio n. 6
0
def get_applications():
    """get_applications()

    Return a list containing all applications.
    """
    applications = []
    for app in cache.get_applications():
        app = cache.get_application(app['id'])
        applications.append(application.appdef_from_app(app))
    return applications
Esempio n. 7
0
def publish_application(app, cloud=None, region=None):
    """Publish the application ``app``."""
    req = {}
    if cloud is None:
        cloud = 'AMAZON'
        region = 'Virginia'
    req = { 'prefferedCloud': cloud,  # sic
            'prefferedRegion': region }
    env.api.publish_application(app, req)
    app = cache.get_application(app['id'], force_reload=True)
    return app
Esempio n. 8
0
def remove_application(name):
    """remove_application(name)

    Delete the application with name *name*.

    It is not an error to delete an application that does not exist. An
    application can always be deleted no matter what in the state its VMs are.
    If the application has running VMs, those will be uncleanly shutdown.

    Deleting an application destroys all data relating to the application
    including its VMs and their disks. This operation cannot be undone.
    """
    app = cache.get_application(name=name)
    if app is None:
        return
    application.remove_application(app)
Esempio n. 9
0
def reuse_existing_application(appdef):
    """Try to re-use an existing application."""
    candidates = []
    pubkey = env.public_key
    if appdef.get('blueprint'):
        blueprint = cache.get_blueprint(name=appdef['blueprint'])
    else:
        blueprint = None
    project = env.manifest['project']
    for app in cache.get_applications():
        parts = app['name'].split(':')
        if len(parts) != 3:
            continue
        if parts[0] != project['name'] or parts[1] != appdef['name']:
            continue
        app = cache.get_application(app['id'])
        vms = app.get('vms', [])
        if not vms:
            continue
        state = get_application_state(app)
        if state not in vm_reuse_states:
            continue
        if blueprint and blueprint['name'] != app.get('blueprintName'):
            continue
        vmsfound = []
        for vmdef in appdef['vms']:
            for vm in vms:
                if vm['name'] == vmdef['name']:
                    break
            if not blueprint:
                image = cache.get_image(name=vmdef['image'])
                if not image:
                    continue
                if vm['shelfVmId'] != image['id']:
                    continue
            userdata = vm.get('customVmConfigurationData', {})
            keypair = userdata.get('keypair')
            if keypair.get('id') != pubkey['id']:
                continue
            vmsfound.append(vmdef['name'])
        if len(vmsfound) != len(appdef['vms']):
            continue
        candidates.append((state, app))
    if not candidates:
        return
    candidates.sort(key=lambda x: vm_reuse_states.index(x[0]))
    return candidates[0][1]
Esempio n. 10
0
def get_application(name):
    """get_application(name)
    
    Lookup the application *name*.

    If the application exists, return the application definition for it. The
    application is be a dictionary with string keys describing the application.
    If the application is not found, return None.

    .. seealso::
       See :ref:`application-ref` for the possible keys in the application
       definition dictionary.
    """
    app = cache.get_application(name=name)
    if app is None:
        return
    return application.appdef_from_app(app)
Esempio n. 11
0
def wait_until_application_is_in_state(app, state, timeout=None,
                                       poll_timeout=None):
    """Wait until an application is in a given state."""
    if timeout is None:
        timeout = 900
    if poll_timeout is None:
        poll_timeout = 10
    end_time = time.time() + timeout
    while True:
        if time.time() > end_time:
            break
        poll_end_time = time.time() + poll_timeout
        app = cache.get_application(app['id'], force_reload=True)
        appstate = get_application_state(app)
        if appstate == state:
            return app
        console.show_progress(appstate[0])
        time.sleep(max(0, poll_end_time - time.time()))
    error.raise_error("Application `{0}` did not reach state '{1}' within "
                      "{2} seconds.", app['name'], state, timeout)
    return app
Esempio n. 12
0
def stop_application(name, wait=True, timeout=300):
    """stop_application(name)

    Stop an application with name *name*.

    This method will stop all VMs in the application that are currently in the
    'STARTED' state. VMs other states are not touched.
    
    The application may not be fully stopped even after this call. For example,
    if a VM is in the 'STARTING' state, it will eventually transition to
    'STARTED'. But a 'STARTNG' VM cannot be stopped before it reaches the
    'STARTED' state.
    """
    app = cache.get_application(name=name)
    if app is None:
        error.raise_error("Application `{0}` does not exist.", name)
    app = application.stop_application(app)
    if wait:
        state = application.get_application_state(app)
        if state not in ('STARTED', 'STOPPING', 'STOPPED'):
            error.raise_error("Cannot wait for app in state '{0}',", state)
        application.wait_until_application_is_in_state(app, 'STOPPED', timeout)
Esempio n. 13
0
def create_new_application(appdef, name_is_template=True):
    """Create a new application based on ``appdef``."""
    if name_is_template:
        project = env.manifest['project']
        template = '{0}:{1}'.format(project['name'], appdef['name'])
        name = util.get_unused_name(template, cache.get_applications())
    else:
        name = appdef['name']
    app = { 'name': name }
    bpname = appdef.get('blueprint')
    if bpname:
        blueprint = cache.get_blueprint(name=bpname)
        app['blueprintId'] = blueprint['id']
    else:
        vms = []
        for vmdef in appdef.get('vms', []):
            vm = create_new_vm(vmdef)
            vms.append(vm)
        app['vms'] = vms
    app = env.api.create_application(app)
    app = cache.get_application(app['id'])  # update cache
    return app
Esempio n. 14
0
def do_ps(args, env):
    """The "ravello ps" command."""
    with env.let(quiet=True):
        login.default_login()
        pubkey = keypair.default_keypair()
        manif = manifest.default_manifest(required=False)

    if manif is None and not args.all:
        error.raise_error('Project manifest ({0}) not found.\n'
                          "Use 'ravtest ps -a' to list all applications.",
                          manifest.manifest_name())
    if args.all:
        project = None
    else:
        project = manif['project']['name']
        console.info('Project name is `{0}`.', project)
    
    if args.blueprint:
        apps = cache.find_blueprints(project)
        what = 'blueprint'
    else:
        apps = cache.find_applications(project)
        what = 'application'

    apps = sorted(apps, key=lambda app: app['name'])
    objs = inflect.plural_noun(what)
    console.writeln('Currently available {0}:\n', objs)

    current_project = None
    for app in apps:
        parts = app['name'].split(':')
        if parts[0] != project and not args.all:
            continue
        if args.all and current_project != parts[0]:
            console.writeln("== Project: `{0}`", parts[0])
            current_project = parts[0]
        if args.full and not args.blueprint:
            app = cache.get_application(app['id'])

        cloud = app.get('cloud')
        region = app.get('regionName')
        started = app.get('totalStartedVms')
        publish_time = app.get('publishStartTime')
        creation_time = app.get('creationTime')
        created = publish_time or creation_time
        if created:
            now = time.time()
            created = util.format_timedelta(now - created/1000)
            created = '{0} ago'.format(created)
        else:
            created = ''
        if args.full and not args.blueprint:
            vms = [ vm['name'] for vm in application.get_vms(app) ]
            vms = '`{0}`'.format('`, `'.join(vms))
            state = application.get_application_state(app)
        else:
            state = app.get('state') or ''

        console.writeln('=== {0}: `{1}:{2}`', what.title(), parts[1], parts[2])
        what2 = inflect.plural_noun('VM', started)
        if state:
            console.writeln('    state: {0}', state)
        if started is not None:
            console.writeln('    {0} {1} running', started, what2)
        if cloud is not None:
            console.writeln('    published to {0}/{1}', cloud, region)
        if created:
            console.writeln('    created: {0}', created)
        if args.full and not args.blueprint:
            console.writeln('    VMs: {0}', vms)
        console.writeln()

    return error.EX_OK
Esempio n. 15
0
def remove_application(app):
    """Remove an application."""
    env.api.remove_application(app)
    cache.get_application(app['id'], force_reload=True)
Esempio n. 16
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