http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/VSwitchRequest.java
----------------------------------------------------------------------
diff --git 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/VSwitchRequest.java 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/VSwitchRequest.java
new file mode 100644
index 0000000..da16205
--- /dev/null
+++ b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/VSwitchRequest.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.domain;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
+
+import java.beans.ConstructorProperties;
+
+public class VSwitchRequest extends Request {
+
+   private final String vSwitchId;
+
+   @ConstructorProperties({ "RequestId", "VSwitchId" })
+   public VSwitchRequest(String requestId, String vSwitchId) {
+      super(requestId);
+      this.vSwitchId = vSwitchId;
+   }
+
+   public String getVSwitchId() {
+      return vSwitchId;
+   }
+
+   @Override
+   public boolean equals(Object o) {
+      if (this == o) return true;
+      if (o == null || getClass() != o.getClass()) return false;
+      if (!super.equals(o)) return false;
+      VSwitchRequest that = (VSwitchRequest) o;
+      return Objects.equal(vSwitchId, that.vSwitchId);
+   }
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(super.hashCode(), vSwitchId);
+   }
+
+   @Override
+   public String toString() {
+      return MoreObjects.toStringHelper(this)
+              .add("vSwitchId", vSwitchId)
+              .toString();
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/VpcAttributes.java
----------------------------------------------------------------------
diff --git 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/VpcAttributes.java 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/VpcAttributes.java
new file mode 100644
index 0000000..f35d6ad
--- /dev/null
+++ b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/VpcAttributes.java
@@ -0,0 +1,48 @@
+/*
+ * 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 VpcAttributes {
+
+   VpcAttributes() {
+   }
+
+   @SerializedNames({ "NatIpAddress", "PrivateIpAddress", "VSwitchId", "VpcId" 
})
+   public static VpcAttributes create(String natIpAddress, Map<String, 
List<String>> privateIpAddress, String vSwitchId,
+                                      String vpcId) {
+      return new AutoValue_VpcAttributes(natIpAddress,
+            privateIpAddress == null ? ImmutableMap.<String, List<String>>of() 
: ImmutableMap.copyOf(privateIpAddress),
+            vSwitchId, vpcId);
+   }
+
+   public abstract String natIpAddress();
+
+   public abstract Map<String, List<String>> privateIpAddress();
+
+   public abstract String vSwitchId();
+
+   public abstract String vpcId();
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/internal/PaginatedCollection.java
----------------------------------------------------------------------
diff --git 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/internal/PaginatedCollection.java
 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/internal/PaginatedCollection.java
index 6cffd72..501bed7 100644
--- 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/internal/PaginatedCollection.java
+++ 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/internal/PaginatedCollection.java
@@ -51,8 +51,8 @@ public class PaginatedCollection<T> extends 
IterableWithMarker<T> {
 
    private final String requestId;
 
-   protected PaginatedCollection(@Nullable Map<String, Iterable<T>> resources, 
int pageNumber, int totalCount,
-                                 int pageSize, String regionId, String 
requestId) {
+   public PaginatedCollection(@Nullable Map<String, Iterable<T>> resources, 
int pageNumber, int totalCount,
+                              int pageSize, String regionId, String requestId) 
{
       this.resources = resources != null ? resources : ImmutableMap.<String, 
Iterable<T>>of();
       this.pageNumber = pageNumber;
       this.totalCount = totalCount;

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/options/CreateInstanceOptions.java
----------------------------------------------------------------------
diff --git 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/options/CreateInstanceOptions.java
 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/options/CreateInstanceOptions.java
new file mode 100644
index 0000000..e1c6956
--- /dev/null
+++ 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/options/CreateInstanceOptions.java
@@ -0,0 +1,161 @@
+/*
+ * 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.options;
+
+import org.jclouds.http.options.BaseHttpRequestOptions;
+
+public class CreateInstanceOptions extends BaseHttpRequestOptions {
+   public static final String VSWITCH_PARAM = "VSwitchId";
+   public static final String INSTANCE_NAME_PARAM = "InstanceName";
+   public static final String KEYPAIR_PARAM = "KeyPairName";
+   public static final String INTERNET_CHARGE_TYPE_PARAM = 
"InternetChargeType";
+   public static final String INTERNET_MAX_BANDWITH_IN_PARAM = 
"InternetMaxBandwidthIn";
+   public static final String INTERNET_MAX_BANDWITH_OUT_PARAM = 
"InternetMaxBandwidthOut";
+   public static final String INSTANCE_CHARGE_TYPE_PARAM = 
"InstanceChargeType";
+
+   /**
+    * Configures the name of the instance to be used.
+    */
+   public CreateInstanceOptions instanceName(String instanceName) {
+      queryParameters.put(INSTANCE_NAME_PARAM, instanceName);
+      return this;
+   }
+
+   /**
+    * Configures the vSwitch Id to be used.
+    */
+   public CreateInstanceOptions vSwitchId(String vSwitchId) {
+      queryParameters.put(VSWITCH_PARAM, vSwitchId);
+      return this;
+   }
+
+   /**
+    * Configures the keyPairName to be used.
+    */
+   public CreateInstanceOptions keyPairName(String keyPairName) {
+      queryParameters.put(KEYPAIR_PARAM, keyPairName);
+      return this;
+   }
+
+   /**
+    * Configures the internet charge type of the instances to be used.
+    */
+   public CreateInstanceOptions internetChargeType(String internetChargeType) {
+      queryParameters.put(INTERNET_CHARGE_TYPE_PARAM, internetChargeType);
+      return this;
+   }
+
+   /**
+    * Configures the internet max bandwidth in of the instances to be used.
+    */
+   public CreateInstanceOptions internetMaxBandwidthIn(int 
internetMaxBandwidthIn) {
+      queryParameters.put(INTERNET_MAX_BANDWITH_IN_PARAM, 
String.valueOf(internetMaxBandwidthIn));
+      return this;
+   }
+
+   /**
+    * Configures the internet max bandwidth out of the instances to be used.
+    */
+   public CreateInstanceOptions internetMaxBandwidthOut(int 
internetMaxBandwidthOut) {
+      queryParameters.put(INTERNET_MAX_BANDWITH_OUT_PARAM, 
String.valueOf(internetMaxBandwidthOut));
+      return this;
+   }
+
+   /**
+    * Configures the instance charge type of the instances to be used.
+    */
+   public CreateInstanceOptions instanceChargeType(String instanceChargeType) {
+      queryParameters.put(INSTANCE_CHARGE_TYPE_PARAM, instanceChargeType);
+      return this;
+   }
+
+   public CreateInstanceOptions paginationOptions(final PaginationOptions 
paginationOptions) {
+      this.queryParameters.putAll(paginationOptions.buildQueryParameters());
+      return this;
+   }
+
+   public CreateInstanceOptions tagOptions(final TagOptions tagOptions) {
+      this.queryParameters.putAll(tagOptions.buildQueryParameters());
+      return this;
+   }
+
+   public static final class Builder {
+
+      /**
+       * @see {@link CreateInstanceOptions#instanceName(String)}
+       */
+      public static CreateInstanceOptions instanceName(String instanceName) {
+         return new CreateInstanceOptions().instanceName(instanceName);
+      }
+
+      /**
+       * @see {@link CreateInstanceOptions#vSwitchId(String)}
+       */
+      public static CreateInstanceOptions vSwitchId(String vSwitchId) {
+         return new CreateInstanceOptions().vSwitchId(vSwitchId);
+      }
+
+      /**
+       * @see {@link CreateInstanceOptions#keyPairName(String)}
+       */
+      public static CreateInstanceOptions keyPairName(String keyPairName) {
+         return new CreateInstanceOptions().keyPairName(keyPairName);
+      }
+
+      /**
+       * @see {@link CreateInstanceOptions#internetChargeType(String)}
+       */
+      public static CreateInstanceOptions internetChargeType(String 
internetChargeType) {
+         return new 
CreateInstanceOptions().internetChargeType(internetChargeType);
+      }
+
+      /**
+       * @see {@link CreateInstanceOptions#internetMaxBandwidthIn(int)}
+       */
+      public static CreateInstanceOptions internetMaxBandwidthIn(int 
internetMaxBandwidthIn) {
+         return new 
CreateInstanceOptions().internetMaxBandwidthIn(internetMaxBandwidthIn);
+      }
+
+      /**
+       * @see {@link CreateInstanceOptions#internetMaxBandwidthOut(int)}
+       */
+      public static CreateInstanceOptions internetMaxBandwidthOut(int 
internetMaxBandwidthOut) {
+         return new 
CreateInstanceOptions().internetMaxBandwidthOut(internetMaxBandwidthOut);
+      }
+
+      /**
+       * @see {@link CreateInstanceOptions#instanceChargeType(String)}
+       */
+      public static CreateInstanceOptions instanceChargeType(String 
instanceChargeType) {
+         return new 
CreateInstanceOptions().instanceChargeType(instanceChargeType);
+      }
+
+      /**
+       * @see CreateInstanceOptions#paginationOptions(PaginationOptions)
+       */
+      public static CreateInstanceOptions paginationOptions(PaginationOptions 
paginationOptions) {
+         return new 
CreateInstanceOptions().paginationOptions(paginationOptions);
+      }
+
+      /**
+       * @see CreateInstanceOptions#tagOptions(TagOptions)
+       */
+      public static CreateInstanceOptions tagOptions(TagOptions tagOptions) {
+         return new CreateInstanceOptions().tagOptions(tagOptions);
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/options/CreateVPCOptions.java
----------------------------------------------------------------------
diff --git 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/options/CreateVPCOptions.java
 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/options/CreateVPCOptions.java
new file mode 100644
index 0000000..b2841b0
--- /dev/null
+++ 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/options/CreateVPCOptions.java
@@ -0,0 +1,106 @@
+/*
+ * 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.options;
+
+import org.jclouds.http.options.BaseHttpRequestOptions;
+
+public class CreateVPCOptions extends BaseHttpRequestOptions {
+   public static final String CIDR_BLOCK_PARAM = "CidrBlock";
+   public static final String VPC_NAME_PARAM = "VpcName";
+   public static final String DESCRIPTION_PARAM = "Description";
+   public static final String CLIENT_TOKEN_PARAM = "ClientToken";
+   public static final String USER_CIDR_PARAM = "UserCidr";
+
+   /**
+    * Configures the IP address range of the VPC_PREFIX in the CIDR block form.
+    */
+   public CreateVPCOptions cidrBlock(String cidrBlock) {
+      queryParameters.put(CIDR_BLOCK_PARAM, cidrBlock);
+      return this;
+   }
+
+   /**
+    * Configures the name of the VPC_PREFIX.
+    */
+   public CreateVPCOptions vpcName(String vpcName) {
+      queryParameters.put(VPC_NAME_PARAM, vpcName);
+      return this;
+   }
+
+   /**
+    * Configures the description of the VPC_PREFIX.
+    */
+   public CreateVPCOptions description(String description) {
+      queryParameters.put(DESCRIPTION_PARAM, description);
+      return this;
+   }
+
+   /**
+    * Configures a client token used to guarantee the idempotence of request.
+    */
+   public CreateVPCOptions clientToken(String clientToken) {
+      queryParameters.put(CLIENT_TOKEN_PARAM, clientToken);
+      return this;
+   }
+
+   /**
+    * Configures the user CIDR.
+    */
+   public CreateVPCOptions userCidr(String userCidr) {
+      queryParameters.put(USER_CIDR_PARAM, userCidr);
+      return this;
+   }
+
+   public static final class Builder {
+
+      /**
+       * @see {@link CreateVPCOptions#cidrBlock(String)}
+       */
+      public static CreateVPCOptions cidrBlock(String cidrBlock) {
+         return new CreateVPCOptions().cidrBlock(cidrBlock);
+      }
+
+      /**
+       * @see {@link CreateVPCOptions#vpcName(String)}
+       */
+      public static CreateVPCOptions vpcName(String vpcName) {
+         return new CreateVPCOptions().vpcName(vpcName);
+      }
+
+      /**
+       * @see {@link CreateVPCOptions#description(String)}
+       */
+      public static CreateVPCOptions description(String description) {
+         return new CreateVPCOptions().description(description);
+      }
+
+      /**
+       * @see {@link CreateVPCOptions#clientToken(String)}
+       */
+      public static CreateVPCOptions clientToken(String clientToken) {
+         return new CreateVPCOptions().clientToken(clientToken);
+      }
+
+      /**
+       * @see {@link CreateVPCOptions#userCidr(String)}
+       */
+      public static CreateVPCOptions userCidr(String userCidr) {
+         return new CreateVPCOptions().userCidr(userCidr);
+      }
+
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/options/CreateVSwitchOptions.java
----------------------------------------------------------------------
diff --git 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/options/CreateVSwitchOptions.java
 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/options/CreateVSwitchOptions.java
new file mode 100644
index 0000000..fe10fe5
--- /dev/null
+++ 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/options/CreateVSwitchOptions.java
@@ -0,0 +1,74 @@
+/*
+ * 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.options;
+
+import org.jclouds.http.options.BaseHttpRequestOptions;
+
+public class CreateVSwitchOptions extends BaseHttpRequestOptions {
+   public static final String VSWITCH_NAME_PARAM = "VSwitchName";
+   public static final String DESCRIPTION_PARAM = "Description";
+   public static final String CLIENT_TOKEN_PARAM = "ClientToken";
+
+   /**
+    * Configures the name of the VSwitch.
+    */
+   public CreateVSwitchOptions vSwitchName(String vSwitchName) {
+      queryParameters.put(VSWITCH_NAME_PARAM, vSwitchName);
+      return this;
+   }
+
+   /**
+    * Configures the description of the VPC_PREFIX.
+    */
+   public CreateVSwitchOptions description(String description) {
+      queryParameters.put(DESCRIPTION_PARAM, description);
+      return this;
+   }
+
+   /**
+    * Configures a client token used to guarantee the idempotence of request.
+    */
+   public CreateVSwitchOptions clientToken(String clientToken) {
+      queryParameters.put(CLIENT_TOKEN_PARAM, clientToken);
+      return this;
+   }
+
+   public static final class Builder {
+
+      /**
+       * @see {@link CreateVSwitchOptions#vSwitchName(String)}
+       */
+      public static CreateVSwitchOptions vSwitchName(String vSwitchName) {
+         return new CreateVSwitchOptions().vSwitchName(vSwitchName);
+      }
+
+      /**
+       * @see {@link CreateVSwitchOptions#description(String)}
+       */
+      public static CreateVSwitchOptions description(String description) {
+         return new CreateVSwitchOptions().description(description);
+      }
+
+      /**
+       * @see {@link CreateVSwitchOptions#clientToken(String)}
+       */
+      public static CreateVSwitchOptions clientToken(String clientToken) {
+         return new CreateVSwitchOptions().clientToken(clientToken);
+      }
+
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/options/ListImagesOptions.java
----------------------------------------------------------------------
diff --git 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/options/ListImagesOptions.java
 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/options/ListImagesOptions.java
index 34972a4..0eec663 100644
--- 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/options/ListImagesOptions.java
+++ 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/options/ListImagesOptions.java
@@ -16,9 +16,11 @@
  */
 package org.jclouds.aliyun.ecs.domain.options;
 
-import org.jclouds.aliyun.ecs.functions.ArrayToCommaSeparatedString;
+import com.google.common.base.Joiner;
 import org.jclouds.http.options.BaseHttpRequestOptions;
 
+import java.util.Arrays;
+
 public class ListImagesOptions extends BaseHttpRequestOptions {
    public static final String IMAGE_ID_PARAM = "ImageId";
    public static final String STATUS_PARAM = "Status";
@@ -28,7 +30,7 @@ public class ListImagesOptions extends BaseHttpRequestOptions 
{
    public static final String USAGE_PARAM = "Usage";
 
    public ListImagesOptions imageIds(String... instanceIds) {
-      queryParameters.put(IMAGE_ID_PARAM, String.format("[%s]", new 
ArrayToCommaSeparatedString().apply(instanceIds)));
+      queryParameters.put(IMAGE_ID_PARAM, 
Joiner.on(",").join(Arrays.asList(instanceIds)));
       return this;
    }
 

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/options/ListInstanceStatusOptions.java
----------------------------------------------------------------------
diff --git 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/options/ListInstanceStatusOptions.java
 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/options/ListInstanceStatusOptions.java
new file mode 100644
index 0000000..9d10467
--- /dev/null
+++ 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/options/ListInstanceStatusOptions.java
@@ -0,0 +1,50 @@
+/*
+ * 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.options;
+
+import org.jclouds.http.options.BaseHttpRequestOptions;
+
+public class ListInstanceStatusOptions extends BaseHttpRequestOptions {
+   public static final String ZONE_ID_PARAM = "ZoneId";
+
+   public ListInstanceStatusOptions zoneId(String zoneId) {
+      queryParameters.put(ZONE_ID_PARAM, zoneId);
+      return this;
+   }
+
+   public ListInstanceStatusOptions paginationOptions(final PaginationOptions 
paginationOptions) {
+      this.queryParameters.putAll(paginationOptions.buildQueryParameters());
+      return this;
+   }
+
+   public static final class Builder {
+
+      /**
+       * @see {@link ListInstanceStatusOptions#zoneId(String)}
+       */
+      public static ListInstanceStatusOptions zoneId(String zoneId) {
+         return new ListInstanceStatusOptions().zoneId(zoneId);
+      }
+
+      /**
+       * @see ListInstanceStatusOptions#paginationOptions(PaginationOptions)
+       */
+      public static ListInstanceStatusOptions 
paginationOptions(PaginationOptions paginationOptions) {
+         return new 
ListInstanceStatusOptions().paginationOptions(paginationOptions);
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/options/ListInstancesOptions.java
----------------------------------------------------------------------
diff --git 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/options/ListInstancesOptions.java
 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/options/ListInstancesOptions.java
new file mode 100644
index 0000000..ec39383
--- /dev/null
+++ 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/options/ListInstancesOptions.java
@@ -0,0 +1,239 @@
+/*
+ * 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.options;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.Iterables;
+import org.jclouds.aliyun.ecs.functions.PutStringInDoubleQuotes;
+import org.jclouds.http.options.BaseHttpRequestOptions;
+
+import java.util.Arrays;
+
+public class ListInstancesOptions extends BaseHttpRequestOptions {
+   public static final String VPC_ID_PARAM = "VpcId";
+   public static final String VSWITCH_ID_PARAM = "VSwitchId";
+   public static final String ZONE_ID_PARAM = "ZoneId";
+   public static final String INSTANCE_IDS_PARAM = "InstanceIds";
+   public static final String INSTANCE_TYPE_PARAM = "InstanceType";
+   public static final String INSTANCE_TYPE_FAMILY_PARAM = 
"InstanceTypeFamily";
+   public static final String INSTANCE_NETWORK_TYPE_PARAM = 
"InstanceNetworkType";
+   public static final String PRIVATE_IP_ADDRESSES_PARAM = 
"PrivateIpAddresses";
+   public static final String INNER_IP_ADDRESSES_PARAM = "InnerIpAddresses";
+   public static final String PUBLIC_IP_ADDRESSES_PARAM = "PublicIpAddresses";
+   public static final String SECURITY_GROUP_ID_PARAM = "SecurityGroupId";
+   public static final String INSTANCE_CHARGE_TYPE_PARAM = 
"InstanceChargeType";
+   public static final String SPOT_STRATEGY_PARAM = "SpotStrategy";
+   public static final String INTERNET_CHARGE_TYPE_PARAM = 
"InternetChargeType";
+   public static final String INSTANCE_NAME_PARAM = "InstanceName";
+   public static final String IMAGE_ID_PARAM = "ImageId";
+   public static final String DEPLOYMENT_SET_ID_PARAM = "DeploymentSetId";
+   public static final String STATUS_PARAM = "Status";
+   public static final String IO_OPTIMIZED_PARAM = "IoOptimized";
+
+   public ListInstancesOptions vpcId(String vpcId) {
+      queryParameters.put(VPC_ID_PARAM, vpcId);
+      return this;
+   }
+
+   public ListInstancesOptions vSwitchId(String vSwitchId) {
+      queryParameters.put(VSWITCH_ID_PARAM, vSwitchId);
+      return this;
+   }
+
+   public ListInstancesOptions zoneId(String zoneId) {
+      queryParameters.put(ZONE_ID_PARAM, zoneId);
+      return this;
+   }
+
+   public ListInstancesOptions instanceIds(String... instanceIds) {
+      String instanceIdsAsString = Joiner.on(",")
+              .join(Iterables.transform(Arrays.asList(instanceIds), new 
PutStringInDoubleQuotes()));
+      queryParameters.put(INSTANCE_IDS_PARAM, String.format("[%s]", 
instanceIdsAsString));
+      return this;
+   }
+
+   public ListInstancesOptions instanceType(String instanceType) {
+      queryParameters.put(INSTANCE_TYPE_PARAM, instanceType);
+      return this;
+   }
+
+   public ListInstancesOptions instanceTypeFamily(String instanceTypeFamily) {
+      queryParameters.put(INSTANCE_TYPE_FAMILY_PARAM, instanceTypeFamily);
+      return this;
+   }
+
+   public ListInstancesOptions instanceNetworkType(String instanceNetworkType) 
{
+      queryParameters.put(INSTANCE_NETWORK_TYPE_PARAM, instanceNetworkType);
+      return this;
+   }
+
+   public ListInstancesOptions privateIpAddresses(String... 
privateIpAddresses) {
+      String instanceIdsAsString = Joiner.on(",")
+              .join(Iterables.transform(Arrays.asList(privateIpAddresses), new 
PutStringInDoubleQuotes()));
+      queryParameters.put(PRIVATE_IP_ADDRESSES_PARAM, String.format("[%s]", 
instanceIdsAsString));
+      return this;
+   }
+
+   public ListInstancesOptions innerIpAddresses(String... innerIpAddresses) {
+      String instanceIdsAsString = Joiner.on(",")
+              .join(Iterables.transform(Arrays.asList(innerIpAddresses), new 
PutStringInDoubleQuotes()));
+      queryParameters.put(INNER_IP_ADDRESSES_PARAM, String.format("[%s]", 
instanceIdsAsString));
+      return this;
+   }
+
+   public ListInstancesOptions publicIpAddresses(String... publicIpAddresses) {
+      String instanceIdsAsString = Joiner.on(",")
+              .join(Iterables.transform(Arrays.asList(publicIpAddresses), new 
PutStringInDoubleQuotes()));
+      queryParameters.put(PUBLIC_IP_ADDRESSES_PARAM, String.format("[%s]", 
instanceIdsAsString));
+      return this;
+   }
+
+   public ListInstancesOptions securityGroupId(String securityGroupId) {
+      queryParameters.put(SECURITY_GROUP_ID_PARAM, securityGroupId);
+      return this;
+   }
+
+   public ListInstancesOptions instanceChargeType(String instanceChargeType) {
+      queryParameters.put(INSTANCE_CHARGE_TYPE_PARAM, instanceChargeType);
+      return this;
+   }
+
+   public ListInstancesOptions spotStrategy(String spotStrategy) {
+      queryParameters.put(SPOT_STRATEGY_PARAM, spotStrategy);
+      return this;
+   }
+
+   public ListInstancesOptions internetChargeType(String internetChargeType) {
+      queryParameters.put(INTERNET_CHARGE_TYPE_PARAM, internetChargeType);
+      return this;
+   }
+
+   public ListInstancesOptions instanceName(String instanceName) {
+      queryParameters.put(INSTANCE_NAME_PARAM, instanceName);
+      return this;
+   }
+
+   public ListInstancesOptions imageId(String imageId) {
+      queryParameters.put(IMAGE_ID_PARAM, imageId);
+      return this;
+   }
+
+   public ListInstancesOptions deploymentSetId(String deploymentSetId) {
+      queryParameters.put(DEPLOYMENT_SET_ID_PARAM, deploymentSetId);
+      return this;
+   }
+
+   public ListInstancesOptions status(String status) {
+      queryParameters.put(STATUS_PARAM, status);
+      return this;
+   }
+
+   public ListInstancesOptions ioOptimized(String ioOptimized) {
+      queryParameters.put(IO_OPTIMIZED_PARAM, ioOptimized);
+      return this;
+   }
+
+   public ListInstancesOptions paginationOptions(final PaginationOptions 
paginationOptions) {
+      this.queryParameters.putAll(paginationOptions.buildQueryParameters());
+      return this;
+   }
+
+   public ListInstancesOptions tagOptions(final TagOptions tagOptions) {
+      this.queryParameters.putAll(tagOptions.buildQueryParameters());
+      return this;
+   }
+
+   public static final class Builder {
+
+      public static ListInstancesOptions vpcId(String vpcId) {
+         return new ListInstancesOptions().vpcId(vpcId);
+      }
+
+      public static ListInstancesOptions vSwitchId(String vSwitchId) {
+         return new ListInstancesOptions().vSwitchId(vSwitchId);
+      }
+
+      public static ListInstancesOptions zoneId(String zoneId) {
+         return new ListInstancesOptions().zoneId(zoneId);
+      }
+
+      public static ListInstancesOptions instanceIds(String... instanceIds) {
+         return new ListInstancesOptions().instanceIds(instanceIds);
+      }
+
+      public static ListInstancesOptions instanceType(String instanceType) {
+         return new ListInstancesOptions().instanceType(instanceType);
+      }
+
+      public static ListInstancesOptions instanceTypeFamily(String 
instanceTypeFamily) {
+         return new 
ListInstancesOptions().instanceTypeFamily(instanceTypeFamily);
+      }
+
+      public static ListInstancesOptions instanceNetworkType(String 
instanceNetworkType) {
+         return new 
ListInstancesOptions().instanceNetworkType(instanceNetworkType);
+      }
+
+      public static ListInstancesOptions privateIpAddresses(String... 
privateIpAddresses) {
+         return new 
ListInstancesOptions().privateIpAddresses(privateIpAddresses);
+      }
+
+      public static ListInstancesOptions innerIpAddresses(String... 
innerIpAddresses) {
+         return new ListInstancesOptions().innerIpAddresses(innerIpAddresses);
+      }
+
+      public static ListInstancesOptions publicIpAddresses(String... 
publicIpAddresses) {
+         return new 
ListInstancesOptions().publicIpAddresses(publicIpAddresses);
+      }
+
+      public static ListInstancesOptions securityGroupId(String 
securityGroupId) {
+         return new ListInstancesOptions().securityGroupId(securityGroupId);
+      }
+
+      public static ListInstancesOptions instanceChargeType(String 
instanceChargeType) {
+         return new 
ListInstancesOptions().instanceChargeType(instanceChargeType);
+      }
+
+      public static ListInstancesOptions instanceName(String instanceName) {
+         return new ListInstancesOptions().instanceName(instanceName);
+      }
+
+      public static ListInstancesOptions imageId(String imageId) {
+         return new ListInstancesOptions().imageId(imageId);
+      }
+
+      public static ListInstancesOptions deploymentSetId(String 
deploymentSetId) {
+         return new ListInstancesOptions().deploymentSetId(deploymentSetId);
+      }
+
+      public static ListInstancesOptions status(String status) {
+         return new ListInstancesOptions().status(status);
+      }
+
+      public static ListInstancesOptions ioOptimized(String ioOptimized) {
+         return new ListInstancesOptions().ioOptimized(ioOptimized);
+      }
+
+      public static ListInstancesOptions paginationOptions(PaginationOptions 
paginationOptions) {
+         return new 
ListInstancesOptions().paginationOptions(paginationOptions);
+      }
+
+      public static ListInstancesOptions tagOptions(TagOptions tagOptions) {
+         return new ListInstancesOptions().tagOptions(tagOptions);
+      }
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/options/ListVPCsOptions.java
----------------------------------------------------------------------
diff --git 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/options/ListVPCsOptions.java
 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/options/ListVPCsOptions.java
new file mode 100644
index 0000000..8915251
--- /dev/null
+++ 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/options/ListVPCsOptions.java
@@ -0,0 +1,63 @@
+/*
+ * 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.options;
+
+import org.jclouds.http.options.BaseHttpRequestOptions;
+
+public class ListVPCsOptions extends BaseHttpRequestOptions {
+   public static final String VPC_ID_PARAM = "VpcId";
+   public static final String IS_DEFAULT_PARAM = "IsDefault";
+
+   public ListVPCsOptions vpcId(String vpcId) {
+      queryParameters.put(VPC_ID_PARAM, vpcId);
+      return this;
+   }
+
+   public ListVPCsOptions isDefault(Boolean isDefault) {
+      queryParameters.put(IS_DEFAULT_PARAM, isDefault.toString());
+      return this;
+   }
+
+   public ListVPCsOptions paginationOptions(final PaginationOptions 
paginationOptions) {
+      this.queryParameters.putAll(paginationOptions.buildQueryParameters());
+      return this;
+   }
+
+   public static final class Builder {
+
+      /**
+       * @see {@link ListVSwitchesOptions#vpcId(String)}
+       */
+      public static ListVPCsOptions vpcId(String vpcId) {
+         return new ListVPCsOptions().vpcId(vpcId);
+      }
+
+      /**
+       * @see {@link ListVPCsOptions#isDefault(Boolean)}
+       */
+      public static ListVPCsOptions isDefault(Boolean isDefault) {
+         return new ListVPCsOptions().isDefault(isDefault);
+      }
+
+      /**
+       * @see ListVPCsOptions#paginationOptions(PaginationOptions)
+       */
+      public static ListVPCsOptions paginationOptions(PaginationOptions 
paginationOptions) {
+         return new ListVPCsOptions().paginationOptions(paginationOptions);
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/options/ListVSwitchesOptions.java
----------------------------------------------------------------------
diff --git 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/options/ListVSwitchesOptions.java
 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/options/ListVSwitchesOptions.java
new file mode 100644
index 0000000..8661175
--- /dev/null
+++ 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/options/ListVSwitchesOptions.java
@@ -0,0 +1,89 @@
+/*
+ * 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.options;
+
+import org.jclouds.http.options.BaseHttpRequestOptions;
+
+public class ListVSwitchesOptions extends BaseHttpRequestOptions {
+   public static final String VPC_ID_PARAM = "VpcId";
+   public static final String VSWITCH_ID_PARAM = "VSwitchId";
+   public static final String ZONE_ID_PARAM = "ZoneId";
+   public static final String IS_DEFAULT_PARAM = "IsDefault";
+
+   public ListVSwitchesOptions vpcId(String vpcId) {
+      queryParameters.put(VPC_ID_PARAM, vpcId);
+      return this;
+   }
+
+   public ListVSwitchesOptions vSwitchId(String vSwitchId) {
+      queryParameters.put(VSWITCH_ID_PARAM, vSwitchId);
+      return this;
+   }
+
+   public ListVSwitchesOptions zoneId(String zoneId) {
+      queryParameters.put(ZONE_ID_PARAM, zoneId);
+      return this;
+   }
+
+   public ListVSwitchesOptions isDefault(Boolean isDefault) {
+      queryParameters.put(IS_DEFAULT_PARAM, isDefault.toString());
+      return this;
+   }
+
+   public ListVSwitchesOptions paginationOptions(final PaginationOptions 
paginationOptions) {
+      this.queryParameters.putAll(paginationOptions.buildQueryParameters());
+      return this;
+   }
+
+   public static final class Builder {
+
+      /**
+       * @see {@link ListVSwitchesOptions#vpcId(String)}
+       */
+      public static ListVSwitchesOptions vpcId(String vpcId) {
+         return new ListVSwitchesOptions().vpcId(vpcId);
+      }
+
+      /**
+       * @see {@link ListVSwitchesOptions#vSwitchId(String)}
+       */
+      public static ListVSwitchesOptions vSwitchId(String vSwitchId) {
+         return new ListVSwitchesOptions().vSwitchId(vSwitchId);
+      }
+
+      /**
+       * @see {@link ListVSwitchesOptions#zoneId(String)}
+       */
+      public static ListVSwitchesOptions zoneId(String zoneId) {
+         return new ListVSwitchesOptions().zoneId(zoneId);
+      }
+
+      /**
+       * @see {@link ListVSwitchesOptions#isDefault(Boolean)}
+       */
+      public static ListVSwitchesOptions isDefault(Boolean isDefault) {
+         return new ListVSwitchesOptions().isDefault(isDefault);
+      }
+
+      /**
+       * @see ListVSwitchesOptions#paginationOptions(PaginationOptions)
+       */
+      public static ListVSwitchesOptions paginationOptions(PaginationOptions 
paginationOptions) {
+         return new 
ListVSwitchesOptions().paginationOptions(paginationOptions);
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/options/TagOptions.java
----------------------------------------------------------------------
diff --git 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/options/TagOptions.java
 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/options/TagOptions.java
index 4e3068e..f220fcd 100644
--- 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/options/TagOptions.java
+++ 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/options/TagOptions.java
@@ -21,8 +21,8 @@ import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import org.jclouds.http.options.BaseHttpRequestOptions;
 
-import javax.annotation.Nullable;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import static com.google.common.base.Preconditions.checkNotNull;
@@ -66,6 +66,16 @@ public class TagOptions extends BaseHttpRequestOptions {
       return this;
    }
 
+   public TagOptions tags(Map<String, String> tags) {
+      checkState(tags.size() <= 5, "tags size must be <= 5");
+      int i = 1;
+      for (Map.Entry<String, String> entry : tags.entrySet()) {
+         tag(i, entry.getKey(), entry.getValue());
+         i++;
+      }
+      return this;
+   }
+
    public static class Builder {
 
       public static TagOptions tag(int pos, String key, String value) {
@@ -79,6 +89,10 @@ public class TagOptions extends BaseHttpRequestOptions {
       public static TagOptions keys(Set<String> keys) {
          return new TagOptions().keys(keys);
       }
+
+      public static TagOptions tags(Map<String, String> tags) {
+         return new TagOptions().tags(tags);
+      }
    }
 
    private void validateInput(final String input, int maxLength) {
@@ -86,10 +100,10 @@ public class TagOptions extends BaseHttpRequestOptions {
       checkState(input.length() <= maxLength, String.format("input must be <= 
%d chars", maxLength));
       checkState(!Iterables.any(FORBIDDEN_PREFIX, new Predicate<String>() {
          @Override
-         public boolean apply(@Nullable String input) {
-            return input.startsWith(input);
+         public boolean apply(String forbiddenPrefix) {
+            return input.startsWith(forbiddenPrefix);
          }
-      }), "Cannot starts with " + Iterables.toString(FORBIDDEN_PREFIX));
+      }), input + " cannot starts with any of " + 
Iterables.toString(FORBIDDEN_PREFIX));
    }
 
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/regionscoped/ImageInRegion.java
----------------------------------------------------------------------
diff --git 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/regionscoped/ImageInRegion.java
 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/regionscoped/ImageInRegion.java
new file mode 100644
index 0000000..5a5bf4f
--- /dev/null
+++ 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/regionscoped/ImageInRegion.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.regionscoped;
+
+import com.google.auto.value.AutoValue;
+import org.jclouds.aliyun.ecs.domain.Image;
+
+
+@AutoValue
+public abstract class ImageInRegion {
+
+   ImageInRegion() {}
+
+   public abstract String regionId();
+   public abstract Image image();
+
+   public static ImageInRegion create(String regionId, Image image) {
+      return new AutoValue_ImageInRegion(regionId, image);
+   }
+
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/regionscoped/RegionAndId.java
----------------------------------------------------------------------
diff --git 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/regionscoped/RegionAndId.java
 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/regionscoped/RegionAndId.java
new file mode 100644
index 0000000..6d2a392
--- /dev/null
+++ 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/regionscoped/RegionAndId.java
@@ -0,0 +1,54 @@
+/*
+ * 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.regionscoped;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Iterables;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+@AutoValue
+public abstract class RegionAndId {
+
+   RegionAndId() {}
+
+   public static RegionAndId create(String regionId, String id) {
+      return new AutoValue_RegionAndId(regionId, id);
+   }
+
+   public static RegionAndId fromSlashEncoded(String id) {
+      Iterable<String> parts = Splitter.on('/').split(checkNotNull(id, "id"));
+      checkArgument(Iterables.size(parts) == 2, "id must be in format 
regionId/id");
+      return RegionAndId.create(Iterables.get(parts, 0), Iterables.get(parts, 
1));
+   }
+
+   public static String slashEncodeRegionAndId(String regionId, String id) {
+      return slashEncodeRegionAndId(RegionAndId.create(regionId, id));
+   }
+
+   public static String slashEncodeRegionAndId(RegionAndId regionAndId) {
+      checkNotNull(regionAndId, "regionAndId");
+      return String.format("%s/%s", regionAndId.regionId(), regionAndId.id());
+   }
+
+   public abstract String regionId();
+
+   public abstract String id();
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/features/InstanceApi.java
----------------------------------------------------------------------
diff --git 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/features/InstanceApi.java 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/features/InstanceApi.java
new file mode 100644
index 0000000..457a3ac
--- /dev/null
+++ b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/features/InstanceApi.java
@@ -0,0 +1,264 @@
+/*
+ * 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 com.google.common.base.Function;
+import com.google.common.base.Predicates;
+import com.google.common.collect.Iterables;
+import com.google.inject.TypeLiteral;
+import org.jclouds.Constants;
+import org.jclouds.Fallbacks;
+import org.jclouds.aliyun.ecs.ECSComputeServiceApi;
+import org.jclouds.aliyun.ecs.domain.AllocatePublicIpAddressRequest;
+import org.jclouds.aliyun.ecs.domain.AvailableZone;
+import org.jclouds.aliyun.ecs.domain.Instance;
+import org.jclouds.aliyun.ecs.domain.InstanceRequest;
+import org.jclouds.aliyun.ecs.domain.InstanceStatus;
+import org.jclouds.aliyun.ecs.domain.InstanceType;
+import org.jclouds.aliyun.ecs.domain.Request;
+import org.jclouds.aliyun.ecs.domain.internal.PaginatedCollection;
+import org.jclouds.aliyun.ecs.domain.options.CreateInstanceOptions;
+import org.jclouds.aliyun.ecs.domain.options.ListInstanceStatusOptions;
+import org.jclouds.aliyun.ecs.domain.options.ListInstancesOptions;
+import org.jclouds.aliyun.ecs.domain.options.PaginationOptions;
+import org.jclouds.aliyun.ecs.filters.FormSign;
+import org.jclouds.collect.IterableWithMarker;
+import org.jclouds.collect.PagedIterable;
+import org.jclouds.collect.internal.ArgsToPagedIterable;
+import org.jclouds.http.functions.ParseJson;
+import org.jclouds.json.Json;
+import org.jclouds.rest.annotations.Fallback;
+import org.jclouds.rest.annotations.QueryParams;
+import org.jclouds.rest.annotations.RequestFilters;
+import org.jclouds.rest.annotations.ResponseParser;
+import org.jclouds.rest.annotations.SelectJson;
+import org.jclouds.rest.annotations.Transform;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import java.beans.ConstructorProperties;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 
https://www.alibabacloud.com/help/doc-detail/25500.htm?spm=a2c63.p38356.b99.287.129a44a8RBMBLH
+ */
+@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 InstanceApi {
+
+   @Named("instance:list")
+   @GET
+   @QueryParams(keys = "Action", values = "DescribeInstances")
+   @ResponseParser(ParseInstances.class)
+   @Fallback(Fallbacks.EmptyIterableWithMarkerOnNotFoundOr404.class)
+   IterableWithMarker<Instance> list(@QueryParam("RegionId") String region, 
ListInstancesOptions options);
+
+   @Named("instance:list")
+   @GET
+   @QueryParams(keys = "Action", values = "DescribeInstances")
+   @ResponseParser(ParseInstances.class)
+   @Transform(ParseInstances.ToPagedIterable.class)
+   @Fallback(Fallbacks.EmptyPagedIterableOnNotFoundOr404.class)
+   PagedIterable<Instance> list(@QueryParam("RegionId") String region);
+
+   @Named("instanceType:list")
+   @GET
+   @QueryParams(keys = "Action", values = "DescribeInstanceTypes")
+   @SelectJson("InstanceType")
+   @Fallback(Fallbacks.EmptyListOnNotFoundOr404.class)
+   List<InstanceType> listTypes();
+
+   @Named("instanceType:list")
+   @GET
+   @QueryParams(keys = { "Action", "DestinationResource", "IoOptimized" },
+                values = { "DescribeAvailableResource", "InstanceType", 
"optimized" })
+   @SelectJson("AvailableZone")
+   @Fallback(Fallbacks.EmptyListOnNotFoundOr404.class)
+   List<AvailableZone> 
listInstanceTypesByAvailableZone(@QueryParam("RegionId") String regionId);
+
+   @Named("instance:create")
+   @POST
+   @Produces(MediaType.APPLICATION_JSON)
+   @QueryParams(keys = "Action", values = "CreateInstance")
+   InstanceRequest create(@QueryParam("RegionId") String regionId,
+                          @QueryParam("ImageId") String imageId,
+                          @QueryParam("SecurityGroupId") String 
securityGroupId,
+                          @QueryParam("HostName") String hostname,
+                          @QueryParam("InstanceType") String instanceType);
+
+   @Named("instance:create")
+   @POST
+   @Produces(MediaType.APPLICATION_JSON)
+   @QueryParams(keys = "Action", values = "CreateInstance")
+   InstanceRequest create(@QueryParam("RegionId") String regionId,
+                          @QueryParam("ImageId") String imageId,
+                          @QueryParam("SecurityGroupId") String 
securityGroupId,
+                          @QueryParam("HostName") String hostname,
+                          @QueryParam("InstanceType") String instanceType,
+                          CreateInstanceOptions options);
+
+   @Named("instance:allocatePublicIpAddress")
+   @POST
+   @Produces(MediaType.APPLICATION_JSON)
+   @QueryParams(keys = "Action", values = "AllocatePublicIpAddress")
+   AllocatePublicIpAddressRequest 
allocatePublicIpAddress(@QueryParam("RegionId") String regionId,
+                                                          
@QueryParam("InstanceId") String instanceId);
+
+   @Named("instance:listInstanceStatus")
+   @GET
+   @QueryParams(keys = "Action", values = "DescribeInstanceStatus")
+   @ResponseParser(ParseInstanceStatus.class)
+   @Fallback(Fallbacks.EmptyIterableWithMarkerOnNotFoundOr404.class)
+   IterableWithMarker<InstanceStatus> 
listInstanceStatus(@QueryParam("RegionId") String region, 
ListInstanceStatusOptions options);
+
+   @Named("instance:listInstanceStatus")
+   @GET
+   @QueryParams(keys = "Action", values = "DescribeInstanceStatus")
+   @ResponseParser(ParseInstanceStatus.class)
+   @Transform(ParseInstanceStatus.ToPagedIterable.class)
+   @Fallback(Fallbacks.EmptyPagedIterableOnNotFoundOr404.class)
+   PagedIterable<InstanceStatus> listInstanceStatus(@QueryParam("RegionId") 
String region);
+
+   /**
+    * You can only release an instance that is in the Stopped (Stopped) status.
+    *
+    * @param instanceId
+    */
+   @Named("instance:delete")
+   @POST
+   @Produces(MediaType.APPLICATION_JSON)
+   @QueryParams(keys = "Action", values = "DeleteInstance")
+   @Fallback(Fallbacks.NullOnNotFoundOr404.class)
+   Request delete(@QueryParam("InstanceId") String instanceId);
+
+   @Named("instance:powerOff")
+   @POST
+   @Produces(MediaType.APPLICATION_JSON)
+   @QueryParams(keys = "Action", values = "StopInstance")
+   Request powerOff(@QueryParam("InstanceId") String instanceId);
+
+   @Named("instance:powerOn")
+   @POST
+   @Produces(MediaType.APPLICATION_JSON)
+   @QueryParams(keys = "Action", values = "StartInstance")
+   Request powerOn(@QueryParam("InstanceId") String instanceId);
+
+   @Named("instance:reboot")
+   @POST
+   @Produces(MediaType.APPLICATION_JSON)
+   @QueryParams(keys = "Action", values = "RebootInstance")
+   Request reboot(@QueryParam("InstanceId") String instanceId);
+
+   @Singleton
+   final class ParseInstances extends ParseJson<ParseInstances.Instances> {
+
+      @Inject
+      ParseInstances(final Json json) {
+         super(json, TypeLiteral.get(Instances.class));
+      }
+
+      private static class Instances extends PaginatedCollection<Instance> {
+
+         @ConstructorProperties({ "Instances", "PageNumber", "TotalCount", 
"PageSize", "RegionId", "RequestId" })
+         public Instances(Map<String, Iterable<Instance>> content, Integer 
pageNumber, Integer totalCount, Integer pageSize, String regionId, String 
requestId) {
+            super(content, pageNumber, totalCount, pageSize, regionId, 
requestId);
+         }
+      }
+
+      private static class ToPagedIterable extends 
ArgsToPagedIterable<Instance, ToPagedIterable> {
+
+         private ECSComputeServiceApi api;
+
+         @Inject
+         ToPagedIterable(final ECSComputeServiceApi api) {
+            this.api = api;
+         }
+
+         @Override
+         protected Function<Object, IterableWithMarker<Instance>> 
markerToNextForArgs(List<Object> args) {
+            if (args == null || args.isEmpty()) throw new 
IllegalStateException("Can't advance the PagedIterable");
+            final String regionId = args.get(0).toString();
+            final ListInstancesOptions original = (ListInstancesOptions) 
Iterables.tryFind(args, 
Predicates.instanceOf(ListInstancesOptions.class)).orNull();
+
+            return new Function<Object, IterableWithMarker<Instance>>() {
+               @Override
+               public IterableWithMarker<Instance> apply(Object input) {
+                  ListInstancesOptions options = original == null ?
+                          
ListInstancesOptions.Builder.paginationOptions(PaginationOptions.class.cast(input))
 :
+                          
original.paginationOptions(PaginationOptions.class.cast(input));
+                  return api.instanceApi().list(regionId, options);
+               }
+            };
+         }
+      }
+   }
+
+   @Singleton
+   final class ParseInstanceStatus extends 
ParseJson<ParseInstanceStatus.InstanceStatuses> {
+
+      @Inject
+      ParseInstanceStatus(final Json json) {
+         super(json, TypeLiteral.get(InstanceStatuses.class));
+      }
+
+      private static class InstanceStatuses extends 
PaginatedCollection<InstanceStatus> {
+
+         @ConstructorProperties({ "InstanceStatuses", "PageNumber", 
"TotalCount", "PageSize", "RegionId", "RequestId" })
+         public InstanceStatuses(Map<String, Iterable<InstanceStatus>> 
content, Integer pageNumber, Integer totalCount, Integer pageSize, String 
regionId, String requestId) {
+            super(content, pageNumber, totalCount, pageSize, regionId, 
requestId);
+         }
+      }
+
+      private static class ToPagedIterable extends 
ArgsToPagedIterable<InstanceStatus, ToPagedIterable> {
+
+         private ECSComputeServiceApi api;
+
+         @Inject
+         ToPagedIterable(final ECSComputeServiceApi api) {
+            this.api = api;
+         }
+
+         @Override
+         protected Function<Object, IterableWithMarker<InstanceStatus>> 
markerToNextForArgs(List<Object> args) {
+            if (args == null || args.isEmpty()) throw new 
IllegalStateException("Can't advance the PagedIterable");
+            final String regionId = args.get(0).toString();
+            final ListInstanceStatusOptions original = 
(ListInstanceStatusOptions) Iterables.tryFind(args, 
Predicates.instanceOf(ListInstanceStatusOptions.class)).orNull();
+
+            return new Function<Object, IterableWithMarker<InstanceStatus>>() {
+               @Override
+               public IterableWithMarker<InstanceStatus> apply(Object input) {
+                  ListInstanceStatusOptions options = original == null ?
+                          
ListInstanceStatusOptions.Builder.paginationOptions(PaginationOptions.class.cast(input))
 :
+                          
original.paginationOptions(PaginationOptions.class.cast(input));
+                  return api.instanceApi().listInstanceStatus(regionId, 
options);
+               }
+            };
+         }
+      }
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/features/SecurityGroupApi.java
----------------------------------------------------------------------
diff --git 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/features/SecurityGroupApi.java
 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/features/SecurityGroupApi.java
index c70bcb6..9b36bf8 100644
--- 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/features/SecurityGroupApi.java
+++ 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/features/SecurityGroupApi.java
@@ -57,6 +57,9 @@ import java.beans.ConstructorProperties;
 import java.util.List;
 import java.util.Map;
 
+/**
+ * 
https://www.alibabacloud.com/help/doc-detail/25553.htm?spm=a2c63.p38356.b99.323.1a3b59abPkInRB
+ */
 @Consumes(MediaType.APPLICATION_JSON)
 @RequestFilters(FormSign.class)
 @QueryParams(keys = {"Version", "Format", "SignatureVersion", "ServiceCode", 
"SignatureMethod"},

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/features/SshKeyPairApi.java
----------------------------------------------------------------------
diff --git 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/features/SshKeyPairApi.java 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/features/SshKeyPairApi.java
index b207335..41e9937 100644
--- 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/features/SshKeyPairApi.java
+++ 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/features/SshKeyPairApi.java
@@ -55,6 +55,9 @@ import java.beans.ConstructorProperties;
 import java.util.List;
 import java.util.Map;
 
+/**
+ * 
https://www.alibabacloud.com/help/doc-detail/51771.htm?spm=a2c63.p38356.b99.338.1da53569DqQAVv
+ */
 @Consumes(MediaType.APPLICATION_JSON)
 @RequestFilters(FormSign.class)
 @QueryParams(keys = { "Version", "Format", "SignatureVersion", "ServiceCode", 
"SignatureMethod" },
@@ -135,6 +138,7 @@ public interface SshKeyPairApi {
    @Named("sshKeyPair:delete")
    @POST
    @QueryParams(keys = "Action", values = "DeleteKeyPairs")
+   @Fallback(Fallbacks.NullOnNotFoundOr404.class)
    Request delete(@QueryParam("RegionId") String region,
                   @ParamParser(ArrayToCommaSeparatedString.class) 
@QueryParam("KeyPairNames") String... keyPairNames);
 

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/features/TagApi.java
----------------------------------------------------------------------
diff --git 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/features/TagApi.java 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/features/TagApi.java
index 67c3b82..e75c086 100644
--- a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/features/TagApi.java
+++ b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/features/TagApi.java
@@ -24,6 +24,7 @@ import org.jclouds.Constants;
 import org.jclouds.Fallbacks;
 import org.jclouds.aliyun.ecs.ECSComputeServiceApi;
 import org.jclouds.aliyun.ecs.domain.Request;
+import org.jclouds.aliyun.ecs.domain.ResourceType;
 import org.jclouds.aliyun.ecs.domain.Tag;
 import org.jclouds.aliyun.ecs.domain.internal.PaginatedCollection;
 import org.jclouds.aliyun.ecs.domain.options.ListTagsOptions;
@@ -53,6 +54,9 @@ import java.beans.ConstructorProperties;
 import java.util.List;
 import java.util.Map;
 
+/**
+ * 
https://www.alibabacloud.com/help/doc-detail/25616.htm?spm=a2c63.p38356.b99.382.580b30373FFIDb
+ */
 @Consumes(MediaType.APPLICATION_JSON)
 @RequestFilters(FormSign.class)
 @QueryParams(keys = { "Version", "Format", "SignatureVersion", "ServiceCode", 
"SignatureMethod" },
@@ -122,7 +126,7 @@ public interface TagApi {
    @POST
    @QueryParams(keys = "Action", values = "AddTags")
    Request add(@QueryParam("RegionId") String region, 
@QueryParam("ResourceId") String resourceId,
-                            @QueryParam("ResourceType") String resourceType,
+                            @QueryParam("ResourceType") ResourceType 
resourceType,
                             TagOptions tagOptions);
 
    @Named("tag:remove")
@@ -130,14 +134,14 @@ public interface TagApi {
    @QueryParams(keys = "Action", values = "RemoveTags")
    Request remove(@QueryParam("RegionId") String region,
                   @QueryParam("ResourceId") String resourceId,
-                  @QueryParam("ResourceType") String resourceType);
+                  @QueryParam("ResourceType") ResourceType resourceType);
 
    @Named("tag:remove")
    @POST
    @QueryParams(keys = "Action", values = "RemoveTags")
    Request remove(@QueryParam("RegionId") String region,
                   @QueryParam("ResourceId") String resourceId,
-                  @QueryParam("ResourceType") String resourceType,
+                  @QueryParam("ResourceType") ResourceType resourceType,
                   TagOptions options);
 }
 

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/features/VPCApi.java
----------------------------------------------------------------------
diff --git 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/features/VPCApi.java 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/features/VPCApi.java
new file mode 100644
index 0000000..f2e9e5f
--- /dev/null
+++ b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/features/VPCApi.java
@@ -0,0 +1,140 @@
+/*
+ * 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 com.google.common.base.Function;
+import com.google.common.base.Predicates;
+import com.google.common.collect.Iterables;
+import com.google.inject.TypeLiteral;
+import org.jclouds.Constants;
+import org.jclouds.Fallbacks;
+import org.jclouds.aliyun.ecs.ECSComputeServiceApi;
+import org.jclouds.aliyun.ecs.domain.Request;
+import org.jclouds.aliyun.ecs.domain.VPC;
+import org.jclouds.aliyun.ecs.domain.VPCRequest;
+import org.jclouds.aliyun.ecs.domain.internal.PaginatedCollection;
+import org.jclouds.aliyun.ecs.domain.options.CreateVPCOptions;
+import org.jclouds.aliyun.ecs.domain.options.ListVPCsOptions;
+import org.jclouds.aliyun.ecs.domain.options.PaginationOptions;
+import org.jclouds.aliyun.ecs.filters.FormSign;
+import org.jclouds.collect.IterableWithMarker;
+import org.jclouds.collect.PagedIterable;
+import org.jclouds.collect.internal.ArgsToPagedIterable;
+import org.jclouds.http.functions.ParseJson;
+import org.jclouds.json.Json;
+import org.jclouds.rest.annotations.Fallback;
+import org.jclouds.rest.annotations.QueryParams;
+import org.jclouds.rest.annotations.RequestFilters;
+import org.jclouds.rest.annotations.ResponseParser;
+import org.jclouds.rest.annotations.Transform;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import java.beans.ConstructorProperties;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 
https://www.alibabacloud.com/help/doc-detail/35737.htm?spm=a2c63.p38356.b99.44.2554c880ZhTTkh
+ */
+@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 VPCApi {
+
+   @Named("vpc:list")
+   @GET
+   @QueryParams(keys = "Action", values = "DescribeVpcs")
+   @ResponseParser(ParseVPCs.class)
+   @Fallback(Fallbacks.EmptyIterableWithMarkerOnNotFoundOr404.class)
+   IterableWithMarker<VPC> list(@QueryParam("RegionId") String region, 
ListVPCsOptions options);
+
+   @Named("vpc:list")
+   @GET
+   @QueryParams(keys = "Action", values = "DescribeVpcs")
+   @ResponseParser(ParseVPCs.class)
+   @Transform(ParseVPCs.ToPagedIterable.class)
+   @Fallback(Fallbacks.EmptyPagedIterableOnNotFoundOr404.class)
+   PagedIterable<VPC> list(@QueryParam("RegionId") String region);
+
+   @Singleton
+   final class ParseVPCs extends ParseJson<ParseVPCs.VPCs> {
+
+      private static class VPCs extends PaginatedCollection<VPC> {
+
+         @ConstructorProperties({ "Vpcs", "PageNumber", "TotalCount", 
"PageSize", "RegionId", "RequestId" })
+         public VPCs(Map<String, Iterable<VPC>> content, Integer pageNumber, 
Integer totalCount, Integer pageSize, String regionId, String requestId) {
+            super(content, pageNumber, totalCount, pageSize, regionId, 
requestId);
+         }
+      }
+      @Inject
+      ParseVPCs(final Json json) {
+         super(json, TypeLiteral.get(VPCs.class));
+      }
+
+      static class ToPagedIterable extends ArgsToPagedIterable<VPC, 
ToPagedIterable> {
+
+         private final ECSComputeServiceApi api;
+
+         @Inject
+         ToPagedIterable(ECSComputeServiceApi api) {
+            this.api = api;
+         }
+
+         @Override
+         protected Function<Object, IterableWithMarker<VPC>> 
markerToNextForArgs(List<Object> args) {
+            if (args == null || args.isEmpty()) throw new 
IllegalStateException("Can't advance the PagedIterable");
+            final String regionId = args.get(0).toString();
+            final ListVPCsOptions original = (ListVPCsOptions) 
Iterables.tryFind(args, Predicates.instanceOf(ListVPCsOptions.class)).orNull();
+
+            return new Function<Object, IterableWithMarker<VPC>>() {
+               @Override
+               public IterableWithMarker<VPC> apply(Object input) {
+                  ListVPCsOptions options = original == null ?
+                          
ListVPCsOptions.Builder.paginationOptions(PaginationOptions.class.cast(input)) :
+                          
original.paginationOptions(PaginationOptions.class.cast(input));
+                  return api.vpcApi().list(regionId, options);
+               }
+            };
+         }
+      }
+   }
+
+   @Named("vpc:create")
+   @POST
+   @QueryParams(keys = "Action", values = "CreateVpc")
+   VPCRequest create(@QueryParam("RegionId") String region);
+
+   @Named("vpc:create")
+   @POST
+   @QueryParams(keys = "Action", values = "CreateVpc")
+   VPCRequest create(@QueryParam("RegionId") String region, CreateVPCOptions 
vpcOptions);
+
+   @Named("vpc:delete")
+   @POST
+   @QueryParams(keys = "Action", values = "DeleteVpc")
+   Request delete(@QueryParam("RegionId") String region,
+                  @QueryParam("VpcId") String vpcId);
+}
+

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/features/VSwitchApi.java
----------------------------------------------------------------------
diff --git 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/features/VSwitchApi.java 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/features/VSwitchApi.java
new file mode 100644
index 0000000..5508ea1
--- /dev/null
+++ b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/features/VSwitchApi.java
@@ -0,0 +1,145 @@
+/*
+ * 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 com.google.common.base.Function;
+import com.google.common.base.Predicates;
+import com.google.common.collect.Iterables;
+import com.google.inject.TypeLiteral;
+import org.jclouds.Constants;
+import org.jclouds.Fallbacks;
+import org.jclouds.aliyun.ecs.ECSComputeServiceApi;
+import org.jclouds.aliyun.ecs.domain.Request;
+import org.jclouds.aliyun.ecs.domain.VSwitch;
+import org.jclouds.aliyun.ecs.domain.VSwitchRequest;
+import org.jclouds.aliyun.ecs.domain.internal.PaginatedCollection;
+import org.jclouds.aliyun.ecs.domain.options.CreateVSwitchOptions;
+import org.jclouds.aliyun.ecs.domain.options.ListVSwitchesOptions;
+import org.jclouds.aliyun.ecs.domain.options.PaginationOptions;
+import org.jclouds.aliyun.ecs.filters.FormSign;
+import org.jclouds.collect.IterableWithMarker;
+import org.jclouds.collect.PagedIterable;
+import org.jclouds.collect.internal.ArgsToPagedIterable;
+import org.jclouds.http.functions.ParseJson;
+import org.jclouds.json.Json;
+import org.jclouds.rest.annotations.Fallback;
+import org.jclouds.rest.annotations.QueryParams;
+import org.jclouds.rest.annotations.RequestFilters;
+import org.jclouds.rest.annotations.ResponseParser;
+import org.jclouds.rest.annotations.Transform;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import java.beans.ConstructorProperties;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * https://www.alibabacloud.com/help/doc-detail/35745.htm
+ */
+@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 VSwitchApi {
+
+   @Named("vswitch:list")
+   @GET
+   @QueryParams(keys = "Action", values = "DescribeVSwitches")
+   @ResponseParser(ParseVSwitches.class)
+   @Fallback(Fallbacks.EmptyIterableWithMarkerOnNotFoundOr404.class)
+   IterableWithMarker<VSwitch> list(@QueryParam("RegionId") String region, 
ListVSwitchesOptions options);
+
+   @Named("vswitch:list")
+   @GET
+   @QueryParams(keys = "Action", values = "DescribeVSwitches")
+   @ResponseParser(ParseVSwitches.class)
+   @Transform(ParseVSwitches.ToPagedIterable.class)
+   @Fallback(Fallbacks.EmptyPagedIterableOnNotFoundOr404.class)
+   PagedIterable<VSwitch> list(@QueryParam("RegionId") String region);
+
+   @Singleton
+   final class ParseVSwitches extends ParseJson<ParseVSwitches.VSwitches> {
+
+      private static class VSwitches extends PaginatedCollection<VSwitch> {
+
+         @ConstructorProperties({ "VSwitches", "PageNumber", "TotalCount", 
"PageSize", "RegionId", "RequestId" })
+         public VSwitches(Map<String, Iterable<VSwitch>> content, Integer 
pageNumber, Integer totalCount, Integer pageSize, String regionId, String 
requestId) {
+            super(content, pageNumber, totalCount, pageSize, regionId, 
requestId);
+         }
+      }
+      @Inject
+      ParseVSwitches(final Json json) {
+         super(json, TypeLiteral.get(VSwitches.class));
+      }
+
+      static class ToPagedIterable extends ArgsToPagedIterable<VSwitch, 
ParseVSwitches.ToPagedIterable> {
+
+         private final ECSComputeServiceApi api;
+
+         @Inject
+         ToPagedIterable(ECSComputeServiceApi api) {
+            this.api = api;
+         }
+
+         @Override
+         protected Function<Object, IterableWithMarker<VSwitch>> 
markerToNextForArgs(List<Object> args) {
+            if (args == null || args.isEmpty()) throw new 
IllegalStateException("Can't advance the PagedIterable");
+            final String regionId = args.get(0).toString();
+            final ListVSwitchesOptions original = (ListVSwitchesOptions) 
Iterables.tryFind(args, 
Predicates.instanceOf(ListVSwitchesOptions.class)).orNull();
+
+            return new Function<Object, IterableWithMarker<VSwitch>>() {
+               @Override
+               public IterableWithMarker<VSwitch> apply(Object input) {
+                  ListVSwitchesOptions options = original == null ?
+                          
ListVSwitchesOptions.Builder.paginationOptions(PaginationOptions.class.cast(input))
 :
+                          
original.paginationOptions(PaginationOptions.class.cast(input));
+                  return api.vSwitchApi().list(regionId, options);
+               }
+            };
+         }
+      }
+   }
+
+   @Named("vswitch:create")
+   @POST
+   @QueryParams(keys = "Action", values = "CreateVSwitch")
+   VSwitchRequest create(@QueryParam("ZoneId") String zone,
+                         @QueryParam("CidrBlock") String cidrBlock,
+                         @QueryParam("VpcId") String vpcId);
+
+   @Named("vswitch:create")
+   @POST
+   @QueryParams(keys = "Action", values = "CreateVSwitch")
+   VSwitchRequest create(@QueryParam("ZoneId") String zone,
+                         @QueryParam("CidrBlock") String cidrBlock,
+                         @QueryParam("VpcId") String vpcId,
+                         CreateVSwitchOptions vSwitchOptions);
+
+   @Named("vswitch:delete")
+   @POST
+   @QueryParams(keys = "Action", values = "DeleteVSwitch")
+   Request delete(@QueryParam("RegionId") String region,
+                  @QueryParam("VSwitchId") String vSwitchId);
+}
+

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/functions/PutStringInDoubleQuotes.java
----------------------------------------------------------------------
diff --git 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/functions/PutStringInDoubleQuotes.java
 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/functions/PutStringInDoubleQuotes.java
new file mode 100644
index 0000000..98efc8d
--- /dev/null
+++ 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/functions/PutStringInDoubleQuotes.java
@@ -0,0 +1,27 @@
+/*
+ * 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.functions;
+
+import com.google.common.base.Function;
+
+public class PutStringInDoubleQuotes implements Function<String, String> {
+
+   @Override
+   public String apply(String input) {
+      return new StringBuilder(input.length() + 
1).append('"').append(input).append('"').toString();
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/handlers/ECSErrorRetryHandler.java
----------------------------------------------------------------------
diff --git 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/handlers/ECSErrorRetryHandler.java
 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/handlers/ECSErrorRetryHandler.java
new file mode 100644
index 0000000..0a32145
--- /dev/null
+++ 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/handlers/ECSErrorRetryHandler.java
@@ -0,0 +1,74 @@
+/*
+ * 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.handlers;
+
+import com.google.inject.Inject;
+import org.jclouds.aliyun.ecs.domain.ErrorMessage;
+import org.jclouds.http.HttpCommand;
+import org.jclouds.http.HttpResponse;
+import org.jclouds.http.HttpRetryHandler;
+import org.jclouds.http.HttpUtils;
+import org.jclouds.http.annotation.ClientError;
+import org.jclouds.http.handlers.BackoffLimitedRetryHandler;
+import org.jclouds.json.Json;
+
+import java.io.ByteArrayInputStream;
+import java.util.Set;
+
+import static org.jclouds.http.HttpUtils.closeClientButKeepContentStream;
+
+/**
+ * Handles Retryable responses with error codes in the 4xx range
+ */
+public class ECSErrorRetryHandler implements HttpRetryHandler {
+
+   private final Json json;
+   private final BackoffLimitedRetryHandler backoffLimitedRetryHandler;
+   private final Set<String> retryableCodes;
+
+   @Inject
+   public ECSErrorRetryHandler(Json json, BackoffLimitedRetryHandler 
backoffLimitedRetryHandler,
+         @ClientError Set<String> retryableCodes) {
+      this.json = json;
+      this.backoffLimitedRetryHandler = backoffLimitedRetryHandler;
+      this.retryableCodes = retryableCodes;
+   }
+
+   @Override
+   public boolean shouldRetryRequest(HttpCommand command, HttpResponse 
response) {
+      byte[] content = HttpUtils.closeClientButKeepContentStream(response);
+
+      if (response.getStatusCode() == 400 || response.getStatusCode() == 403 
|| response.getStatusCode() == 409) {
+         // Content can be null in the case of HEAD requests
+         if (response.getPayload() != null) {
+            closeClientButKeepContentStream(response);
+            ErrorMessage error = json.fromJson(new 
ByteArrayInputStream(content), ErrorMessage.class);
+            if (error != null) {
+               return shouldRetryRequestOnError(command, response, error);
+            }
+         }
+      }
+      return false;
+   }
+
+   protected boolean shouldRetryRequestOnError(HttpCommand command, 
HttpResponse response, ErrorMessage error) {
+      if (retryableCodes.contains(error.code()))
+         return backoffLimitedRetryHandler.shouldRetryRequest(command, 
response);
+      return false;
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/predicates/InstanceStatusPredicate.java
----------------------------------------------------------------------
diff --git 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/predicates/InstanceStatusPredicate.java
 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/predicates/InstanceStatusPredicate.java
new file mode 100644
index 0000000..9a42261
--- /dev/null
+++ 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/predicates/InstanceStatusPredicate.java
@@ -0,0 +1,33 @@
+/*
+ * 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.predicates;
+
+import com.google.common.base.Predicate;
+import org.jclouds.aliyun.ecs.domain.InstanceStatus;
+
+public class InstanceStatusPredicate implements Predicate<InstanceStatus> {
+   private final String instanceId;
+
+   public InstanceStatusPredicate(String instanceId) {
+      this.instanceId = instanceId;
+   }
+
+   @Override
+   public boolean apply(InstanceStatus instanceStatus) {
+      return instanceStatus.instanceId().equalsIgnoreCase(instanceId);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/test/java/org/jclouds/aliyun/ecs/compute/ECSComputeServiceLiveTest.java
----------------------------------------------------------------------
diff --git 
a/aliyun-ecs/src/test/java/org/jclouds/aliyun/ecs/compute/ECSComputeServiceLiveTest.java
 
b/aliyun-ecs/src/test/java/org/jclouds/aliyun/ecs/compute/ECSComputeServiceLiveTest.java
new file mode 100644
index 0000000..74e635c
--- /dev/null
+++ 
b/aliyun-ecs/src/test/java/org/jclouds/aliyun/ecs/compute/ECSComputeServiceLiveTest.java
@@ -0,0 +1,46 @@
+/*
+ * 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;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.inject.Module;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.internal.BaseComputeServiceLiveTest;
+import org.jclouds.sshj.config.SshjSshClientModule;
+import org.testng.annotations.Test;
+
+/**
+ * Live tests for the {@link org.jclouds.compute.ComputeService} integration.
+ */
+@Test(groups = "live", singleThreaded = true, testName = 
"ECSComputeServiceLiveTest")
+public class ECSComputeServiceLiveTest extends BaseComputeServiceLiveTest {
+
+   public ECSComputeServiceLiveTest() {
+      provider = "alibaba-ecs";
+   }
+
+   @Override
+   protected Module getSshModule() {
+      return new SshjSshClientModule();
+   }
+
+   @Override
+   protected void checkUserMetadataContains(NodeMetadata node, 
ImmutableMap<String, String> userMetadata) {
+      // The ECS API does not return the user data
+   }
+
+}

Reply via email to