# -*- coding: utf-8 -*-
"""
AliYun ECS Cloud Module
=======================

.. versionadded:: 2014.7.0

The Aliyun cloud module is used to control access to the aliyun ECS.
http://www.aliyun.com/

Use of this module requires the ``id`` and ``key`` parameter to be set.
Set up the cloud configuration at ``/etc/salt/cloud.providers`` or
``/etc/salt/cloud.providers.d/aliyun.conf``:

.. code-block:: yaml

    my-aliyun-config:
      # aliyun Access Key ID
      id: wFGEwgregeqw3435gDger
      # aliyun Access Key Secret
      key: GDE43t43REGTrkilg43934t34qT43t4dgegerGEgg
      location: cn-qingdao
      driver: aliyun

:depends: requests
"""

# Import python libs
from __future__ import absolute_import, print_function, unicode_literals

import base64
import hmac
import logging
import pprint
import sys
import time
import uuid
from hashlib import sha1

import salt.config as config

# Import salt cloud libs
import salt.utils.cloud
import salt.utils.data
import salt.utils.json
from salt.exceptions import (
    SaltCloudExecutionFailure,
    SaltCloudExecutionTimeout,
    SaltCloudNotFound,
    SaltCloudSystemExit,
)

# Import 3rd-party libs
from salt.ext import six

# Import Salt libs
# pylint: disable=import-error,no-name-in-module
from salt.ext.six.moves.urllib.parse import quote as _quote

# pylint: enable=import-error,no-name-in-module
from salt.utils.stringutils import to_bytes

try:
    import requests

    HAS_REQUESTS = True
except ImportError:
    HAS_REQUESTS = False

# Get logging started
log = logging.getLogger(__name__)

ALIYUN_LOCATIONS = {
    # 'us-west-2': 'ec2_us_west_oregon',
    "cn-hangzhou": "AliYun HangZhou Region",
    "cn-beijing": "AliYun BeiJing Region",
    "cn-hongkong": "AliYun HongKong Region",
    "cn-qingdao": "AliYun QingDao Region",
    "cn-shanghai": "AliYun ShangHai Region",
    "cn-shenzhen": "AliYun ShenZheng Region",
    "ap-northeast-1": "AliYun DongJing Region",
    "ap-southeast-1": "AliYun XinJiaPo Region",
    "ap-southeast-2": "AliYun XiNi Region",
    "eu-central-1": "EU FalaKeFu Region",
    "me-east-1": "ME DiBai Region",
    "us-east-1": "US FuJiNiYa Region",
    "us-west-1": "US GuiGu Region",
}
DEFAULT_LOCATION = "cn-hangzhou"

DEFAULT_ALIYUN_API_VERSION = "2014-05-26"

__virtualname__ = "aliyun"


# Only load in this module if the aliyun configurations are in place
def __virtual__():
    """
    Check for aliyun configurations
    """
    if get_configured_provider() is False:
        return False

    if get_dependencies() is False:
        return False

    return __virtualname__


def get_configured_provider():
    """
    Return the first configured instance.
    """
    return config.is_provider_configured(
        __opts__, __active_provider_name__ or __virtualname__, ("id", "key")
    )


def get_dependencies():
    """
    Warn if dependencies aren't met.
    """
    return config.check_driver_dependencies(__virtualname__, {"requests": HAS_REQUESTS})


def avail_locations(call=None):
    """
    Return a dict of all available VM locations on the cloud provider with
    relevant data
    """
    if call == "action":
        raise SaltCloudSystemExit(
            "The avail_locations function must be called with "
            "-f or --function, or with the --list-locations option"
        )

    params = {"Action": "DescribeRegions"}
    items = query(params=params)

    ret = {}
    for region in items["Regions"]["Region"]:
        ret[region["RegionId"]] = {}
        for item in region:
            ret[region["RegionId"]][item] = six.text_type(region[item])

    return ret


def avail_images(kwargs=None, call=None):
    """
    Return a list of the images that are on the provider
    """
    if call == "action":
        raise SaltCloudSystemExit(
            "The avail_images function must be called with "
            "-f or --function, or with the --list-images option"
        )

    if not isinstance(kwargs, dict):
        kwargs = {}

    provider = get_configured_provider()
    location = provider.get("location", DEFAULT_LOCATION)

    if "location" in kwargs:
        location = kwargs["location"]

    params = {
        "Action": "DescribeImages",
        "RegionId": location,
        "PageSize": "100",
    }
    items = query(params=params)

    ret = {}
    for image in items["Images"]["Image"]:
        ret[image["ImageId"]] = {}
        for item in image:
            ret[image["ImageId"]][item] = six.text_type(image[item])

    return ret


def avail_sizes(call=None):
    """
    Return a list of the image sizes that are on the provider
    """
    if call == "action":
        raise SaltCloudSystemExit(
            "The avail_sizes function must be called with "
            "-f or --function, or with the --list-sizes option"
        )

    params = {"Action": "DescribeInstanceTypes"}
    items = query(params=params)

    ret = {}
    for image in items["InstanceTypes"]["InstanceType"]:
        ret[image["InstanceTypeId"]] = {}
        for item in image:
            ret[image["InstanceTypeId"]][item] = six.text_type(image[item])

    return ret


def get_location(vm_=None):
    """
    Return the aliyun region to use, in this order:
        - CLI parameter
        - VM parameter
        - Cloud profile setting
    """
    return __opts__.get(
        "location",
        config.get_cloud_config_value(
            "location",
            vm_ or get_configured_provider(),
            __opts__,
            default=DEFAULT_LOCATION,
            search_global=False,
        ),
    )


def list_availability_zones(call=None):
    """
    List all availability zones in the current region
    """
    ret = {}

    params = {"Action": "DescribeZones", "RegionId": get_location()}
    items = query(params)

    for zone in items["Zones"]["Zone"]:
        ret[zone["ZoneId"]] = {}
        for item in zone:
            ret[zone["ZoneId"]][item] = six.text_type(zone[item])

    return ret


def list_nodes_min(call=None):
    """
    Return a list of the VMs that are on the provider. Only a list of VM names,
    and their state, is returned. This is the minimum amount of information
    needed to check for existing VMs.
    """
    if call == "action":
        raise SaltCloudSystemExit(
            "The list_nodes_min function must be called with -f or --function."
        )

    ret = {}
    location = get_location()
    params = {
        "Action": "DescribeInstanceStatus",
        "RegionId": location,
    }
    nodes = query(params)

    log.debug("Total %s instance found in Region %s", nodes["TotalCount"], location)
    if "Code" in nodes or nodes["TotalCount"] == 0:
        return ret

    for node in nodes["InstanceStatuses"]["InstanceStatus"]:
        ret[node["InstanceId"]] = {}
        for item in node:
            ret[node["InstanceId"]][item] = node[item]

    return ret


def list_nodes(call=None):
    """
    Return a list of the VMs that are on the provider
    """
    if call == "action":
        raise SaltCloudSystemExit(
            "The list_nodes function must be called with -f or --function."
        )

    nodes = list_nodes_full()
    ret = {}
    for instanceId in nodes:
        node = nodes[instanceId]
        ret[node["name"]] = {
            "id": node["id"],
            "name": node["name"],
            "public_ips": node["public_ips"],
            "private_ips": node["private_ips"],
            "size": node["size"],
            "state": six.text_type(node["state"]),
        }
    return ret


def list_nodes_full(call=None):
    """
    Return a list of the VMs that are on the provider
    """
    if call == "action":
        raise SaltCloudSystemExit(
            "The list_nodes_full function must be called with -f " "or --function."
        )

    ret = {}
    location = get_location()
    params = {
        "Action": "DescribeInstanceStatus",
        "RegionId": location,
        "PageSize": "50",
    }
    result = query(params=params)

    log.debug("Total %s instance found in Region %s", result["TotalCount"], location)
    if "Code" in result or result["TotalCount"] == 0:
        return ret

    # aliyun max 100 top instance in api
    result_instancestatus = result["InstanceStatuses"]["InstanceStatus"]
    if result["TotalCount"] > 50:
        params["PageNumber"] = "2"
        result = query(params=params)
        result_instancestatus.update(result["InstanceStatuses"]["InstanceStatus"])

    for node in result_instancestatus:

        instanceId = node.get("InstanceId", "")

        params = {"Action": "DescribeInstanceAttribute", "InstanceId": instanceId}
        items = query(params=params)
        if "Code" in items:
            log.warning("Query instance:%s attribute failed", instanceId)
            continue

        name = items["InstanceName"]
        ret[name] = {
            "id": items["InstanceId"],
            "name": name,
            "image": items["ImageId"],
            "size": "TODO",
            "state": items["Status"],
        }
        for item in items:
            value = items[item]
            if value is not None:
                value = six.text_type(value)
            if item == "PublicIpAddress":
                ret[name]["public_ips"] = items[item]["IpAddress"]
            if item == "InnerIpAddress" and "private_ips" not in ret[name]:
                ret[name]["private_ips"] = items[item]["IpAddress"]
            if item == "VpcAttributes":
                vpc_ips = items[item]["PrivateIpAddress"]["IpAddress"]
                if vpc_ips:
                    ret[name]["private_ips"] = vpc_ips
            ret[name][item] = value

    provider = __active_provider_name__ or "aliyun"
    if ":" in provider:
        comps = provider.split(":")
        provider = comps[0]

    __opts__["update_cachedir"] = True
    __utils__["cloud.cache_node_list"](ret, provider, __opts__)

    return ret


def list_nodes_select(call=None):
    """
    Return a list of the VMs that are on the provider, with select fields
    """
    return salt.utils.cloud.list_nodes_select(
        list_nodes_full("function"), __opts__["query.selection"], call,
    )


def list_securitygroup(call=None):
    """
    Return a list of security group
    """
    if call == "action":
        raise SaltCloudSystemExit(
            "The list_nodes function must be called with -f or --function."
        )

    params = {
        "Action": "DescribeSecurityGroups",
        "RegionId": get_location(),
        "PageSize": "50",
    }

    result = query(params)
    if "Code" in result:
        return {}

    ret = {}
    for sg in result["SecurityGroups"]["SecurityGroup"]:
        ret[sg["SecurityGroupId"]] = {}
        for item in sg:
            ret[sg["SecurityGroupId"]][item] = sg[item]

    return ret


def get_image(vm_):
    """
    Return the image object to use
    """
    images = avail_images()
    vm_image = six.text_type(
        config.get_cloud_config_value("image", vm_, __opts__, search_global=False)
    )

    if not vm_image:
        raise SaltCloudNotFound("No image specified for this VM.")

    if vm_image and six.text_type(vm_image) in images:
        return images[vm_image]["ImageId"]
    raise SaltCloudNotFound(
        "The specified image, '{0}', could not be found.".format(vm_image)
    )


def get_securitygroup(vm_):
    """
    Return the security group
    """
    sgs = list_securitygroup()
    securitygroup = config.get_cloud_config_value(
        "securitygroup", vm_, __opts__, search_global=False
    )

    if not securitygroup:
        raise SaltCloudNotFound("No securitygroup ID specified for this VM.")

    if securitygroup and six.text_type(securitygroup) in sgs:
        return sgs[securitygroup]["SecurityGroupId"]
    raise SaltCloudNotFound(
        "The specified security group, '{0}', could not be found.".format(securitygroup)
    )


def get_size(vm_):
    """
    Return the VM's size. Used by create_node().
    """
    sizes = avail_sizes()
    vm_size = six.text_type(
        config.get_cloud_config_value("size", vm_, __opts__, search_global=False)
    )

    if not vm_size:
        raise SaltCloudNotFound("No size specified for this VM.")

    if vm_size and six.text_type(vm_size) in sizes:
        return sizes[vm_size]["InstanceTypeId"]

    raise SaltCloudNotFound(
        "The specified size, '{0}', could not be found.".format(vm_size)
    )


def __get_location(vm_):
    """
    Return the VM's location
    """
    locations = avail_locations()
    vm_location = six.text_type(
        config.get_cloud_config_value("location", vm_, __opts__, search_global=False)
    )

    if not vm_location:
        raise SaltCloudNotFound("No location specified for this VM.")

    if vm_location and six.text_type(vm_location) in locations:
        return locations[vm_location]["RegionId"]
    raise SaltCloudNotFound(
        "The specified location, '{0}', could not be found.".format(vm_location)
    )


def start(name, call=None):
    """
    Start a node

    CLI Examples:

    .. code-block:: bash

        salt-cloud -a start myinstance
    """
    if call != "action":
        raise SaltCloudSystemExit("The stop action must be called with -a or --action.")

    log.info("Starting node %s", name)

    instanceId = _get_node(name)["InstanceId"]

    params = {"Action": "StartInstance", "InstanceId": instanceId}
    result = query(params)

    return result


def stop(name, force=False, call=None):
    """
    Stop a node

    CLI Examples:

    .. code-block:: bash

        salt-cloud -a stop myinstance
        salt-cloud -a stop myinstance force=True
    """
    if call != "action":
        raise SaltCloudSystemExit("The stop action must be called with -a or --action.")

    log.info("Stopping node %s", name)

    instanceId = _get_node(name)["InstanceId"]

    params = {
        "Action": "StopInstance",
        "InstanceId": instanceId,
        "ForceStop": six.text_type(force).lower(),
    }
    result = query(params)

    return result


def reboot(name, call=None):
    """
    Reboot a node

    CLI Examples:

    .. code-block:: bash

        salt-cloud -a reboot myinstance
    """
    if call != "action":
        raise SaltCloudSystemExit("The stop action must be called with -a or --action.")

    log.info("Rebooting node %s", name)

    instance_id = _get_node(name)["InstanceId"]

    params = {"Action": "RebootInstance", "InstanceId": instance_id}
    result = query(params)

    return result


def create_node(kwargs):
    """
    Convenience function to make the rest api call for node creation.
    """
    if not isinstance(kwargs, dict):
        kwargs = {}

    # Required parameters
    params = {
        "Action": "CreateInstance",
        "InstanceType": kwargs.get("size_id", ""),
        "RegionId": kwargs.get("region_id", DEFAULT_LOCATION),
        "ImageId": kwargs.get("image_id", ""),
        "SecurityGroupId": kwargs.get("securitygroup_id", ""),
        "InstanceName": kwargs.get("name", ""),
    }

    # Optional parameters'
    optional = [
        "InstanceName",
        "InternetChargeType",
        "InternetMaxBandwidthIn",
        "InternetMaxBandwidthOut",
        "HostName",
        "Password",
        "SystemDisk.Category",
        "VSwitchId"
        # 'DataDisk.n.Size', 'DataDisk.n.Category', 'DataDisk.n.SnapshotId'
    ]

    for item in optional:
        if item in kwargs:
            params.update({item: kwargs[item]})

    # invoke web call
    result = query(params)
    return result["InstanceId"]


def create(vm_):
    """
    Create a single VM from a data dict
    """
    try:
        # Check for required profile parameters before sending any API calls.
        if (
            vm_["profile"]
            and config.is_profile_configured(
                __opts__, __active_provider_name__ or "aliyun", vm_["profile"], vm_=vm_
            )
            is False
        ):
            return False
    except AttributeError:
        pass

    __utils__["cloud.fire_event"](
        "event",
        "starting create",
        "salt/cloud/{0}/creating".format(vm_["name"]),
        args=__utils__["cloud.filter_event"](
            "creating", vm_, ["name", "profile", "provider", "driver"]
        ),
        sock_dir=__opts__["sock_dir"],
        transport=__opts__["transport"],
    )

    log.info("Creating Cloud VM %s", vm_["name"])
    kwargs = {
        "name": vm_["name"],
        "size_id": get_size(vm_),
        "image_id": get_image(vm_),
        "region_id": __get_location(vm_),
        "securitygroup_id": get_securitygroup(vm_),
    }
    if "vswitch_id" in vm_:
        kwargs["VSwitchId"] = vm_["vswitch_id"]
    if "internet_chargetype" in vm_:
        kwargs["InternetChargeType"] = vm_["internet_chargetype"]
    if "internet_maxbandwidthin" in vm_:
        kwargs["InternetMaxBandwidthIn"] = six.text_type(vm_["internet_maxbandwidthin"])
    if "internet_maxbandwidthout" in vm_:
        kwargs["InternetMaxBandwidthOut"] = six.text_type(
            vm_["internet_maxbandwidthOut"]
        )
    if "hostname" in vm_:
        kwargs["HostName"] = vm_["hostname"]
    if "password" in vm_:
        kwargs["Password"] = vm_["password"]
    if "instance_name" in vm_:
        kwargs["InstanceName"] = vm_["instance_name"]
    if "systemdisk_category" in vm_:
        kwargs["SystemDisk.Category"] = vm_["systemdisk_category"]

    __utils__["cloud.fire_event"](
        "event",
        "requesting instance",
        "salt/cloud/{0}/requesting".format(vm_["name"]),
        args=__utils__["cloud.filter_event"]("requesting", kwargs, list(kwargs)),
        sock_dir=__opts__["sock_dir"],
        transport=__opts__["transport"],
    )

    try:
        ret = create_node(kwargs)
    except Exception as exc:  # pylint: disable=broad-except
        log.error(
            "Error creating %s on Aliyun ECS\n\n"
            "The following exception was thrown when trying to "
            "run the initial deployment: %s",
            vm_["name"],
            six.text_type(exc),
            # Show the traceback if the debug logging level is enabled
            exc_info_on_loglevel=logging.DEBUG,
        )
        return False
    # repair ip address error and start vm
    time.sleep(8)
    params = {"Action": "StartInstance", "InstanceId": ret}
    query(params)

    def __query_node_data(vm_name):
        data = show_instance(vm_name, call="action")
        if not data:
            # Trigger an error in the wait_for_ip function
            return False
        if data.get("PublicIpAddress", None) is not None:
            return data

    try:
        data = salt.utils.cloud.wait_for_ip(
            __query_node_data,
            update_args=(vm_["name"],),
            timeout=config.get_cloud_config_value(
                "wait_for_ip_timeout", vm_, __opts__, default=10 * 60
            ),
            interval=config.get_cloud_config_value(
                "wait_for_ip_interval", vm_, __opts__, default=10
            ),
        )
    except (SaltCloudExecutionTimeout, SaltCloudExecutionFailure) as exc:
        try:
            # It might be already up, let's destroy it!
            destroy(vm_["name"])
        except SaltCloudSystemExit:
            pass
        finally:
            raise SaltCloudSystemExit(six.text_type(exc))

    if data["public_ips"]:
        ssh_ip = data["public_ips"][0]
    elif data["private_ips"]:
        ssh_ip = data["private_ips"][0]
    else:
        log.info("No available ip:cant connect to salt")
        return False
    log.debug("VM %s is now running", ssh_ip)
    vm_["ssh_host"] = ssh_ip

    # The instance is booted and accessible, let's Salt it!
    ret = __utils__["cloud.bootstrap"](vm_, __opts__)
    ret.update(data)

    log.info("Created Cloud VM '%s'", vm_["name"])
    log.debug("'%s' VM creation details:\n%s", vm_["name"], pprint.pformat(data))

    __utils__["cloud.fire_event"](
        "event",
        "created instance",
        "salt/cloud/{0}/created".format(vm_["name"]),
        args=__utils__["cloud.filter_event"](
            "created", vm_, ["name", "profile", "provider", "driver"]
        ),
        sock_dir=__opts__["sock_dir"],
        transport=__opts__["transport"],
    )

    return ret


def _compute_signature(parameters, access_key_secret):
    """
    Generate aliyun request signature
    """

    def percent_encode(line):
        if not isinstance(line, six.string_types):
            return line

        s = line
        if sys.stdin.encoding is None:
            s = line.decode().encode("utf8")
        else:
            s = line.decode(sys.stdin.encoding).encode("utf8")
        res = _quote(s, "")
        res = res.replace("+", "%20")
        res = res.replace("*", "%2A")
        res = res.replace("%7E", "~")
        return res

    sortedParameters = sorted(list(parameters.items()), key=lambda items: items[0])

    canonicalizedQueryString = ""
    for k, v in sortedParameters:
        canonicalizedQueryString += "&" + percent_encode(k) + "=" + percent_encode(v)

    # All aliyun API only support GET method
    stringToSign = "GET&%2F&" + percent_encode(canonicalizedQueryString[1:])

    h = hmac.new(to_bytes(access_key_secret + "&"), stringToSign, sha1)
    signature = base64.encodestring(h.digest()).strip()
    return signature


def query(params=None):
    """
    Make a web call to aliyun ECS REST API
    """
    path = "https://ecs-cn-hangzhou.aliyuncs.com"

    access_key_id = config.get_cloud_config_value(
        "id", get_configured_provider(), __opts__, search_global=False
    )
    access_key_secret = config.get_cloud_config_value(
        "key", get_configured_provider(), __opts__, search_global=False
    )

    timestamp = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())

    # public interface parameters
    parameters = {
        "Format": "JSON",
        "Version": DEFAULT_ALIYUN_API_VERSION,
        "AccessKeyId": access_key_id,
        "SignatureVersion": "1.0",
        "SignatureMethod": "HMAC-SHA1",
        "SignatureNonce": six.text_type(uuid.uuid1()),
        "TimeStamp": timestamp,
    }

    # include action or function parameters
    if params:
        parameters.update(params)

    # Calculate the string for Signature
    signature = _compute_signature(parameters, access_key_secret)
    parameters["Signature"] = signature

    request = requests.get(path, params=parameters, verify=True)
    if request.status_code != 200:
        raise SaltCloudSystemExit(
            "An error occurred while querying aliyun ECS. HTTP Code: {0}  "
            "Error: '{1}'".format(request.status_code, request.text)
        )

    log.debug(request.url)

    content = request.text

    result = salt.utils.json.loads(content)
    if "Code" in result:
        raise SaltCloudSystemExit(pprint.pformat(result.get("Message", {})))
    return result


def script(vm_):
    """
    Return the script deployment object
    """
    deploy_script = salt.utils.cloud.os_script(
        config.get_cloud_config_value("script", vm_, __opts__),
        vm_,
        __opts__,
        salt.utils.cloud.salt_config_to_yaml(
            salt.utils.cloud.minion_config(__opts__, vm_)
        ),
    )
    return deploy_script


def show_disk(name, call=None):
    """
    Show the disk details of the instance

    CLI Examples:

    .. code-block:: bash

        salt-cloud -a show_disk aliyun myinstance
    """
    if call != "action":
        raise SaltCloudSystemExit(
            "The show_disks action must be called with -a or --action."
        )

    ret = {}
    params = {"Action": "DescribeInstanceDisks", "InstanceId": name}
    items = query(params=params)

    for disk in items["Disks"]["Disk"]:
        ret[disk["DiskId"]] = {}
        for item in disk:
            ret[disk["DiskId"]][item] = six.text_type(disk[item])

    return ret


def list_monitor_data(kwargs=None, call=None):
    """
    Get monitor data of the instance. If instance name is
    missing, will show all the instance monitor data on the region.

    CLI Examples:

    .. code-block:: bash

        salt-cloud -f list_monitor_data aliyun
        salt-cloud -f list_monitor_data aliyun name=AY14051311071990225bd
    """
    if call != "function":
        raise SaltCloudSystemExit(
            "The list_monitor_data must be called with -f or --function."
        )

    if not isinstance(kwargs, dict):
        kwargs = {}

    ret = {}
    params = {"Action": "GetMonitorData", "RegionId": get_location()}
    if "name" in kwargs:
        params["InstanceId"] = kwargs["name"]

    items = query(params=params)

    monitorData = items["MonitorData"]

    for data in monitorData["InstanceMonitorData"]:
        ret[data["InstanceId"]] = {}
        for item in data:
            ret[data["InstanceId"]][item] = six.text_type(data[item])

    return ret


def show_instance(name, call=None):
    """
    Show the details from aliyun instance
    """
    if call != "action":
        raise SaltCloudSystemExit(
            "The show_instance action must be called with -a or --action."
        )

    return _get_node(name)


def _get_node(name):
    attempts = 5
    while attempts >= 0:
        try:
            return list_nodes_full()[name]
        except KeyError:
            attempts -= 1
            log.debug(
                "Failed to get the data for node '%s'. Remaining " "attempts: %s",
                name,
                attempts,
            )
            # Just a little delay between attempts...
            time.sleep(0.5)
    raise SaltCloudNotFound("The specified instance {0} not found".format(name))


def show_image(kwargs, call=None):
    """
    Show the details from aliyun image
    """
    if call != "function":
        raise SaltCloudSystemExit(
            "The show_images function must be called with " "-f or --function"
        )

    if not isinstance(kwargs, dict):
        kwargs = {}

    location = get_location()
    if "location" in kwargs:
        location = kwargs["location"]

    params = {
        "Action": "DescribeImages",
        "RegionId": location,
        "ImageId": kwargs["image"],
    }

    ret = {}
    items = query(params=params)
    # DescribeImages so far support input multi-image. And
    # if not found certain image, the response will include
    # blank image list other than 'not found' error message
    if "Code" in items or not items["Images"]["Image"]:
        raise SaltCloudNotFound("The specified image could not be found.")

    log.debug("Total %s image found in Region %s", items["TotalCount"], location)

    for image in items["Images"]["Image"]:
        ret[image["ImageId"]] = {}
        for item in image:
            ret[image["ImageId"]][item] = six.text_type(image[item])

    return ret


def destroy(name, call=None):
    """
    Destroy a node.

    CLI Example:

    .. code-block:: bash

        salt-cloud -a destroy myinstance
        salt-cloud -d myinstance
    """
    if call == "function":
        raise SaltCloudSystemExit(
            "The destroy action must be called with -d, --destroy, " "-a or --action."
        )

    __utils__["cloud.fire_event"](
        "event",
        "destroying instance",
        "salt/cloud/{0}/destroying".format(name),
        args={"name": name},
        sock_dir=__opts__["sock_dir"],
        transport=__opts__["transport"],
    )

    instanceId = _get_node(name)["InstanceId"]

    # have to stop instance before del it
    stop_params = {"Action": "StopInstance", "InstanceId": instanceId}
    query(stop_params)

    params = {"Action": "DeleteInstance", "InstanceId": instanceId}

    node = query(params)

    __utils__["cloud.fire_event"](
        "event",
        "destroyed instance",
        "salt/cloud/{0}/destroyed".format(name),
        args={"name": name},
        sock_dir=__opts__["sock_dir"],
        transport=__opts__["transport"],
    )

    return node
