You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jclouds.apache.org by an...@apache.org on 2018/07/06 11:41:39 UTC

jclouds-labs git commit: [JCLOUDS-1430] - add region and zone API

Repository: jclouds-labs
Updated Branches:
  refs/heads/master 5bd2a80fa -> f38f80453


[JCLOUDS-1430] - add region and zone API


Project: http://git-wip-us.apache.org/repos/asf/jclouds-labs/repo
Commit: http://git-wip-us.apache.org/repos/asf/jclouds-labs/commit/f38f8045
Tree: http://git-wip-us.apache.org/repos/asf/jclouds-labs/tree/f38f8045
Diff: http://git-wip-us.apache.org/repos/asf/jclouds-labs/diff/f38f8045

Branch: refs/heads/master
Commit: f38f804537c2c7a3b76ba355ab113475ef862fc1
Parents: 5bd2a80
Author: andreaturli <an...@gmail.com>
Authored: Tue Jul 3 14:47:43 2018 +0200
Committer: andreaturli <an...@gmail.com>
Committed: Fri Jul 6 13:41:24 2018 +0200

----------------------------------------------------------------------
 .../aliyun/ecs/ECSComputeServiceApi.java        |   4 +
 .../ecs/ECSComputeServiceProviderMetadata.java  |   2 +-
 .../org/jclouds/aliyun/ecs/domain/Region.java   |  36 ++
 .../jclouds/aliyun/ecs/domain/ResourceInfo.java |  66 +++
 .../org/jclouds/aliyun/ecs/domain/Zone.java     |  71 +++
 .../aliyun/ecs/features/RegionAndZoneApi.java   |  58 +++
 .../ecs/compute/features/ImageApiMockTest.java  |  28 +-
 .../features/RegionAndZoneApiLiveTest.java      |  62 +++
 .../features/RegionAndZoneApiMockTest.java      |  68 +++
 .../BaseECSComputeServiceApiMockTest.java       |  27 +-
 aliyun-ecs/src/test/resources/regions.json      |  79 +++
 aliyun-ecs/src/test/resources/zones.json        | 477 +++++++++++++++++++
 12 files changed, 949 insertions(+), 29 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/f38f8045/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/ECSComputeServiceApi.java
----------------------------------------------------------------------
diff --git a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/ECSComputeServiceApi.java b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/ECSComputeServiceApi.java
index 268d773..140b098 100644
--- a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/ECSComputeServiceApi.java
+++ b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/ECSComputeServiceApi.java
@@ -17,6 +17,7 @@
 package org.jclouds.aliyun.ecs;
 
 import org.jclouds.aliyun.ecs.features.ImageApi;
+import org.jclouds.aliyun.ecs.features.RegionAndZoneApi;
 import org.jclouds.rest.annotations.Delegate;
 
 import java.io.Closeable;
@@ -26,4 +27,7 @@ public interface ECSComputeServiceApi extends Closeable {
    @Delegate
    ImageApi imageApi();
 
+   @Delegate
+   RegionAndZoneApi regionAndZoneApi();
+
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/f38f8045/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/ECSComputeServiceProviderMetadata.java
----------------------------------------------------------------------
diff --git a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/ECSComputeServiceProviderMetadata.java b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/ECSComputeServiceProviderMetadata.java
index 61b4df9..fbd7206 100644
--- a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/ECSComputeServiceProviderMetadata.java
+++ b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/ECSComputeServiceProviderMetadata.java
@@ -57,7 +57,7 @@ public class ECSComputeServiceProviderMetadata extends BaseProviderMetadata {
                .homepage(URI.create("https://www.alibabacloud.com"))
                .console(URI.create("https://ecs.console.aliyun.com"))
                .endpoint("https://ecs.aliyuncs.com")
-               .iso3166Codes("US-CA", "US-VA", "DE", "JP", "ID-JK", "SG", "IN", "AU-NSW", "MY", "CN-HE", "CN-SH", "CN-ZJ", "CN-GD", "HK", "AE-DU") // TODO
+               .iso3166Codes("US-CA", "US-VA", "DE", "JP", "ID-JK", "SG", "IN", "AU-NSW", "MY", "CN-HE", "CN-SH", "CN-ZJ", "CN-GD", "HK", "AE-DU")
                .defaultProperties(ECSServiceApiMetadata.defaultProperties());
       }
 

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/f38f8045/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/Region.java
----------------------------------------------------------------------
diff --git a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/Region.java b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/Region.java
new file mode 100644
index 0000000..ce2f6b7
--- /dev/null
+++ b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/Region.java
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+package org.jclouds.aliyun.ecs.domain;
+
+import com.google.auto.value.AutoValue;
+import org.jclouds.json.SerializedNames;
+
+@AutoValue
+public abstract class Region {
+
+   Region() {}
+
+   @SerializedNames({ "RegionId", "LocalName" })
+   public static Region create(String regionId, String localName) {
+      return new AutoValue_Region(regionId, localName);
+   }
+
+   public abstract String regionId();
+
+   public abstract String localName();
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/f38f8045/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/ResourceInfo.java
----------------------------------------------------------------------
diff --git a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/ResourceInfo.java b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/ResourceInfo.java
new file mode 100644
index 0000000..ea4bbc3
--- /dev/null
+++ b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/ResourceInfo.java
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+package org.jclouds.aliyun.ecs.domain;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableMap;
+import org.jclouds.json.SerializedNames;
+
+import java.util.List;
+import java.util.Map;
+
+@AutoValue
+public abstract class ResourceInfo {
+
+   ResourceInfo() {
+   }
+
+   @SerializedNames(
+         { "IoOptimized", "SystemDiskCategories", "InstanceTypes", "InstanceTypeFamilies", "DataDiskCategories",
+               "InstanceGenerations", "NetworkTypes" })
+   public static ResourceInfo create(boolean ioOptimized, Map<String, List<String>> systemDiskCategories,
+                                     Map<String, List<String>> instanceTypes, Map<String, List<String>> instanceTypeFamilies,
+                                     Map<String, List<String>> dataDiskCategories, Map<String, List<String>> instanceGenerations,
+                                     Map<String, List<String>> networkTypes) {
+      return new AutoValue_ResourceInfo(ioOptimized, systemDiskCategories == null ?
+            ImmutableMap.<String, List<String>>of() :
+            ImmutableMap.copyOf(systemDiskCategories),
+            instanceTypes == null ? ImmutableMap.<String, List<String>>of() : ImmutableMap.copyOf(instanceTypes),
+            instanceTypeFamilies == null ?
+                  ImmutableMap.<String, List<String>>of() :
+                  ImmutableMap.copyOf(instanceTypeFamilies), dataDiskCategories == null ?
+            ImmutableMap.<String, List<String>>of() :
+            ImmutableMap.copyOf(dataDiskCategories), instanceGenerations == null ?
+            ImmutableMap.<String, List<String>>of() :
+            ImmutableMap.copyOf(instanceGenerations),
+            networkTypes == null ? ImmutableMap.<String, List<String>>of() : ImmutableMap.copyOf(networkTypes));
+   }
+
+   public abstract boolean ioOptimized();
+
+   public abstract Map<String, List<String>> systemDiskCategories();
+
+   public abstract Map<String, List<String>> instanceTypes();
+
+   public abstract Map<String, List<String>> instanceTypeFamilies();
+
+   public abstract Map<String, List<String>> dataDiskCategories();
+
+   public abstract Map<String, List<String>> instanceGenerations();
+
+   public abstract Map<String, List<String>> networkTypes();
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/f38f8045/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/Zone.java
----------------------------------------------------------------------
diff --git a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/Zone.java b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/Zone.java
new file mode 100644
index 0000000..87709e2
--- /dev/null
+++ b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/Zone.java
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+package org.jclouds.aliyun.ecs.domain;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableMap;
+import org.jclouds.json.SerializedNames;
+
+import java.util.List;
+import java.util.Map;
+
+@AutoValue
+public abstract class Zone {
+
+   Zone() {}
+
+   @SerializedNames({ "ZoneId", "LocalName", "DedicatedHostGenerations", "AvailableResourceCreation",
+                          "AvailableDedicatedHostTypes", "AvailableResources", "AvailableInstanceTypes",
+                          "AvailableVolumeCategories", "AvailableDiskCategories" })
+   public static Zone create(String zoneId, String localName,
+                             Map<String, List<Object>> dedicatedHostGenerations, // FIXME neither doc nor example showed the type in the list
+                             Map<String, List<String>> availableResourceCreation,
+                             Map<String, List<String>> availableDedicatedHostTypes,
+                             Map<String, List<ResourceInfo>> availableResources,
+                             Map<String, List<String>> availableInstanceTypes,
+                             Map<String, List<String>> availableVolumeCategories,
+                             Map<String, List<String>> availableDiskCategories) {
+      return new AutoValue_Zone(zoneId, localName,
+              dedicatedHostGenerations == null ? ImmutableMap.<String, List<Object>>of() : ImmutableMap.copyOf(dedicatedHostGenerations),
+              availableResourceCreation == null ? ImmutableMap.<String, List<String>>of() : ImmutableMap.copyOf(availableResourceCreation),
+              availableDedicatedHostTypes == null ? ImmutableMap.<String, List<String>>of() : ImmutableMap.copyOf(availableDedicatedHostTypes),
+              availableResources == null ? ImmutableMap.<String, List<ResourceInfo>>of() : ImmutableMap.copyOf(availableResources),
+              availableInstanceTypes == null ? ImmutableMap.<String, List<String>>of() : ImmutableMap.copyOf(availableInstanceTypes),
+              availableVolumeCategories == null ? ImmutableMap.<String, List<String>>of() : ImmutableMap.copyOf(availableVolumeCategories),
+              availableDiskCategories == null ? ImmutableMap.<String, List<String>>of() : ImmutableMap.copyOf(availableDiskCategories)
+      );
+   }
+
+   public abstract String zoneId();
+
+   public abstract String localName();
+
+   public abstract Map<String, List<Object>> dedicatedHostGenerations();
+
+   public abstract Map<String, List<String>> availableResourceCreation();
+
+   public abstract Map<String, List<String>> availableDedicatedHostTypes();
+
+   public abstract Map<String, List<ResourceInfo>> availableResources();
+
+   public abstract Map<String, List<String>> availableInstanceTypes();
+
+   public abstract Map<String, List<String>> availableVolumeCategories();
+
+   public abstract Map<String, List<String>> availableDiskCategories();
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/f38f8045/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/features/RegionAndZoneApi.java
----------------------------------------------------------------------
diff --git a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/features/RegionAndZoneApi.java b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/features/RegionAndZoneApi.java
new file mode 100644
index 0000000..8216ab0
--- /dev/null
+++ b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/features/RegionAndZoneApi.java
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+package org.jclouds.aliyun.ecs.features;
+
+import org.jclouds.Constants;
+import org.jclouds.Fallbacks;
+import org.jclouds.aliyun.ecs.domain.Region;
+import org.jclouds.aliyun.ecs.domain.Zone;
+import org.jclouds.aliyun.ecs.filters.FormSign;
+import org.jclouds.rest.annotations.Fallback;
+import org.jclouds.rest.annotations.QueryParams;
+import org.jclouds.rest.annotations.RequestFilters;
+import org.jclouds.rest.annotations.SelectJson;
+
+import javax.inject.Named;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import java.util.List;
+
+/**
+ * https://www.alibabacloud.com/help/doc-detail/25609.htm?spm=a2c63.p38356.a1.4.7dd43c1aeoTmzO
+ */
+@Consumes(MediaType.APPLICATION_JSON)
+@RequestFilters(FormSign.class)
+@QueryParams(keys = { "Version", "Format", "SignatureVersion", "ServiceCode", "SignatureMethod" },
+        values = {"{" + Constants.PROPERTY_API_VERSION + "}", "JSON", "1.0", "ecs", "HMAC-SHA1"})
+public interface RegionAndZoneApi {
+
+   @Named("region:list")
+   @GET
+   @SelectJson("Region")
+   @QueryParams(keys = "Action", values = "DescribeRegions")
+   @Fallback(Fallbacks.EmptyListOnNotFoundOr404.class)
+   List<Region> describeRegions();
+
+   @Named("zone:list")
+   @GET
+   @SelectJson("Zone")
+   @QueryParams(keys = "Action", values = "DescribeZones")
+   @Fallback(Fallbacks.EmptyListOnNotFoundOr404.class)
+   List<Zone> describeZones(@QueryParam("RegionId") String region);
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/f38f8045/aliyun-ecs/src/test/java/org/jclouds/aliyun/ecs/compute/features/ImageApiMockTest.java
----------------------------------------------------------------------
diff --git a/aliyun-ecs/src/test/java/org/jclouds/aliyun/ecs/compute/features/ImageApiMockTest.java b/aliyun-ecs/src/test/java/org/jclouds/aliyun/ecs/compute/features/ImageApiMockTest.java
index a29c067..b333e6d 100644
--- a/aliyun-ecs/src/test/java/org/jclouds/aliyun/ecs/compute/features/ImageApiMockTest.java
+++ b/aliyun-ecs/src/test/java/org/jclouds/aliyun/ecs/compute/features/ImageApiMockTest.java
@@ -16,16 +16,17 @@
  */
 package org.jclouds.aliyun.ecs.compute.features;
 
+import com.google.common.collect.ImmutableMap;
 import org.jclouds.aliyun.ecs.compute.internal.BaseECSComputeServiceApiMockTest;
 import org.jclouds.aliyun.ecs.domain.Image;
 import org.jclouds.aliyun.ecs.domain.Regions;
-import org.jclouds.aliyun.ecs.domain.options.ListImagesOptions;
-import org.jclouds.aliyun.ecs.domain.options.PaginationOptions;
 import org.jclouds.collect.IterableWithMarker;
 import org.testng.annotations.Test;
 
 import static com.google.common.collect.Iterables.isEmpty;
 import static com.google.common.collect.Iterables.size;
+import static org.jclouds.aliyun.ecs.domain.options.ListImagesOptions.Builder.paginationOptions;
+import static org.jclouds.aliyun.ecs.domain.options.PaginationOptions.Builder.pageNumber;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertTrue;
 
@@ -36,13 +37,12 @@ public class ImageApiMockTest extends BaseECSComputeServiceApiMockTest {
       server.enqueue(jsonResponse("/images-first.json"));
       server.enqueue(jsonResponse("/images-second.json"));
       server.enqueue(jsonResponse("/images-last.json"));
-
       Iterable<Image> images = api.imageApi().list(Regions.EU_CENTRAL_1.getName()).concat();
       assertEquals(size(images), 28); // Force the PagedIterable to advance
       assertEquals(server.getRequestCount(), 3);
-      assertSent(server, "GET", "DescribeImages");
-      assertSent(server, "GET", "DescribeImages", 2);
-      assertSent(server, "GET", "DescribeImages", 3);
+      assertSent(server, "GET", "DescribeImages", ImmutableMap.of("RegionId", Regions.EU_CENTRAL_1.getName()));
+      assertSent(server, "GET", "DescribeImages", ImmutableMap.of("RegionId", Regions.EU_CENTRAL_1.getName()), 2);
+      assertSent(server, "GET", "DescribeImages", ImmutableMap.of("RegionId", Regions.EU_CENTRAL_1.getName()), 3);
    }
 
    public void testListImagesReturns404() {
@@ -54,26 +54,18 @@ public class ImageApiMockTest extends BaseECSComputeServiceApiMockTest {
 
    public void testListImagesWithOptions() throws InterruptedException {
       server.enqueue(jsonResponse("/images-first.json"));
-
-      IterableWithMarker<Image> images = api.imageApi().list(Regions.EU_CENTRAL_1.getName(), ListImagesOptions.Builder
-              .paginationOptions(PaginationOptions.Builder.pageNumber(1)));
-
+      IterableWithMarker<Image> images = api.imageApi().list(Regions.EU_CENTRAL_1.getName(), paginationOptions(pageNumber(1)));
       assertEquals(size(images), 10);
       assertEquals(server.getRequestCount(), 1);
-
-      assertSent(server, "GET", "DescribeImages", 1);
+      assertSent(server, "GET", "DescribeImages", ImmutableMap.of("RegionId", Regions.EU_CENTRAL_1.getName()), 1);
    }
 
    public void testListImagesWithOptionsReturns404() throws InterruptedException {
       server.enqueue(response404());
-
-      IterableWithMarker<Image> images = api.imageApi().list(Regions.EU_CENTRAL_1.getName(), ListImagesOptions.Builder
-              .paginationOptions(PaginationOptions.Builder.pageNumber(2)));
-
+      IterableWithMarker<Image> images = api.imageApi().list(Regions.EU_CENTRAL_1.getName(), paginationOptions(pageNumber(2)));
       assertTrue(isEmpty(images));
-
       assertEquals(server.getRequestCount(), 1);
-      assertSent(server, "GET", "DescribeImages", 2);
+      assertSent(server, "GET", "DescribeImages", ImmutableMap.of("RegionId", Regions.EU_CENTRAL_1.getName()), 2);
    }
 
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/f38f8045/aliyun-ecs/src/test/java/org/jclouds/aliyun/ecs/compute/features/RegionAndZoneApiLiveTest.java
----------------------------------------------------------------------
diff --git a/aliyun-ecs/src/test/java/org/jclouds/aliyun/ecs/compute/features/RegionAndZoneApiLiveTest.java b/aliyun-ecs/src/test/java/org/jclouds/aliyun/ecs/compute/features/RegionAndZoneApiLiveTest.java
new file mode 100644
index 0000000..91239a1
--- /dev/null
+++ b/aliyun-ecs/src/test/java/org/jclouds/aliyun/ecs/compute/features/RegionAndZoneApiLiveTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+package org.jclouds.aliyun.ecs.compute.features;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import org.jclouds.aliyun.ecs.compute.internal.BaseECSComputeServiceApiLiveTest;
+import org.jclouds.aliyun.ecs.domain.Region;
+import org.jclouds.aliyun.ecs.domain.Zone;
+import org.jclouds.aliyun.ecs.features.RegionAndZoneApi;
+import org.testng.annotations.Test;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.testng.Assert.assertTrue;
+import static org.testng.util.Strings.isNullOrEmpty;
+
+@Test(groups = "live", testName = "RegionAndZoneApiLiveTest")
+public class RegionAndZoneApiLiveTest extends BaseECSComputeServiceApiLiveTest {
+
+   public void testListRegions() {
+      final AtomicInteger found = new AtomicInteger(0);
+      assertTrue(Iterables.all(api().describeRegions(), new Predicate<Region>() {
+         @Override
+         public boolean apply(Region input) {
+            found.incrementAndGet();
+            return !isNullOrEmpty(input.regionId());
+         }
+      }), "All regions must have the 'id' field populated");
+      assertTrue(found.get() > 0, "Expected some region to be returned");
+   }
+
+   public void testListZones() {
+      final AtomicInteger found = new AtomicInteger(0);
+      assertTrue(Iterables.all(api().describeZones("eu-central-1"), new Predicate<Zone>() {
+         @Override
+         public boolean apply(Zone input) {
+            found.incrementAndGet();
+            return !isNullOrEmpty(input.zoneId());
+         }
+      }), "All zones must have the 'id' field populated");
+      assertTrue(found.get() > 0, "Expected some zone to be returned");
+   }
+
+   private RegionAndZoneApi api() {
+      return api.regionAndZoneApi();
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/f38f8045/aliyun-ecs/src/test/java/org/jclouds/aliyun/ecs/compute/features/RegionAndZoneApiMockTest.java
----------------------------------------------------------------------
diff --git a/aliyun-ecs/src/test/java/org/jclouds/aliyun/ecs/compute/features/RegionAndZoneApiMockTest.java b/aliyun-ecs/src/test/java/org/jclouds/aliyun/ecs/compute/features/RegionAndZoneApiMockTest.java
new file mode 100644
index 0000000..291d1b0
--- /dev/null
+++ b/aliyun-ecs/src/test/java/org/jclouds/aliyun/ecs/compute/features/RegionAndZoneApiMockTest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+package org.jclouds.aliyun.ecs.compute.features;
+
+import com.google.common.collect.ImmutableMap;
+import org.jclouds.aliyun.ecs.compute.internal.BaseECSComputeServiceApiMockTest;
+import org.jclouds.aliyun.ecs.domain.Region;
+import org.jclouds.aliyun.ecs.domain.Regions;
+import org.jclouds.aliyun.ecs.domain.Zone;
+import org.testng.annotations.Test;
+
+import java.util.List;
+
+import static com.google.common.collect.Iterables.isEmpty;
+import static com.google.common.collect.Iterables.size;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+@Test(groups = "unit", testName = "RegionAndZoneApiMockTest", singleThreaded = true)
+public class RegionAndZoneApiMockTest extends BaseECSComputeServiceApiMockTest {
+
+   public void testListRegions() throws InterruptedException {
+      server.enqueue(jsonResponse("/regions.json"));
+      List<Region> regions = api.regionAndZoneApi().describeRegions();
+      assertEquals(size(regions), 18);
+      assertEquals(server.getRequestCount(), 1);
+      assertSent(server, "GET", "DescribeRegions");
+   }
+
+   public void testListRegionsReturns404() throws InterruptedException {
+      server.enqueue(response404());
+      List<Region> regions = api.regionAndZoneApi().describeRegions();
+      assertTrue(isEmpty(regions));
+      assertEquals(server.getRequestCount(), 1);
+      assertSent(server, "GET", "DescribeRegions");
+   }
+
+   public void testListZones() throws InterruptedException {
+      server.enqueue(jsonResponse("/zones.json"));
+      List<Zone> zones = api.regionAndZoneApi().describeZones(Regions.EU_CENTRAL_1.getName());
+      assertEquals(size(zones), 2);
+      assertEquals(server.getRequestCount(), 1);
+      assertSent(server, "GET", "DescribeZones", ImmutableMap.of("RegionId", Regions.EU_CENTRAL_1.getName()));
+   }
+
+   public void testListZonesReturns404() throws InterruptedException {
+      server.enqueue(response404());
+      List<Zone> zones = api.regionAndZoneApi().describeZones(Regions.EU_CENTRAL_1.getName());
+      assertTrue(isEmpty(zones));
+      assertEquals(server.getRequestCount(), 1);
+      assertSent(server, "GET", "DescribeZones", ImmutableMap.of("RegionId", Regions.EU_CENTRAL_1.getName()));
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/f38f8045/aliyun-ecs/src/test/java/org/jclouds/aliyun/ecs/compute/internal/BaseECSComputeServiceApiMockTest.java
----------------------------------------------------------------------
diff --git a/aliyun-ecs/src/test/java/org/jclouds/aliyun/ecs/compute/internal/BaseECSComputeServiceApiMockTest.java b/aliyun-ecs/src/test/java/org/jclouds/aliyun/ecs/compute/internal/BaseECSComputeServiceApiMockTest.java
index 5845339..1b27556 100644
--- a/aliyun-ecs/src/test/java/org/jclouds/aliyun/ecs/compute/internal/BaseECSComputeServiceApiMockTest.java
+++ b/aliyun-ecs/src/test/java/org/jclouds/aliyun/ecs/compute/internal/BaseECSComputeServiceApiMockTest.java
@@ -19,6 +19,7 @@ package org.jclouds.aliyun.ecs.compute.internal;
 import com.google.common.base.Charsets;
 import com.google.common.base.Splitter;
 import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.io.Resources;
 import com.google.inject.Module;
@@ -42,6 +43,7 @@ import java.util.Set;
 import static com.google.common.util.concurrent.MoreExecutors.newDirectExecutorService;
 import static org.jclouds.Constants.PROPERTY_MAX_RETRIES;
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
 
 public class BaseECSComputeServiceApiMockTest {
 
@@ -97,20 +99,25 @@ public class BaseECSComputeServiceApiMockTest {
    }
 
    protected RecordedRequest assertSent(MockWebServer server, String method, String action) throws InterruptedException {
-      RecordedRequest request = server.takeRequest();
-      assertEquals(request.getMethod(), method);
-      Map<String, String> queryParameters = Splitter.on('&').trimResults().withKeyValueSeparator("=").split(request.getPath());
-      assertEquals(queryParameters.get("Action"), action);
-      assertEquals(request.getHeader("Accept"), "application/json");
-      return request;
+      return assertSent(server, method, action, ImmutableMap.<String, String>of(), null);
    }
 
-   protected RecordedRequest assertSent(MockWebServer server, String method, String action, Integer page) throws InterruptedException {
+   protected RecordedRequest assertSent(MockWebServer server, String method, String action, Map<String, String> queryParameters) throws InterruptedException {
+      return assertSent(server, method, action, queryParameters, null);
+   }
+
+   protected RecordedRequest assertSent(MockWebServer server, String method, String action, Map<String, String> queryParameters, Integer page) throws InterruptedException {
       RecordedRequest request = server.takeRequest();
       assertEquals(request.getMethod(), method);
-      Map<String, String> queryParameters = Splitter.on('&').trimResults().withKeyValueSeparator("=").split(request.getPath());
-      assertEquals(queryParameters.get("Action"), action);
-      assertEquals(Integer.valueOf(queryParameters.get("pageNumber")), page);
+      Map<String, String> requestQueryParameters = Splitter.on('&').trimResults().withKeyValueSeparator("=").split(request.getPath());
+      assertEquals(requestQueryParameters.get("Action"), action);
+
+      for (Map.Entry<String, String> keyAndValue : queryParameters.entrySet()) {
+         assertTrue(requestQueryParameters.containsKey(keyAndValue.getKey()));
+         assertTrue(requestQueryParameters.containsValue(keyAndValue.getValue()));
+         assertEquals(requestQueryParameters.get(keyAndValue.getKey()), keyAndValue.getValue());
+      }
+      if (page != null) assertEquals(Integer.valueOf(requestQueryParameters.get("pageNumber")), page);
       assertEquals(request.getHeader("Accept"), "application/json");
       return request;
    }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/f38f8045/aliyun-ecs/src/test/resources/regions.json
----------------------------------------------------------------------
diff --git a/aliyun-ecs/src/test/resources/regions.json b/aliyun-ecs/src/test/resources/regions.json
new file mode 100644
index 0000000..734e35c
--- /dev/null
+++ b/aliyun-ecs/src/test/resources/regions.json
@@ -0,0 +1,79 @@
+{
+  "RequestId": "A3EF3FCC-020E-4056-9C20-DD9902227CD6",
+  "Regions": {
+    "Region": [
+      {
+        "RegionId": "cn-qingdao",
+        "LocalName": "[0xe5][0x8d][0x8e][0xe5][0x8c][0x97] 1"
+      },
+      {
+        "RegionId": "cn-beijing",
+        "LocalName": "[0xe5][0x8d][0x8e][0xe5][0x8c][0x97] 2"
+      },
+      {
+        "RegionId": "cn-zhangjiakou",
+        "LocalName": "[0xe5][0x8d][0x8e][0xe5][0x8c][0x97] 3"
+      },
+      {
+        "RegionId": "cn-huhehaote",
+        "LocalName": "[0xe5][0x8d][0x8e][0xe5][0x8c][0x97] 5"
+      },
+      {
+        "RegionId": "cn-hangzhou",
+        "LocalName": "[0xe5][0x8d][0x8e][0xe4][0xb8][0x9c] 1"
+      },
+      {
+        "RegionId": "cn-shanghai",
+        "LocalName": "[0xe5][0x8d][0x8e][0xe4][0xb8][0x9c] 2"
+      },
+      {
+        "RegionId": "cn-shenzhen",
+        "LocalName": "[0xe5][0x8d][0x8e][0xe5][0x8d][0x97] 1"
+      },
+      {
+        "RegionId": "cn-hongkong",
+        "LocalName": "[0xe9][0xa6][0x99][0xe6][0xb8][0xaf]"
+      },
+      {
+        "RegionId": "ap-northeast-1",
+        "LocalName": "[0xe4][0xba][0x9a][0xe5][0xa4][0xaa][0xe4][0xb8][0x9c][0xe5][0x8c][0x97] 1 ([0xe4][0xb8][0x9c][0xe4][0xba][0xac])"
+      },
+      {
+        "RegionId": "ap-southeast-1",
+        "LocalName": "[0xe4][0xba][0x9a][0xe5][0xa4][0xaa][0xe4][0xb8][0x9c][0xe5][0x8d][0x97] 1 ([0xe6][0x96][0xb0][0xe5][0x8a][0xa0][0xe5][0x9d][0xa1])"
+      },
+      {
+        "RegionId": "ap-southeast-2",
+        "LocalName": "[0xe4][0xba][0x9a][0xe5][0xa4][0xaa][0xe4][0xb8][0x9c][0xe5][0x8d][0x97] 2 ([0xe6][0x82][0x89][0xe5][0xb0][0xbc])"
+      },
+      {
+        "RegionId": "ap-southeast-3",
+        "LocalName": "[0xe4][0xba][0x9a][0xe5][0xa4][0xaa][0xe4][0xb8][0x9c][0xe5][0x8d][0x97] 3 ([0xe5][0x90][0x89][0xe9][0x9a][0x86][0xe5][0x9d][0xa1])"
+      },
+      {
+        "RegionId": "ap-southeast-5",
+        "LocalName": "[0xe4][0xba][0x9a][0xe5][0xa4][0xaa][0xe4][0xb8][0x9c][0xe5][0x8d][0x97] 5 ([0xe9][0x9b][0x85][0xe5][0x8a][0xa0][0xe8][0xbe][0xbe])"
+      },
+      {
+        "RegionId": "ap-south-1",
+        "LocalName": "[0xe4][0xba][0x9a][0xe5][0xa4][0xaa][0xe5][0x8d][0x97][0xe9][0x83][0xa8] 1 ([0xe5][0xad][0x9f][0xe4][0xb9][0xb0])"
+      },
+      {
+        "RegionId": "us-east-1",
+        "LocalName": "[0xe7][0xbe][0x8e][0xe5][0x9b][0xbd][0xe4][0xb8][0x9c][0xe9][0x83][0xa8] 1 ([0xe5][0xbc][0x97][0xe5][0x90][0x89][0xe5][0xb0][0xbc][0xe4][0xba][0x9a])"
+      },
+      {
+        "RegionId": "us-west-1",
+        "LocalName": "[0xe7][0xbe][0x8e][0xe5][0x9b][0xbd][0xe8][0xa5][0xbf][0xe9][0x83][0xa8] 1 ([0xe7][0xa1][0x85][0xe8][0xb0][0xb7])"
+      },
+      {
+        "RegionId": "me-east-1",
+        "LocalName": "[0xe4][0xb8][0xad][0xe4][0xb8][0x9c][0xe4][0xb8][0x9c][0xe9][0x83][0xa8] 1 ([0xe8][0xbf][0xaa][0xe6][0x8b][0x9c])"
+      },
+      {
+        "RegionId": "eu-central-1",
+        "LocalName": "[0xe6][0xac][0xa7][0xe6][0xb4][0xb2][0xe4][0xb8][0xad][0xe9][0x83][0xa8] 1 ([0xe6][0xb3][0x95][0xe5][0x85][0xb0][0xe5][0x85][0x8b][0xe7][0xa6][0x8f])"
+      }
+    ]
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/f38f8045/aliyun-ecs/src/test/resources/zones.json
----------------------------------------------------------------------
diff --git a/aliyun-ecs/src/test/resources/zones.json b/aliyun-ecs/src/test/resources/zones.json
new file mode 100644
index 0000000..a76baa8
--- /dev/null
+++ b/aliyun-ecs/src/test/resources/zones.json
@@ -0,0 +1,477 @@
+{
+  "RequestId": "A1E88D60-8179-4524-B4D4-1293A5CD986F",
+  "Zones": {
+    "Zone": [
+      {
+        "DedicatedHostGenerations": {
+          "DedicatedHostGeneration": []
+        },
+        "AvailableResourceCreation": {
+          "ResourceTypes": [
+            "VSwitch",
+            "IoOptimized",
+            "Instance",
+            "Disk"
+          ]
+        },
+        "AvailableDedicatedHostTypes": {
+          "DedicatedHostType": []
+        },
+        "AvailableResources": {
+          "ResourcesInfo": [
+            {
+              "IoOptimized": true,
+              "SystemDiskCategories": {
+                "supportedSystemDiskCategory": [
+                  "cloud_ssd",
+                  "cloud_efficiency"
+                ]
+              },
+              "InstanceTypes": {
+                "supportedInstanceType": [
+                  "ecs.hfg5.large",
+                  "ecs.hfc5.xlarge",
+                  "ecs.hfc5.8xlarge",
+                  "ecs.hfc5.4xlarge",
+                  "ecs.se1ne.2xlarge",
+                  "ecs.hfg5.6xlarge",
+                  "ecs.t5-c1m2.4xlarge",
+                  "ecs.se1ne.xlarge",
+                  "ecs.t5-c1m2.2xlarge",
+                  "ecs.hfg5.xlarge",
+                  "ecs.sn2ne.8xlarge",
+                  "ecs.se1ne.4xlarge",
+                  "ecs.sn2ne.4xlarge",
+                  "ecs.sn1ne.8xlarge",
+                  "ecs.t5-c1m1.xlarge",
+                  "ecs.sn2ne.large",
+                  "ecs.sn2ne.xlarge",
+                  "ecs.hfg5.4xlarge",
+                  "ecs.sn1ne.large",
+                  "ecs.t5-c1m4.xlarge",
+                  "ecs.sn1ne.2xlarge",
+                  "ecs.hfc5.2xlarge",
+                  "ecs.t5-lc1m4.large",
+                  "ecs.hfg5.2xlarge",
+                  "ecs.t5-c1m1.4xlarge",
+                  "ecs.t5-c1m2.xlarge",
+                  "ecs.t5-c1m1.large",
+                  "ecs.sn2ne.2xlarge",
+                  "ecs.hfc5.6xlarge",
+                  "ecs.t5-lc1m2.small",
+                  "ecs.t5-c1m4.large",
+                  "ecs.t5-c1m1.2xlarge",
+                  "ecs.sn1ne.4xlarge",
+                  "ecs.t5-lc1m1.small",
+                  "ecs.t5-c1m4.2xlarge",
+                  "ecs.sn1ne.xlarge",
+                  "ecs.se1ne.large",
+                  "ecs.t5-lc1m2.large",
+                  "ecs.hfc5.large",
+                  "ecs.se1ne.8xlarge",
+                  "ecs.t5-lc2m1.nano",
+                  "ecs.t5-c1m2.large"
+                ]
+              },
+              "InstanceTypeFamilies": {
+                "supportedInstanceTypeFamily": [
+                  "ecs.hfc5",
+                  "ecs.sn1ne",
+                  "ecs.hfg5",
+                  "ecs.se1ne",
+                  "ecs.t5",
+                  "ecs.sn2ne"
+                ]
+              },
+              "DataDiskCategories": {
+                "supportedDataDiskCategory": [
+                  "cloud_ssd",
+                  "cloud_efficiency"
+                ]
+              },
+              "InstanceGenerations": {
+                "supportedInstanceGeneration": [
+                  "ecs-3",
+                  "ecs-4"
+                ]
+              },
+              "NetworkTypes": {
+                "supportedNetworkCategory": [
+                  "vpc"
+                ]
+              }
+            }
+          ]
+        },
+        "AvailableInstanceTypes": {
+          "InstanceTypes": [
+            "ecs.hfc5.xlarge",
+            "ecs.hfc5.8xlarge",
+            "ecs.t5-c1m2.4xlarge",
+            "ecs.hfg5.6xlarge",
+            "ecs.se1ne.2xlarge",
+            "ecs.sn1ne.3xlarge",
+            "ecs.sn2ne.3xlarge",
+            "ecs.t5-c1m2.2xlarge",
+            "ecs.se1ne.3xlarge",
+            "ecs.hfg5.xlarge",
+            "ecs.sn2ne.8xlarge",
+            "ecs.hfg5.3xlarge",
+            "ecs.sn2ne.4xlarge",
+            "ecs.sn1ne.8xlarge",
+            "ecs.sn2ne.large",
+            "ecs.sn2ne.xlarge",
+            "ecs.hfg5.4xlarge",
+            "ecs.hfc5.2xlarge",
+            "ecs.sn1ne.2xlarge",
+            "ecs.se1ne.6xlarge",
+            "ecs.sn2ne.2xlarge",
+            "ecs.hfc5.3xlarge",
+            "ecs.sn2ne.6xlarge",
+            "ecs.t5-lc1m2.small",
+            "ecs.t5-c1m4.large",
+            "ecs.t5-lc1m1.small",
+            "ecs.t5-c1m4.2xlarge",
+            "ecs.sn1ne.xlarge",
+            "ecs.se1ne.large",
+            "ecs.t5-lc1m2.large",
+            "ecs.t5-c1m2.large",
+            "ecs.hfg5.large",
+            "ecs.hfc5.4xlarge",
+            "ecs.se1ne.xlarge",
+            "ecs.se1ne.4xlarge",
+            "ecs.t5-c1m1.xlarge",
+            "ecs.sn1ne.large",
+            "ecs.t5-c1m4.xlarge",
+            "ecs.sn1ne.6xlarge",
+            "ecs.t5-lc1m4.large",
+            "ecs.hfg5.2xlarge",
+            "ecs.t5-c1m1.4xlarge",
+            "ecs.t5-c1m2.xlarge",
+            "ecs.t5-c1m1.large",
+            "ecs.hfc5.6xlarge",
+            "ecs.sn1ne.4xlarge",
+            "ecs.t5-c1m1.2xlarge",
+            "ecs.hfc5.large",
+            "ecs.se1ne.8xlarge",
+            "ecs.t5-lc2m1.nano"
+          ]
+        },
+        "ZoneId": "eu-central-1b",
+        "AvailableVolumeCategories": {
+          "VolumeCategories": [
+            "san_ssd",
+            "san_efficiency"
+          ]
+        },
+        "LocalName": "[0xe6][0xac][0xa7][0xe6][0xb4][0xb2][0xe4][0xb8][0xad][0xe9][0x83][0xa8]1 [0xe5][0x8f][0xaf][0xe7][0x94][0xa8][0xe5][0x8c][0xba]B",
+        "AvailableDiskCategories": {
+          "DiskCategories": [
+            "cloud_ssd",
+            "cloud_efficiency"
+          ]
+        }
+      },
+      {
+        "DedicatedHostGenerations": {
+          "DedicatedHostGeneration": [
+            "ddh-3"
+          ]
+        },
+        "AvailableResourceCreation": {
+          "ResourceTypes": [
+            "IoOptimized",
+            "VSwitch",
+            "Instance",
+            "DedicatedHost",
+            "Disk"
+          ]
+        },
+        "AvailableDedicatedHostTypes": {
+          "DedicatedHostType": [
+            "ddh.se1ne"
+          ]
+        },
+        "AvailableResources": {
+          "ResourcesInfo": [
+            {
+              "IoOptimized": true,
+              "SystemDiskCategories": {
+                "supportedSystemDiskCategory": [
+                  "cloud_ssd",
+                  "cloud_efficiency"
+                ]
+              },
+              "InstanceTypes": {
+                "supportedInstanceType": [
+                  "ecs.hfc5.xlarge",
+                  "ecs.hfc5.8xlarge",
+                  "ecs.hfg5.8xlarge",
+                  "ecs.n4.4xlarge",
+                  "ecs.sn2.7xlarge",
+                  "ecs.sn2ne.14xlarge",
+                  "ecs.sn2.medium",
+                  "ecs.gn5-c48g1.12xlarge",
+                  "ecs.n4.xlarge",
+                  "ecs.se1.large",
+                  "ecs.t5-c1m2.4xlarge",
+                  "ecs.hfg5.6xlarge",
+                  "ecs.se1ne.2xlarge",
+                  "ecs.t5-c1m2.2xlarge",
+                  "ecs.hfg5.xlarge",
+                  "ecs.xn4.small",
+                  "ecs.se1.14xlarge",
+                  "ecs.sn2ne.8xlarge",
+                  "ecs.sn2ne.4xlarge",
+                  "ecs.mn4.large",
+                  "ecs.sn1ne.8xlarge",
+                  "ecs.sn1.7xlarge",
+                  "ecs.mn4.2xlarge",
+                  "ecs.mn4.8xlarge",
+                  "ecs.n4.2xlarge",
+                  "ecs.sn2ne.large",
+                  "ecs.hfg5.4xlarge",
+                  "ecs.sn2ne.xlarge",
+                  "ecs.hfc5.2xlarge",
+                  "ecs.sn1ne.2xlarge",
+                  "ecs.e4.small",
+                  "ecs.i1.4xlarge",
+                  "ecs.i2.16xlarge",
+                  "ecs.sn2ne.2xlarge",
+                  "ecs.se1.8xlarge",
+                  "ecs.t5-lc1m2.small",
+                  "ecs.t5-c1m4.large",
+                  "ecs.sn2.3xlarge",
+                  "ecs.n4.small",
+                  "ecs.t5-lc1m1.small",
+                  "ecs.i2.xlarge",
+                  "ecs.se1ne.large",
+                  "ecs.t5-c1m4.2xlarge",
+                  "ecs.sn1ne.xlarge",
+                  "ecs.t5-lc1m2.large",
+                  "ecs.sn2.large",
+                  "ecs.i2.2xlarge",
+                  "ecs.sn1.large",
+                  "ecs.t5-c1m2.large",
+                  "ecs.mn4.xlarge",
+                  "ecs.se1.xlarge",
+                  "ecs.gn5-c8g1.4xlarge",
+                  "ecs.hfg5.large",
+                  "ecs.i1.8xlarge",
+                  "ecs.gn5-c4g1.xlarge",
+                  "ecs.hfc5.4xlarge",
+                  "ecs.gn5-c8g1.2xlarge",
+                  "ecs.se1.2xlarge",
+                  "ecs.i1.2xlarge",
+                  "ecs.se1ne.14xlarge",
+                  "ecs.sn2.xlarge",
+                  "ecs.se1ne.xlarge",
+                  "ecs.se1ne.4xlarge",
+                  "ecs.sn1.medium",
+                  "ecs.n4.8xlarge",
+                  "ecs.t5-c1m1.xlarge",
+                  "ecs.sn1ne.large",
+                  "ecs.t5-c1m4.xlarge",
+                  "ecs.n4.large",
+                  "ecs.sn1.3xlarge",
+                  "ecs.hfg5.2xlarge",
+                  "ecs.t5-lc1m4.large",
+                  "ecs.t5-c1m1.4xlarge",
+                  "ecs.gn5-c4g1.2xlarge",
+                  "ecs.se1.4xlarge",
+                  "ecs.t5-c1m2.xlarge",
+                  "ecs.t5-c1m1.large",
+                  "ecs.sn2.13xlarge",
+                  "ecs.i2.8xlarge",
+                  "ecs.i1.14xlarge",
+                  "ecs.hfc5.6xlarge",
+                  "ecs.i2.4xlarge",
+                  "ecs.i1.xlarge",
+                  "ecs.sn1ne.4xlarge",
+                  "ecs.t5-c1m1.2xlarge",
+                  "ecs.mn4.4xlarge",
+                  "ecs.hfc5.large",
+                  "ecs.se1ne.8xlarge",
+                  "ecs.sn1.xlarge",
+                  "ecs.t5-lc2m1.nano",
+                  "ecs.mn4.small"
+                ]
+              },
+              "InstanceTypeFamilies": {
+                "supportedInstanceTypeFamily": [
+                  "ecs.hfc5",
+                  "ecs.gn5",
+                  "ecs.sn1ne",
+                  "ecs.hfg5",
+                  "ecs.t5",
+                  "ecs.mn4",
+                  "ecs.sn1",
+                  "ecs.sn2",
+                  "ecs.d1",
+                  "ecs.xn4",
+                  "ecs.i1",
+                  "ecs.n4",
+                  "ecs.se1ne",
+                  "ecs.e4",
+                  "ecs.se1",
+                  "ecs.i2",
+                  "ecs.sn2ne"
+                ]
+              },
+              "DataDiskCategories": {
+                "supportedDataDiskCategory": [
+                  "cloud_ssd",
+                  "cloud_efficiency"
+                ]
+              },
+              "InstanceGenerations": {
+                "supportedInstanceGeneration": [
+                  "ecs-3",
+                  "ecs-2",
+                  "ecs-4"
+                ]
+              },
+              "NetworkTypes": {
+                "supportedNetworkCategory": [
+                  "vpc"
+                ]
+              }
+            }
+          ]
+        },
+        "AvailableInstanceTypes": {
+          "InstanceTypes": [
+            "ecs.hfc5.xlarge",
+            "ecs.hfg5.8xlarge",
+            "ecs.sn2.7xlarge",
+            "ecs.sn2ne.14xlarge",
+            "ecs.sn2.medium",
+            "ecs.n4.xlarge",
+            "ecs.t5-c1m2.4xlarge",
+            "ecs.se1.large",
+            "ecs.sn1ne.3xlarge",
+            "ecs.sn2ne.3xlarge",
+            "ecs.t5-c1m2.2xlarge",
+            "ecs.xn4.small",
+            "ecs.se1ne.3xlarge",
+            "ecs.i1.3xlarge",
+            "ecs.sn2ne.8xlarge",
+            "ecs.hfg5.3xlarge",
+            "ecs.mn4.large",
+            "ecs.sn1.7xlarge",
+            "ecs.mn4.8xlarge",
+            "ecs.n4.2xlarge",
+            "ecs.hfg5.4xlarge",
+            "ecs.hfc5.2xlarge",
+            "ecs.i1.4xlarge",
+            "ecs.i2.16xlarge",
+            "ecs.se1.8xlarge",
+            "ecs.hfc5.3xlarge",
+            "ecs.t5-lc1m2.small",
+            "ecs.sn2.3xlarge",
+            "ecs.t5-lc1m1.small",
+            "ecs.i2.xlarge",
+            "ecs.t5-c1m4.2xlarge",
+            "ecs.se1ne.large",
+            "ecs.i2.2xlarge",
+            "ecs.sn1.large",
+            "ecs.mn4.xlarge",
+            "ecs.hfg5.large",
+            "ecs.gn5-c4g1.xlarge",
+            "ecs.hfc5.4xlarge",
+            "ecs.gn5-c8g1.2xlarge",
+            "ecs.i1.2xlarge",
+            "ecs.se1ne.xlarge",
+            "ecs.d1-c8d3.8xlarge",
+            "ecs.se1ne.4xlarge",
+            "ecs.sn1.medium",
+            "ecs.n4.8xlarge",
+            "ecs.e4.xlarge",
+            "ecs.n4.large",
+            "ecs.sn1ne.6xlarge",
+            "ecs.t5-lc1m4.large",
+            "ecs.t5-c1m1.4xlarge",
+            "ecs.d1.2xlarge",
+            "ecs.se1.4xlarge",
+            "ecs.t5-c1m2.xlarge",
+            "ecs.t5-c1m1.large",
+            "ecs.i2.8xlarge",
+            "ecs.i1.14xlarge",
+            "ecs.hfc5.6xlarge",
+            "ecs.i2.4xlarge",
+            "ecs.t5-c1m1.2xlarge",
+            "ecs.t5-lc2m1.nano",
+            "ecs.sn1.xlarge",
+            "ecs.hfc5.8xlarge",
+            "ecs.n4.4xlarge",
+            "ecs.gn5-c48g1.12xlarge",
+            "ecs.hfg5.6xlarge",
+            "ecs.se1ne.2xlarge",
+            "ecs.i1.6xlarge",
+            "ecs.hfg5.xlarge",
+            "ecs.se1.14xlarge",
+            "ecs.sn2ne.4xlarge",
+            "ecs.sn1ne.8xlarge",
+            "ecs.mn4.2xlarge",
+            "ecs.sn2ne.large",
+            "ecs.sn2ne.xlarge",
+            "ecs.i1-c10d1.8xlarge",
+            "ecs.e4.4xlarge",
+            "ecs.sn1ne.2xlarge",
+            "ecs.e4.small",
+            "ecs.se1ne.6xlarge",
+            "ecs.sn2ne.2xlarge",
+            "ecs.sn2ne.6xlarge",
+            "ecs.t5-c1m4.large",
+            "ecs.n4.small",
+            "ecs.gn5-c28g1.14xlarge",
+            "ecs.sn1ne.xlarge",
+            "ecs.t5-lc1m2.large",
+            "ecs.sn2.large",
+            "ecs.i1-c5d1.4xlarge",
+            "ecs.t5-c1m2.large",
+            "ecs.d1.6xlarge",
+            "ecs.se1.xlarge",
+            "ecs.gn5-c8g1.4xlarge",
+            "ecs.i1.8xlarge",
+            "ecs.e4.large",
+            "ecs.se1.2xlarge",
+            "ecs.se1ne.14xlarge",
+            "ecs.sn2.xlarge",
+            "ecs.gn5-c28g1.7xlarge",
+            "ecs.e4.2xlarge",
+            "ecs.t5-c1m1.xlarge",
+            "ecs.sn1ne.large",
+            "ecs.t5-c1m4.xlarge",
+            "ecs.sn1.3xlarge",
+            "ecs.hfg5.2xlarge",
+            "ecs.d1.4xlarge",
+            "ecs.gn5-c4g1.2xlarge",
+            "ecs.sn2.13xlarge",
+            "ecs.i1.xlarge",
+            "ecs.sn1ne.4xlarge",
+            "ecs.mn4.4xlarge",
+            "ecs.d1-c14d3.14xlarge",
+            "ecs.hfc5.large",
+            "ecs.se1ne.8xlarge",
+            "ecs.mn4.small"
+          ]
+        },
+        "ZoneId": "eu-central-1a",
+        "AvailableVolumeCategories": {
+          "VolumeCategories": [
+            "san_ssd",
+            "san_efficiency"
+          ]
+        },
+        "LocalName": "[0xe6][0xac][0xa7][0xe6][0xb4][0xb2][0xe4][0xb8][0xad][0xe9][0x83][0xa8]1 [0xe5][0x8f][0xaf][0xe7][0x94][0xa8][0xe5][0x8c][0xba]A",
+        "AvailableDiskCategories": {
+          "DiskCategories": [
+            "cloud_ssd",
+            "cloud_efficiency"
+          ]
+        }
+      }
+    ]
+  }
+}
\ No newline at end of file