You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jclouds.apache.org by an...@apache.org on 2018/09/12 04:25:24 UTC
[7/7] jclouds-labs git commit: [JCLOUDS-1430] Aliyun ECS
[JCLOUDS-1430] Aliyun ECS
- add instance API
- add compute abstraction
- add validation for vpc and vSwitch IDs
- add builders for Image and Instance
- add unit tests for compute/functions
- add pagination to instanceStatus api
- rename provider id
- clean up code
- add network apis
- vpc api + tests
- vswitch api + tests
- improve CreateResourcesThenCreateNodes
- create default vpc and vswitch in case needed
- fix InstanceApiLiveTest
- add ECSDependencyViolationRetryHandler
- add ErrorRetryHandler
- fix ListImagesOptions.imageId
- fix enums in Instance and EIPAddress
Project: http://git-wip-us.apache.org/repos/asf/jclouds-labs/repo
Commit: http://git-wip-us.apache.org/repos/asf/jclouds-labs/commit/2c7db7e8
Tree: http://git-wip-us.apache.org/repos/asf/jclouds-labs/tree/2c7db7e8
Diff: http://git-wip-us.apache.org/repos/asf/jclouds-labs/diff/2c7db7e8
Branch: refs/heads/master
Commit: 2c7db7e809085a7738d76f5714663231dff0a9f3
Parents: a5dbf00
Author: andreaturli <an...@gmail.com>
Authored: Tue Jul 3 14:47:43 2018 +0200
Committer: Andrea Turli <an...@gmail.com>
Committed: Tue Sep 11 21:19:59 2018 -0700
----------------------------------------------------------------------
aliyun-ecs/README.md | 43 +
aliyun-ecs/pom.xml | 3 -
.../aliyun/ecs/ECSComputeServiceApi.java | 11 +
.../ecs/ECSComputeServiceProviderMetadata.java | 4 +-
.../aliyun/ecs/ECSServiceApiMetadata.java | 6 +-
.../aliyun/ecs/compute/ECSComputeService.java | 154 +
.../ecs/compute/ECSComputeServiceAdapter.java | 278 ++
.../compute/config/ECSServiceContextModule.java | 160 +
.../compute/functions/ImageInRegionToImage.java | 104 +
.../functions/InstanceStatusToStatus.java | 44 +
.../functions/InstanceToNodeMetadata.java | 126 +
.../functions/InstanceTypeToHardware.java | 49 +
.../ecs/compute/functions/RegionToLocation.java | 54 +
.../functions/internal/OperatingSystems.java | 51 +
.../options/ECSServiceTemplateOptions.java | 180 +
.../ecs/compute/strategy/CleanupResources.java | 112 +
.../CreateResourcesThenCreateNodes.java | 360 ++
.../config/ECSComputeServiceHttpApiModule.java | 24 +
.../domain/AllocatePublicIpAddressRequest.java | 59 +
.../aliyun/ecs/domain/AvailableResource.java | 43 +
.../aliyun/ecs/domain/AvailableZone.java | 47 +
.../ecs/domain/DedicatedHostAttribute.java | 37 +
.../jclouds/aliyun/ecs/domain/EipAddress.java | 67 +
.../jclouds/aliyun/ecs/domain/ErrorMessage.java | 39 +
.../org/jclouds/aliyun/ecs/domain/Image.java | 112 +-
.../org/jclouds/aliyun/ecs/domain/Instance.java | 321 ++
.../aliyun/ecs/domain/InstanceRequest.java | 59 +
.../aliyun/ecs/domain/InstanceStatus.java | 55 +
.../jclouds/aliyun/ecs/domain/InstanceType.java | 85 +
.../aliyun/ecs/domain/NetworkInterface.java | 41 +
.../jclouds/aliyun/ecs/domain/Permission.java | 2 +-
.../jclouds/aliyun/ecs/domain/ResourceType.java | 42 +
.../aliyun/ecs/domain/SecurityGroup.java | 14 +-
.../aliyun/ecs/domain/SupportedResource.java | 52 +
.../java/org/jclouds/aliyun/ecs/domain/Tag.java | 14 +-
.../org/jclouds/aliyun/ecs/domain/UserCidr.java | 27 +
.../java/org/jclouds/aliyun/ecs/domain/VPC.java | 131 +
.../jclouds/aliyun/ecs/domain/VPCRequest.java | 74 +
.../org/jclouds/aliyun/ecs/domain/VSwitch.java | 112 +
.../aliyun/ecs/domain/VSwitchRequest.java | 58 +
.../aliyun/ecs/domain/VpcAttributes.java | 48 +
.../domain/internal/PaginatedCollection.java | 4 +-
.../domain/options/CreateInstanceOptions.java | 161 +
.../ecs/domain/options/CreateVPCOptions.java | 106 +
.../domain/options/CreateVSwitchOptions.java | 74 +
.../ecs/domain/options/ListImagesOptions.java | 6 +-
.../options/ListInstanceStatusOptions.java | 50 +
.../domain/options/ListInstancesOptions.java | 239 +
.../ecs/domain/options/ListVPCsOptions.java | 63 +
.../domain/options/ListVSwitchesOptions.java | 89 +
.../aliyun/ecs/domain/options/TagOptions.java | 22 +-
.../ecs/domain/regionscoped/ImageInRegion.java | 36 +
.../ecs/domain/regionscoped/RegionAndId.java | 54 +
.../aliyun/ecs/features/InstanceApi.java | 264 ++
.../aliyun/ecs/features/SecurityGroupApi.java | 3 +
.../aliyun/ecs/features/SshKeyPairApi.java | 4 +
.../org/jclouds/aliyun/ecs/features/TagApi.java | 10 +-
.../org/jclouds/aliyun/ecs/features/VPCApi.java | 140 +
.../jclouds/aliyun/ecs/features/VSwitchApi.java | 145 +
.../ecs/functions/PutStringInDoubleQuotes.java | 27 +
.../ecs/handlers/ECSErrorRetryHandler.java | 74 +
.../ecs/predicates/InstanceStatusPredicate.java | 33 +
.../ecs/compute/ECSComputeServiceLiveTest.java | 46 +
.../ecs/compute/ECSTemplateBuilderLiveTest.java | 53 +
.../ecs/compute/features/ImageApiLiveTest.java | 13 +-
.../ecs/compute/features/ImageApiMockTest.java | 19 +-
.../compute/features/InstanceApiLiveTest.java | 189 +
.../compute/features/InstanceApiMockTest.java | 168 +
.../features/RegionAndZoneApiMockTest.java | 9 +-
.../features/SecurityGroupApiLiveTest.java | 11 +-
.../features/SecurityGroupApiMockTest.java | 19 +-
.../compute/features/SshKeyPairApiLiveTest.java | 13 +-
.../compute/features/SshKeyPairApiMockTest.java | 21 +-
.../ecs/compute/features/TagApiLiveTest.java | 16 +-
.../ecs/compute/features/TagApiMockTest.java | 19 +-
.../ecs/compute/features/VPCApiLiveTest.java | 89 +
.../ecs/compute/features/VPCApiMockTest.java | 87 +
.../compute/features/VSwitchApiLiveTest.java | 100 +
.../compute/features/VSwitchApiMockTest.java | 87 +
.../functions/ImageInRegionToImageTest.java | 141 +
.../functions/InstanceStatusToStatusTest.java | 58 +
.../functions/InstanceToHardwareTest.java | 61 +
.../functions/InstanceToNodeMetadataTest.java | 249 +
.../BaseECSComputeServiceApiLiveTest.java | 22 +-
.../BaseECSComputeServiceApiMockTest.java | 5 +-
.../CreateResourcesThenCreateNodesTest.java | 395 ++
.../src/test/resources/availableZones.json | 423 ++
.../src/test/resources/instanceStatus.json | 18 +
.../src/test/resources/instanceTypes.json | 4281 ++++++++++++++++++
.../src/test/resources/instances-first.json | 960 ++++
.../src/test/resources/instances-last.json | 960 ++++
aliyun-ecs/src/test/resources/logback-test.xml | 42 +
.../src/test/resources/vpc-create-res.json | 6 +
.../src/test/resources/vpc-delete-res.json | 3 +
aliyun-ecs/src/test/resources/vpcs-first.json | 29 +
aliyun-ecs/src/test/resources/vpcs-last.json | 29 +
.../src/test/resources/vswitch-create-res.json | 4 +
.../src/test/resources/vswitch-delete-res.json | 3 +
.../src/test/resources/vswitches-first.json | 22 +
.../src/test/resources/vswitches-last.json | 22 +
100 files changed, 13485 insertions(+), 113 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/README.md
----------------------------------------------------------------------
diff --git a/aliyun-ecs/README.md b/aliyun-ecs/README.md
new file mode 100644
index 0000000..5bb8723
--- /dev/null
+++ b/aliyun-ecs/README.md
@@ -0,0 +1,43 @@
+alibaba Elastic Compute Service Provider
+==========================
+
+# How to use it
+
+alibaba ECS provider works exactly as any other jclouds provider.
+Notice that as alibaba supports dozens of locations and to limit the scope of some operations, one may want to use:
+
+and
+```bash
+jclouds.regions
+```
+which is by default `null`. If you want to target only the `north europe` region, you can use
+
+```bash
+jclouds.regions="eu-central-1"
+```
+
+# Setting Up Test Environment
+
+Get or create the `User Access Key` and `Access Key Secret` for your account at `https://usercenter.console.alibaba.com/#/manage/ak`
+
+# Run Live Tests
+
+Use the following to run one live test:
+
+```bash
+mvn -Dtest=<name of the live test> \
+ -Dtest.alibaba-ecs.identity="<AccessKey ID>" \
+ -Dtest.alibaba-ecs.credential="<Access Key Secret>"
+ integration-test -Plive
+```
+
+Use the following to run all the live tests:
+
+```bash
+
+mvn clean verify -Plive \
+ -Dtest.alibaba-ecs.identity="<AccessKey ID>" \
+ -Dtest.alibaba-ecs.credential="<Access Key Secret>"
+```
+
+
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/pom.xml
----------------------------------------------------------------------
diff --git a/aliyun-ecs/pom.xml b/aliyun-ecs/pom.xml
index da1b900..7b81aaf 100644
--- a/aliyun-ecs/pom.xml
+++ b/aliyun-ecs/pom.xml
@@ -32,8 +32,6 @@
<properties>
<test.aliyun-ecs.endpoint>https://ecs.aliyuncs.com/</test.aliyun-ecs.endpoint>
- <test.aliyun-ecs.api-version></test.aliyun-ecs.api-version>
- <test.aliyun-ecs.build-version/>
<test.aliyun-ecs.identity>FIXME_IDENTITY</test.aliyun-ecs.identity>
<test.aliyun-ecs.credential>FIXME_CREDENTIALS</test.aliyun-ecs.credential>
<test.aliyun-ecs.template/>
@@ -142,6 +140,5 @@
</profile>
</profiles>
-
</project>
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/ECSComputeServiceApi.java
----------------------------------------------------------------------
diff --git a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/ECSComputeServiceApi.java b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/ECSComputeServiceApi.java
index bb24cf0..2b0c9f8 100644
--- a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/ECSComputeServiceApi.java
+++ b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/ECSComputeServiceApi.java
@@ -17,10 +17,13 @@
package org.jclouds.aliyun.ecs;
import org.jclouds.aliyun.ecs.features.ImageApi;
+import org.jclouds.aliyun.ecs.features.InstanceApi;
import org.jclouds.aliyun.ecs.features.RegionAndZoneApi;
import org.jclouds.aliyun.ecs.features.SecurityGroupApi;
import org.jclouds.aliyun.ecs.features.SshKeyPairApi;
import org.jclouds.aliyun.ecs.features.TagApi;
+import org.jclouds.aliyun.ecs.features.VPCApi;
+import org.jclouds.aliyun.ecs.features.VSwitchApi;
import org.jclouds.rest.annotations.Delegate;
import java.io.Closeable;
@@ -42,4 +45,12 @@ public interface ECSComputeServiceApi extends Closeable {
@Delegate
TagApi tagApi();
+ @Delegate
+ InstanceApi instanceApi();
+
+ @Delegate
+ VPCApi vpcApi();
+
+ @Delegate
+ VSwitchApi vSwitchApi();
}
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/ECSComputeServiceProviderMetadata.java
----------------------------------------------------------------------
diff --git a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/ECSComputeServiceProviderMetadata.java b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/ECSComputeServiceProviderMetadata.java
index fbd7206..3ffed6d 100644
--- a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/ECSComputeServiceProviderMetadata.java
+++ b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/ECSComputeServiceProviderMetadata.java
@@ -51,8 +51,8 @@ public class ECSComputeServiceProviderMetadata extends BaseProviderMetadata {
public static class Builder extends BaseProviderMetadata.Builder {
protected Builder() {
- id("aliyun-ecs")
- .name("Alibaba Elastic Compute Service")
+ id("alibaba-ecs")
+ .name("Alibaba Cloud Elastic Compute Service")
.apiMetadata(new ECSServiceApiMetadata())
.homepage(URI.create("https://www.alibabacloud.com"))
.console(URI.create("https://ecs.console.aliyun.com"))
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/ECSServiceApiMetadata.java
----------------------------------------------------------------------
diff --git a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/ECSServiceApiMetadata.java b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/ECSServiceApiMetadata.java
index d81ef96..54e725c 100644
--- a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/ECSServiceApiMetadata.java
+++ b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/ECSServiceApiMetadata.java
@@ -18,6 +18,7 @@ package org.jclouds.aliyun.ecs;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Module;
+import org.jclouds.aliyun.ecs.compute.config.ECSServiceContextModule;
import org.jclouds.aliyun.ecs.config.ECSComputeServiceHttpApiModule;
import org.jclouds.aliyun.ecs.config.ECSComputeServiceParserModule;
import org.jclouds.apis.ApiMetadata;
@@ -46,7 +47,7 @@ public class ECSServiceApiMetadata extends BaseHttpApiMetadata<ECSComputeService
public static Properties defaultProperties() {
Properties properties = BaseHttpApiMetadata.defaultProperties();
- properties.put(TEMPLATE, "osFamily=CENTOS,os64Bit=true,osVersionMatches=7.4");
+ properties.put(TEMPLATE, "osFamily=CENTOS,os64Bit=true,osVersionMatches=7.*");
properties.put(TIMEOUT_NODE_RUNNING, 900000); // 15 mins
properties.put(TIMEOUT_NODE_SUSPENDED, 900000); // 15 mins
return properties;
@@ -60,7 +61,7 @@ public class ECSServiceApiMetadata extends BaseHttpApiMetadata<ECSComputeService
public static class Builder extends BaseHttpApiMetadata.Builder<ECSComputeServiceApi, Builder> {
protected Builder() {
- id("aliyun-ecs")
+ id("alibaba-ecs")
.name("Alibaba Elastic Compute Service API")
.identityName("user name")
.credentialName("user password")
@@ -72,6 +73,7 @@ public class ECSServiceApiMetadata extends BaseHttpApiMetadata<ECSComputeService
.defaultModules(ImmutableSet.<Class<? extends Module>>builder()
.add(ECSComputeServiceHttpApiModule.class)
.add(ECSComputeServiceParserModule.class)
+ .add(ECSServiceContextModule.class)
.build());
}
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/ECSComputeService.java
----------------------------------------------------------------------
diff --git a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/ECSComputeService.java b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/ECSComputeService.java
new file mode 100644
index 0000000..b3c6802
--- /dev/null
+++ b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/ECSComputeService.java
@@ -0,0 +1,154 @@
+/*
+ * 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.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.base.Supplier;
+import com.google.common.collect.Iterables;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import org.jclouds.Constants;
+import org.jclouds.aliyun.ecs.ECSComputeServiceApi;
+import org.jclouds.aliyun.ecs.compute.strategy.CleanupResources;
+import org.jclouds.aliyun.ecs.domain.SecurityGroup;
+import org.jclouds.aliyun.ecs.domain.VSwitch;
+import org.jclouds.aliyun.ecs.domain.options.ListVSwitchesOptions;
+import org.jclouds.aliyun.ecs.domain.regionscoped.RegionAndId;
+import org.jclouds.collect.Memoized;
+import org.jclouds.compute.ComputeServiceContext;
+import org.jclouds.compute.callables.RunScriptOnNode;
+import org.jclouds.compute.domain.Hardware;
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.TemplateBuilder;
+import org.jclouds.compute.extensions.ImageExtension;
+import org.jclouds.compute.extensions.SecurityGroupExtension;
+import org.jclouds.compute.extensions.internal.DelegatingImageExtension;
+import org.jclouds.compute.internal.BaseComputeService;
+import org.jclouds.compute.internal.PersistNodeCredentials;
+import org.jclouds.compute.options.TemplateOptions;
+import org.jclouds.compute.strategy.CreateNodesInGroupThenAddToSet;
+import org.jclouds.compute.strategy.DestroyNodeStrategy;
+import org.jclouds.compute.strategy.GetImageStrategy;
+import org.jclouds.compute.strategy.GetNodeMetadataStrategy;
+import org.jclouds.compute.strategy.InitializeRunScriptOnNodeOrPlaceInBadMap;
+import org.jclouds.compute.strategy.ListNodesStrategy;
+import org.jclouds.compute.strategy.RebootNodeStrategy;
+import org.jclouds.compute.strategy.ResumeNodeStrategy;
+import org.jclouds.compute.strategy.SuspendNodeStrategy;
+import org.jclouds.domain.Credentials;
+import org.jclouds.domain.Location;
+import org.jclouds.scriptbuilder.functions.InitAdminAccess;
+
+import javax.annotation.Nullable;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Provider;
+import javax.inject.Singleton;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_RUNNING;
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_SUSPENDED;
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_TERMINATED;
+
+@Singleton
+public class ECSComputeService extends BaseComputeService {
+ private final CleanupResources cleanupResources;
+
+ @Inject
+ protected ECSComputeService(ComputeServiceContext context, Map<String, Credentials> credentialStore,
+ @Memoized Supplier<Set<? extends Image>> images, @Memoized Supplier<Set<? extends Hardware>> sizes,
+ @Memoized Supplier<Set<? extends Location>> locations, ListNodesStrategy listNodesStrategy,
+ GetImageStrategy getImageStrategy, GetNodeMetadataStrategy getNodeMetadataStrategy,
+ CreateNodesInGroupThenAddToSet runNodesAndAddToSetStrategy, RebootNodeStrategy rebootNodeStrategy,
+ DestroyNodeStrategy destroyNodeStrategy, ResumeNodeStrategy startNodeStrategy,
+ SuspendNodeStrategy stopNodeStrategy, Provider<TemplateBuilder> templateBuilderProvider,
+ @Named("DEFAULT") Provider<TemplateOptions> templateOptionsProvider,
+ @Named(TIMEOUT_NODE_RUNNING) Predicate<AtomicReference<NodeMetadata>> nodeRunning,
+ @Named(TIMEOUT_NODE_TERMINATED) Predicate<AtomicReference<NodeMetadata>> nodeTerminated,
+ @Named(TIMEOUT_NODE_SUSPENDED) Predicate<AtomicReference<NodeMetadata>> nodeSuspended,
+ InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory,
+ RunScriptOnNode.Factory runScriptOnNodeFactory, InitAdminAccess initAdminAccess,
+ PersistNodeCredentials persistNodeCredentials,
+ @Named(Constants.PROPERTY_USER_THREADS) ListeningExecutorService userExecutor,
+ CleanupResources cleanupResources, Optional<ImageExtension> imageExtension,
+ Optional<SecurityGroupExtension> securityGroupExtension,
+ DelegatingImageExtension.Factory delegatingImageExtension) {
+ super(context, credentialStore, images, sizes, locations, listNodesStrategy, getImageStrategy,
+ getNodeMetadataStrategy, runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy,
+ startNodeStrategy, stopNodeStrategy, templateBuilderProvider, templateOptionsProvider, nodeRunning,
+ nodeTerminated, nodeSuspended, initScriptRunnerFactory, initAdminAccess, runScriptOnNodeFactory,
+ persistNodeCredentials, userExecutor, imageExtension, securityGroupExtension, delegatingImageExtension);
+ this.cleanupResources = cleanupResources;
+ }
+
+ @Override
+ protected void cleanUpIncidentalResourcesOfDeadNodes(Set<? extends NodeMetadata> deadNodes) {
+ for (NodeMetadata deadNode : deadNodes) {
+ RegionAndId regionAndId = RegionAndId.fromSlashEncoded(deadNode.getId());
+ Set<String> tags = deadNode.getTags();
+ String vSwitchId = extractVSwitchId(tags);
+ VSwitch vSwitch = context.unwrapApi(ECSComputeServiceApi.class).vSwitchApi().list(deadNode.getLocation().getId(), ListVSwitchesOptions.Builder.vSwitchId(vSwitchId)).first().orNull();
+ String vpcId = vSwitch.vpcId();
+
+ try {
+ cleanupResources.cleanupNode(regionAndId);
+ } catch (Exception ex) {
+ logger.warn(ex, "Error cleaning up resources for node %s", deadNode);
+ }
+
+ List<SecurityGroup> securityGroups = cleanupResources.findOrphanedSecurityGroups(regionAndId.regionId(), deadNode.getGroup());
+ for (SecurityGroup securityGroup : securityGroups) {
+ logger.debug(">> destroying security group %s ...", securityGroup.id());
+ if (cleanupResources.cleanupSecurityGroupIfOrphaned(regionAndId.regionId(), securityGroup.id())) {
+ logger.debug(">> security group: (%s) has been deleted.", securityGroup.id());
+ } else {
+ logger.warn(">> security group: (%s) has not been deleted.", securityGroup.id());
+ }
+ }
+
+ // FIXME not sure it is correct to always delete vSwitch and VPC_PREFIX
+ logger.debug(">> destroying vSwitch %s ...", vSwitchId);
+ if (cleanupResources.cleanupVSwitchIfOrphaned(regionAndId.regionId(), vSwitchId)) {
+ logger.debug(">> vSwitch: (%s) has been deleted.", vSwitchId);
+ } else {
+ logger.warn(">> vSwitch: (%s) has not been deleted.", vSwitchId);
+ }
+
+ logger.debug(">> destroying vpc %s ...", vpcId);
+ try {
+ cleanupResources.cleanupVPCIfOrphaned(regionAndId.regionId(), vpcId);
+ logger.debug(">> VPC_PREFIX: (%s) has been deleted.", vpcId);
+ } catch (IllegalArgumentException e) {
+ logger.warn(">> VPC_PREFIX: (%s) has not been deleted.", vpcId);
+ }
+ }
+ }
+
+ private String extractVSwitchId(Set<String> tags) {
+ String vSwitchIdTag = Iterables.tryFind(tags, new Predicate<String>() {
+ @Override
+ public boolean apply(@Nullable String input) {
+ return input.startsWith("vsw-");
+ }
+ }).orNull();
+ return vSwitchIdTag;
+ }
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/ECSComputeServiceAdapter.java
----------------------------------------------------------------------
diff --git a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/ECSComputeServiceAdapter.java b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/ECSComputeServiceAdapter.java
new file mode 100644
index 0000000..7e12ed8
--- /dev/null
+++ b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/ECSComputeServiceAdapter.java
@@ -0,0 +1,278 @@
+/*
+ * 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.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.base.Supplier;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import org.jclouds.aliyun.ecs.ECSComputeServiceApi;
+import org.jclouds.aliyun.ecs.compute.strategy.CleanupResources;
+import org.jclouds.aliyun.ecs.domain.AvailableResource;
+import org.jclouds.aliyun.ecs.domain.AvailableZone;
+import org.jclouds.aliyun.ecs.domain.Image;
+import org.jclouds.aliyun.ecs.domain.Instance;
+import org.jclouds.aliyun.ecs.domain.InstanceRequest;
+import org.jclouds.aliyun.ecs.domain.InstanceType;
+import org.jclouds.aliyun.ecs.domain.Region;
+import org.jclouds.aliyun.ecs.domain.SupportedResource;
+import org.jclouds.aliyun.ecs.domain.options.CreateInstanceOptions;
+import org.jclouds.aliyun.ecs.domain.options.ListImagesOptions;
+import org.jclouds.aliyun.ecs.domain.options.ListInstancesOptions;
+import org.jclouds.aliyun.ecs.domain.options.TagOptions;
+import org.jclouds.aliyun.ecs.domain.regionscoped.ImageInRegion;
+import org.jclouds.aliyun.ecs.domain.regionscoped.RegionAndId;
+import org.jclouds.aliyun.ecs.compute.options.ECSServiceTemplateOptions;
+import org.jclouds.compute.ComputeServiceAdapter;
+import org.jclouds.compute.domain.Template;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.compute.util.ComputeServiceUtils;
+import org.jclouds.logging.Logger;
+
+import javax.annotation.Nullable;
+import javax.annotation.Resource;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.collect.Iterables.contains;
+import static com.google.common.collect.Lists.newArrayList;
+import static java.lang.String.format;
+import static org.jclouds.aliyun.ecs.domain.regionscoped.RegionAndId.fromSlashEncoded;
+import static org.jclouds.aliyun.ecs.domain.regionscoped.RegionAndId.slashEncodeRegionAndId;
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_SUSPENDED;
+
+/**
+ * defines the connection between the {@link ECSComputeServiceApi} implementation and
+ * the jclouds {@link org.jclouds.compute.ComputeService}
+ */
+@Singleton
+public class ECSComputeServiceAdapter implements ComputeServiceAdapter<Instance, InstanceType, ImageInRegion, Region> {
+
+ private final ECSComputeServiceApi api;
+ private final Predicate<String> instanceSuspendedPredicate;
+
+ private final Supplier<Set<String>> regionIds;
+ private final CleanupResources cleanupResources;
+
+ @Resource
+ @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+ protected Logger logger = Logger.NULL;
+
+ @Inject
+ ECSComputeServiceAdapter(ECSComputeServiceApi api,
+ @Named(TIMEOUT_NODE_SUSPENDED) Predicate<String> instanceSuspendedPredicate,
+ @org.jclouds.location.Region Supplier<Set<String>> regionIds,
+ CleanupResources cleanupResources) {
+ this.api = api;
+ this.instanceSuspendedPredicate = instanceSuspendedPredicate;
+ this.regionIds = regionIds;
+ this.cleanupResources = cleanupResources;
+ }
+
+ @Override
+ public NodeAndInitialCredentials<Instance> createNodeWithGroupEncodedIntoName(String group, String name, Template template) {
+ String instanceType = template.getHardware().getId();
+ String regionId = template.getLocation().getId();
+ String imageId = template.getImage().getId();
+
+ ECSServiceTemplateOptions templateOptions = template.getOptions().as(ECSServiceTemplateOptions.class);
+
+ String keyPairName = templateOptions.getKeyPairName();
+ String securityGroupId = Iterables.getOnlyElement(templateOptions.getGroups());
+ String vSwitchId = templateOptions.getVSwitchId();
+ Instance.InternetChargeType internetChargeType = Instance.InternetChargeType.fromValue(templateOptions.getInternetChargeType());
+ int internetMaxBandwidthOut = templateOptions.getInternetMaxBandwidthOut();
+ String instanceChargeType = templateOptions.getInstanceChargeType();
+
+ Map<String, String> tags = ComputeServiceUtils.metadataAndTagsAsValuesOfEmptyString(templateOptions);
+ tags = new ImmutableMap.Builder()
+ .putAll(tags)
+ .put(vSwitchId, "")
+ .build();
+ TagOptions tagOptions = TagOptions.Builder.tags(tags);
+
+ InstanceRequest instanceRequest = api.instanceApi().create(regionId, RegionAndId.fromSlashEncoded(imageId).id(), securityGroupId, name, instanceType,
+ CreateInstanceOptions.Builder
+ .vSwitchId(vSwitchId)
+ .internetChargeType(internetChargeType.toString())
+ .internetMaxBandwidthOut(internetMaxBandwidthOut)
+ .instanceChargeType(instanceChargeType)
+ .instanceName(name)
+ .keyPairName(keyPairName)
+ .tagOptions(tagOptions)
+ );
+
+ String regionAndInstanceId = slashEncodeRegionAndId(regionId, instanceRequest.getInstanceId());
+ if (!instanceSuspendedPredicate.apply(regionAndInstanceId)) {
+ final String message = format("Instance %s was not created correctly. The associated resources created for it will be destroyed", instanceRequest.getInstanceId());
+ logger.warn(message);
+ cleanupResources.cleanupNode(RegionAndId.create(regionId, instanceRequest.getInstanceId()));
+ cleanupResources.cleanupSecurityGroupIfOrphaned(regionId, securityGroupId);
+ }
+
+ api.instanceApi().allocatePublicIpAddress(regionId, instanceRequest.getInstanceId());
+ api.instanceApi().powerOn(instanceRequest.getInstanceId());
+ Instance instance = Iterables.get(api.instanceApi().list(regionId, ListInstancesOptions.Builder.instanceIds(instanceRequest.getInstanceId())), 0);
+
+ // Safe to pass null credentials here, as jclouds will default populate
+ // the node with the default credentials from the image, or the ones in
+ // the options, if provided.
+ return new NodeAndInitialCredentials(instance,
+ slashEncodeRegionAndId(regionId, instanceRequest.getInstanceId()), null);
+ }
+
+ @Override
+ public Iterable<InstanceType> listHardwareProfiles() {
+ final ImmutableSet.Builder<String> instanceTypeIdsBuilder = ImmutableSet.builder();
+ for (String regionId : getAvailableLocationNames()) {
+ instanceTypeIdsBuilder.addAll(getInstanceTypeIds(regionId));
+ }
+ final Set<String> ids = instanceTypeIdsBuilder.build();
+
+ List<InstanceType> instanceTypes = FluentIterable.from(api.instanceApi().listTypes())
+ .filter(new Predicate<InstanceType>() {
+ @Override
+ public boolean apply(@Nullable InstanceType input) {
+ return contains(ids, input.id());
+ }
+ }).toList();
+
+ return instanceTypes;
+ }
+
+ private List<String> getInstanceTypeIds(String regionId) {
+ List<String> instanceTypeIds = Lists.newArrayList();
+ for (AvailableZone availableZone : api.instanceApi().listInstanceTypesByAvailableZone(regionId)) {
+ for (AvailableResource availableResource : availableZone.availableResources().get("AvailableResource")) {
+ for (SupportedResource supportedResource : availableResource.supportedResources()
+ .get("SupportedResource")) {
+ if (SupportedResource.Status.AVAILABLE == supportedResource.status()) {
+ instanceTypeIds.add(supportedResource.value());
+ }
+ }
+ }
+ }
+ return instanceTypeIds;
+ }
+
+ @Override
+ public Iterable<ImageInRegion> listImages() {
+ final ImmutableList.Builder<ImageInRegion> imagesInRegion = ImmutableList.builder();
+
+ for (final String regionId : getAvailableLocationNames()) {
+ imagesInRegion.addAll(api.imageApi().list(regionId).concat()
+ .transform(new Function<Image, ImageInRegion>() {
+ @Override
+ public ImageInRegion apply(Image image) {
+ return ImageInRegion.create(regionId, image);
+ }
+ })
+ );
+ }
+ return imagesInRegion.build();
+ }
+
+ @Override
+ public ImageInRegion getImage(final String id) {
+ RegionAndId regionAndId = fromSlashEncoded(id);
+ Image image = api.imageApi().list(regionAndId.regionId(), ListImagesOptions.Builder.imageIds(regionAndId.id()))
+ .firstMatch(Predicates.<Image>notNull())
+ .orNull();
+ if (image == null) return null;
+ return ImageInRegion.create(regionAndId.regionId(), image);
+ }
+
+ @Override
+ public Iterable<Region> listLocations() {
+ return FluentIterable.from(api.regionAndZoneApi().describeRegions()).filter(new Predicate<Region>() {
+ @Override
+ public boolean apply(Region region) {
+ return regionIds.get().isEmpty() ? true : regionIds.get().contains(region.id());
+ }
+ }).toList();
+ }
+
+ @Override
+ public Instance getNode(final String id) {
+ RegionAndId regionAndId = fromSlashEncoded(id);
+ return api.instanceApi().list(regionAndId.regionId(),
+ ListInstancesOptions.Builder.instanceIds(regionAndId.id()))
+ .firstMatch(Predicates.<Instance>notNull())
+ .orNull();
+ }
+
+ @Override
+ public void destroyNode(String id) {
+ checkState(cleanupResources.cleanupNode(RegionAndId.fromSlashEncoded(id)), "server(%s) and its resources still there after deleting!?", id);
+ }
+
+ @Override
+ public void rebootNode(String id) {
+ api.instanceApi().reboot(id);
+ }
+
+ @Override
+ public void resumeNode(String id) {
+ api.instanceApi().powerOn(id);
+ }
+
+ @Override
+ public void suspendNode(String id) {
+ api.instanceApi().powerOff(id);
+ }
+
+ @Override
+ public Iterable<Instance> listNodes() {
+ final ImmutableList.Builder<Instance> instances = ImmutableList.builder();
+ for (String regionId : getAvailableLocationNames()) {
+ instances.addAll(api.instanceApi().list(regionId).concat());
+ }
+ return instances.build();
+ }
+
+ @Override
+ public Iterable<Instance> listNodesByIds(final Iterable<String> ids) {
+
+ final ImmutableList.Builder<Instance> instancesBuilder = ImmutableList.builder();
+ for (String regionId : getAvailableLocationNames()) {
+ instancesBuilder.addAll(api.instanceApi().list(regionId, ListInstancesOptions.Builder.instanceIds(Iterables.toArray(ids, String.class))));
+ }
+ return instancesBuilder.build();
+ }
+
+ private List<String> getAvailableLocationNames() {
+ return newArrayList(
+ Iterables.transform(listLocations(), new Function<Region, String>() {
+ @Override
+ public String apply(Region location) {
+ return location.id();
+ }
+ }));
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/config/ECSServiceContextModule.java
----------------------------------------------------------------------
diff --git a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/config/ECSServiceContextModule.java b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/config/ECSServiceContextModule.java
new file mode 100644
index 0000000..ace98e9
--- /dev/null
+++ b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/config/ECSServiceContextModule.java
@@ -0,0 +1,160 @@
+/*
+ * 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.config;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.inject.Provides;
+import com.google.inject.TypeLiteral;
+import com.google.inject.name.Named;
+import org.jclouds.aliyun.ecs.ECSComputeServiceApi;
+import org.jclouds.aliyun.ecs.compute.ECSComputeService;
+import org.jclouds.aliyun.ecs.compute.ECSComputeServiceAdapter;
+import org.jclouds.aliyun.ecs.compute.functions.ImageInRegionToImage;
+import org.jclouds.aliyun.ecs.compute.functions.InstanceStatusToStatus;
+import org.jclouds.aliyun.ecs.compute.functions.InstanceToNodeMetadata;
+import org.jclouds.aliyun.ecs.compute.functions.InstanceTypeToHardware;
+import org.jclouds.aliyun.ecs.compute.functions.RegionToLocation;
+import org.jclouds.aliyun.ecs.compute.strategy.CreateResourcesThenCreateNodes;
+import org.jclouds.aliyun.ecs.domain.Instance;
+import org.jclouds.aliyun.ecs.domain.InstanceStatus;
+import org.jclouds.aliyun.ecs.domain.InstanceType;
+import org.jclouds.aliyun.ecs.domain.Region;
+import org.jclouds.aliyun.ecs.domain.regionscoped.ImageInRegion;
+import org.jclouds.aliyun.ecs.domain.regionscoped.RegionAndId;
+import org.jclouds.aliyun.ecs.compute.options.ECSServiceTemplateOptions;
+import org.jclouds.aliyun.ecs.predicates.InstanceStatusPredicate;
+import org.jclouds.compute.ComputeService;
+import org.jclouds.compute.ComputeServiceAdapter;
+import org.jclouds.compute.config.ComputeServiceAdapterContextModule;
+import org.jclouds.compute.domain.Hardware;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.functions.NodeAndTemplateOptionsToStatement;
+import org.jclouds.compute.functions.NodeAndTemplateOptionsToStatementWithoutPublicKey;
+import org.jclouds.compute.options.TemplateOptions;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.compute.strategy.CreateNodesInGroupThenAddToSet;
+import org.jclouds.domain.Location;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_RUNNING;
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_SUSPENDED;
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_TERMINATED;
+import static org.jclouds.util.Predicates2.retry;
+
+public class ECSServiceContextModule extends ComputeServiceAdapterContextModule<Instance, InstanceType, ImageInRegion, Region> {
+
+ @Override
+ protected void configure() {
+ super.configure();
+
+ bind(new TypeLiteral<ComputeServiceAdapter<Instance, InstanceType, ImageInRegion, Region>>() {
+ }).to(ECSComputeServiceAdapter.class);
+ bind(ComputeService.class).to(ECSComputeService.class);
+
+ bind(new TypeLiteral<Function<Instance, NodeMetadata>>() {
+ }).to(InstanceToNodeMetadata.class);
+ bind(new TypeLiteral<Function<InstanceType, Hardware>>() {
+ }).to(InstanceTypeToHardware.class);
+ bind(new TypeLiteral<Function<ImageInRegion, org.jclouds.compute.domain.Image>>() {
+ }).to(ImageInRegionToImage.class);
+ bind(new TypeLiteral<Function<Region, Location>>() {
+ }).to(RegionToLocation.class);
+ bind(new TypeLiteral<Function<Instance.Status, NodeMetadata.Status>>() {
+ }).to(InstanceStatusToStatus.class);
+ install(new LocationsFromComputeServiceAdapterModule<Instance, InstanceType, ImageInRegion, Region>() {
+ });
+ bind(TemplateOptions.class).to(ECSServiceTemplateOptions.class);
+ bind(CreateNodesInGroupThenAddToSet.class).to(CreateResourcesThenCreateNodes.class);
+ bind(NodeAndTemplateOptionsToStatement.class).to(NodeAndTemplateOptionsToStatementWithoutPublicKey.class);
+ }
+
+ @Provides
+ @Named(TIMEOUT_NODE_RUNNING)
+ protected Predicate<String> provideInstanceRunningPredicate(final ECSComputeServiceApi api,
+ ComputeServiceConstants.Timeouts timeouts, ComputeServiceConstants.PollPeriod pollPeriod) {
+ return retry(new InstanceInStatusPredicate(api, InstanceStatus.Status.RUNNING), timeouts.nodeRunning,
+ pollPeriod.pollInitialPeriod, pollPeriod.pollMaxPeriod);
+ }
+
+ @Provides
+ @Named(TIMEOUT_NODE_SUSPENDED)
+ protected Predicate<String> provideInstanceSuspendedPredicate(final ECSComputeServiceApi api,
+ ComputeServiceConstants.Timeouts timeouts, ComputeServiceConstants.PollPeriod pollPeriod) {
+ return retry(new InstanceInStatusPredicate(api, InstanceStatus.Status.STOPPED), timeouts.nodeSuspended,
+ pollPeriod.pollInitialPeriod, pollPeriod.pollMaxPeriod);
+ }
+
+ @Provides
+ @Named(TIMEOUT_NODE_TERMINATED)
+ protected Predicate<String> provideInstanceTerminatedPredicate(final ECSComputeServiceApi api,
+ ComputeServiceConstants.Timeouts timeouts, ComputeServiceConstants.PollPeriod pollPeriod) {
+ return retry(new InstanceTerminatedPredicate(api), timeouts.nodeTerminated, pollPeriod.pollInitialPeriod,
+ pollPeriod.pollMaxPeriod);
+ }
+
+ @VisibleForTesting
+ static class InstanceInStatusPredicate implements Predicate<String> {
+
+ private final ECSComputeServiceApi api;
+ private final InstanceStatus.Status desiredStatus;
+
+ public InstanceInStatusPredicate(ECSComputeServiceApi api, InstanceStatus.Status desiredStatus) {
+ this.api = checkNotNull(api, "api must not be null");
+ this.desiredStatus = checkNotNull(desiredStatus, "instance status must not be null");
+ }
+
+ @Override
+ public boolean apply(String id) {
+ checkNotNull(id, "id");
+ RegionAndId regionAndId = RegionAndId.fromSlashEncoded(id);
+ String regionId = regionAndId.regionId();
+ String instanceId = regionAndId.id();
+ InstanceStatus instanceStatus = api.instanceApi().listInstanceStatus(regionId)
+ .concat()
+ .firstMatch(new InstanceStatusPredicate(instanceId))
+ .orNull();
+ return instanceStatus != null && desiredStatus == instanceStatus.status();
+ }
+ }
+
+ @VisibleForTesting
+ static class InstanceTerminatedPredicate implements Predicate<String> {
+
+ private final ECSComputeServiceApi api;
+
+ public InstanceTerminatedPredicate(ECSComputeServiceApi api) {
+ this.api = checkNotNull(api, "api must not be null");
+ }
+
+ @Override
+ public boolean apply(String id) {
+ checkNotNull(id, "id");
+ RegionAndId regionAndId = RegionAndId.fromSlashEncoded(id);
+ String regionId = regionAndId.regionId();
+ final String instanceId = regionAndId.id();
+ InstanceStatus instanceStatus = api.instanceApi().listInstanceStatus(regionId)
+ .concat()
+ .firstMatch(new InstanceStatusPredicate(instanceId))
+ .orNull();
+ return instanceStatus == null;
+ }
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/functions/ImageInRegionToImage.java
----------------------------------------------------------------------
diff --git a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/functions/ImageInRegionToImage.java b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/functions/ImageInRegionToImage.java
new file mode 100644
index 0000000..72c45cf
--- /dev/null
+++ b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/functions/ImageInRegionToImage.java
@@ -0,0 +1,104 @@
+/*
+ * 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.functions;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableMap;
+import com.google.inject.Inject;
+import org.jclouds.aliyun.ecs.compute.functions.internal.OperatingSystems;
+import org.jclouds.aliyun.ecs.domain.regionscoped.ImageInRegion;
+import org.jclouds.aliyun.ecs.domain.regionscoped.RegionAndId;
+import org.jclouds.collect.Memoized;
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.ImageBuilder;
+import org.jclouds.compute.domain.OsFamily;
+import org.jclouds.domain.Location;
+import org.jclouds.location.predicates.LocationPredicates;
+
+import java.util.Map;
+import java.util.Set;
+
+import static com.google.common.collect.FluentIterable.from;
+import static com.google.common.collect.Iterables.tryFind;
+import static java.util.Arrays.asList;
+import static org.jclouds.compute.domain.OperatingSystem.builder;
+
+public class ImageInRegionToImage implements Function<ImageInRegion, Image> {
+
+ private final Supplier<Set<? extends Location>> locations;
+
+ private static final Map<String, OsFamily> OTHER_OS_MAP = ImmutableMap.<String, OsFamily>builder()
+ .put("Aliyun", OsFamily.LINUX).build();
+
+ private static Optional<OsFamily> findInStandardFamilies(final String platform) {
+ return tryFind(asList(OsFamily.values()), new Predicate<OsFamily>() {
+ @Override
+ public boolean apply(OsFamily input) {
+ return platform.toUpperCase().startsWith(input.name());
+ }
+ });
+ }
+
+ private static Optional<OsFamily> findInOtherOSMap(final String label) {
+ return tryFind(OTHER_OS_MAP.keySet(), new Predicate<String>() {
+ @Override
+ public boolean apply(String input) {
+ return label.contains(input);
+ }
+ }).transform(new Function<String, OsFamily>() {
+ @Override
+ public OsFamily apply(String input) {
+ return OTHER_OS_MAP.get(input);
+ }
+ });
+ }
+
+ @Inject
+ ImageInRegionToImage(@Memoized Supplier<Set<? extends Location>> locations) {
+ this.locations = locations;
+ }
+
+ @Override
+ public Image apply(ImageInRegion from) {
+ ImageBuilder builder = new ImageBuilder();
+ builder.id(RegionAndId.slashEncodeRegionAndId(from.regionId(), from.image().id()));
+ builder.providerId(from.image().id());
+ builder.name(from.image().name());
+ builder.description(from.image().description());
+ builder.status(from.image().status() == org.jclouds.aliyun.ecs.domain.Image.Status.AVAILABLE ?
+ Image.Status.AVAILABLE : Image.Status.PENDING);
+
+ OsFamily family = findInStandardFamilies(from.image().platform())
+ .or(findInOtherOSMap(from.image().platform()))
+ .or(OsFamily.UNRECOGNIZED);
+
+ String osVersion = OperatingSystems.version().apply(from.image());
+
+ builder.operatingSystem(
+ builder().name(from.image().osName()).family(family)
+ .description(from.image().description())
+ .version(osVersion)
+ .is64Bit("x86_64".equals(from.image().architecture()) ? true : false).build());
+
+ builder.location(from(locations.get()).firstMatch(LocationPredicates.idEquals(from.regionId())).orNull());
+ return builder.build();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/functions/InstanceStatusToStatus.java
----------------------------------------------------------------------
diff --git a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/functions/InstanceStatusToStatus.java b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/functions/InstanceStatusToStatus.java
new file mode 100644
index 0000000..8fcaf18
--- /dev/null
+++ b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/functions/InstanceStatusToStatus.java
@@ -0,0 +1,44 @@
+/*
+ * 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.functions;
+
+import com.google.common.base.Function;
+import com.google.common.base.Functions;
+import com.google.common.collect.ImmutableMap;
+import org.jclouds.aliyun.ecs.domain.Instance;
+import org.jclouds.compute.domain.NodeMetadata;
+
+import javax.inject.Singleton;
+
+/**
+ * Transforms an {@link Instance.Status} to the jclouds portable model.
+ */
+@Singleton
+public class InstanceStatusToStatus implements Function<Instance.Status, NodeMetadata.Status> {
+
+ private static final Function<Instance.Status, NodeMetadata.Status> toPortableStatus = Functions.forMap(
+ ImmutableMap.<Instance.Status, NodeMetadata.Status>builder()
+ .put(Instance.Status.STARTING, NodeMetadata.Status.PENDING)
+ .put(Instance.Status.STOPPING, NodeMetadata.Status.PENDING)
+ .put(Instance.Status.STOPPED, NodeMetadata.Status.SUSPENDED)
+ .put(Instance.Status.RUNNING, NodeMetadata.Status.RUNNING).build(), NodeMetadata.Status.UNRECOGNIZED);
+
+ @Override
+ public NodeMetadata.Status apply(Instance.Status input) {
+ return toPortableStatus.apply(input);
+ }
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/functions/InstanceToNodeMetadata.java
----------------------------------------------------------------------
diff --git a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/functions/InstanceToNodeMetadata.java b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/functions/InstanceToNodeMetadata.java
new file mode 100644
index 0000000..1ead7ce
--- /dev/null
+++ b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/functions/InstanceToNodeMetadata.java
@@ -0,0 +1,126 @@
+/*
+ * 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.functions;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableMap;
+import org.jclouds.aliyun.ecs.domain.Instance;
+import org.jclouds.aliyun.ecs.domain.Tag;
+import org.jclouds.aliyun.ecs.domain.regionscoped.RegionAndId;
+import org.jclouds.collect.Memoized;
+import org.jclouds.compute.domain.Hardware;
+import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.NodeMetadataBuilder;
+import org.jclouds.compute.functions.GroupNamingConvention;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.domain.Location;
+import org.jclouds.location.predicates.LocationPredicates;
+import org.jclouds.logging.Logger;
+
+import javax.annotation.Resource;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+import java.util.Map;
+import java.util.Set;
+
+import static com.google.common.collect.FluentIterable.from;
+import static org.jclouds.compute.util.ComputeServiceUtils.addMetadataAndParseTagsFromValuesOfEmptyString;
+
+/**
+ * Transforms an {@link Instance} to the jclouds portable model.
+ */
+@Singleton
+public class InstanceToNodeMetadata implements Function<Instance, NodeMetadata> {
+
+ private final Supplier<Map<String, ? extends Image>> images;
+ private final Supplier<Map<String, ? extends Hardware>> hardwares;
+ private final Supplier<Set<? extends Location>> locations;
+ private final Function<Instance.Status, NodeMetadata.Status> toPortableStatus;
+ private final GroupNamingConvention groupNamingConvention;
+
+ @Resource @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+ protected Logger logger = Logger.NULL;
+
+ @Inject
+ InstanceToNodeMetadata(Supplier<Map<String, ? extends Image>> images,
+ Supplier<Map<String, ? extends Hardware>> hardwares,
+ @Memoized Supplier<Set<? extends Location>> locations,
+ Function<Instance.Status, NodeMetadata.Status> toPortableStatus,
+ GroupNamingConvention.Factory groupNamingConvention) {
+ this.images = images;
+ this.hardwares = hardwares;
+ this.locations = locations;
+ this.toPortableStatus = toPortableStatus;
+ this.groupNamingConvention = groupNamingConvention.createWithoutPrefix();
+ }
+
+ @Override
+ public NodeMetadata apply(Instance from) {
+ NodeMetadataBuilder builder = new NodeMetadataBuilder();
+
+ Optional<? extends Image> image = findImage(from.imageId());
+ if (image.isPresent()) {
+ builder.imageId(image.get().getId());
+ builder.operatingSystem(image.get().getOperatingSystem());
+ } else {
+ logger.info(">> image with id %s for instance %s was not found. "
+ + "This might be because the image that was used to create the instance has a new id.",
+ from.instanceType(), from.id());
+ }
+ Optional<? extends Hardware> hardware = findHardware(from.instanceType());
+ if (hardware.isPresent()) {
+ builder.hardware(hardware.get());
+ } else {
+ logger.info(">> hardware with id %s for instance %s was not found. "
+ + "This might be because the image that was used to create the instance has a new id.",
+ from.instanceType(), from.id());
+ }
+
+ builder.id(RegionAndId.slashEncodeRegionAndId(from.regionId(), from.id()));
+ builder.providerId(from.id());
+ builder.name(from.name());
+ builder.hostname(String.format("%s", from.hostname()));
+ builder.group(groupNamingConvention.extractGroup(from.name()));
+ builder.status(toPortableStatus.apply(from.status()));
+ builder.privateAddresses(from.innerIpAddress().entrySet().iterator().next().getValue());
+ builder.publicAddresses(from.publicIpAddress().entrySet().iterator().next().getValue());
+ builder.location(from(locations.get()).firstMatch(LocationPredicates.idEquals(from.regionId())).orNull());
+ if (from.tags() != null && !from.tags().isEmpty()) {
+ ImmutableMap.Builder tagsBuilder = new ImmutableMap.Builder();
+ for (Tag tag : from.tags().entrySet().iterator().next().getValue()) {
+ tagsBuilder.put(tag.key(), tag.value());
+ }
+ addMetadataAndParseTagsFromValuesOfEmptyString(builder, tagsBuilder.build());
+ }
+
+ NodeMetadata nodeMetadata = builder.build();
+ return nodeMetadata;
+ }
+
+ private Optional<? extends Image> findImage(String imageId) {
+ return Optional.fromNullable(images.get().get(imageId));
+ }
+
+ private Optional<? extends Hardware> findHardware(String instanceType) {
+ return Optional.fromNullable(hardwares.get().get(instanceType));
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/functions/InstanceTypeToHardware.java
----------------------------------------------------------------------
diff --git a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/functions/InstanceTypeToHardware.java b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/functions/InstanceTypeToHardware.java
new file mode 100644
index 0000000..8197979
--- /dev/null
+++ b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/functions/InstanceTypeToHardware.java
@@ -0,0 +1,49 @@
+/*
+ * 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.functions;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
+import org.jclouds.aliyun.ecs.domain.InstanceType;
+import org.jclouds.compute.domain.Hardware;
+import org.jclouds.compute.domain.HardwareBuilder;
+import org.jclouds.compute.domain.Processor;
+
+import javax.inject.Singleton;
+
+@Singleton
+public class InstanceTypeToHardware implements Function<InstanceType, Hardware> {
+
+ private static final int GB_TO_MB_MULTIPLIER = 1024;
+
+ @Override
+ public Hardware apply(InstanceType input) {
+ HardwareBuilder builder = new HardwareBuilder()
+ .ids(input.id())
+ .name(input.id())
+ .hypervisor("none")
+ .processors(getProcessors(input.cpuCoreCount()))
+ .ram(input.memorySize().intValue() * GB_TO_MB_MULTIPLIER);
+ return builder.build();
+ }
+
+ private Iterable<Processor> getProcessors(Integer cpuCoreCount) {
+ // No cpu speed from API, so assume more cores == faster
+ return ImmutableList.of(new Processor(cpuCoreCount, cpuCoreCount));
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/functions/RegionToLocation.java
----------------------------------------------------------------------
diff --git a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/functions/RegionToLocation.java b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/functions/RegionToLocation.java
new file mode 100644
index 0000000..2bca519
--- /dev/null
+++ b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/functions/RegionToLocation.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.compute.functions;
+
+import com.google.common.base.Function;
+import org.jclouds.aliyun.ecs.domain.Region;
+import org.jclouds.domain.Location;
+import org.jclouds.domain.LocationBuilder;
+import org.jclouds.domain.LocationScope;
+import org.jclouds.location.suppliers.all.JustProvider;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import static com.google.common.collect.Iterables.getOnlyElement;
+
+/**
+ * Transforms an {@link Region} to the jclouds portable model.
+ */
+@Singleton
+public class RegionToLocation implements Function<Region, Location> {
+
+ private final JustProvider justProvider;
+
+ // allow us to lazy discover the provider of a resource
+ @Inject
+ RegionToLocation(JustProvider justProvider) {
+ this.justProvider = justProvider;
+ }
+
+ @Override
+ public Location apply(final Region region) {
+ final LocationBuilder builder = new LocationBuilder();
+ builder.id(region.id());
+ builder.description(region.localName());
+ builder.parent(getOnlyElement(justProvider.get()));
+ builder.scope(LocationScope.REGION);
+ return builder.build();
+ }
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/functions/internal/OperatingSystems.java
----------------------------------------------------------------------
diff --git a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/functions/internal/OperatingSystems.java b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/functions/internal/OperatingSystems.java
new file mode 100644
index 0000000..93681b2
--- /dev/null
+++ b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/functions/internal/OperatingSystems.java
@@ -0,0 +1,51 @@
+/*
+ * 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.functions.internal;
+
+import com.google.common.base.Function;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Iterables;
+import org.jclouds.aliyun.ecs.domain.Image;
+import org.jclouds.compute.domain.OsFamily;
+
+public class OperatingSystems {
+
+ public static Function<Image, String> version() {
+ return new Function<Image, String>() {
+ @Override
+ public String apply(final Image image) {
+ return parseVersion(image);
+ }
+ };
+ }
+
+ private static String parseVersion(Image image) {
+ String sequence = image.osName().trim().replaceAll("\\s+", " ");
+ int offset = 2;
+ if (isWindows(image)) {
+ sequence = image.platform();
+ offset = 1;
+ }
+ Iterable<String> splitted = Splitter.on(" ").split(sequence);
+ return Iterables.get(splitted, Iterables.size(splitted) - offset);
+ }
+
+ public static boolean isWindows(Image image) {
+ return image.platform().toUpperCase().startsWith(OsFamily.WINDOWS.name());
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/options/ECSServiceTemplateOptions.java
----------------------------------------------------------------------
diff --git a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/options/ECSServiceTemplateOptions.java b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/options/ECSServiceTemplateOptions.java
new file mode 100644
index 0000000..c378cbd
--- /dev/null
+++ b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/options/ECSServiceTemplateOptions.java
@@ -0,0 +1,180 @@
+/*
+ * 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.options;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
+import org.jclouds.compute.options.TemplateOptions;
+
+import static com.google.common.base.Objects.equal;
+
+/**
+ * Custom options for the Alibaba Elastic Compute Service API.
+ */
+public class ECSServiceTemplateOptions extends TemplateOptions implements Cloneable {
+
+ private String keyPairName = "";
+ private String vSwitchId = "";
+ private String internetChargeType = "PayByTraffic";
+ private String instanceChargeType = "PostPaid";
+ private int internetMaxBandwidthOut = 5;
+
+ public ECSServiceTemplateOptions keyPairName(String keyPairName) {
+ this.keyPairName = keyPairName;
+ return this;
+ }
+
+ public ECSServiceTemplateOptions vSwitchId(String vSwitchId) {
+ this.vSwitchId = vSwitchId;
+ return this;
+ }
+
+ public ECSServiceTemplateOptions internetChargeType(String internetChargeType) {
+ this.internetChargeType = internetChargeType;
+ return this;
+ }
+
+ public ECSServiceTemplateOptions instanceChargeType(String instanceChargeType) {
+ this.instanceChargeType = instanceChargeType;
+ return this;
+ }
+
+ public ECSServiceTemplateOptions internetMaxBandwidthOut(int internetMaxBandwidthOut) {
+ this.internetMaxBandwidthOut = internetMaxBandwidthOut;
+ return this;
+ }
+
+ public String getKeyPairName() {
+ return keyPairName;
+ }
+
+ public String getVSwitchId() {
+ return vSwitchId;
+ }
+
+ public String getInternetChargeType() {
+ return internetChargeType;
+ }
+
+ public String getInstanceChargeType() {
+ return instanceChargeType;
+ }
+
+ public int getInternetMaxBandwidthOut() {
+ return internetMaxBandwidthOut;
+ }
+
+ @Override
+ public ECSServiceTemplateOptions clone() {
+ ECSServiceTemplateOptions options = new ECSServiceTemplateOptions();
+ copyTo(options);
+ return options;
+ }
+
+ @Override
+ public void copyTo(TemplateOptions to) {
+ super.copyTo(to);
+ if (to instanceof ECSServiceTemplateOptions) {
+ ECSServiceTemplateOptions eTo = ECSServiceTemplateOptions.class.cast(to);
+ eTo.keyPairName(keyPairName);
+ eTo.vSwitchId(vSwitchId);
+ eTo.internetChargeType(internetChargeType);
+ eTo.instanceChargeType(instanceChargeType);
+ eTo.internetMaxBandwidthOut(internetMaxBandwidthOut);
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(super.hashCode(), keyPairName, vSwitchId, internetChargeType, instanceChargeType, internetMaxBandwidthOut);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!super.equals(obj)) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ ECSServiceTemplateOptions other = (ECSServiceTemplateOptions) obj;
+ return super.equals(other) &&
+ equal(this.keyPairName, other.keyPairName) &&
+ equal(this.vSwitchId, other.vSwitchId) &&
+ equal(this.internetChargeType, other.internetChargeType) &&
+ equal(this.instanceChargeType, other.instanceChargeType) &&
+ equal(this.internetMaxBandwidthOut, other.internetMaxBandwidthOut);
+ }
+
+ @Override
+ public MoreObjects.ToStringHelper string() {
+ MoreObjects.ToStringHelper toString = super.string().omitNullValues();
+ toString.add("keyPairName", keyPairName);
+ toString.add("vSwitchId", vSwitchId);
+ toString.add("internetChargeType", internetChargeType);
+ toString.add("instanceChargeType", instanceChargeType);
+ toString.add("internetMaxBandwidthOut", internetMaxBandwidthOut);
+ return toString;
+ }
+
+ public static class Builder {
+
+ /**
+ * @see ECSServiceTemplateOptions#keyPairName
+ */
+ public static ECSServiceTemplateOptions keyPairName(String keyPairName) {
+ ECSServiceTemplateOptions options = new ECSServiceTemplateOptions();
+ return options.keyPairName(keyPairName);
+ }
+
+ /**
+ * @see ECSServiceTemplateOptions#vSwitchId
+ */
+ public static ECSServiceTemplateOptions vSwitchId(String vSwitchId) {
+ ECSServiceTemplateOptions options = new ECSServiceTemplateOptions();
+ return options.vSwitchId(vSwitchId);
+ }
+
+ /**
+ * @see ECSServiceTemplateOptions#internetChargeType
+ */
+ public static ECSServiceTemplateOptions internetChargeType(String internetChargeType) {
+ ECSServiceTemplateOptions options = new ECSServiceTemplateOptions();
+ return options.internetChargeType(internetChargeType);
+ }
+
+ /**
+ * @see ECSServiceTemplateOptions#instanceChargeType
+ */
+ public static ECSServiceTemplateOptions instanceChargeType(String instanceChargeType) {
+ ECSServiceTemplateOptions options = new ECSServiceTemplateOptions();
+ return options.instanceChargeType(instanceChargeType);
+ }
+
+ /**
+ * @see ECSServiceTemplateOptions#internetMaxBandwidthOut
+ */
+ public static ECSServiceTemplateOptions internetMaxBandwidthOut(int internetMaxBandwidthOut) {
+ ECSServiceTemplateOptions options = new ECSServiceTemplateOptions();
+ return options.internetMaxBandwidthOut(internetMaxBandwidthOut);
+ }
+
+ }
+}
http://git-wip-us.apache.org/repos/asf/jclouds-labs/blob/2c7db7e8/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/strategy/CleanupResources.java
----------------------------------------------------------------------
diff --git a/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/strategy/CleanupResources.java b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/strategy/CleanupResources.java
new file mode 100644
index 0000000..8fb5730
--- /dev/null
+++ b/aliyun-ecs/src/main/java/org/jclouds/aliyun/ecs/compute/strategy/CleanupResources.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.compute.strategy;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import org.jclouds.aliyun.ecs.ECSComputeServiceApi;
+import org.jclouds.aliyun.ecs.domain.InstanceStatus;
+import org.jclouds.aliyun.ecs.domain.SecurityGroup;
+import org.jclouds.aliyun.ecs.domain.Tag;
+import org.jclouds.aliyun.ecs.domain.regionscoped.RegionAndId;
+import org.jclouds.aliyun.ecs.predicates.InstanceStatusPredicate;
+import org.jclouds.compute.reference.ComputeServiceConstants;
+import org.jclouds.logging.Logger;
+
+import javax.annotation.Nullable;
+import javax.annotation.Resource;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+import java.util.List;
+
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_SUSPENDED;
+import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_TERMINATED;
+
+/**
+ * This utility takes care of cleaning up all the resources created to deploy the node
+ *
+ * Specifically, it tries to delete the security group created for the group of nodes.
+ * In case a VPC_PREFIX and a vSwitch were created for the node, it tries to remove them
+ */
+@Singleton
+public class CleanupResources {
+
+ @Resource
+ @Named(ComputeServiceConstants.COMPUTE_LOGGER)
+ protected Logger logger = Logger.NULL;
+
+ private final ECSComputeServiceApi api;
+ private final Predicate<String> instanceSuspendedPredicate;
+ private final Predicate<String> instanceTerminatedPredicate;
+
+ @Inject
+ public CleanupResources(ECSComputeServiceApi api,
+ @Named(TIMEOUT_NODE_SUSPENDED) Predicate<String> instanceSuspendedPredicate,
+ @Named(TIMEOUT_NODE_TERMINATED) Predicate<String> instanceTerminatedPredicate
+ ) {
+ this.api = api;
+ this.instanceSuspendedPredicate = instanceSuspendedPredicate;
+ this.instanceTerminatedPredicate = instanceTerminatedPredicate;
+ }
+
+ /**
+ * @param regionAndId
+ * @return whether the node and its resources have been deleted
+ */
+ public boolean cleanupNode(final RegionAndId regionAndId) {
+ String instanceId = regionAndId.id();
+ InstanceStatus instanceStatus = Iterables.tryFind(api.instanceApi().listInstanceStatus(regionAndId.regionId()).concat(),
+ new InstanceStatusPredicate(instanceId)).orNull();
+ if (instanceStatus == null) return true;
+ if (InstanceStatus.Status.STOPPED != instanceStatus.status()) {
+ logger.debug(">> powering off %s ...", RegionAndId.slashEncodeRegionAndId(regionAndId));
+ api.instanceApi().powerOff(instanceId);
+ instanceSuspendedPredicate.apply(RegionAndId.slashEncodeRegionAndId(regionAndId));
+ }
+ logger.debug(">> destroying %s ...", RegionAndId.slashEncodeRegionAndId(regionAndId));
+ api.instanceApi().delete(instanceId);
+ return instanceTerminatedPredicate.apply(RegionAndId.slashEncodeRegionAndId(regionAndId));
+ }
+
+ public List<SecurityGroup> findOrphanedSecurityGroups(final String regionId, final String group) {
+ return api.securityGroupApi().list(regionId).concat().filter(new Predicate<SecurityGroup>() {
+ @Override
+ public boolean apply(@Nullable SecurityGroup input) {
+ List<Tag> actual = input.tags().entrySet().iterator().next().getValue();
+ List<Tag> expected = ImmutableList.of(
+ Tag.create(Tag.DEFAULT_OWNER_KEY, Tag.DEFAULT_OWNER_VALUE), Tag.create(Tag.GROUP, group)
+ );
+ return actual.containsAll(expected) && expected.containsAll(actual);
+ }
+ }).toList();
+ }
+
+ public boolean cleanupSecurityGroupIfOrphaned(final String regionId, String securityGroupId) {
+ return api.securityGroupApi().delete(regionId, securityGroupId) != null;
+ }
+
+ public boolean cleanupVSwitchIfOrphaned(final String regionId, String vSwitchId) {
+ return api.vSwitchApi().delete(regionId, vSwitchId) != null;
+ }
+
+ public boolean cleanupVPCIfOrphaned(final String regionId, String vpcId) {
+ return api.vpcApi().delete(regionId, vpcId) != null;
+ }
+
+}