#!/usr/bin/env python
#
# Script to enable an image or VM for nested virtualization.
#
# Copyright (c) 2014 Ravello Systems Inc. Released under Apache 2 license.

"""Enable an image or VM for nested virtualization.

Usage:
  ravello-set-svm [--image] [options] <image>
  ravello-set-svm --vm [options] <application> <vm>
  ravello-set-svm (-h | --help)

The arguments <image>, <application, and <vm> specify the object to enable
nested virtualization for. These arguments are interpreted as names first,
and if no such object is found, as IDs.

Arguments:
  <image>           The image name or ID.
  <application>     The application name or ID.
  <vm>              The VM name or ID.

Options:
  --image           Enable nested virt for an image.
  --vm              Enable nested virt for a VM in an application.
  -n, --numeric     Skip name resolution and load the object directly by ID.
  -u <username>, --username=<username>
                    Ravello API username. If absent use $RAVELLO_USERNAME.
  -p <password>, --password=<password>
                    Ravello API password. If absent use $RAVELLO_PASSWORD.
  -d, --debug       Enable debugging mode.
"""

import os
import sys

import logging
from docopt import docopt
from getpass import getpass
from ravello_sdk import RavelloClient

svm_cpuids = [
    { "value": "0000000768747541444d416369746e65", "index": "0" },
    { "value": "000006fb00000800c0802000078bfbfd", "index": "1" },
    { "value": "8000000a000000000000000000000000", "index": "80000000" },
    { "value": "00000000000000000000001520100800", "index": "80000001" },
    { "value": "00000001000000400000000000000088", "index": "8000000a" }, ]


def parse_args():
    """Parse and validate arguments."""
    args = docopt(__doc__)
    if not args.get('--vm'):
        args['--image'] = True
    if not args['--username']:
        args['--username'] = os.environ.get('RAVELLO_USERNAME')
    if not args['--username']:
        raise ValueError('specify --username or set $RAVELLO_USERNAME')
    if not args['--password']:
        args['--password'] = os.environ.get('RAVELLO_PASSWORD')
    if not args['--password'] and sys.stdin.isatty():
        args['--password'] = getpass('Enter Ravello API password: ')
    if not args['--password']:
        raise ValueError('specify --password or set $RAVELLO_PASSWORD')
    if args['--numeric']:
        for key in ('<image>', '<application>', '<vm>'):
            if args.get(key) and not args[key].isdigit():
                raise ValueError('{0} must be numeric when --numeric'.format(key))
    return args


def get_image(client, name, numeric=False):
    """Load an image by name or ID."""
    image = None
    if not numeric:
        images = client.get_images({'name': name})
        if len(images) == 1:
            image = client.reload(images[0])
        elif len(images) > 1:
            raise ValueError('{0} images match name {1!r}'.format(len(images), name))
    if name.isdigit():
        image = client.get_image(name)
    if image is None:
        raise ValueError('image not found: {0}'.format(name))
    return image


def get_application(client, name, numeric=False):
    """Load an application by name or ID."""
    app = None
    if not numeric:
        apps = client.get_applications({'name': name})
        if len(apps) == 1:
            app = client.reload(apps[0])
        elif len(apps) > 1:
            raise ValueError('{0} applications match name {1!r}'.format(len(apps), name))
    if name.isdigit():
        app = client.get_application(name)
    if app is None:
        raise ValueError('application not found: {0}'.format(name))
    return app


def get_design_vm(application, name, numeric=False):
    """Get a design VM by name or ID."""
    vms = application.get('design', {}).get('vms', [])
    if not numeric:
        for vm in vms:
            if vm['name'] == name:
                return vm
    for vm in vms:
        if vm['id'] == name:
            return vm
    raise ValueError('vm not found: {0}'.format(name))


def main():
    """Main entry point."""
    debug = True
    try:
        args = parse_args()
        debug = args['--debug']
        logging.basicConfig(level=logging.DEBUG if debug else logging.INFO)

        client = RavelloClient(args['--username'], args['--password'])

        if args['--image']:
            image = get_image(client, args['<image>'], args['--numeric'])
            image['cpuIds'] = svm_cpuids
            client.update_image(image)

        else:
            app = get_application(client, args['<application>'], args['--numeric'])
            vm = get_design_vm(app, args['<vm>'], args['--numeric'])
            vm['cpuIds'] = svm_cpuids
            client.update_application(app)

        print('Nested virtualization enabled.')

    except Exception as e:
        if debug:
            raise
        sys.stderr.write('Error: {0!s}\n'.format(e))
        sys.exit(1)
    sys.exit(0)


if __name__ == '__main__':
    main()
