http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/strategy/CreateResourcesThenCreateNodes.java
----------------------------------------------------------------------
diff --git 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/strategy/CreateResourcesThenCreateNodes.java
 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/strategy/CreateResourcesThenCreateNodes.java
new file mode 100644
index 0000000..8d302a2
--- /dev/null
+++ 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/strategy/CreateResourcesThenCreateNodes.java
@@ -0,0 +1,360 @@
+/*
+ * 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.strategy;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.base.Splitter;
+import com.google.common.base.Strings;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimap;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import org.jclouds.Constants;
+import org.jclouds.aliyun.ecs.ECSComputeServiceApi;
+import org.jclouds.aliyun.ecs.compute.options.ECSServiceTemplateOptions;
+import org.jclouds.aliyun.ecs.domain.IpProtocol;
+import org.jclouds.aliyun.ecs.domain.KeyPair;
+import org.jclouds.aliyun.ecs.domain.KeyPairRequest;
+import org.jclouds.aliyun.ecs.domain.SecurityGroup;
+import org.jclouds.aliyun.ecs.domain.SecurityGroupRequest;
+import org.jclouds.aliyun.ecs.domain.Tag;
+import org.jclouds.aliyun.ecs.domain.VPCRequest;
+import org.jclouds.aliyun.ecs.domain.VSwitch;
+import org.jclouds.aliyun.ecs.domain.VSwitchRequest;
+import org.jclouds.aliyun.ecs.domain.Zone;
+import org.jclouds.aliyun.ecs.domain.options.CreateSecurityGroupOptions;
+import org.jclouds.aliyun.ecs.domain.options.CreateVPCOptions;
+import org.jclouds.aliyun.ecs.domain.options.CreateVSwitchOptions;
+import org.jclouds.aliyun.ecs.domain.options.ListVSwitchesOptions;
+import org.jclouds.aliyun.ecs.domain.options.TagOptions;
+import org.jclouds.compute.config.CustomizationResponse;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.Template;
+import org.jclouds.compute.functions.GroupNamingConvention;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.compute.strategy.CreateNodeWithGroupEncodedIntoName;
+import 
org.jclouds.compute.strategy.CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap;
+import org.jclouds.compute.strategy.ListNodesStrategy;
+import 
org.jclouds.compute.strategy.impl.CreateNodesWithGroupEncodedIntoNameThenAddToSet;
+import org.jclouds.logging.Logger;
+import org.jclouds.ssh.SshKeys;
+
+import javax.annotation.Nullable;
+import javax.annotation.Resource;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.RSAPublicKeySpec;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.Iterables.get;
+import static com.google.common.collect.Iterables.size;
+import static org.jclouds.aliyun.ecs.domain.ResourceType.SECURITYGROUP;
+import static 
org.jclouds.compute.util.ComputeServiceUtils.getPortRangesFromList;
+
+@Singleton
+public class CreateResourcesThenCreateNodes extends 
CreateNodesWithGroupEncodedIntoNameThenAddToSet {
+
+   public static final String INTERNET = "0.0.0.0/0";
+   public static final String DEFAULT_CIDR_BLOCK = "172.16.1.0/24";
+   public static final String JCLOUDS_KEYPAIR_IMPORTED = "jclouds-imported";
+   public static final String PORT_RANGE_FORMAT = "%d/%d";
+   protected static final String DEFAULT_DESCRIPTION_SUFFIX = "created by 
jclouds";
+   protected static final String VSWITCH_PREFIX = "vswitch";
+   protected static final String VPC_PREFIX = "vpc";
+
+   private final ECSComputeServiceApi api;
+
+   @Resource
+   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+   protected Logger logger = Logger.NULL;
+
+   @Inject
+   protected CreateResourcesThenCreateNodes(CreateNodeWithGroupEncodedIntoName 
addNodeWithGroupStrategy,
+                                          ListNodesStrategy listNodesStrategy, 
GroupNamingConvention.Factory namingConvention,
+                                          
@Named(Constants.PROPERTY_USER_THREADS) ListeningExecutorService userExecutor,
+                                          
CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap.Factory 
customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory,
+                                          ECSComputeServiceApi api) {
+      super(addNodeWithGroupStrategy, listNodesStrategy, namingConvention, 
userExecutor,
+            customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory);
+      this.api = api;
+   }
+
+   @Override
+   public Map<?, ListenableFuture<Void>> execute(String group, int count, 
Template template,
+                                                 Set<NodeMetadata> goodNodes, 
Map<NodeMetadata, Exception> badNodes,
+                                                 Multimap<NodeMetadata, 
CustomizationResponse> customizationResponses) {
+
+      String regionId = template.getLocation().getId();
+      ECSServiceTemplateOptions options = 
template.getOptions().as(ECSServiceTemplateOptions.class);
+
+      Optional<SecurityGroup> securityGroupOptional = 
tryFindSecurityGroupInRegion(regionId, options.getGroups());
+
+      String vpcIdFromSecurityGroup;
+      String vpcId;
+
+      if (securityGroupOptional.isPresent()) {
+         vpcIdFromSecurityGroup = securityGroupOptional.get().vpcId();
+         if (!Strings.isNullOrEmpty(options.getVSwitchId())) {
+            validateVSwitchId(regionId, options.getVSwitchId(), 
securityGroupOptional.get().name(), vpcIdFromSecurityGroup);
+         } else {
+            String message = String.format("Security group (%s) belongs to VPC 
(%s). Please specify a vSwitch Id of that VPC (%s) using 
ECSServiceTemplateOptions.vSwitchId",
+            securityGroupOptional.get().name(),
+                    vpcIdFromSecurityGroup,
+                    vpcIdFromSecurityGroup);
+            throw new IllegalStateException(message);
+         }
+      } else {
+         if (!Strings.isNullOrEmpty(options.getVSwitchId())) {
+            VSwitch vSwitch = tryFindVSwitch(regionId, options.getVSwitchId());
+            vpcId = vSwitch.vpcId();
+         } else {
+            vpcId = createDefaultVPC(regionId, group);
+            String vSwitchId = createDefaultVSwitch(regionId, vpcId, group);
+            options.vSwitchId(vSwitchId);
+         }
+         String createdSecurityGroupId = createSecurityGroupForOptions(group, 
regionId, vpcId, options);
+         options.securityGroups(createdSecurityGroupId);
+      }
+
+
+      // If keys haven't been configured, generate a key pair
+      if (Strings.isNullOrEmpty(options.getPublicKey()) &&
+          Strings.isNullOrEmpty(options.getLoginPrivateKey())) {
+         String uniqueNameForGroup = 
namingConvention.create().uniqueNameForGroup(group);
+         KeyPairRequest keyPairRequest = generateKeyPair(regionId, 
uniqueNameForGroup);
+         options.keyPairName(keyPairRequest.getKeyPairName());
+         options.overrideLoginPrivateKey(keyPairRequest.getPrivateKeyBody());
+      }
+
+      // If there is a script to run in the node, make sure a private key has
+      // been configured so jclouds will be able to access the node
+      if (options.getRunScript() != null && 
Strings.isNullOrEmpty(options.getLoginPrivateKey())) {
+         logger.warn(">> A runScript has been configured but no SSH key has 
been provided. Authentication will delegate to the ssh-agent");
+      }
+
+      // If there is a public key configured, then make sure there is a key 
pair for it
+      if (!Strings.isNullOrEmpty(options.getPublicKey())) {
+         KeyPair keyPair = getOrImportKeyPairForPublicKey(options, regionId);
+         options.keyPairName(keyPair.name());
+      }
+
+      Map<?, ListenableFuture<Void>> responses = super.execute(group, count, 
template, goodNodes, badNodes, customizationResponses);
+
+      // Key pairs are only required to create the devices.
+      // Better to delete the auto-generated key pairs when they are mo more 
required
+      registerAutoGeneratedKeyPairCleanupCallbacks(responses, regionId, 
options.getKeyPairName());
+
+      return responses;
+   }
+
+   private void validateVSwitchId(String regionId,
+                                  String vSwitchId,
+                                  String securityGroupName,
+                                  String vpcIdFromSecurityGroup) {
+      Optional<VSwitch> optionalVSwitch = tryFindVSwitchInVPC(regionId, 
vpcIdFromSecurityGroup, vSwitchId);
+      if (!optionalVSwitch.isPresent()) {
+         String message = String.format("security group (%s) and vSwitch (%s) 
must be in the same VPC_PREFIX (%s)",
+                 securityGroupName,
+                 optionalVSwitch.get().name(),
+                 vpcIdFromSecurityGroup);
+
+         throw new IllegalStateException(message);
+      }
+   }
+
+   private String createDefaultVPC(String regionId, String group) {
+      String vpcName = String.format("%s-%s", VPC_PREFIX, group);
+      VPCRequest vpcRequest = api.vpcApi().create(regionId, 
CreateVPCOptions.Builder.vpcName(vpcName).description(String.format("%s - %s", 
VPC_PREFIX, DEFAULT_DESCRIPTION_SUFFIX)));
+      return vpcRequest.getVpcId();
+   }
+
+   private String createDefaultVSwitch(String regionId, String vpcId, String 
name) {
+      String vSwitchName = String.format("%s-%s", VSWITCH_PREFIX, name);
+      Zone zone = 
Iterables.getFirst(api.regionAndZoneApi().describeZones(regionId), null);
+      VSwitchRequest vSwitchRequest = api.vSwitchApi().create(zone.id(), 
DEFAULT_CIDR_BLOCK, vpcId,
+              
CreateVSwitchOptions.Builder.vSwitchName(vSwitchName).description(String.format("%s
 - %s", vSwitchName, DEFAULT_DESCRIPTION_SUFFIX)));
+      return vSwitchRequest.getVSwitchId();
+   }
+
+   private KeyPair getOrImportKeyPairForPublicKey(ECSServiceTemplateOptions 
options, String regionId) {
+      logger.debug(">> checking if the key pair already exists...");
+      PublicKey userKey = readPublicKey(options.getPublicKey());
+      final String fingerprint = computeFingerprint(userKey);
+      KeyPair keyPair;
+
+      synchronized (CreateResourcesThenCreateNodes.class) {
+         Optional<KeyPair> keyPairOptional = Iterables
+               .tryFind(api.sshKeyPairApi().list(regionId).concat(), new 
Predicate<KeyPair>() {
+                  @Override
+                  public boolean apply(KeyPair input) {
+                     return 
input.keyPairFingerPrint().equals(fingerprint.replace(":", ""));
+                  }
+               });
+         if (!keyPairOptional.isPresent()) {
+            logger.debug(">> key pair not found. Importing a new key pair %s 
...", fingerprint);
+            keyPair = api.sshKeyPairApi().importKeyPair(
+                    regionId,
+                    options.getPublicKey(),
+                    
namingConvention.create().uniqueNameForGroup(JCLOUDS_KEYPAIR_IMPORTED));
+            logger.debug(">> key pair imported! %s", keyPair);
+         } else {
+            logger.debug(">> key pair found for key %s", fingerprint);
+            keyPair = keyPairOptional.get();
+         }
+         return keyPair;
+      }
+   }
+
+   private KeyPairRequest generateKeyPair(String regionId, String 
uniqueNameForGroup) {
+      logger.debug(">> creating default keypair for node...");
+      KeyPairRequest keyPairRequest = api.sshKeyPairApi().create(regionId, 
uniqueNameForGroup);
+      logger.debug(">> keypair created! %s", keyPairRequest);
+      return keyPairRequest;
+   }
+
+   private Optional<SecurityGroup> tryFindSecurityGroupInRegion(String 
regionId, final Set<String> securityGroups) {
+      checkArgument(securityGroups.size() <= 1, "Only one security group can 
be configured for each network interface");
+      final String securityGroupId = Iterables.get(securityGroups, 0, null);
+
+      if (securityGroupId != null) {
+         return api.securityGroupApi().list(regionId).concat().firstMatch(new 
Predicate<SecurityGroup>() {
+            @Override
+            public boolean apply(@Nullable SecurityGroup input) {
+               return securityGroupId.equals(input.id());
+            }
+         });
+      }
+      return Optional.absent();
+   }
+
+   private VSwitch tryFindVSwitch(String regionId, String vSwitchId) {
+      ListVSwitchesOptions listVSwitchesOptions = 
ListVSwitchesOptions.Builder.vSwitchId(vSwitchId);
+      Optional<VSwitch> optionalVSwitch = api.vSwitchApi().list(regionId, 
listVSwitchesOptions).first();
+      if (!optionalVSwitch.isPresent()) {
+         String message = String.format("Cannot find a valid vSwitch with id 
(%s) within region (%s)",
+                 vSwitchId,
+                 regionId);
+         throw new IllegalStateException(message);
+      }
+      return optionalVSwitch.get();
+   }
+
+   private Optional<VSwitch> tryFindVSwitchInVPC(String regionId, String 
vpcId, String vSwitchId) {
+      ListVSwitchesOptions listVSwitchesOptions = 
ListVSwitchesOptions.Builder.vpcId(vpcId).vSwitchId(vSwitchId);
+      return api.vSwitchApi().list(regionId, listVSwitchesOptions).first();
+   }
+
+   private String createSecurityGroupForOptions(String group, String regionId, 
String vpcId,
+                                                ECSServiceTemplateOptions 
options) {
+      String name = namingConvention.create().sharedNameForGroup(group);
+      SecurityGroupRequest securityGroupRequest = 
api.securityGroupApi().create(regionId,
+              CreateSecurityGroupOptions.Builder
+                      .securityGroupName(name)
+                      .vpcId(vpcId));
+      // add rules
+      Map<Integer, Integer> portRanges = 
getPortRangesFromList(options.getInboundPorts());
+      for (Map.Entry<Integer, Integer> portRange : portRanges.entrySet()) {
+         String range = String.format(PORT_RANGE_FORMAT, portRange.getKey(), 
portRange.getValue());
+         // TODO makes protocol and source CIDR configurable?
+         api.securityGroupApi().addInboundRule(
+                 regionId,
+                 securityGroupRequest.getSecurityGroupId(),
+                 IpProtocol.TCP,
+                 range,
+                 INTERNET);
+      }
+      api.tagApi().add(regionId, securityGroupRequest.getSecurityGroupId(), 
SECURITYGROUP,
+              TagOptions.Builder
+                      .tag(1, Tag.DEFAULT_OWNER_KEY, Tag.DEFAULT_OWNER_VALUE)
+                      .tag(2, Tag.GROUP, group));
+      return securityGroupRequest.getSecurityGroupId();
+   }
+
+   private void registerAutoGeneratedKeyPairCleanupCallbacks(Map<?, 
ListenableFuture<Void>> responses,
+         final String regionId, final String keyPairName) {
+      // The Futures.allAsList fails immediately if some of the futures fail.
+      // The Futures.successfulAsList, however,
+      // returns a list containing the results or 'null' for those futures that
+      // failed. We want to wait for all them
+      // (even if they fail), so better use the latter form.
+      ListenableFuture<List<Void>> aggregatedResponses = 
Futures.successfulAsList(responses.values());
+
+      // Key pairs must be cleaned up after all futures completed (even if some
+      // failed).
+      Futures.addCallback(aggregatedResponses, new 
FutureCallback<List<Void>>() {
+         @Override
+         public void onSuccess(List<Void> result) {
+            cleanupAutoGeneratedKeyPairs(keyPairName);
+         }
+
+         @Override
+         public void onFailure(Throwable t) {
+            cleanupAutoGeneratedKeyPairs(keyPairName);
+         }
+
+         private void cleanupAutoGeneratedKeyPairs(String keyPairName) {
+            logger.debug(">> cleaning up auto-generated key pairs...");
+            try {
+               api.sshKeyPairApi().delete(regionId, keyPairName);
+            } catch (Exception ex) {
+               logger.warn(">> could not delete key pair %s: %s", keyPairName, 
ex.getMessage());
+            }
+         }
+      }, userExecutor);
+   }
+
+
+   private static PublicKey readPublicKey(String publicKey) {
+      Iterable<String> parts = Splitter.on(' ').split(publicKey);
+      checkArgument(size(parts) >= 2, "bad format, should be: ssh-rsa 
AAAAB3...");
+      String type = get(parts, 0);
+
+      try {
+         if ("ssh-rsa".equals(type)) {
+            RSAPublicKeySpec spec = 
SshKeys.publicKeySpecFromOpenSSH(publicKey);
+            return KeyFactory.getInstance("RSA").generatePublic(spec);
+         } else {
+            throw new IllegalArgumentException("bad format, ssh-rsa is only 
supported");
+         }
+      } catch (InvalidKeySpecException ex) {
+         throw new RuntimeException(ex);
+      } catch (NoSuchAlgorithmException ex) {
+         throw new RuntimeException(ex);
+      }
+   }
+
+   private static String computeFingerprint(PublicKey key) {
+      if (key instanceof RSAPublicKey) {
+         RSAPublicKey rsaKey = (RSAPublicKey) key;
+         return SshKeys.fingerprint(rsaKey.getPublicExponent(), 
rsaKey.getModulus());
+      } else {
+         throw new IllegalArgumentException("Only RSA keys are supported");
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/config/ECSComputeServiceHttpApiModule.java
----------------------------------------------------------------------
diff --git 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/config/ECSComputeServiceHttpApiModule.java
 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/config/ECSComputeServiceHttpApiModule.java
index 05ebc9f..1e84c6b 100644
--- 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/config/ECSComputeServiceHttpApiModule.java
+++ 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/config/ECSComputeServiceHttpApiModule.java
@@ -16,15 +16,22 @@
  */
 package org.jclouds.aliyun.ecs.config;
 
+import com.google.common.collect.ImmutableSet;
+import com.google.inject.Provides;
 import org.jclouds.aliyun.ecs.ECSComputeServiceApi;
 import org.jclouds.aliyun.ecs.handlers.ECSComputeServiceErrorHandler;
+import org.jclouds.aliyun.ecs.handlers.ECSErrorRetryHandler;
 import org.jclouds.http.HttpErrorHandler;
+import org.jclouds.http.HttpRetryHandler;
 import org.jclouds.http.annotation.ClientError;
 import org.jclouds.http.annotation.Redirection;
 import org.jclouds.http.annotation.ServerError;
 import org.jclouds.rest.ConfiguresHttpApi;
 import org.jclouds.rest.config.HttpApiModule;
 
+import javax.inject.Singleton;
+import java.util.Set;
+
 @ConfiguresHttpApi
 public class ECSComputeServiceHttpApiModule extends 
HttpApiModule<ECSComputeServiceApi> {
 
@@ -35,4 +42,21 @@ public class ECSComputeServiceHttpApiModule extends 
HttpApiModule<ECSComputeServ
       
bind(HttpErrorHandler.class).annotatedWith(ServerError.class).to(ECSComputeServiceErrorHandler.class);
    }
 
+   @Override
+   protected void bindRetryHandlers() {
+      
bind(HttpRetryHandler.class).annotatedWith(ClientError.class).to(ECSErrorRetryHandler.class);
+   }
+
+   /**
+    * It combines the error codes explicitly described as retryable from ECS 
and VPC
+    * 
https://error-center.alibabacloud.com/status/product/Ecs?spm=a2c63.p38356.a3.5.2a9859c1Fzi5nr
+    * 
https://error-center.alibabacloud.com/status/product/Vpc?spm=a2c63.p38356.a3.1.1442dd2f4qFMSW
+    */
+   @Provides
+   @ClientError
+   @Singleton
+   protected final Set<String> provideRetryableCodes() {
+      return ImmutableSet.of("InstanceNotReady", 
"IncorrectInstanceStatus.Initializing", "DependencyViolation", 
"IncorrectVpcStatus", "IncorrectStatus");
+   }
+
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/AllocatePublicIpAddressRequest.java
----------------------------------------------------------------------
diff --git 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/AllocatePublicIpAddressRequest.java
 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/AllocatePublicIpAddressRequest.java
new file mode 100644
index 0000000..8f03160
--- /dev/null
+++ 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/AllocatePublicIpAddressRequest.java
@@ -0,0 +1,59 @@
+/*
+ * 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 AllocatePublicIpAddressRequest extends Request {
+
+   private final String ipAddress;
+
+   @ConstructorProperties({ "RequestId", "IpAddress" })
+   public AllocatePublicIpAddressRequest(String requestId, String ipAddress) {
+      super(requestId);
+      this.ipAddress = ipAddress;
+   }
+
+   public String getInstanceId() {
+      return ipAddress;
+   }
+
+   @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;
+      AllocatePublicIpAddressRequest that = (AllocatePublicIpAddressRequest) o;
+      return Objects.equal(ipAddress, that.ipAddress);
+   }
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(super.hashCode(), ipAddress);
+   }
+
+   @Override
+   public String toString() {
+      return MoreObjects.toStringHelper(this).add("ipAddress", 
ipAddress).toString();
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/AvailableResource.java
----------------------------------------------------------------------
diff --git 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/AvailableResource.java 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/AvailableResource.java
new file mode 100644
index 0000000..338663c
--- /dev/null
+++ 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/AvailableResource.java
@@ -0,0 +1,43 @@
+/*
+ * 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 AvailableResource {
+
+   AvailableResource() {
+   }
+
+   @SerializedNames({ "Type", "SupportedResources" })
+   public static AvailableResource create(String type, Map<String, 
List<SupportedResource>> supportedResources) {
+      return new AutoValue_AvailableResource(type, supportedResources == null ?
+            ImmutableMap.<String, List<SupportedResource>>of() :
+            ImmutableMap.copyOf(supportedResources));
+   }
+
+   public abstract String type();
+
+   public abstract Map<String, List<SupportedResource>> supportedResources();
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/AvailableZone.java
----------------------------------------------------------------------
diff --git 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/AvailableZone.java 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/AvailableZone.java
new file mode 100644
index 0000000..08e65b3
--- /dev/null
+++ b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/AvailableZone.java
@@ -0,0 +1,47 @@
+/*
+ * 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 AvailableZone {
+
+   AvailableZone() {
+   }
+
+   @SerializedNames({ "Status", "RegionId", "AvailableResources", "ZoneId" })
+   public static AvailableZone create(String status, String regionId,
+                                      Map<String, List<AvailableResource>> 
availableResources, String zoneId) {
+      return new AutoValue_AvailableZone(status, regionId, availableResources 
== null ?
+            ImmutableMap.<String, List<AvailableResource>>of() :
+            ImmutableMap.copyOf(availableResources), zoneId);
+   }
+
+   public abstract String status();
+
+   public abstract String regionId();
+
+   public abstract Map<String, List<AvailableResource>> availableResources();
+
+   public abstract String zoneId();
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/DedicatedHostAttribute.java
----------------------------------------------------------------------
diff --git 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/DedicatedHostAttribute.java
 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/DedicatedHostAttribute.java
new file mode 100644
index 0000000..dff8e3f
--- /dev/null
+++ 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/DedicatedHostAttribute.java
@@ -0,0 +1,37 @@
+/*
+ * 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 DedicatedHostAttribute {
+
+   DedicatedHostAttribute() {
+   }
+
+   @SerializedNames({ "DedicatedHostId", "DedicatedHostName" })
+   public static DedicatedHostAttribute create(String id, String name) {
+      return new AutoValue_DedicatedHostAttribute(id, name);
+   }
+
+   public abstract String id();
+
+   public abstract String name();
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/EipAddress.java
----------------------------------------------------------------------
diff --git 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/EipAddress.java 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/EipAddress.java
new file mode 100644
index 0000000..10c158d
--- /dev/null
+++ b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/EipAddress.java
@@ -0,0 +1,67 @@
+/*
+ * 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.base.CaseFormat;
+import com.google.common.base.Enums;
+import org.jclouds.json.SerializedNames;
+
+@AutoValue
+public abstract class EipAddress {
+
+   public enum InternetChargeType {
+      ECS_INSTANCE("EcsInstance"),
+      SLB_INSTANCE ("SlbInstance"),
+      NAT ("Nat"),
+      HA_VIP("HaVip"),
+      DEFAULT("");
+
+      private final String internetChargeType;
+
+      InternetChargeType(String internetChargeType) {
+         this.internetChargeType = internetChargeType;
+      }
+
+      public static InternetChargeType fromValue(String value) {
+         return Enums.getIfPresent(InternetChargeType.class, 
CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, 
value)).or(InternetChargeType.DEFAULT);
+      }
+
+      public String internetChargeType() {
+         return internetChargeType;
+      }
+
+      @Override
+      public String toString() {
+         return internetChargeType();
+      }
+   }
+
+   EipAddress() {}
+
+   @SerializedNames({ "IpAddress", "AllocationId", "InternetChargeType" })
+   public static EipAddress create(String ipAddress, String allocationId, 
InternetChargeType internetChargeType) {
+      return new AutoValue_EipAddress(ipAddress, allocationId, 
internetChargeType);
+   }
+
+   public abstract String ipAddress();
+
+   public abstract String allocationId();
+
+   public abstract InternetChargeType internetChargeType();
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/ErrorMessage.java
----------------------------------------------------------------------
diff --git 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/ErrorMessage.java 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/ErrorMessage.java
new file mode 100644
index 0000000..aa3f684
--- /dev/null
+++ b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/ErrorMessage.java
@@ -0,0 +1,39 @@
+/*
+ * 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 ErrorMessage {
+
+   ErrorMessage() {}
+
+   @SerializedNames({ "RequestId", "HostId", "Code", "Message" })
+   public static ErrorMessage create(String requestId, String hostId, String 
code, String message) {
+      return new AutoValue_ErrorMessage(requestId, hostId, code, message);
+   }
+
+   public abstract String requestId();
+
+   public abstract String hostId();
+
+   public abstract String code();
+
+   public abstract String message();
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/Image.java
----------------------------------------------------------------------
diff --git a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/Image.java 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/Image.java
index 328d6fe..1aee2c1 100644
--- a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/Image.java
+++ b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/Image.java
@@ -17,6 +17,10 @@
 package org.jclouds.aliyun.ecs.domain;
 
 import com.google.auto.value.AutoValue;
+import com.google.common.base.CaseFormat;
+import com.google.common.base.Enums;
+import com.google.common.base.Joiner;
+import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableMap;
 import org.jclouds.json.SerializedNames;
 
@@ -24,9 +28,31 @@ import java.util.Date;
 import java.util.List;
 import java.util.Map;
 
+import static com.google.common.base.Preconditions.checkArgument;
+
 @AutoValue
 public abstract class Image {
 
+   public enum Status {
+      AVAILABLE, UNAVAILABLE;
+
+
+      public static Status fromValue(String value) {
+         Optional<Status> status = Enums.getIfPresent(Status.class, 
value.toUpperCase());
+         checkArgument(status.isPresent(), "Expected one of %s but was %s", 
Joiner.on(',').join(Status.values()), value);
+         return status.get();
+      }
+
+      public String value() {
+         return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, name());
+      }
+
+      @Override
+      public String toString() {
+         return value();
+      }
+   }
+
    Image() {}
 
    @SerializedNames({"ImageId", "Description", "ProductCode", "OSType", 
"Architecture", "OSName", "DiskDeviceMappings",
@@ -36,15 +62,15 @@ public abstract class Image {
    public static Image create(String id, String description, String 
productCode, String osType,
                               String architecture, String osName, Map<String, 
List<DiskDeviceMapping>> diskDeviceMappings,
                               String imageOwnerAlias, String progress, Boolean 
isSupportCloudinit, String usage, Date creationTime,
-                              Map<String, List<Tag>> tags, String 
imageVersion, String status, String name,
+                              Map<String, List<Tag>> tags, String 
imageVersion, Status status, String name,
                               Boolean isSupportIoOptimized, Boolean 
isSelfShared, Boolean isCopied, Boolean isSubscribed, String platform,
-                              String size) {
-      return new AutoValue_Image(id, description, productCode, osType, 
architecture, osName,
-              diskDeviceMappings == null ?
-                      ImmutableMap.<String, List<DiskDeviceMapping>>of() :
-                      ImmutableMap.copyOf(diskDeviceMappings), 
imageOwnerAlias, progress, isSupportCloudinit, usage,
-              creationTime, tags == null ? ImmutableMap.<String, 
List<Tag>>of() : ImmutableMap.copyOf(tags), imageVersion,
-              status, name, isSupportIoOptimized, isSelfShared, isCopied, 
isSubscribed, platform, size);
+                              int size) {
+      return 
builder().id(id).description(description).productCode(productCode).osType(osType)
+              
.architecture(architecture).osName(osName).diskDeviceMappings(diskDeviceMappings).imageOwnerAlias(imageOwnerAlias)
+              
.progress(progress).isSupportCloudinit(isSupportCloudinit).usage(usage).creationTime(creationTime)
+              
.tags(tags).imageVersion(imageVersion).status(status).name(name).isSupportIoOptimizeds(isSupportIoOptimized)
+              
.isSelfShared(isSelfShared).isCopied(isCopied).isSubscribed(isSubscribed).platform(platform).size(size)
+              .build();
    }
 
    public abstract String id();
@@ -75,7 +101,7 @@ public abstract class Image {
 
    public abstract String imageVersion();
 
-   public abstract String status();
+   public abstract Status status();
 
    public abstract String name();
 
@@ -89,6 +115,72 @@ public abstract class Image {
 
    public abstract String platform();
 
-   public abstract String size();
+   public abstract int size();
+
+   public abstract Builder toBuilder();
+
+   public static Builder builder() {
+      return new AutoValue_Image.Builder();
+   }
+
+   @AutoValue.Builder
+   public abstract static class Builder {
+
+      public abstract Builder id(String id);
+
+      public abstract Builder description(String description);
+
+      public abstract Builder productCode(String productCode);
+
+      public abstract Builder osType(String osType);
+
+      public abstract Builder architecture(String architecture);
+
+      public abstract Builder osName(String osName);
+
+      public abstract Builder diskDeviceMappings(Map<String, 
List<DiskDeviceMapping>> diskDeviceMappings);
+
+      public abstract Builder imageOwnerAlias(String imageOwnerAlias);
+
+      public abstract Builder progress(String progress);
+
+      public abstract Builder isSupportCloudinit(Boolean isSupportCloudinit);
+
+      public abstract Builder usage(String usage);
+
+      public abstract Builder creationTime(Date creationTime);
+
+      public abstract Builder tags(Map<String, List<Tag>> tags);
+
+      public abstract Builder imageVersion(String imageVersion);
+
+      public abstract Builder status(Status status);
+
+      public abstract Builder name(String name);
+
+      public abstract Builder isSupportIoOptimizeds(Boolean 
isSupportIoOptimizeds);
+
+      public abstract Builder isSelfShared(Boolean isSelfShared);
+
+      public abstract Builder isCopied(Boolean isCopied);
+
+      public abstract Builder isSubscribed(Boolean isSubscribed);
+
+      public abstract Builder platform(String platform);
+
+      public abstract Builder size(int size);
+
+      abstract Image autoBuild();
+
+      abstract Map<String, List<DiskDeviceMapping>> diskDeviceMappings();
+
+      abstract Map<String, List<Tag>> tags();
+
+      public Image build() {
+         diskDeviceMappings(diskDeviceMappings() != null ? 
ImmutableMap.copyOf(diskDeviceMappings()) : ImmutableMap.<String, 
List<DiskDeviceMapping>>of());
+         tags(tags() != null ? ImmutableMap.copyOf(tags()) : 
ImmutableMap.<String, List<Tag>>of());
+         return autoBuild();
+      }
+   }
 
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/Instance.java
----------------------------------------------------------------------
diff --git 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/Instance.java 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/Instance.java
new file mode 100644
index 0000000..ddf2036
--- /dev/null
+++ b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/Instance.java
@@ -0,0 +1,321 @@
+/*
+ * 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.base.CaseFormat;
+import com.google.common.base.Enums;
+import com.google.common.base.Joiner;
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableMap;
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.json.SerializedNames;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+@AutoValue
+public abstract class Instance {
+
+   public enum InternetChargeType {
+      PAY_BY_TRAFFIC("PayByTraffic"),
+      DEFAULT("");
+
+      private final String internetChargeType;
+
+      InternetChargeType(String internetChargeType) {
+         this.internetChargeType = internetChargeType;
+      }
+
+      public static InternetChargeType fromValue(String value) {
+         return Enums.getIfPresent(InternetChargeType.class, 
CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, 
value)).or(InternetChargeType.DEFAULT);
+      }
+
+      public String internetChargeType() {
+         return internetChargeType;
+      }
+
+      @Override
+      public String toString() {
+         return internetChargeType();
+      }
+   }
+
+
+   public enum Status {
+      STARTING, RUNNING, STOPPING, STOPPED;
+
+      public static Status fromValue(String value) {
+         Optional<Status> status = Enums.getIfPresent(Status.class, 
value.toUpperCase());
+         checkArgument(status.isPresent(), "Expected one of %s but was %s", 
Joiner.on(',').join(Status.values()),
+                 value);
+         return status.get();
+      }
+   }
+
+   Instance() {}
+
+   @SerializedNames(
+         { "InnerIpAddress", "ImageId", "InstanceTypeFamily", "VlanId", 
"NetworkInterfaces", "InstanceId", "EipAddress",
+               "InternetMaxBandwidthIn", "ZoneId", "InternetChargeType", 
"SpotStrategy", "StoppedMode", "SerialNumber",
+               "IoOptimized", "Memory", "Cpu", "VpcAttributes", 
"InternetMaxBandwidthOut", "DeviceAvailable",
+               "SecurityGroupIds", "SaleCycle", "SpotPriceLimit", 
"AutoReleaseTime", "StartTime", "InstanceName",
+               "Description", "ResourceGroupId", "OSType", "OSName", 
"InstanceNetworkType", "PublicIpAddress",
+               "HostName", "InstanceType", "CreationTime", "Status", "Tags", 
"ClusterId", "Recyclable", "RegionId",
+               "GPUSpec", "DedicatedHostAttribute", "OperationLocks", 
"InstanceChargeType", "GPUAmount",
+               "ExpiredTime" })
+   public static Instance create(Map<String, List<String>> innerIpAddress, 
String imageId, String instanceTypeFamily,
+                                 String vlanId, Map<String, 
List<NetworkInterface>> networkInterfaces, String id, EipAddress eipAddress,
+                                 Integer internetMaxBandwidthIn, String 
zoneId, InternetChargeType internetChargeType, String spotStrategy,
+                                 String stoppedMode, String serialNumber, 
Boolean ioOptimized, Integer memory, Integer cpu,
+                                 VpcAttributes vpcAttributes, Integer 
internetMaxBandwidthOut, Boolean deviceAvailable,
+                                 Map<String, List<String>> securityGroupIds, 
String saleCycle, Double spotPriceLimit, String autoReleaseTime,
+                                 Date startTime, String name, String 
description, String resourceGroupId, String osType, String osName,
+                                 String instanceNetworkType, Map<String, 
List<String>> publicIpAddress, String hostname, String instanceType,
+                                 Date creationTime, Status status, Map<String, 
List<Tag>> tags, String clusterId, Boolean recyclable,
+                                 String regionId, String gpuSpec, 
DedicatedHostAttribute dedicatedHostAttribute,
+                                 Map<String, List<String>> operationLocks, 
String instanceChargeType, Integer gpuAmount, Date expiredTime) {
+      return 
builder().innerIpAddress(innerIpAddress).imageId(imageId).instanceTypeFamily(instanceTypeFamily).vlanId(vlanId)
+              
.networkInterfaces(networkInterfaces).id(id).eipAddress(eipAddress).internetMaxBandwidthIn(internetMaxBandwidthIn)
+              
.zoneId(zoneId).internetChargeType(internetChargeType).spotStrategy(spotStrategy).stoppedMode(stoppedMode).serialNumber(serialNumber)
+              
.ioOptimized(ioOptimized).memory(memory).cpu(cpu).vpcAttributes(vpcAttributes).internetMaxBandwidthOut(internetMaxBandwidthOut).deviceAvailable(deviceAvailable)
+              
.securityGroupIds(securityGroupIds).saleCycle(saleCycle).spotPriceLimit(spotPriceLimit).autoReleaseTime(autoReleaseTime).startTime(startTime).name(name)
+              
.description(description).resourceGroupId(resourceGroupId).osType(osType).osName(osName).instanceNetworkType(instanceNetworkType).publicIpAddress(publicIpAddress)
+              
.hostname(hostname).instanceType(instanceType).creationTime(creationTime).status(status).tags(tags).clusterId(clusterId).recyclable(recyclable).regionId(regionId)
+              
.gpuSpec(gpuSpec).dedicatedHostAttribute(dedicatedHostAttribute).operationLocks(operationLocks).instanceChargeType(instanceChargeType).gpuAmount(gpuAmount)
+              .expiredTime(expiredTime).build();
+   }
+
+   public abstract Map<String, List<String>> innerIpAddress();
+
+   public abstract String imageId();
+
+   public abstract String instanceTypeFamily();
+
+   public abstract String vlanId();
+
+   public abstract Map<String, List<NetworkInterface>> networkInterfaces();
+
+   public abstract String id();
+
+   public abstract EipAddress eipAddress();
+
+   public abstract Integer internetMaxBandwidthIn();
+
+   public abstract String zoneId();
+
+   public abstract InternetChargeType internetChargeType();
+
+   public abstract String spotStrategy();
+
+   public abstract String stoppedMode();
+
+   public abstract String serialNumber();
+
+   public abstract Boolean ioOptimized();
+
+   public abstract Integer memory();
+
+   public abstract Integer cpu();
+
+   public abstract VpcAttributes vpcAttributes();
+
+   public abstract Integer internetMaxBandwidthOut();
+
+   public abstract Boolean deviceAvailable();
+
+   public abstract Map<String, List<String>> securityGroupIds();
+
+   public abstract String saleCycle();
+
+   public abstract Double spotPriceLimit();
+
+   public abstract String autoReleaseTime();
+
+   public abstract Date startTime();
+
+   public abstract String name();
+
+   public abstract String description();
+
+   public abstract String resourceGroupId();
+
+   public abstract String osType();
+
+   public abstract String osName();
+
+   public abstract String instanceNetworkType();
+
+   public abstract Map<String, List<String>> publicIpAddress();
+
+   public abstract String hostname();
+
+   public abstract String instanceType();
+
+   public abstract Date creationTime();
+
+   public abstract Status status();
+
+   @Nullable
+   public abstract Map<String, List<Tag>> tags();
+
+   public abstract String clusterId();
+
+   public abstract Boolean recyclable();
+
+   public abstract String regionId();
+
+   public abstract String gpuSpec();
+
+   public abstract DedicatedHostAttribute dedicatedHostAttribute();
+
+   public abstract Map<String, List<String>> operationLocks();
+
+   public abstract String instanceChargeType();
+
+   public abstract Integer gpuAmount();
+
+   public abstract Date expiredTime();
+
+   public abstract Builder toBuilder();
+
+   public static Builder builder() {
+      return new AutoValue_Instance.Builder();
+   }
+
+   @AutoValue.Builder
+   public abstract static class Builder {
+
+      public abstract Builder innerIpAddress(Map<String, List<String>> 
innerIpAddress);
+
+      public abstract Builder imageId(String imageId);
+
+      public abstract Builder instanceTypeFamily(String instanceTypeFamily);
+
+      public abstract Builder vlanId(String vlanId);
+
+      public abstract Builder networkInterfaces(Map<String, 
List<NetworkInterface>> networkInterfaces);
+
+      public abstract Builder id(String id);
+
+      public abstract Builder eipAddress(EipAddress eipAddress);
+
+      public abstract Builder internetMaxBandwidthIn(Integer 
internetMaxBandwidthIn);
+
+      public abstract Builder zoneId(String zoneId);
+
+      public abstract Builder internetChargeType(InternetChargeType 
internetChargeType);
+
+      public abstract Builder spotStrategy(String spotStrategy);
+
+      public abstract Builder stoppedMode(String stoppedMode);
+
+      public abstract Builder serialNumber(String serialNumber);
+
+      public abstract Builder ioOptimized(Boolean ioOptimized);
+
+      public abstract Builder memory(Integer memory);
+
+      public abstract Builder cpu(Integer cpu);
+
+      public abstract Builder vpcAttributes(VpcAttributes vpcAttributes);
+
+      public abstract Builder internetMaxBandwidthOut(Integer 
internetMaxBandwidthOut);
+
+      public abstract Builder deviceAvailable(Boolean deviceAvailable);
+
+      public abstract Builder securityGroupIds(Map<String, List<String>> 
securityGroupIds);
+
+      public abstract Builder saleCycle(String saleCycle);
+
+      public abstract Builder spotPriceLimit(Double spotPriceLimit);
+
+      public abstract Builder autoReleaseTime(String autoReleaseTime);
+
+      public abstract Builder startTime(Date startTime);
+
+      public abstract Builder name(String name);
+
+      public abstract Builder description(String description);
+
+      public abstract Builder resourceGroupId(String resourceGroupId);
+
+      public abstract Builder osType(String osType);
+
+      public abstract Builder osName(String osName);
+
+      public abstract Builder instanceNetworkType(String instanceNetworkType);
+
+      public abstract Builder publicIpAddress(Map<String, List<String>> 
publicIpAddress);
+
+      public abstract Builder hostname(String hostname);
+
+      public abstract Builder instanceType(String instanceType);
+
+      public abstract Builder creationTime(Date creationTime);
+
+      public abstract Builder status(Status status);
+
+      public abstract Builder tags(Map<String, List<Tag>> tags);
+
+      public abstract Builder clusterId(String clusterId);
+
+      public abstract Builder recyclable(Boolean recyclable);
+
+      public abstract Builder regionId(String regionId);
+
+      public abstract Builder gpuSpec(String gpuSpec);
+
+      public abstract Builder dedicatedHostAttribute(DedicatedHostAttribute 
dedicatedHostAttribute);
+
+      public abstract Builder operationLocks(Map<String, List<String>> 
operationLocks);
+
+      public abstract Builder instanceChargeType(String InstanceChargeType);
+
+      public abstract Builder gpuAmount(Integer gpuAmount);
+
+      public abstract Builder expiredTime(Date expiredTime);
+
+      abstract Instance autoBuild();
+
+      abstract Map<String, List<String>> innerIpAddress();
+
+      abstract Map<String, List<NetworkInterface>> networkInterfaces();
+
+      abstract Map<String, List<String>> securityGroupIds();
+
+      abstract Map<String, List<String>> publicIpAddress();
+
+      abstract Map<String, List<String>> operationLocks();
+
+      abstract Map<String, List<Tag>> tags();
+
+      public Instance build() {
+         innerIpAddress(innerIpAddress() == null ? ImmutableMap.<String, 
List<String>>of() : ImmutableMap.copyOf(innerIpAddress()));
+         securityGroupIds(securityGroupIds() == null ? ImmutableMap.<String, 
List<String>>of() : ImmutableMap.copyOf(securityGroupIds()));
+         networkInterfaces(networkInterfaces() == null ? ImmutableMap.<String, 
List<NetworkInterface>>of() : ImmutableMap.copyOf(networkInterfaces()));
+         publicIpAddress(publicIpAddress() == null ? ImmutableMap.<String, 
List<String>>of() : ImmutableMap.copyOf(publicIpAddress()));
+         operationLocks(operationLocks() == null ? ImmutableMap.<String, 
List<String>>of() : ImmutableMap.copyOf(operationLocks()));
+         tags(tags() != null ? ImmutableMap.copyOf(tags()) : 
ImmutableMap.<String, List<Tag>>of());
+         return autoBuild();
+      }
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/InstanceRequest.java
----------------------------------------------------------------------
diff --git 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/InstanceRequest.java 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/InstanceRequest.java
new file mode 100644
index 0000000..ca615d0
--- /dev/null
+++ 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/InstanceRequest.java
@@ -0,0 +1,59 @@
+/*
+ * 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 InstanceRequest extends Request {
+
+   private final String instanceId;
+
+   @ConstructorProperties({ "RequestId", "InstanceId" })
+   public InstanceRequest(String requestId, String instanceId) {
+      super(requestId);
+      this.instanceId = instanceId;
+   }
+
+   public String getInstanceId() {
+      return instanceId;
+   }
+
+   @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;
+      InstanceRequest that = (InstanceRequest) o;
+      return Objects.equal(instanceId, that.instanceId);
+   }
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(super.hashCode(), instanceId);
+   }
+
+   @Override
+   public String toString() {
+      return MoreObjects.toStringHelper(this).add("instanceId", 
instanceId).toString();
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/InstanceStatus.java
----------------------------------------------------------------------
diff --git 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/InstanceStatus.java 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/InstanceStatus.java
new file mode 100644
index 0000000..4ee6777
--- /dev/null
+++ b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/InstanceStatus.java
@@ -0,0 +1,55 @@
+/*
+ * 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.base.Enums;
+import com.google.common.base.Joiner;
+import com.google.common.base.Optional;
+import org.jclouds.json.SerializedNames;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * 
https://www.alibabacloud.com/help/doc-detail/25687.htm?spm=a2c63.p38356.a3.3.25a15566k0U5A4
+ */
+@AutoValue
+public abstract class InstanceStatus {
+
+   InstanceStatus() {
+   }
+
+   @SerializedNames({ "InstanceId", "Status" })
+   public static InstanceStatus create(String instanceId, Status status) {
+      return new AutoValue_InstanceStatus(instanceId, status);
+   }
+
+   public abstract String instanceId();
+
+   public abstract Status status();
+
+   public enum Status {
+      PENDING, STARTING, RUNNING, STOPPING, STOPPED;
+
+      public static InstanceStatus.Status fromValue(String value) {
+         Optional<Status> status = 
Enums.getIfPresent(InstanceStatus.Status.class, value.toUpperCase());
+         checkArgument(status.isPresent(), "Expected one of %s but was %s",
+               Joiner.on(',').join(InstanceStatus.Status.values()), value);
+         return status.get();
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/InstanceType.java
----------------------------------------------------------------------
diff --git 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/InstanceType.java 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/InstanceType.java
new file mode 100644
index 0000000..1802084
--- /dev/null
+++ b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/InstanceType.java
@@ -0,0 +1,85 @@
+/*
+ * 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 InstanceType {
+
+   InstanceType() {
+   }
+
+   @SerializedNames(
+           {"InstanceTypeId", "InstanceTypeFamily", "EniQuantity",
+            "GPUSpec", "CpuCoreCount", "MemorySize", "GPUAmount", 
"LocalStorageCategory" })
+   public static InstanceType create(String id, String instanceTypeFamily, 
Integer eniQuantity,
+                                     String gpuSpec, Integer cpuCoreCount, 
Double memorySize, Double gpuAmount, String localStorageCategory) {
+      return builder()
+              
.id(id).instanceTypeFamily(instanceTypeFamily).eniQuantity(eniQuantity)
+              
.gpuSpec(gpuSpec).cpuCoreCount(cpuCoreCount).memorySize(memorySize)
+              .gpuAmount(gpuAmount).localStorageCategory(localStorageCategory)
+              .build();
+   }
+
+   public abstract String id();
+
+   public abstract Integer cpuCoreCount();
+
+   public abstract String instanceTypeFamily();
+
+   public abstract Integer eniQuantity();
+
+   public abstract String gpuSpec();
+
+   public abstract Double memorySize();
+
+   public abstract Double gpuAmount();
+
+   public abstract String localStorageCategory();
+
+   public abstract Builder toBuilder();
+
+   public static Builder builder() {
+      return new AutoValue_InstanceType.Builder();
+   }
+
+   @AutoValue.Builder
+   public abstract static class Builder {
+
+      public abstract Builder id(String id);
+
+      public abstract Builder cpuCoreCount(Integer cpuCoreCount);
+
+      public abstract Builder instanceTypeFamily(String instanceTypeFamily);
+
+      public abstract Builder eniQuantity(Integer eniQuantity);
+
+      public abstract Builder gpuSpec(String gpuSpec);
+
+      public abstract Builder memorySize(Double memorySize);
+
+      public abstract Builder gpuAmount(Double gpuAmount);
+
+      public abstract Builder localStorageCategory(String 
localStorageCategory);
+
+      public abstract InstanceType build();
+
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/NetworkInterface.java
----------------------------------------------------------------------
diff --git 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/NetworkInterface.java 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/NetworkInterface.java
new file mode 100644
index 0000000..6a3b6e9
--- /dev/null
+++ 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/NetworkInterface.java
@@ -0,0 +1,41 @@
+/*
+ * 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.javax.annotation.Nullable;
+import org.jclouds.json.SerializedNames;
+
+@AutoValue
+public abstract class NetworkInterface {
+
+   NetworkInterface() {
+   }
+
+   @SerializedNames({ "MacAddress", "PrimaryIpAddress", "NetworkInterfaceId" })
+   public static NetworkInterface create(String macAddress, String 
primaryIpAddress, String id) {
+      return new AutoValue_NetworkInterface(macAddress, primaryIpAddress, id);
+   }
+
+   @Nullable
+   public abstract String macAddress();
+
+   public abstract String primaryIpAddress();
+
+   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/domain/Permission.java
----------------------------------------------------------------------
diff --git 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/Permission.java 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/Permission.java
index 003ccf1..bda9cb5 100644
--- a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/Permission.java
+++ b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/Permission.java
@@ -50,7 +50,7 @@ public abstract class Permission {
    }
 
    public enum Direction {
-      EGRESS, ALL;
+      EGRESS, INGRESS, ALL;
 
       public static Direction fromValue(String value) {
          Optional<Direction> direction = Enums.getIfPresent(Direction.class, 
value.toUpperCase());

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/ResourceType.java
----------------------------------------------------------------------
diff --git 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/ResourceType.java 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/ResourceType.java
new file mode 100644
index 0000000..9807d83
--- /dev/null
+++ b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/ResourceType.java
@@ -0,0 +1,42 @@
+/*
+ * 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.Enums;
+import com.google.common.base.Joiner;
+import com.google.common.base.Optional;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * The type of the ECS resource. All values must be lowercase.
+ */
+public enum ResourceType {
+   DISK, INSTANCE, IMAGE, SECURITYGROUP, SNAPSHOT;
+
+   public static ResourceType fromValue(String value) {
+      Optional<ResourceType> resourceType = 
Enums.getIfPresent(ResourceType.class, value.toUpperCase());
+      checkArgument(resourceType.isPresent(), "Expected one of %s but was %s", 
Joiner.on(',').join(ResourceType.values()), value);
+      return resourceType.get();
+   }
+
+   @Override
+   public String toString() {
+      return name().toLowerCase();
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/SecurityGroup.java
----------------------------------------------------------------------
diff --git 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/SecurityGroup.java 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/SecurityGroup.java
index 3fd0658..8709948 100644
--- a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/SecurityGroup.java
+++ b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/SecurityGroup.java
@@ -17,18 +17,24 @@
 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 SecurityGroup {
 
    SecurityGroup() {
    }
 
-   @SerializedNames({ "SecurityGroupId", "Description", "SecurityGroupName", 
"VpcId" })
+   @SerializedNames({ "SecurityGroupId", "Description", "SecurityGroupName", 
"VpcId", "Tags" })
    public static SecurityGroup create(String id, String description, String 
name,
-                                      String vpcId) {
-      return new AutoValue_SecurityGroup(id, description, name, vpcId);
+                                      String vpcId, Map<String, List<Tag>> 
tags) {
+      return new AutoValue_SecurityGroup(id, description, name, vpcId, tags == 
null ?
+              ImmutableMap.<String, List<Tag>>of() :
+              ImmutableMap.copyOf(tags));
    }
 
    public abstract String id();
@@ -39,4 +45,6 @@ public abstract class SecurityGroup {
 
    public abstract String vpcId();
 
+   public abstract Map<String, List<Tag>> tags();
+
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/SupportedResource.java
----------------------------------------------------------------------
diff --git 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/SupportedResource.java 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/SupportedResource.java
new file mode 100644
index 0000000..3547004
--- /dev/null
+++ 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/SupportedResource.java
@@ -0,0 +1,52 @@
+/*
+ * 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.base.Enums;
+import com.google.common.base.Joiner;
+import com.google.common.base.Optional;
+import org.jclouds.json.SerializedNames;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+@AutoValue
+public abstract class SupportedResource {
+
+   public enum Status {
+      AVAILABLE, SOLDOUT;
+
+      public static Status fromValue(String value) {
+         Optional<Status> status = Enums.getIfPresent(Status.class, 
value.toUpperCase());
+         checkArgument(status.isPresent(), "Expected one of %s but was %s", 
Joiner.on(',').join(Status.values()), value);
+         return status.get();
+      }
+   }
+
+   SupportedResource() {
+   }
+
+   @SerializedNames({ "Status", "Value" })
+   public static SupportedResource create(Status status, String value) {
+      return new AutoValue_SupportedResource(status, value);
+   }
+
+   public abstract Status status();
+
+   public abstract String value();
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/Tag.java
----------------------------------------------------------------------
diff --git a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/Tag.java 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/Tag.java
index 1b34226..33bc688 100644
--- a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/Tag.java
+++ b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/Tag.java
@@ -17,19 +17,25 @@
 package org.jclouds.aliyun.ecs.domain;
 
 import com.google.auto.value.AutoValue;
+import org.jclouds.javax.annotation.Nullable;
 import org.jclouds.json.SerializedNames;
 
 @AutoValue
 public abstract class Tag {
 
+   public static final String DEFAULT_OWNER_KEY = "owner";
+   public static final String DEFAULT_OWNER_VALUE = "jclouds";
+   public static final String GROUP = "group";
+
    Tag() {}
 
    @SerializedNames({ "TagKey", "TagValue" })
-   public static Tag create(String tagKey, String tagValue) {
-      return new AutoValue_Tag(tagKey, tagValue);
+   public static Tag create(String key, String value) {
+      return new AutoValue_Tag(key, value);
    }
 
-   public abstract String tagKey();
+   public abstract String key();
 
-   public abstract String tagValue();
+   @Nullable
+   public abstract String value();
 }

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/UserCidr.java
----------------------------------------------------------------------
diff --git 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/UserCidr.java 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/UserCidr.java
new file mode 100644
index 0000000..ff91eae
--- /dev/null
+++ b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/UserCidr.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.domain;
+
+import com.google.auto.value.AutoValue;
+
+// FIXME not possible to find properties from either the API or the doc
+@AutoValue
+public abstract class UserCidr {
+
+   UserCidr() {}
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/VPC.java
----------------------------------------------------------------------
diff --git a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/VPC.java 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/VPC.java
new file mode 100644
index 0000000..a1ed1d6
--- /dev/null
+++ b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/VPC.java
@@ -0,0 +1,131 @@
+/*
+ * 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.base.CaseFormat;
+import com.google.common.base.Enums;
+import com.google.common.base.Joiner;
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableMap;
+import org.jclouds.json.SerializedNames;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+@AutoValue
+public abstract class VPC {
+
+   public enum Status {
+      AVAILABLE, UNAVAILABLE, PENDING;
+
+      public static Status fromValue(String value) {
+         Optional<Status> status = Enums.getIfPresent(Status.class, 
value.toUpperCase());
+         checkArgument(status.isPresent(), "Expected one of %s but was %s", 
Joiner.on(',').join(Status.values()), value);
+         return status.get();
+      }
+
+      public String value() {
+         return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, name());
+      }
+
+      @Override
+      public String toString() {
+         return value();
+      }
+   }
+
+   VPC() {}
+
+   @SerializedNames({"CidrBlock", "CreationTime", "Description", "RegionId", 
"Status",
+           "UserCidrs", "VRouterId", "VSwitchIds", "VpcId", "VpcName" })
+   public static VPC create(String cidrBlock, Date creationTime, String 
description, String regionId, Status status,
+                            Map<String, List<UserCidr>> userCidrs,
+                            String vRouterId,
+                            Map<String, List<String>> vSwitchIds,
+                            String id, String name) {
+      return 
builder().cidrBlock(cidrBlock).creationTime(creationTime).description(description).regionId(regionId)
+              
.status(status).userCidrs(userCidrs).vRouterId(vRouterId).vSwitchIds(vSwitchIds)
+              .id(id).name(name).build();
+   }
+
+   public abstract String cidrBlock();
+
+   public abstract Date creationTime();
+
+   public abstract String description();
+
+   public abstract String regionId();
+
+   public abstract Status status();
+
+   public abstract Map<String, List<UserCidr>> userCidrs();
+
+   public abstract String vRouterId();
+
+   public abstract Map<String, List<String>> vSwitchIds();
+
+   public abstract String id();
+
+   public abstract String name();
+
+   public abstract VPC.Builder toBuilder();
+
+   public static VPC.Builder builder() {
+      return new AutoValue_VPC.Builder();
+   }
+
+   @AutoValue.Builder
+   public abstract static class Builder {
+
+      public abstract Builder cidrBlock(String cidrBlock);
+
+      public abstract Builder creationTime(Date creationTime);
+
+      public abstract Builder description(String description);
+
+      public abstract Builder regionId(String regionId);
+
+      public abstract Builder status(Status status);
+
+      public abstract Builder userCidrs(Map<String, List<UserCidr>> userCidrs);
+
+      public abstract Builder vRouterId(String vRouterId);
+
+      public abstract Builder vSwitchIds(Map<String, List<String>> vSwitchIds);
+
+      public abstract Builder id(String id);
+
+      public abstract Builder name(String name);
+
+      abstract VPC autoBuild();
+
+      abstract Map<String, List<UserCidr>> userCidrs();
+
+      abstract Map<String, List<String>> vSwitchIds();
+
+      public VPC build() {
+         userCidrs(userCidrs() == null ? ImmutableMap.<String, 
List<UserCidr>>of() : ImmutableMap.copyOf(userCidrs()));
+         vSwitchIds(vSwitchIds() == null ? ImmutableMap.<String, 
List<String>>of() : ImmutableMap.copyOf(vSwitchIds()));
+         return autoBuild();
+      }
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/VPCRequest.java
----------------------------------------------------------------------
diff --git 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/VPCRequest.java 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/VPCRequest.java
new file mode 100644
index 0000000..868c550
--- /dev/null
+++ b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/VPCRequest.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;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
+
+import java.beans.ConstructorProperties;
+
+public class VPCRequest extends Request {
+
+   private final String routeTableId;
+   private final String vRouterId;
+   private final String vpcId;
+
+   @ConstructorProperties({ "RequestId", "RouteTableId", "VRouterId", "VpcId" 
})
+   public VPCRequest(String requestId, String routeTableId, String vRouterId, 
String vpcId) {
+      super(requestId);
+      this.routeTableId = routeTableId;
+      this.vRouterId = vRouterId;
+      this.vpcId = vpcId;
+   }
+
+   public String getRouteTableId() {
+      return routeTableId;
+   }
+
+   public String getvRouterId() {
+      return vRouterId;
+   }
+
+   public String getVpcId() {
+      return vpcId;
+   }
+
+   @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;
+      VPCRequest that = (VPCRequest) o;
+      return Objects.equal(routeTableId, that.routeTableId) &&
+              Objects.equal(vRouterId, that.vRouterId) &&
+              Objects.equal(vpcId, that.vpcId);
+   }
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(super.hashCode(), routeTableId, vRouterId, 
vpcId);
+   }
+
+   @Override
+   public String toString() {
+      return MoreObjects.toStringHelper(this)
+              .add("routeTableId", routeTableId)
+              .add("vRouterId", vRouterId)
+              .add("vpcId", vpcId)
+              .toString();
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/VSwitch.java
----------------------------------------------------------------------
diff --git 
a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/VSwitch.java 
b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/VSwitch.java
new file mode 100644
index 0000000..9f73e30
--- /dev/null
+++ b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/domain/VSwitch.java
@@ -0,0 +1,112 @@
+/*
+ * 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.base.CaseFormat;
+import com.google.common.base.Enums;
+import com.google.common.base.Joiner;
+import com.google.common.base.Optional;
+import org.jclouds.json.SerializedNames;
+
+import java.util.Date;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+@AutoValue
+public abstract class VSwitch {
+
+   public enum Status {
+      AVAILABLE, UNAVAILABLE, PENDING;
+
+      public static Status fromValue(String value) {
+         Optional<Status> status = Enums.getIfPresent(Status.class, 
value.toUpperCase());
+         checkArgument(status.isPresent(), "Expected one of %s but was %s", 
Joiner.on(',').join(Status.values()), value);
+         return status.get();
+      }
+
+      public String value() {
+         return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, name());
+      }
+
+      @Override
+      public String toString() {
+         return value();
+      }
+   }
+
+   VSwitch() {}
+
+   @SerializedNames({"CidrBlock", "CreationTime", "Description", "ZoneId", 
"Status",
+           "AvailableIpAddressCount", "VpcId", "VSwitchId", "VSwitchName" })
+   public static VSwitch create(String cidrBlock, Date creationTime, String 
description, String zoneId, Status status,
+                                int availableIpAddressCount,
+                                String vpcId, String id, String name) {
+      return 
builder().cidrBlock(cidrBlock).creationTime(creationTime).description(description).zoneId(zoneId).status(status)
+              
.availableIpAddressCount(availableIpAddressCount).vpcId(vpcId).id(id).name(name).build();
+   }
+
+   public abstract String cidrBlock();
+
+   public abstract Date creationTime();
+
+   public abstract String description();
+
+   public abstract String zoneId();
+
+   public abstract Status status();
+
+   public abstract int availableIpAddressCount();
+
+   public abstract String vpcId();
+
+   public abstract String id();
+
+   public abstract String name();
+
+   public abstract Builder toBuilder();
+
+   public static Builder builder() {
+      return new AutoValue_VSwitch.Builder();
+   }
+
+   @AutoValue.Builder
+   public abstract static class Builder {
+
+      public abstract Builder cidrBlock(String cidrBlock);
+
+      public abstract Builder creationTime(Date creationTime);
+
+      public abstract Builder description(String description);
+
+      public abstract Builder zoneId(String regionId);
+
+      public abstract Builder status(Status status);
+
+      public abstract Builder availableIpAddressCount(int 
availableIpAddressCount);
+
+      public abstract Builder vpcId(String vpcId);
+
+      public abstract Builder id(String id);
+
+      public abstract Builder name(String name);
+
+      abstract VSwitch build();
+   }
+
+
+}

Reply via email to