#!/usr/bin/env python
#
# Created as part of the StratusLab project (http://stratuslab.eu),
# co-funded by the European Commission under the Grant Agreement
# INFSO-RI-261552."
#
# Copyright (c) 2010, SixSq Sarl
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

import sys

sys.path.append('/var/lib/stratuslab/python')

from stratuslab.Runner import Runner
from stratuslab.Creator import Creator
from stratuslab.Runnable import Runnable
from stratuslab.ConfigHolder import ConfigHolder
from stratuslab.commandbase.StorageCommand import StorageCommand

# initialize console logging
import stratuslab.api.LogUtil as LogUtil

LogUtil.get_console_logger()


class MainProgram(Runnable):
    """A command-line program to run a virtual machine."""

    parser_description = '''
Defines and starts a virtual machine instance.  The image argument is
the Marketplace ID or persistent volume UUID to start.
'''

    def parse(self):
        options = Runner.defaultRunOptions()

        self.parser.add_option('-n', '--number', dest='instanceNumber',
                               help='number of machines to start (default 1)', metavar='INT',
                               default=options['instanceNumber'], type='int')

        self.parser.add_option('--local-ip', dest='isLocalIp', action='store_true',
                               help='By default, virtual machines are assigned a public IP address. '
                                    'With this option, a local address is provided, which means that it '
                                    'not be reachable from the outside, but will be reachable from other '
                                    'instances in the same data-center.',
                               default=False)

        self.parser.add_option('--private-ip', dest='isPrivateIp', action='store_true',
                               help='By default, virtual machines are assigned a public IP address. '
                                    'With this option, a private address is provided, which means that it '
                                    'not be reachable anywhere. This is useful for instances that initiate '
                                    'the connection to other machinces.',
                               default=False)

        self.parser.add_option('--save', dest='saveDisk', action='store_true',
                               help='save image after VM shutdown',
                               default=False)

        self.parser.add_option('--author-email', dest='authorEmail',
                               help='Used with --save option. Email address of the author of '
                                    'the new image. After image is saved a notification is sent '
                                    'with further instructions.',
                               default='', metavar='EMAIL')

        self.parser.add_option('--author', dest='author',
                               help='Used with --save option. Author of the new image',
                               default='', metavar='NAME')

        self.parser.add_option('--image-version', dest='newImageGroupVersion',
                               help='Used with --save option. Version for the new image.',
                               default='', metavar='VERSION')

        self.parser.add_option('--title', dest='title',
                               help='Used with --save option. Title of the new image',
                               default='', metavar='TEXT')

        self.parser.add_option('--comment', dest='comment',
                               help='Used with --save option. Description of the new image',
                               default='', metavar='TEXT')

        self.parser.add_option('--marketplace-endpoint-newimage', dest='marketplaceEndpointNewimage',
                               help='Used with --save option. Marketplace to register the new image manifest in. '
                                    'No default. If not provided, either base image Marketplace or defined '
                                    'by cloud site will be used.',
                               default='', metavar='URL')

        self.parser.add_option('-o', '--output', dest='outVmIdsFile',
                               help='save vm-id to a file', metavar='FILE',
                               default=None)

        self.parser.add_option('--volatile-disk', dest='extraDiskSize',
                               help='volatile data disk size in GB', metavar='INT',
                               action='callback', callback=self.diskSizeOptionCallback,
                               default=0, type='int')

        self.parser.add_option('--persistent-disk', dest='persistentDiskUUID',
                               help='persistent disk UUID',
                               default=None)

        self.parser.add_option('--readonly-disk', dest='readonlyDiskId',
                               help='marketplace readonly disk image ID',
                               default=None, metavar='MARKETPLACEID')

        self.parser.add_option('--qcow-disk-format', dest='useQcowDiskFormat',
                               help='launch instance from an image in qcow2 disk format. Only '
                                    'used when image is provided as URL. When image is given by ID, disk '
                                    'format is inferred from the image metadata.',
                               action='store_true',
                               default=False)

        self.parser.add_option('--address', dest='specificAddressRequest',
                               help='request a specific ip address',
                               metavar='IP', default=None)

        self.parser.add_option('--no-check-image-url', dest='noCheckImageUrl',
                               help='Turn off image availability check. '
                               'Do not check if the location URLs in the image metadata exist.',
                               action='store_true',
                               default=False)

        self.parser.add_option('--vm-name', dest='vmName',
                               help='name of VM. If not given, a default name will be assigned by cloud layer.',
                               metavar='NAME', default=options['vmName'])

        self.parser.add_option('--notify', dest='msgRecipients', action='append',
                               help='recipient coordinates (host,vhost,user,pass,queue)',
                               default=[])

        StorageCommand.addPDiskEndpointOptions(self.parser)

        super(MainProgram, self).parse()

    def diskSizeOptionCallback(self, option, opt_str, value, parser):
        setattr(parser.values, option.dest, 1024 * value)

    def checkOptions(self):

        super(MainProgram, self).checkOptions()

        if self.options.saveDisk:
            pre_message = 'With the --save option, please '
            if not self.options.author:
                self.parser.error(pre_message + 'provide a valid author name of the image (--author).')
            if not self.options.authorEmail:
                self.parser.error(
                    pre_message + 'provide a valid email address of the author of the image (--author-email).')
            if not self.options.newImageGroupVersion:
                self.parser.error(pre_message + 'provide a version number for the new image (--image-version).')
            if not self.options.title:
                self.parser.error(pre_message + 'provide a title for the image (--title).')

            if not self.options.comment:
                self.parser.error(pre_message + 'provide a comment for the image (--comment).')

    def _printShortResults(self, vmInfo):
        for vmId, networkName, ip in vmInfo:
            print '%s, %s' % (vmId, ip)

    def _printLongResults(self, vmInfo):
        vmNb = 0
        for vmId, networkName, ip in vmInfo:
            vmIpPretty = '\t%s ip: %s' % (networkName.title(), ip)
            msg = 'Machine %s (vm ID: %s)\n%s' % (vmNb + 1, vmId, vmIpPretty)
            print ' :: %s' % msg
            vmNb += 1

    def doWork(self):
        configHolder = ConfigHolder(self.options.__dict__, self.config)

        if self.options.saveDisk:
            creator = Creator(self.image, configHolder)
            creator.createRunner()
            runner = creator.runner
        else:
            runner = Runner(self.image, configHolder)

        if self.options.listType:
            print runner.listInstanceTypes()
        else:
            vmInfo = runner.runInstance(details=True)

            # TODO: This is anti-intuitive and should be reviewed.
            #
            # To keep compatibility with the previous output, this will
            # only print something when quiet is set.  The 'normal' output
            # will be printed via INFO logging statements in the code.
            if self.options.quiet > 0:
                self._printShortResults(vmInfo)


if __name__ == '__main__':
    try:
        MainProgram()
    except KeyboardInterrupt:
        print '\n\nExecution interrupted by the user... goodbye!'
