# 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.

import sys
import unittest

from libcloud.test import MockHttp, LibcloudTestCase
from libcloud.utils.py3 import httplib
from libcloud.common.types import LibcloudError
from libcloud.compute.base import (
    Node,
    NodeSize,
    NodeImage,
    NodeLocation,
    StorageVolume,
    VolumeSnapshot,
    NodeAuthPassword,
)
from libcloud.test.secrets import ECS_PARAMS
from libcloud.compute.types import NodeState, StorageVolumeState
from libcloud.test.file_fixtures import ComputeFileFixtures
from libcloud.compute.drivers.ecs import ECSDriver


class ECSDriverTestCase(LibcloudTestCase):
    region = "cn-qingdao"
    zone = "cn-qingdao-b"
    image_id = "ubuntu1404_64_20G_aliaegis_20150325.vhd"

    def setUp(self):
        ECSMockHttp.test = self
        ECSDriver.connectionCls.conn_class = ECSMockHttp
        ECSMockHttp.use_param = "Action"
        ECSMockHttp.type = None

        self.driver = ECSDriver(*ECS_PARAMS, region=self.region)
        self.fake_size = NodeSize(
            "ecs.t1.small", "ecs t1 small", None, None, None, None, self.driver
        )
        self.fake_image = NodeImage(self.image_id, name="ubuntu 14.04 64bit", driver=self.driver)
        self.fake_node = Node(
            id="fake-node1",
            name="fake-node",
            state=NodeState.RUNNING,
            public_ips=None,
            private_ips=None,
            driver=self.driver,
        )
        self.fake_volume = StorageVolume(
            id="fake-volume1",
            name="fake-volume",
            size=self.fake_size,
            driver=self.driver,
        )
        self.fake_snapshot = VolumeSnapshot(id="fake-snapshot1", driver=self.driver)
        self.fake_location = NodeLocation(
            id=self.region, name=self.region, country=None, driver=self.driver
        )
        self.fake_instance_id = "fake_instance_id"
        self.fake_security_group_id = "fake_security_group_id"

    def test_list_nodes(self):
        nodes = self.driver.list_nodes()
        self.assertIsNotNone(nodes)
        self.assertEqual(1, len(nodes))
        node = nodes[0]
        self.assertEqual("iZ28n7dkvovZ", node.name)
        self.assertEqual("i-28n7dkvov", node.id)
        self.assertEqual(NodeState.PENDING, node.state)
        self.assertEqual(1, len(node.public_ips))
        self.assertEqual("114.215.124.73", node.public_ips[0])
        self.assertEqual(1, len(node.private_ips))
        self.assertEqual("10.163.197.74", node.private_ips[0])
        expected_extra = {
            "image_id": "ubuntu1404_64_20G_aliaegis_20150325.vhd",
            "description": "",
            "instance_type_family": "ecs.t1",
            "zone_id": "cn-qingdao-b",
            "internet_charge_type": "PayByTraffic",
            "serial_number": "ca0122d9-374d-4fce-9fc0-71f7c3eaf1c3",
            "io_optimized": "false",
            "device_available": "true",
            "instance_network_type": "classic",
            "hostname": "iZ28n7dkvovZ",
            "instance_type": "ecs.t1.small",
            "creation_time": "2015-12-27T07:35Z",
            "instance_charge_type": "PostPaid",
            "expired_time": "2999-09-08T16:00Z",
        }
        self._validate_extras(expected_extra, node.extra)
        vpc = {
            "vpc_id": "",
            "vswitch_id": "",
            "private_ip_address": None,
            "nat_ip_address": "",
        }
        self._validate_extras(vpc, node.extra["vpc_attributes"])
        eip_address = {
            "allocation_id": "",
            "ip_address": "",
            "internet_charge_type": "",
            "bandwidth": None,
        }
        self._validate_extras(eip_address, node.extra["eip_address"])
        self.assertIsNone(node.extra["operation_locks"]["lock_reason"])

    def test_list_nodes_with_ex_node_ids(self):
        ECSMockHttp.type = "list_nodes_ex_node_ids"
        nodes = self.driver.list_nodes(ex_node_ids=["i-28n7dkvov", "not-existed-id"])
        self.assertIsNotNone(nodes)

    def test_list_nodes_with_ex_filters(self):
        ECSMockHttp.type = "list_nodes_ex_filters"
        nodes = self.driver.list_nodes(ex_filters={"ZoneId": self.zone})
        self.assertIsNotNone(nodes)

    def _validate_extras(self, expected, actual):
        self.assertIsNotNone(actual)
        for key, value in iter(expected.items()):
            self.assertTrue(key in actual)
            self.assertEqual(
                value,
                actual[key],
                (
                    "extra %(key)s not equal, "
                    'expected: "%(expected)s", '
                    'actual: "%(actual)s"' % {"key": key, "expected": value, "actual": actual[key]}
                ),
            )

    def test_create_node(self):
        ECSMockHttp.type = "create_node"
        name = "test_create_node"
        node = self.driver.create_node(
            name=name,
            image=self.fake_image,
            size=self.fake_size,
            ex_security_group_id="sg-28ou0f3xa",
            ex_description="description",
            ex_internet_charge_type="PayByTraffic",
            ex_internet_max_bandwidth_out=1,
            ex_internet_max_bandwidth_in=200,
            ex_hostname="hostname",
            auth=NodeAuthPassword("password"),
            ex_io_optimized=True,
            ex_system_disk={
                "category": "cloud",
                "disk_name": "root",
                "description": "sys",
            },
            ex_vswitch_id="vswitch-id1",
            ex_private_ip_address="1.1.1.2",
            ex_client_token="client_token",
        )
        self.assertIsNotNone(node)

    def test_create_node_with_data_disk(self):
        ECSMockHttp.type = "create_node_with_data"
        self.name = "test_create_node"
        self.data_disk = {
            "size": 5,
            "category": self.driver.disk_categories.CLOUD,
            "disk_name": "data1",
            "description": "description",
            "device": "/dev/xvdb",
            "delete_with_instance": True,
        }
        node = self.driver.create_node(
            name=self.name,
            image=self.fake_image,
            size=self.fake_size,
            ex_security_group_id="sg-28ou0f3xa",
            ex_data_disks=self.data_disk,
        )
        self.assertIsNotNone(node)

    def test_list_sizes(self):
        sizes = self.driver.list_sizes()
        self.assertEqual(2, len(sizes))
        size = sizes[0]
        self.assertEqual("ecs.t1.xsmall", size.id)
        self.assertEqual("ecs.t1.xsmall", size.name)
        self.assertEqual(0.5, size.ram)
        self.assertEqual(1, size.extra["cpu_core_count"])
        self.assertEqual("ecs.t1", size.extra["instance_type_family"])
        size = sizes[1]
        self.assertEqual("ecs.s2.small", size.id)
        self.assertEqual("ecs.s2.small", size.name)
        self.assertEqual(1.0, size.ram)
        self.assertEqual(2, size.extra["cpu_core_count"])
        self.assertEqual("ecs.s2", size.extra["instance_type_family"])

    def test_list_locations(self):
        locations = self.driver.list_locations()
        self.assertEqual(9, len(locations))
        location = locations[0]
        self.assertEqual("ap-southeast-1", location.id)
        self.assertIsNone(location.country)

    def test_create_node_without_sg_id_exception(self):
        name = "test_create_node_without_sg_id_exception"
        self.assertRaises(
            AttributeError,
            self.driver.create_node,
            name=name,
            image=self.fake_image,
            size=self.fake_size,
        )

    def test_creat_node_paybytraffic_exception(self):
        name = "test_create_node_paybytraffic_exception"
        self.assertRaises(
            AttributeError,
            self.driver.create_node,
            name=name,
            image=self.fake_image,
            size=self.fake_size,
            ex_security_group_id="sg-id1",
            ex_internet_charge_type="PayByTraffic",
        )

    def test_create_node_ex_system_disk_exception(self):
        name = "test_creat_node_ex_system_disk_exception"
        self.assertRaises(
            AttributeError,
            self.driver.create_node,
            name=name,
            image=self.fake_image,
            size=self.fake_size,
            ex_security_group_id="sg-id1",
            ex_system_disk=None,
        )

    def test_create_node_ex_private_ip_address_exception(self):
        name = "test_create_node_ex_private_ip_address_exception"
        self.assertRaises(
            AttributeError,
            self.driver.create_node,
            name=name,
            image=self.fake_image,
            size=self.fake_size,
            ex_security_group_id="sg-id1",
            ex_private_ip_address="1.1.1.2",
        )

    def test_reboot_node(self):
        ECSMockHttp.type = "reboot_node"
        result = self.driver.reboot_node(self.fake_node)
        self.assertTrue(result)

    def test_reboot_node_with_ex_force_stop(self):
        ECSMockHttp.type = "reboot_node_force_stop"
        result = self.driver.reboot_node(self.fake_node, ex_force_stop=True)
        self.assertTrue(result)

    def test_destroy_node(self):
        ECSMockHttp.type = "destroy_node"
        result = self.driver.destroy_node(self.fake_node)
        self.assertTrue(result)

    def test_ex_start_node(self):
        ECSMockHttp.type = "start_node"
        result = self.driver.ex_start_node(self.fake_node)
        self.assertTrue(result)

    def test_ex_stop_node(self):
        ECSMockHttp.type = "stop_node"
        result = self.driver.ex_stop_node(self.fake_node)
        self.assertTrue(result)

    def test_stop_node_with_ex_force_stop(self):
        ECSMockHttp.type = "stop_node_force_stop"
        result = self.driver.ex_stop_node(self.fake_node, ex_force_stop=True)
        self.assertTrue(result)

    def test_create_public_ip(self):
        ECSMockHttp.type = "create_public_ip"
        result = self.driver.create_public_ip(self.fake_instance_id)
        self.assertTrue(result)

    def test_list_volumes(self):
        volumes = self.driver.list_volumes()
        self.assertEqual(2, len(volumes))
        volume = volumes[0]
        self.assertEqual("d-28m5zbua0", volume.id)
        self.assertEqual("", volume.name)
        self.assertEqual(5, volume.size)
        self.assertEqual(StorageVolumeState.AVAILABLE, volume.state)
        expected_extras = {
            "region_id": "cn-qingdao",
            "zone_id": "cn-qingdao-b",
            "description": "",
            "type": "data",
            "category": "cloud",
            "image_id": "",
            "source_snapshot_id": "",
            "product_code": "",
            "portable": True,
            "instance_id": "",
            "device": "",
            "delete_with_instance": False,
            "enable_auto_snapshot": False,
            "creation_time": "2014-07-23T02:44:07Z",
            "attached_time": "2014-07-23T07:47:35Z",
            "detached_time": "2014-07-23T08:28:48Z",
            "disk_charge_type": "PostPaid",
            "operation_locks": {"lock_reason": None},
        }
        self._validate_extras(expected_extras, volume.extra)
        volume = volumes[1]
        self.assertEqual("d-28zfrmo13", volume.id)
        self.assertEqual("ubuntu1404sys", volume.name)
        self.assertEqual(5, volume.size)
        self.assertEqual(StorageVolumeState.INUSE, volume.state)
        expected_extras = {
            "region_id": "cn-qingdao",
            "zone_id": "cn-qingdao-b",
            "description": "Description",
            "type": "system",
            "category": "cloud",
            "image_id": "ubuntu1404_64_20G_aliaegis_20150325.vhd",
            "source_snapshot_id": "",
            "product_code": "",
            "portable": False,
            "instance_id": "i-28whl2nj2",
            "device": "/dev/xvda",
            "delete_with_instance": True,
            "enable_auto_snapshot": True,
            "creation_time": "2014-07-23T02:44:06Z",
            "attached_time": "2016-01-04T15:02:17Z",
            "detached_time": "",
            "disk_charge_type": "PostPaid",
            "operation_locks": {"lock_reason": None},
        }
        self._validate_extras(expected_extras, volume.extra)

    def test_list_volumes_with_ex_volume_ids(self):
        ECSMockHttp.type = "list_volumes_ex_volume_ids"
        volumes = self.driver.list_volumes(ex_volume_ids=["i-28n7dkvov", "not-existed-id"])
        self.assertIsNotNone(volumes)

    def test_list_volumes_with_ex_filters(self):
        ECSMockHttp.type = "list_volumes_ex_filters"
        ex_filters = {"InstanceId": self.fake_node.id}
        volumes = self.driver.list_volumes(ex_filters=ex_filters)
        self.assertIsNotNone(volumes)

    def test_list_volume_snapshots(self):
        snapshots = self.driver.list_volume_snapshots(self.fake_volume)
        self.assertEqual(1, len(snapshots))

    def test_list_volume_snapshots_with_ex_snapshot_ids(self):
        ECSMockHttp.type = "list_volume_snapshots_ex_snapshot_ids"
        ex_snapshot_ids = ["fake-snapshot1"]
        self.driver.list_volume_snapshots(self.fake_volume, ex_snapshot_ids=ex_snapshot_ids)

    def test_list_volume_snapshots_with_ex_filters(self):
        ECSMockHttp.type = "list_volume_snapshots_ex_filters"
        ex_filters = {"InstanceId": self.fake_node.id}
        self.driver.list_volume_snapshots(self.fake_volume, ex_filters=ex_filters)

    def test_create_volume(self):
        ECSMockHttp.type = "create_volume"
        self.volume_size = 1
        self.volume_name = "fake-volume-name"
        self.description = "fake-description"
        self.disk_category = "system"
        self.client_token = "client_token"
        volume = self.driver.create_volume(
            self.volume_size,
            self.volume_name,
            snapshot=self.fake_snapshot,
            ex_zone_id=self.zone,
            ex_description=self.description,
            ex_disk_category=self.disk_category,
            ex_client_token=self.client_token,
        )
        self.assertIsNotNone(volume)

    def test_create_volume_without_ex_zone_id_exception(self):
        self.assertRaises(AttributeError, self.driver.create_volume, 1, "fake-volume-name")

    def test_create_volume_snapshot(self):
        ECSMockHttp.type = "create_volume_snapshot"
        self.snapshot_name = "fake-snapshot1"
        self.description = "fake-description"
        self.client_token = "client-token"
        snapshot = self.driver.create_volume_snapshot(
            self.fake_volume,
            name=self.snapshot_name,
            ex_description=self.description,
            ex_client_token=self.client_token,
        )
        self.assertIsNotNone(snapshot)

    def test_attach_volume(self):
        self.device = "/dev/sdb"
        self.delete_with_instance = True
        attached = self.driver.attach_volume(
            self.fake_node,
            self.fake_volume,
            device=self.device,
            ex_delete_with_instance=self.delete_with_instance,
        )
        self.assertTrue(attached)

    def test_detach_volume(self):
        self.instance_id = "fake-node1"
        result = self.driver.detach_volume(self.fake_volume, ex_instance_id=self.instance_id)
        self.assertTrue(result)

    def test_detach_volume_query_instance_id(self):
        ECSMockHttp.type = "detach_volume"
        result = self.driver.detach_volume(self.fake_volume)
        self.assertTrue(result)

    def test_detach_volume_query_instance_id_exception(self):
        self.assertRaises(AttributeError, self.driver.detach_volume, self.fake_volume)

    def test_destroy_volume(self):
        ECSMockHttp.type = "destroy_volume"
        result = self.driver.destroy_volume(self.fake_volume)
        self.assertTrue(result)

    def test_destroy_volume_query_volumes_exception(self):
        self.assertRaises(LibcloudError, self.driver.destroy_volume, self.fake_volume)

    def test_destroy_volume_state_exception(self):
        ECSMockHttp.type = "destroy_volume_state"
        self.assertRaises(LibcloudError, self.driver.destroy_volume, self.fake_volume)

    def test_destroy_volume_snapshot(self):
        result = self.driver.destroy_volume_snapshot(self.fake_snapshot)
        self.assertTrue(result)

    def test_destroy_volume_snapshot_exception(self):
        self.assertRaises(AttributeError, self.driver.destroy_volume_snapshot, self.fake_volume)

    def test_list_images(self):
        images = self.driver.list_images(self.fake_location)
        self.assertEqual(1, len(images))
        image = images[0]
        self.assertEqual("freebsd1001_64_20G_aliaegis_20150527.vhd", image.id)
        self.assertEqual("freebsd1001_64_20G_aliaegis_20150527.vhd", image.name)
        expected_extra = {
            "image_version": "1.0.0",
            "os_type": "linux",
            "platform": "Freebsd",
            "architecture": "x86_64",
            "description": "freebsd1001_64_20G_aliaegis_20150527.vhd",
            "size": 20,
            "image_owner_alias": "system",
            "os_name": "FreeBSD  10.1 64位",
            "product_code": "",
            "is_subscribed": False,
            "progress": "100%",
            "creation_time": "2015-06-19T07:25:42Z",
            "usage": "instance",
            "is_copied": False,
        }
        self._validate_extras(expected_extra, image.extra)
        expected_dev_mappings = {
            "snapshot_id": "",
            "size": 20,
            "device": "/dev/xvda",
            "format": "",
            "import_oss_bucket": "",
            "import_oss_object": "",
        }
        self._validate_extras(expected_dev_mappings, image.extra["disk_device_mappings"])

    def test_list_images_with_ex_image_ids(self):
        ECSMockHttp.type = "list_images_ex_image_ids"
        self.driver.list_images(
            location=self.fake_location,
            ex_image_ids=[self.fake_image.id, "not-existed"],
        )

    def test_list_images_with_ex_image_ids_type_exception(self):
        self.assertRaises(
            AttributeError,
            self.driver.list_images,
            location=self.fake_location,
            ex_image_ids={"image_ids": "id1,id2"},
        )

    def test_list_images_with_ex_filters(self):
        ECSMockHttp.type = "list_images_ex_filters"
        ex_filters = {"Status": "Available"}
        self.driver.list_images(location=self.fake_location, ex_filters=ex_filters)

    def test_list_images_multiple_pages(self):
        ECSMockHttp.type = "list_images_pages"
        images = self.driver.list_images()
        self.assertEqual(2, len(images))

    def test_create_image(self):
        self.image_name = "fake-image1"
        self.description = "description"
        self.image_version = "1.0.0"
        self.client_token = "client_token"
        image = self.driver.create_image(
            None,
            self.image_name,
            self.description,
            ex_snapshot_id=self.fake_snapshot.id,
            ex_image_version=self.image_version,
            ex_client_token=self.client_token,
        )
        self.assertIsNotNone(image)

    def test_creaet_image_exception(self):
        self.assertRaises(AttributeError, self.driver.create_image, None, None)

    def test_delete_image(self):
        result = self.driver.delete_image(self.fake_image)
        self.assertTrue(result)

    def test_get_image(self):
        ECSMockHttp.type = "get_image"
        image = self.driver.get_image(self.fake_image.id)
        self.assertIsNotNone(image)

    def test_get_image_not_found_exception(self):
        ECSMockHttp.type = "get_image_not_found"
        self.assertRaises(LibcloudError, self.driver.get_image, self.fake_image.id)

    def test_copy_image(self):
        self.image_name = "copied-image1"
        self.description = "description"
        self.dest_region = "cn-hangzhou"
        self.client_token = "client-token"
        image = self.driver.copy_image(
            self.region,
            self.fake_image,
            self.image_name,
            description=self.description,
            ex_destination_region_id=self.dest_region,
            ex_client_token=self.client_token,
        )
        self.assertIsNotNone(image)

    def test_copy_image_in_the_same_region(self):
        ECSMockHttp.type = "copy_image_same_region"
        image = self.driver.copy_image(self.region, self.fake_image, None)
        self.assertIsNotNone(image)

    def test_ex_create_security_group(self):
        self.sg_description = "description"
        self.client_token = "client-token"
        sg_id = self.driver.ex_create_security_group(
            description=self.sg_description, client_token=self.client_token
        )
        self.assertEqual("sg-F876FF7BA", sg_id)

    def test_ex_list_security_groups(self):
        sgs = self.driver.ex_list_security_groups()
        self.assertEqual(1, len(sgs))
        sg = sgs[0]
        self.assertEqual("sg-28ou0f3xa", sg.id)
        self.assertEqual("sg-28ou0f3xa", sg.name)
        self.assertEqual("System created security group.", sg.description)
        self.assertEqual("", sg.vpc_id)
        self.assertEqual("2015-06-26T08:35:30Z", sg.creation_time)

    def test_ex_join_security_group(self):
        result = self.driver.ex_join_security_group(
            self.fake_node, group_id=self.fake_security_group_id
        )
        self.assertTrue(result)

    def test_ex_leave_security_group(self):
        result = self.driver.ex_leave_security_group(
            self.fake_node, group_id=self.fake_security_group_id
        )
        self.assertTrue(result)

    def test_ex_delete_security_group_by_id(self):
        result = self.driver.ex_delete_security_group_by_id(group_id=self.fake_security_group_id)
        self.assertTrue(result)

    def test_ex_modify_security_group_by_id(self):
        self.sg_name = "name"
        self.sg_description = "description"
        result = self.driver.ex_modify_security_group_by_id(
            group_id=self.fake_security_group_id,
            name=self.sg_name,
            description=self.sg_description,
        )
        self.assertTrue(result)

    def test_ex_list_security_groups_with_ex_filters(self):
        ECSMockHttp.type = "list_sgs_filters"
        self.vpc_id = "vpc1"
        ex_filters = {"VpcId": self.vpc_id}
        sgs = self.driver.ex_list_security_groups(ex_filters=ex_filters)
        self.assertEqual(1, len(sgs))

    def test_ex_list_security_group_attributes(self):
        self.sga_nictype = "internet"
        sgas = self.driver.ex_list_security_group_attributes(
            group_id=self.fake_security_group_id, nic_type=self.sga_nictype
        )
        self.assertEqual(1, len(sgas))
        sga = sgas[0]
        self.assertEqual("ALL", sga.ip_protocol)
        self.assertEqual("-1/-1", sga.port_range)
        self.assertEqual("Accept", sga.policy)
        self.assertEqual("internet", sga.nic_type)

    def test_ex_list_zones(self):
        zones = self.driver.ex_list_zones()
        self.assertEqual(1, len(zones))
        zone = zones[0]
        self.assertEqual("cn-qingdao-b", zone.id)
        self.assertEqual(self.driver, zone.driver)
        self.assertEqual("青岛可用区B", zone.name)
        self.assertIsNotNone(zone.available_resource_types)
        self.assertEqual("IoOptimized", zone.available_resource_types[0])
        self.assertIsNotNone(zone.available_instance_types)
        self.assertEqual("ecs.m2.medium", zone.available_instance_types[0])
        self.assertIsNotNone(zone.available_disk_categories)
        self.assertEqual("cloud_ssd", zone.available_disk_categories[0])


class ECSMockHttp(MockHttp):
    fixtures = ComputeFileFixtures("ecs")

    def _DescribeInstances(self, method, url, body, headers):
        resp_body = self.fixtures.load("describe_instances.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _list_nodes_ex_node_ids_DescribeInstances(self, method, url, body, headers):
        params = {"InstanceIds": '["i-28n7dkvov", "not-existed-id"]'}
        self.assertUrlContainsQueryParams(url, params)
        return self._DescribeInstances(method, url, body, headers)

    def _list_nodes_ex_filters_DescribeInstances(self, method, url, body, headers):
        params = {"ZoneId": self.test.zone}
        self.assertUrlContainsQueryParams(url, params)
        return self._DescribeInstances(method, url, body, headers)

    def _DescribeInstanceTypes(self, method, url, body, headers):
        resp_body = self.fixtures.load("describe_instance_types.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _DescribeRegions(self, method, url, body, headers):
        resp_body = self.fixtures.load("describe_regions.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _create_node_CreateInstance(self, method, url, body, headers):
        params = {
            "SecurityGroupId": "sg-28ou0f3xa",
            "Description": "description",
            "InternetChargeType": "PayByTraffic",
            "InternetMaxBandwidthOut": "1",
            "InternetMaxBandwidthIn": "200",
            "HostName": "hostname",
            "Password": "password",
            "IoOptimized": "optimized",
            "SystemDisk.Category": "cloud",
            "SystemDisk.DiskName": "root",
            "SystemDisk.Description": "sys",
            "VSwitchId": "vswitch-id1",
            "PrivateIpAddress": "1.1.1.2",
            "ClientToken": "client_token",
        }
        self.assertUrlContainsQueryParams(url, params)
        resp_body = self.fixtures.load("create_instance.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _create_node_DescribeInstances(self, method, url, body, headers):
        resp_body = self.fixtures.load("create_node_describe_instances.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _create_node_StartInstance(self, method, url, body, headers):
        resp_body = self.fixtures.load("start_instance.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _create_node_with_data_CreateInstance(self, method, url, body, headers):
        params = {
            "SecurityGroupId": "sg-28ou0f3xa",
            "DataDisk.1.Size": "5",
            "DataDisk.1.Category": "cloud",
            "DataDisk.1.DiskName": "data1",
            "DataDisk.1.Description": "description",
            "DataDisk.1.Device": "/dev/xvdb",
            "DataDisk.1.DeleteWithInstance": "true",
        }
        self.assertUrlContainsQueryParams(url, params)
        resp_body = self.fixtures.load("create_instance.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _create_node_with_data_DescribeInstances(self, method, url, body, headers):
        resp_body = self.fixtures.load("create_node_describe_instances.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _create_node_with_data_StartInstance(self, method, url, body, headers):
        resp_body = self.fixtures.load("start_instance.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _reboot_node_RebootInstance(self, method, url, body, headers):
        node_id = self.test.fake_node.id
        self.assertUrlContainsQueryParams(url, {"InstanceId": node_id, "ForceStop": "false"})
        resp_body = self.fixtures.load("reboot_instance.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _reboot_node_DescribeInstances(self, method, url, body, headers):
        resp_body = self.fixtures.load("reboot_node_describe_instances.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _reboot_node_force_stop_RebootInstance(self, method, url, body, headers):
        node_id = self.test.fake_node.id
        self.assertUrlContainsQueryParams(url, {"InstanceId": node_id, "ForceStop": "true"})
        resp_body = self.fixtures.load("reboot_instance.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _reboot_node_force_stop_DescribeInstances(self, method, url, body, headers):
        resp_body = self.fixtures.load("reboot_node_describe_instances.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _destroy_node_DescribeInstances(self, method, url, body, headers):
        resp_body = self.fixtures.load("destroy_node_describe_instances.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _destroy_node_DeleteInstance(self, method, url, body, headers):
        node_id = self.test.fake_node.id
        self.assertUrlContainsQueryParams(url, {"InstanceId": node_id})
        resp_body = self.fixtures.load("delete_instance.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _start_node_StartInstance(self, method, url, body, headers):
        node_id = self.test.fake_node.id
        self.assertUrlContainsQueryParams(url, {"InstanceId": node_id})
        resp_body = self.fixtures.load("start_instance.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _start_node_DescribeInstances(self, method, url, body, headers):
        resp_body = self.fixtures.load("reboot_node_describe_instances.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _stop_node_StopInstance(self, method, url, body, headers):
        node_id = self.test.fake_node.id
        self.assertUrlContainsQueryParams(url, {"InstanceId": node_id, "ForceStop": "false"})
        resp_body = self.fixtures.load("stop_instance.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _stop_node_DescribeInstances(self, method, url, body, headers):
        resp_body = self.fixtures.load("stop_node_describe_instances.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _stop_node_force_stop_StopInstance(self, method, url, body, headers):
        node_id = self.test.fake_node.id
        self.assertUrlContainsQueryParams(url, {"InstanceId": node_id, "ForceStop": "true"})
        resp_body = self.fixtures.load("stop_instance.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _stop_node_force_stop_DescribeInstances(self, method, url, body, headers):
        resp_body = self.fixtures.load("stop_node_describe_instances.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _DescribeDisks(self, method, url, body, headers):
        resp_body = self.fixtures.load("describe_disks.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _list_volumes_ex_volume_ids_DescribeDisks(self, method, url, body, headers):
        region = self.test.region
        params = {"DiskIds": '["i-28n7dkvov", "not-existed-id"]', "RegionId": region}
        self.assertUrlContainsQueryParams(url, params)
        return self._DescribeInstances(method, url, body, headers)

    def _list_volumes_ex_filters_DescribeDisks(self, method, url, body, headers):
        params = {"InstanceId": self.test.fake_node.id}
        self.assertUrlContainsQueryParams(url, params)
        return self._DescribeDisks(method, url, body, headers)

    def _DescribeSnapshots(self, method, url, body, headers):
        region = self.test.region
        volume_id = self.test.fake_volume.id
        params = {"RegionId": region, "DiskId": volume_id}
        self.assertUrlContainsQueryParams(url, params)
        resp_body = self.fixtures.load("describe_snapshots.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _list_volume_snapshots_ex_snapshot_ids_DescribeSnapshots(self, method, url, body, headers):
        params = {"RegionId": self.test.region, "SnapshotIds": '["fake-snapshot1"]'}
        self.assertUrlContainsQueryParams(url, params)
        return self._DescribeSnapshots(method, url, body, headers)

    def _list_volume_snapshots_ex_filters_DescribeSnapshots(self, method, url, body, headers):
        params = {"InstanceId": self.test.fake_node.id}
        self.assertUrlContainsQueryParams(url, params)
        return self._DescribeSnapshots(method, url, body, headers)

    def _create_volume_CreateDisk(self, method, url, body, headers):
        params = {
            "RegionId": self.test.region,
            "DiskName": self.test.volume_name,
            "Size": str(self.test.volume_size),
            "ZoneId": self.test.zone,
            "SnapshotId": self.test.fake_snapshot.id,
            "Description": self.test.description,
            "DiskCategory": self.test.disk_category,
            "ClientToken": self.test.client_token,
        }
        self.assertUrlContainsQueryParams(url, params)
        resp_body = self.fixtures.load("create_disk.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _create_volume_DescribeDisks(self, method, url, body, headers):
        resp_body = self.fixtures.load("create_volume_describe_disks.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _create_volume_snapshot_CreateSnapshot(self, method, url, body, headers):
        params = {
            "DiskId": self.test.fake_volume.id,
            "SnapshotName": self.test.snapshot_name,
            "Description": self.test.description,
            "ClientToken": self.test.client_token,
        }
        self.assertUrlContainsQueryParams(url, params)
        resp_body = self.fixtures.load("create_snapshot.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _create_volume_snapshot_DescribeSnapshots(self, method, url, body, headers):
        resp_body = self.fixtures.load("describe_snapshots.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _AttachDisk(self, method, url, body, headers):
        delete_with_instance = str(self.test.delete_with_instance).lower()
        params = {
            "InstanceId": self.test.fake_node.id,
            "DiskId": self.test.fake_volume.id,
            "Device": self.test.device,
            "DeleteWithInstance": delete_with_instance,
        }
        self.assertUrlContainsQueryParams(url, params)
        resp_body = self.fixtures.load("attach_disk.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _DetachDisk(self, method, url, body, headers):
        params = {
            "DiskId": self.test.fake_volume.id,
            "InstanceId": self.test.instance_id,
        }
        self.assertUrlContainsQueryParams(url, params)
        resp_body = self.fixtures.load("detach_disk.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _detach_volume_DescribeDisks(self, method, url, body, headers):
        params = {"DiskIds": '["' + self.test.fake_volume.id + '"]'}
        self.assertUrlContainsQueryParams(url, params)
        resp_body = self.fixtures.load("detach_volume_describe_disks.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _detach_volume_DetachDisk(self, method, url, body, headers):
        params = {"DiskId": self.test.fake_volume.id, "InstanceId": "i-28whl2nj2"}
        self.assertUrlContainsQueryParams(url, params)
        resp_body = self.fixtures.load("detach_disk.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _destroy_volume_DescribeDisks(self, method, url, body, headers):
        params = {"DiskIds": '["' + self.test.fake_volume.id + '"]'}
        self.assertUrlContainsQueryParams(url, params)
        resp_body = self.fixtures.load("destroy_volume_describe_disks.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _destroy_volume_DeleteDisk(self, method, url, body, headers):
        params = {"DiskId": self.test.fake_volume.id}
        self.assertUrlContainsQueryParams(url, params)
        resp_body = self.fixtures.load("delete_disk.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _destroy_volume_state_DescribeDisks(self, method, url, body, headers):
        return self._detach_volume_DescribeDisks(method, url, body, headers)

    def _DeleteSnapshot(self, method, url, body, header):
        params = {"SnapshotId": self.test.fake_snapshot.id}
        self.assertUrlContainsQueryParams(url, params)
        resp_body = self.fixtures.load("delete_snapshot.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _DescribeImages(self, method, url, body, headers):
        params = {"RegionId": self.test.fake_location.id}
        self.assertUrlContainsQueryParams(url, params)
        resp_body = self.fixtures.load("describe_images.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _list_images_pages_DescribeImages(self, method, url, body, headers):
        if "PageNumber=2" in url:
            resp_body = self.fixtures.load("pages_describe_images_page2.xml")
        else:
            resp_body = self.fixtures.load("pages_describe_images.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _list_images_ex_image_ids_DescribeImages(self, method, url, body, headers):
        params = {"ImageId": self.test.fake_image.id + ",not-existed"}
        self.assertUrlContainsQueryParams(url, params)
        return self._DescribeImages(method, url, body, headers)

    def _list_images_ex_filters_DescribeImages(self, method, url, body, headers):
        params = {"Status": "Available"}
        self.assertUrlContainsQueryParams(url, params)
        return self._DescribeImages(method, url, body, headers)

    def _CreateImage(self, method, url, body, headers):
        params = {
            "RegionId": self.test.region,
            "ImageName": self.test.image_name,
            "Description": self.test.description,
            "SnapshotId": self.test.fake_snapshot.id,
            "ImageVersion": self.test.image_version,
            "ClientToken": self.test.client_token,
        }
        self.assertUrlContainsQueryParams(url, params)
        resp_body = self.fixtures.load("create_image.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _DeleteImage(self, method, url, body, headers):
        params = {"RegionId": self.test.region, "ImageId": self.test.fake_image.id}
        self.assertUrlContainsQueryParams(url, params)
        resp_body = self.fixtures.load("delete_image.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _get_image_DescribeImages(self, method, url, body, headers):
        params = {"RegionId": self.test.region, "ImageId": self.test.fake_image.id}
        self.assertUrlContainsQueryParams(url, params)
        resp_body = self.fixtures.load("describe_images.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _get_image_not_found_DescribeImages(self, method, url, body, headers):
        params = {"RegionId": self.test.region, "ImageId": self.test.fake_image.id}
        self.assertUrlContainsQueryParams(url, params)
        resp_body = self.fixtures.load("get_image_describe_images.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _CopyImage(self, method, url, body, headers):
        params = {
            "RegionId": self.test.region,
            "ImageId": self.test.fake_image.id,
            "DestinationRegionId": self.test.dest_region,
            "DestinationImageName": self.test.image_name,
            "DestinationDescription": self.test.description,
            "ClientToken": self.test.client_token,
        }
        self.assertUrlContainsQueryParams(url, params)
        resp_body = self.fixtures.load("copy_image.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _copy_image_same_region_CopyImage(self, method, url, body, headers):
        params = {
            "RegionId": self.test.region,
            "ImageId": self.test.fake_image.id,
            "DestinationRegionId": self.test.region,
        }
        self.assertUrlContainsQueryParams(url, params)
        resp_body = self.fixtures.load("copy_image.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _copy_image_same_region_DescribeImages(self, method, url, body, headers):
        return self._DescribeImages(method, url, body, headers)

    def _DescribeSecurityGroups(self, method, url, body, headers):
        params = {"RegionId": self.test.region}
        self.assertUrlContainsQueryParams(url, params)
        resp_body = self.fixtures.load("describe_security_groups.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _JoinSecurityGroup(self, method, url, body, headers):
        params = {
            "InstanceId": self.test.fake_node.id,
            "SecurityGroupId": self.test.fake_security_group_id,
        }
        self.assertUrlContainsQueryParams(url, params)
        body = self.fixtures.load("join_security_group_by_id.xml")
        return (httplib.OK, body, {}, httplib.responses[httplib.OK])

    def _LeaveSecurityGroup(self, method, url, body, headers):
        params = {
            "InstanceId": self.test.fake_node.id,
            "SecurityGroupId": self.test.fake_security_group_id,
        }
        self.assertUrlContainsQueryParams(url, params)
        body = self.fixtures.load("leave_security_group_by_id.xml")
        return (httplib.OK, body, {}, httplib.responses[httplib.OK])

    def _list_sgs_filters_DescribeSecurityGroups(self, method, url, body, headers):
        params = {"VpcId": self.test.vpc_id}
        self.assertUrlContainsQueryParams(url, params)
        return self._DescribeSecurityGroups(method, url, body, headers)

    def _CreateSecurityGroup(self, method, url, body, headers):
        params = {
            "RegionId": self.test.region,
            "Description": self.test.sg_description,
            "ClientToken": self.test.client_token,
        }
        self.assertUrlContainsQueryParams(url, params)
        resp_body = self.fixtures.load("create_security_group.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _DeleteSecurityGroup(self, method, url, body, headers):
        params = {
            "RegionId": self.test.region,
            "SecurityGroupId": self.test.fake_security_group_id,
        }
        self.assertUrlContainsQueryParams(url, params)
        resp_body = self.fixtures.load("delete_security_group_by_id.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _ModifySecurityGroupAttribute(self, method, url, body, headers):
        params = {
            "RegionId": self.test.region,
            "SecurityGroupId": self.test.fake_security_group_id,
            "SecurityGroupName": self.test.sg_name,
            "Description": self.test.sg_description,
        }
        self.assertUrlContainsQueryParams(url, params)
        resp_body = self.fixtures.load("modify_security_group_by_id.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _DescribeSecurityGroupAttribute(self, method, url, body, headers):
        params = {
            "RegionId": self.test.region,
            "SecurityGroupId": self.test.fake_security_group_id,
            "NicType": self.test.sga_nictype,
        }
        self.assertUrlContainsQueryParams(url, params)
        resp_body = self.fixtures.load("describe_security_group_attributes.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _DescribeZones(self, method, url, body, headers):
        resp_body = self.fixtures.load("describe_zones.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])

    def _create_public_ip_AllocatePublicIpAddress(self, method, url, body, headers):
        resp_body = self.fixtures.load("create_public_ip.xml")
        return (httplib.OK, resp_body, {}, httplib.responses[httplib.OK])


if __name__ == "__main__":
    sys.exit(unittest.main())
