blob: 7e12ed8c17664adf2647ab260921edb79b5be553 [file] [log] [blame]
/*
* 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();
}
}));
}
}