#!/usr/bin/env python
# encoding: utf-8
#
#  Licensed to the Apache Software Foundation (ASF) under one
#  or more contributor license agreements.  See the NOTICE file
#  distributed with this work for additional information
#  regarding copyright ownership.  The ASF licenses this file
#  to you 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.
#

"""This module contains functions for handling requests in relation to
instances.
"""

from flask import current_app

from ec2stack.providers import cloudstack
from ec2stack.providers.cloudstack import requester, service_offerings, zones, \
    disk_offerings
from ec2stack import helpers, errors


@helpers.authentication_required
def describe_instance_attribute():
    """
    Describes an instance attribute.

    @return: Response.
    """
    instance_id = helpers.get('InstanceId')
    attribute = helpers.get('Attribute')

    supported_attribute_map = {
        'instanceType': 'serviceofferingname',
        'groupSet': 'securitygroup'
    }

    if attribute not in supported_attribute_map.iterkeys():
        errors.invalid_parameter_value(
            'The specified attribute is not valid, please specify a valid ' +
            'instance attribute.'
        )

    response = describe_instance_by_id(instance_id)
    return _describe_instance_attribute_response(
        response, attribute, supported_attribute_map)


def _describe_instance_attribute_response(response, attribute, attr_map):
    """
    Generates a response for a describe instance attribute request.

    @param response: Response from Cloudstack.
    @param attribute: Attribute to Describe.
    @param attr_map: Map of attributes from EC2 to Cloudstack.
    @return: Response.
    """
    response = {
        'template_name_or_list': 'instance_attribute.xml',
        'response_type': 'DescribeInstanceAttributeResponse',
        'attribute': attribute,
        'response': response[attr_map[attribute]],
        'id': response['id']
    }

    return response


@helpers.authentication_required
def describe_instances():
    """
    Describe a specific instance or all instances.

    @return: Response.
    """
    args = {'command': 'listVirtualMachines'}
    response = cloudstack.describe_item(
        args, 'virtualmachine', errors.invalid_instance_id, 'InstanceId'
    )

    return _describe_instances_response(
        response
    )


def describe_instance_by_id(instance_id):
    """
    Describe an instance by Id.

    @param instance_id: Id of the instance.
    @return: Response.
    """
    args = {'id': instance_id, 'command': 'listVirtualMachines'}
    response = cloudstack.describe_item_request(
        args, 'virtualmachine', errors.invalid_instance_id
    )
    return response


def _describe_instances_response(response):
    """
    Generates a response for a describe instance request.

    @param response: Response from Cloudstack.
    @return: Response.
    """
    return {
        'template_name_or_list': 'instances.xml',
        'response_type': 'DescribeInstancesResponse',
        'response': response
    }


@helpers.authentication_required
def reboot_instance():
    """
    Reboot an instance.

    @return: Response.
    """
    helpers.require_parameters(['InstanceId.1'])
    instance_id = helpers.get('InstanceId.1')
    _reboot_instance_request(instance_id)
    return _reboot_instance_response()


def _reboot_instance_request(instance_id):
    """
    Request to reboot an instance.

    @param instance_id: Id of instance to be rebooted.
    @return: Response.
    """
    args = {'command': 'rebootVirtualMachine',
            'id': instance_id}
    response = requester.make_request_async(args)
    response = response['virtualmachine']
    return response


def _reboot_instance_response():
    """
    Generates a response for a reboot instance request.

    @return: Response.
    """
    response = {
        'template_name_or_list': 'status.xml',
        'response_type': 'RebootInstancesResponse',
        'return': 'true'
    }

    return response


@helpers.authentication_required
def run_instance():
    """
    Run a instance.

    @return: Response.
    """
    helpers.require_parameters(
        ['ImageId', 'MinCount', 'MaxCount'])
    response = _run_instance_request()
    return _run_instance_response(response)


def _run_instance_request():
    """
    Request to bring up an instance.

    @return: Response.
    """
    args = {}
    if helpers.contains_parameter('Placement.AvailabilityZone'):
        args['zoneid'] = zones.get_zone(
            helpers.get('Placement.AvailabilityZone')
        )
    else:
        args['zoneid'] = zones.get_zone(
            current_app.config['CLOUDSTACK_DEFAULT_ZONE']
        )['id']

    if helpers.get('BlockDeviceMapping.1.Ebs.VolumeType') is not None:
        disk_type = helpers.get('BlockDeviceMapping.1.Ebs.VolumeType')
        if disk_type == 'gp2':
            args['diskofferingid'] = disk_offerings.get_disk_offering(
                current_app.config['CLOUDSTACK_CUSTOM_DISK_OFFERING']
            )['id']

        if helpers.get('BlockDeviceMapping.1.Ebs.VolumeSize') is None:
            errors.invalid_request(
                "VolumeSize not found in BlockDeviceMapping")
        else:
            args['size'] = helpers.get('BlockDeviceMapping.1.Ebs.VolumeSize')

    if helpers.get('InstanceType') is None:
        instance_type = 'm1.small'
    else:
        instance_type = helpers.get('InstanceType')

    if instance_type in current_app.config['INSTANCE_TYPE_MAP']:
        instance_type = current_app.config[
            'INSTANCE_TYPE_MAP'][instance_type]
    else:
        instance_type = instance_type

    args['serviceofferingid'] = \
        service_offerings.get_service_offering(instance_type)['id']
    args['templateid'] = helpers.get('ImageId')

    if helpers.contains_parameter('KeyName'):
        args['keypair'] = helpers.get('KeyName')

    if helpers.contains_parameter('UserData'):
        args['userdata'] = helpers.get('UserData')

    if helpers.contains_parameter_with_keyword('SecurityGroupId.'):
        keys = helpers.get_request_parameter_keys('SecurityGroupId.')
        securitygroupids = []

        for key in keys:
            securitygroupids.append(helpers.get(key))

        args['securitygroupids'] = ",".join(securitygroupids)

    if helpers.contains_parameter_with_keyword('SecurityGroup.'):
        keys = helpers.get_request_parameter_keys('SecurityGroup.')
        securitygroupnames = []

        for key in keys:
            securitygroupnames.append(helpers.get(key))

        args['securitygroupnames'] = ",".join(securitygroupnames)

    if helpers.get('SubnetId') is not None:
        args['networkids'] = helpers.get('SubnetId')

    args['command'] = 'deployVirtualMachine'

    response = requester.make_request_async(args)

    return response


def _run_instance_response(response):
    """
    Generates a response for a run instance request.

    @param response: Response from Cloudstack.
    @return: Response.
    """
    if 'errortext' in response:
        if 'Object vm_template' in response['errortext']:
            errors.invalid_image_id()
        elif 'Object security_group' in response['errortext']:
            errors.invalid_security_group()
        elif 'A key pair with name' in response['errortext']:
            errors.invalid_keypair_name()
        else:
            errors.invalid_request(response['errortext'])
    else:
        response = response['virtualmachine']
        response = {
            'template_name_or_list': 'run_instance.xml',
            'response_type': 'RunInstancesResponse',
            'response': response
        }

    return response


@helpers.authentication_required
def start_instance():
    """
    Starts an instance.

    @return: Response.
    """
    helpers.require_parameters(['InstanceId.1'])
    instance_id = helpers.get('InstanceId.1')
    previous_state = describe_instance_by_id(instance_id)
    new_state = _start_instance_request(instance_id)
    return _modify_instance_state_response(
        'StartInstancesResponse',
        previous_state,
        new_state
    )


def _start_instance_request(instance_id):
    """
    Request to start an instance.

    @param instance_id: Id of instance to start.
    @return: Response.
    """
    args = {'command': 'startVirtualMachine',
            'id': instance_id}

    response = requester.make_request_async(args)

    response = response['virtualmachine']

    return response


def _modify_instance_state_response(response_type, previous_state, new_state):
    """
    Generates a response for a Start, Stop, Terminate requests.

    @param response_type: Cloudstack response.
    @param previous_state: The previous state of the instance.
    @param new_state: The new state of the instance.
    @return: Response.
    """
    response = {
        'template_name_or_list': 'change_instance_state.xml',
        'response_type': response_type,
        'previous_state': previous_state,
        'new_state': new_state
    }

    return response


@helpers.authentication_required
def stop_instance():
    """
    Stop an instance.

    @return: Response.
    """
    helpers.require_parameters(['InstanceId.1'])
    instance_id = helpers.get('InstanceId.1')
    previous_state = describe_instance_by_id(instance_id)
    new_state = _stop_instance_request(instance_id)
    return _modify_instance_state_response(
        'StopInstancesResponse',
        previous_state,
        new_state
    )


def _stop_instance_request(instance_id):
    """
    Request to stop an instance.

    @param instance_id: Id of instance to stop.
    @return: Response.
    """
    args = {'command': 'stopVirtualMachine',
            'id': instance_id}
    response = requester.make_request_async(args)
    response = response['virtualmachine']
    return response


@helpers.authentication_required
def terminate_instance():
    """
    Terminate an instance.

    @return: Response.
    """
    helpers.require_parameters(['InstanceId.1'])
    instance_id = helpers.get('InstanceId.1')
    previous_state = describe_instance_by_id(instance_id)
    new_state = _terminate_instance_request(instance_id)
    return _modify_instance_state_response(
        'TerminateInstancesResponse',
        previous_state,
        new_state
    )


def _terminate_instance_request(instance_id):
    """
    Request to terminate an instance.

    @param instance_id: Id of instance to terminate.
    @return: Response.
    """
    args = {'command': 'destroyVirtualMachine',
            'id': instance_id}

    response = requester.make_request_async(args)

    response = response['virtualmachine']

    return response

#!/usr/bin/env python
# encoding: utf-8
#
#  Licensed to the Apache Software Foundation (ASF) under one
#  or more contributor license agreements.  See the NOTICE file
#  distributed with this work for additional information
#  regarding copyright ownership.  The ASF licenses this file
#  to you 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.
#

"""This module contains functions for handling requests in relation to
instances.
"""

from flask import current_app

from ec2stack.providers import cloudstack
from ec2stack.providers.cloudstack import requester, service_offerings, zones, \
    disk_offerings
from ec2stack import helpers, errors


@helpers.authentication_required
def describe_instance_attribute():
    """
    Describes an instance attribute.

    @return: Response.
    """
    instance_id = helpers.get('InstanceId')
    attribute = helpers.get('Attribute')

    supported_attribute_map = {
        'instanceType': 'serviceofferingname',
        'groupSet': 'securitygroup'
    }

    if attribute not in supported_attribute_map.iterkeys():
        errors.invalid_parameter_value(
            'The specified attribute is not valid, please specify a valid ' +
            'instance attribute.'
        )

    response = describe_instance_by_id(instance_id)
    return _describe_instance_attribute_response(
        response, attribute, supported_attribute_map)


def _describe_instance_attribute_response(response, attribute, attr_map):
    """
    Generates a response for a describe instance attribute request.

    @param response: Response from Cloudstack.
    @param attribute: Attribute to Describe.
    @param attr_map: Map of attributes from EC2 to Cloudstack.
    @return: Response.
    """
    response = {
        'template_name_or_list': 'instance_attribute.xml',
        'response_type': 'DescribeInstanceAttributeResponse',
        'attribute': attribute,
        'response': response[attr_map[attribute]],
        'id': response['id']
    }

    return response


@helpers.authentication_required
def describe_instances():
    """
    Describe a specific instance or all instances.

    @return: Response.
    """
    args = {'command': 'listVirtualMachines'}
    response = cloudstack.describe_item(
        args, 'virtualmachine', errors.invalid_instance_id, 'InstanceId'
    )

    return _describe_instances_response(
        response
    )


def describe_instance_by_id(instance_id):
    """
    Describe an instance by Id.

    @param instance_id: Id of the instance.
    @return: Response.
    """
    args = {'id': instance_id, 'command': 'listVirtualMachines'}
    response = cloudstack.describe_item_request(
        args, 'virtualmachine', errors.invalid_instance_id
    )
    return response


def _describe_instances_response(response):
    """
    Generates a response for a describe instance request.

    @param response: Response from Cloudstack.
    @return: Response.
    """
    return {
        'template_name_or_list': 'instances.xml',
        'response_type': 'DescribeInstancesResponse',
        'response': response
    }


@helpers.authentication_required
def reboot_instance():
    """
    Reboot an instance.

    @return: Response.
    """
    helpers.require_parameters(['InstanceId.1'])
    instance_id = helpers.get('InstanceId.1')
    _reboot_instance_request(instance_id)
    return _reboot_instance_response()


def _reboot_instance_request(instance_id):
    """
    Request to reboot an instance.

    @param instance_id: Id of instance to be rebooted.
    @return: Response.
    """
    args = {'command': 'rebootVirtualMachine',
            'id': instance_id}
    response = requester.make_request_async(args)
    response = response['virtualmachine']
    return response


def _reboot_instance_response():
    """
    Generates a response for a reboot instance request.

    @return: Response.
    """
    response = {
        'template_name_or_list': 'status.xml',
        'response_type': 'RebootInstancesResponse',
        'return': 'true'
    }

    return response


@helpers.authentication_required
def run_instance():
    """
    Run a instance.

    @return: Response.
    """
    helpers.require_parameters(
        ['ImageId', 'MinCount', 'MaxCount'])
    response = _run_instance_request()
    return _run_instance_response(response)


def _run_instance_request():
    """
    Request to bring up an instance.

    @return: Response.
    """
    args = {}
    if helpers.contains_parameter('Placement.AvailabilityZone'):
        args['zoneid'] = zones.get_zone(
            helpers.get('Placement.AvailabilityZone')
        )
    else:
        args['zoneid'] = zones.get_zone(
            current_app.config['CLOUDSTACK_DEFAULT_ZONE']
        )['id']

    if helpers.get('BlockDeviceMapping.1.Ebs.VolumeType') is not None:
        disk_type = helpers.get('BlockDeviceMapping.1.Ebs.VolumeType')
        if disk_type == 'gp2':
            args['diskofferingid'] = disk_offerings.get_disk_offering(
                current_app.config['CLOUDSTACK_CUSTOM_DISK_OFFERING']
            )['id']

        if helpers.get('BlockDeviceMapping.1.Ebs.VolumeSize') is None:
            errors.invalid_request(
                "VolumeSize not found in BlockDeviceMapping")
        else:
            args['size'] = helpers.get('BlockDeviceMapping.1.Ebs.VolumeSize')

    if helpers.get('InstanceType') is None:
        instance_type = 'm1.small'
    else:
        instance_type = helpers.get('InstanceType')

    if instance_type in current_app.config['INSTANCE_TYPE_MAP']:
        instance_type = current_app.config[
            'INSTANCE_TYPE_MAP'][instance_type]
    else:
        instance_type = instance_type

    args['serviceofferingid'] = \
        service_offerings.get_service_offering(instance_type)['id']
    args['templateid'] = helpers.get('ImageId')

    if helpers.contains_parameter('KeyName'):
        args['keypair'] = helpers.get('KeyName')

    if helpers.contains_parameter('UserData'):
        args['userdata'] = helpers.get('UserData')

    if helpers.contains_parameter_with_keyword('SecurityGroupId.'):
        keys = helpers.get_request_parameter_keys('SecurityGroupId.')
        securitygroupids = []

        for key in keys:
            securitygroupids.append(helpers.get(key))

        args['securitygroupids'] = ",".join(securitygroupids)

    if helpers.contains_parameter_with_keyword('SecurityGroup.'):
        keys = helpers.get_request_parameter_keys('SecurityGroup.')
        securitygroupnames = []

        for key in keys:
            securitygroupnames.append(helpers.get(key))

        args['securitygroupnames'] = ",".join(securitygroupnames)

    if helpers.get('SubnetId') is not None:
        args['networkids'] = helpers.get('SubnetId')

    args['command'] = 'deployVirtualMachine'

    response = requester.make_request_async(args)

    return response


def _run_instance_response(response):
    """
    Generates a response for a run instance request.

    @param response: Response from Cloudstack.
    @return: Response.
    """
    if 'errortext' in response:
        if 'Object vm_template' in response['errortext']:
            errors.invalid_image_id()
        elif 'Object security_group' in response['errortext']:
            errors.invalid_security_group()
        elif 'A key pair with name' in response['errortext']:
            errors.invalid_keypair_name()
        else:
            errors.invalid_request(response['errortext'])
    else:
        response = response['virtualmachine']
        response = {
            'template_name_or_list': 'run_instance.xml',
            'response_type': 'RunInstancesResponse',
            'response': response
        }

    return response


@helpers.authentication_required
def start_instance():
    """
    Starts an instance.

    @return: Response.
    """
    helpers.require_parameters(['InstanceId.1'])
    instance_id = helpers.get('InstanceId.1')
    previous_state = describe_instance_by_id(instance_id)
    new_state = _start_instance_request(instance_id)
    return _modify_instance_state_response(
        'StartInstancesResponse',
        previous_state,
        new_state
    )


def _start_instance_request(instance_id):
    """
    Request to start an instance.

    @param instance_id: Id of instance to start.
    @return: Response.
    """
    args = {'command': 'startVirtualMachine',
            'id': instance_id}

    response = requester.make_request_async(args)

    response = response['virtualmachine']

    return response


def _modify_instance_state_response(response_type, previous_state, new_state):
    """
    Generates a response for a Start, Stop, Terminate requests.

    @param response_type: Cloudstack response.
    @param previous_state: The previous state of the instance.
    @param new_state: The new state of the instance.
    @return: Response.
    """
    response = {
        'template_name_or_list': 'change_instance_state.xml',
        'response_type': response_type,
        'previous_state': previous_state,
        'new_state': new_state
    }

    return response


@helpers.authentication_required
def stop_instance():
    """
    Stop an instance.

    @return: Response.
    """
    helpers.require_parameters(['InstanceId.1'])
    instance_id = helpers.get('InstanceId.1')
    previous_state = describe_instance_by_id(instance_id)
    new_state = _stop_instance_request(instance_id)
    return _modify_instance_state_response(
        'StopInstancesResponse',
        previous_state,
        new_state
    )


def _stop_instance_request(instance_id):
    """
    Request to stop an instance.

    @param instance_id: Id of instance to stop.
    @return: Response.
    """
    args = {'command': 'stopVirtualMachine',
            'id': instance_id}
    response = requester.make_request_async(args)
    response = response['virtualmachine']
    return response


@helpers.authentication_required
def terminate_instance():
    """
    Terminate an instance.

    @return: Response.
    """
    helpers.require_parameters(['InstanceId.1'])
    instance_id = helpers.get('InstanceId.1')
    previous_state = describe_instance_by_id(instance_id)
    new_state = _terminate_instance_request(instance_id)
    return _modify_instance_state_response(
        'TerminateInstancesResponse',
        previous_state,
        new_state
    )


def _terminate_instance_request(instance_id):
    """
    Request to terminate an instance.

    @param instance_id: Id of instance to terminate.
    @return: Response.
    """
    args = {'command': 'destroyVirtualMachine',
            'id': instance_id}

    response = requester.make_request_async(args)

    response = response['virtualmachine']

    return response

#!/usr/bin/env python # encoding: utf-8 # #  Licensed to the Apache Software Foundation (ASF) under one #  or more contributor license agreements.  See the NOTICE file #  distributed with this work for additional information #  regarding copyright ownership.  The ASF licenses this file #  to you 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. # This module contains functions for handling requests in relation to instances. Describes an instance attribute.      @return: Response. Generates a response for a describe instance attribute request.      @param response: Response from Cloudstack.     @param attribute: Attribute to Describe.     @param attr_map: Map of attributes from EC2 to Cloudstack.     @return: Response. Describe a specific instance or all instances.      @return: Response. Describe an instance by Id.      @param instance_id: Id of the instance.     @return: Response. Generates a response for a describe instance request.      @param response: Response from Cloudstack.     @return: Response. Reboot an instance.      @return: Response. Request to reboot an instance.      @param instance_id: Id of instance to be rebooted.     @return: Response. Generates a response for a reboot instance request.      @return: Response. Run a instance.      @return: Response. Request to bring up an instance.      @return: Response. Generates a response for a run instance request.      @param response: Response from Cloudstack.     @return: Response. Starts an instance.      @return: Response. Request to start an instance.      @param instance_id: Id of instance to start.     @return: Response. Generates a response for a Start, Stop, Terminate requests.      @param response_type: Cloudstack response.     @param previous_state: The previous state of the instance.     @param new_state: The new state of the instance.     @return: Response. Stop an instance.      @return: Response. Request to stop an instance.      @param instance_id: Id of instance to stop.     @return: Response. Terminate an instance.      @return: Response. Request to terminate an instance.      @param instance_id: Id of instance to terminate.     @return: Response.