From 281c92a58113292f38895f6f66510c3696cddc41 Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Thu, 10 Nov 2011 02:11:10 +0100 Subject: [PATCH] roughed in cloudstack compute service adapter --- .../reference/ComputeServiceConstants.java | 2 +- .../cloudstack/CloudStackContextBuilder.java | 11 +- ...CloudStackComputeServiceContextModule.java | 137 +++++++++ .../functions/ServiceOfferingToHardware.java | 55 ++++ .../compute/functions/TemplateToImage.java | 82 ++++++ .../functions/TemplateToOperatingSystem.java | 76 +++++ .../VirtualMachineToNodeMetadata.java | 162 +++++++++++ .../compute/functions/ZoneToLocation.java | 54 ++++ .../options/CloudStackTemplateOptions.java | 260 ++++++++++++++++++ .../CloudStackComputeServiceAdapter.java | 165 +++++++++++ .../config/CloudStackRestClientModule.java | 10 - .../jclouds/cloudstack/domain/Template.java | 2 + ...oudStackComputeServiceAdapterLiveTest.java | 155 +++++++++++ .../CloudStackComputeServiceLiveTest.java | 63 +++++ .../compute/CloudStackExperimentLiveTest.java | 85 ++++++ .../ServiceOfferingToHardwareTest.java | 61 ++++ .../functions/TemplateToImageTest.java | 81 ++++++ .../TemplateToOperatingSystemTest.java | 89 ++++++ .../VirtualMachineToNodeMetadataTest.java | 125 +++++++++ .../compute/functions/ZoneToLocationTest.java | 63 +++++ .../CloudStackTemplateOptionsTest.java | 86 ++++++ .../BaseCloudStackClientLiveTest.java | 9 +- 22 files changed, 1818 insertions(+), 15 deletions(-) create mode 100644 sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/config/CloudStackComputeServiceContextModule.java create mode 100644 sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/ServiceOfferingToHardware.java create mode 100644 sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/TemplateToImage.java create mode 100644 sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/TemplateToOperatingSystem.java create mode 100644 sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/VirtualMachineToNodeMetadata.java create mode 100644 sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/ZoneToLocation.java create mode 100644 sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/options/CloudStackTemplateOptions.java create mode 100644 sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/strategy/CloudStackComputeServiceAdapter.java create mode 100644 sandbox-apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/CloudStackComputeServiceAdapterLiveTest.java create mode 100644 sandbox-apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/CloudStackComputeServiceLiveTest.java create mode 100644 sandbox-apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/CloudStackExperimentLiveTest.java create mode 100644 sandbox-apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/functions/ServiceOfferingToHardwareTest.java create mode 100644 sandbox-apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/functions/TemplateToImageTest.java create mode 100644 sandbox-apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/functions/TemplateToOperatingSystemTest.java create mode 100644 sandbox-apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/functions/VirtualMachineToNodeMetadataTest.java create mode 100644 sandbox-apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/functions/ZoneToLocationTest.java create mode 100644 sandbox-apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/options/CloudStackTemplateOptionsTest.java diff --git a/compute/src/main/java/org/jclouds/compute/reference/ComputeServiceConstants.java b/compute/src/main/java/org/jclouds/compute/reference/ComputeServiceConstants.java index a4dc955ce2..745af1cd2f 100644 --- a/compute/src/main/java/org/jclouds/compute/reference/ComputeServiceConstants.java +++ b/compute/src/main/java/org/jclouds/compute/reference/ComputeServiceConstants.java @@ -57,7 +57,7 @@ public interface ComputeServiceConstants { public static class ReferenceData { @Inject(optional = true) @Named(PROPERTY_OS_VERSION_MAP_JSON) - public String osVersionMapJson = "{\"suse\":{\"\":\"\",\"11\":\"11\",\"11 SP1\":\"11 SP1\"},\"debian\":{\"\":\"\",\"lenny\":\"5.0\",\"squeeze\":\"6.0\"},\"centos\":{\"\":\"\",\"5\":\"5.0\",\"5.2\":\"5.2\",\"5.3\":\"5.3\",\"5.4\":\"5.4\",\"5.5\":\"5.5\",\"5.6\":\"5.6\",\"5.7\":\"5.7\",\"6.0\":\"6.0\"},\"rhel\":{\"\":\"\",\"5\":\"5.0\",\"5.2\":\"5.2\",\"5.3\":\"5.3\",\"5.4\":\"5.4\",\"5.5\":\"5.5\",\"5.6\":\"5.6\",\"5.7\":\"5.7\",\"6.0\":\"6.0\"},\"solaris\":{\"\":\"\",\"10\":\"10\"},\"ubuntu\":{\"\":\"\",\"hardy\":\"8.04\",\"karmic\":\"9.10\",\"lucid\":\"10.04\",\"10.04.1\":\"10.04\",\"maverick\":\"10.10\",\"natty\":\"11.04\",\"oneiric\":\"11.10\",\"precise\":\"12.04\"},\"windows\":{\"\":\"\",\"2003\":\"2003\",\"2003 Standard\":\"2003\",\"2003 R2\":\"2003 R2\",\"2008\":\"2008\",\"2008 Web\":\"2008\",\"2008 Server\":\"2008\",\"Server 2008\":\"2008\",\"2008 R1\":\"2008 R1\",\"2008 R2\":\"2008 R2\",\"Server 2008 R2\":\"2008 R2\",\"2008 Server R2\":\"2008 R2\",\"2008 SP2\":\"2008 SP2\",\"Server 2008 SP2\":\"2008 SP2\"}}"; + public String osVersionMapJson = "{\"suse\":{\"\":\"\",\"11\":\"11\",\"11 SP1\":\"11 SP1\"},\"debian\":{\"\":\"\",\"lenny\":\"5.0\",\"squeeze\":\"6.0\"},\"centos\":{\"\":\"\",\"5\":\"5.0\",\"5.2\":\"5.2\",\"5.3\":\"5.3\",\"5.4\":\"5.4\",\"5.5\":\"5.5\",\"5.6\":\"5.6\",\"5.7\":\"5.7\",\"6.0\":\"6.0\"},\"rhel\":{\"\":\"\",\"5\":\"5.0\",\"5.2\":\"5.2\",\"5.3\":\"5.3\",\"5.4\":\"5.4\",\"5.5\":\"5.5\",\"5.6\":\"5.6\",\"5.7\":\"5.7\",\"6.0\":\"6.0\"},\"solaris\":{\"\":\"\",\"10\":\"10\"},\"ubuntu\":{\"\":\"\",\"hardy\":\"8.04\",\"karmic\":\"9.10\",\"lucid\":\"10.04\",\"10.04.1\":\"10.04\",\"maverick\":\"10.10\",\"natty\":\"11.04\",\"oneiric\":\"11.10\",\"precise\":\"12.04\"},\"windows\":{\"\":\"\",\"7\":\"7\",\"2003\":\"2003\",\"2003 Standard\":\"2003\",\"2003 R2\":\"2003 R2\",\"2008\":\"2008\",\"2008 Web\":\"2008\",\"2008 Server\":\"2008\",\"Server 2008\":\"2008\",\"2008 R1\":\"2008 R1\",\"2008 R2\":\"2008 R2\",\"Server 2008 R2\":\"2008 R2\",\"2008 Server R2\":\"2008 R2\",\"2008 SP2\":\"2008 SP2\",\"Server 2008 SP2\":\"2008 SP2\"}}"; } @Singleton diff --git a/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackContextBuilder.java b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackContextBuilder.java index bd28432462..ff99d19aeb 100644 --- a/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackContextBuilder.java +++ b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/CloudStackContextBuilder.java @@ -21,8 +21,9 @@ package org.jclouds.cloudstack; import java.util.List; import java.util.Properties; +import org.jclouds.cloudstack.compute.config.CloudStackComputeServiceContextModule; import org.jclouds.cloudstack.config.CloudStackRestClientModule; -import org.jclouds.rest.RestContextBuilder; +import org.jclouds.compute.ComputeServiceContextBuilder; import com.google.inject.Module; @@ -30,12 +31,18 @@ import com.google.inject.Module; * * @author Adrian Cole */ -public class CloudStackContextBuilder extends RestContextBuilder { +public class CloudStackContextBuilder extends ComputeServiceContextBuilder { public CloudStackContextBuilder(Properties props) { super(CloudStackClient.class, CloudStackAsyncClient.class, props); } + @Override + protected void addContextModule(List modules) { + modules.add(new CloudStackComputeServiceContextModule()); + } + + @Override protected void addClientModule(List modules) { modules.add(new CloudStackRestClientModule()); } diff --git a/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/config/CloudStackComputeServiceContextModule.java b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/config/CloudStackComputeServiceContextModule.java new file mode 100644 index 0000000000..6e1eef7e9f --- /dev/null +++ b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/config/CloudStackComputeServiceContextModule.java @@ -0,0 +1,137 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.cloudstack.compute.config; + +import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL; + +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import javax.inject.Named; +import javax.inject.Singleton; + +import org.jclouds.cloudstack.CloudStackAsyncClient; +import org.jclouds.cloudstack.CloudStackClient; +import org.jclouds.cloudstack.compute.functions.ServiceOfferingToHardware; +import org.jclouds.cloudstack.compute.functions.TemplateToImage; +import org.jclouds.cloudstack.compute.functions.TemplateToOperatingSystem; +import org.jclouds.cloudstack.compute.functions.VirtualMachineToNodeMetadata; +import org.jclouds.cloudstack.compute.functions.ZoneToLocation; +import org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions; +import org.jclouds.cloudstack.compute.strategy.CloudStackComputeServiceAdapter; +import org.jclouds.cloudstack.domain.OSType; +import org.jclouds.cloudstack.domain.ServiceOffering; +import org.jclouds.cloudstack.domain.Template; +import org.jclouds.cloudstack.domain.VirtualMachine; +import org.jclouds.cloudstack.domain.Zone; +import org.jclouds.cloudstack.features.GuestOSClient; +import org.jclouds.cloudstack.predicates.JobComplete; +import org.jclouds.collect.Memoized; +import org.jclouds.compute.ComputeServiceAdapter; +import org.jclouds.compute.config.ComputeServiceAdapterContextModule; +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.compute.domain.OperatingSystem; +import org.jclouds.compute.options.TemplateOptions; +import org.jclouds.domain.Location; +import org.jclouds.location.suppliers.OnlyLocationOrFirstZone; +import org.jclouds.predicates.RetryablePredicate; +import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier; + +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.base.Supplier; +import com.google.common.collect.Maps; +import com.google.inject.Provides; +import com.google.inject.TypeLiteral; + +/** + * + * @author Adrian Cole + */ +public class CloudStackComputeServiceContextModule + extends + ComputeServiceAdapterContextModule { + + public CloudStackComputeServiceContextModule() { + super(CloudStackClient.class, CloudStackAsyncClient.class); + } + + @Override + protected void configure() { + super.configure(); + bind(new TypeLiteral>() { + }).to(CloudStackComputeServiceAdapter.class); + bind(new TypeLiteral>() { + }).to(VirtualMachineToNodeMetadata.class); + bind(new TypeLiteral>() { + }).to(TemplateToImage.class); + bind(new TypeLiteral>() { + }).to(ServiceOfferingToHardware.class); + bind(new TypeLiteral>() { + }).to(ZoneToLocation.class); + bind(new TypeLiteral>() { + }).to(OnlyLocationOrFirstZone.class); + bind(TemplateOptions.class).to(CloudStackTemplateOptions.class); + bind(new TypeLiteral>() { + }).to(TemplateToOperatingSystem.class); + } + + @Provides + @Singleton + @Memoized + public Supplier> listOSCategories(@Named(PROPERTY_SESSION_INTERVAL) long seconds, + final CloudStackClient client) { + return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier>(authException, + seconds, new Supplier>() { + @Override + public Map get() { + GuestOSClient guestOSClient = client.getGuestOSClient(); + return guestOSClient.listOSCategories(); + } + }); + } + + @Provides + @Singleton + @Memoized + public Supplier> listOSTypes(@Named(PROPERTY_SESSION_INTERVAL) long seconds, + final CloudStackClient client) { + return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier>(authException, + seconds, new Supplier>() { + @Override + public Map get() { + GuestOSClient guestOSClient = client.getGuestOSClient(); + return Maps.uniqueIndex(guestOSClient.listOSTypes(), new Function() { + + @Override + public Long apply(OSType arg0) { + return arg0.getId(); + } + }); + } + }); + } + + @Provides + @Singleton + protected Predicate jobComplete(JobComplete jobComplete) { + // TODO: parameterize + return new RetryablePredicate(jobComplete, 1200, 1, 5, TimeUnit.SECONDS); + } +} \ No newline at end of file diff --git a/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/ServiceOfferingToHardware.java b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/ServiceOfferingToHardware.java new file mode 100644 index 0000000000..876afdf71c --- /dev/null +++ b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/ServiceOfferingToHardware.java @@ -0,0 +1,55 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.cloudstack.compute.functions; + +import javax.inject.Singleton; + +import org.jclouds.cloudstack.domain.ServiceOffering; +import org.jclouds.compute.domain.Hardware; +import org.jclouds.compute.domain.HardwareBuilder; +import org.jclouds.compute.domain.Processor; + +import com.google.common.base.Function; +import com.google.common.collect.ImmutableList; + +/** + */ +@Singleton +public class ServiceOfferingToHardware implements Function { + + @Override + public Hardware apply(ServiceOffering offering) { + return new HardwareBuilder() + .id(offering.getId() + "") + // TODO: can the id be ambigious? should we include the domain if available? + .providerId(offering.getId() + "") + .name(offering.getName()) + .tags(offering.getTags()) + .processors(ImmutableList.of(new Processor(offering.getCpuNumber(), offering.getCpuSpeed()))) + .ram(offering.getMemory())// + // TODO .volumes() + // displayText + // created + // haSupport + // storageType + // TODO where's the location of this? + .build(); + } + +} diff --git a/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/TemplateToImage.java b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/TemplateToImage.java new file mode 100644 index 0000000000..785150eefd --- /dev/null +++ b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/TemplateToImage.java @@ -0,0 +1,82 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.cloudstack.compute.functions; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Set; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.jclouds.cloudstack.domain.Template; +import org.jclouds.collect.FindResourceInSet; +import org.jclouds.collect.Memoized; +import org.jclouds.compute.domain.Image; +import org.jclouds.compute.domain.ImageBuilder; +import org.jclouds.compute.domain.OperatingSystem; +import org.jclouds.domain.Location; + +import com.google.common.base.Function; +import com.google.common.base.Supplier; + +/** + */ +@Singleton +public class TemplateToImage implements Function { + private final FindLocationForTemplate findLocationForTemplate; + private final Function templateToOperatingSystem; + + @Inject + public TemplateToImage(FindLocationForTemplate findLocationForTemplate, + Function templateToOperatingSystem) { + this.findLocationForTemplate = checkNotNull(findLocationForTemplate, "findLocationForTemplate"); + this.templateToOperatingSystem = checkNotNull(templateToOperatingSystem, "templateToOperatingSystem"); + } + + @Override + public Image apply(Template template) { + checkNotNull(template, "template"); + + OperatingSystem os = templateToOperatingSystem.apply(template); + + ImageBuilder builder = new ImageBuilder().id(template.getZoneId() + "/" + template.getId()) + .providerId(template.getId() + "").name(template.getName()).description(template.getDisplayText()) + .operatingSystem(os); + + if (!template.isCrossZones()) + builder.location(findLocationForTemplate.apply(template)); + + return builder.build(); + } + + @Singleton + public static class FindLocationForTemplate extends FindResourceInSet { + + @Inject + public FindLocationForTemplate(@Memoized Supplier> location) { + super(location); + } + + @Override + public boolean matches(Template from, Location input) { + return input.getId().equals(Long.toString(from.getZoneId())); + } + } +} diff --git a/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/TemplateToOperatingSystem.java b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/TemplateToOperatingSystem.java new file mode 100644 index 0000000000..a98fcbdad8 --- /dev/null +++ b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/TemplateToOperatingSystem.java @@ -0,0 +1,76 @@ +package org.jclouds.cloudstack.compute.functions; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.annotation.Resource; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.jclouds.cloudstack.domain.OSType; +import org.jclouds.cloudstack.domain.Template; +import org.jclouds.collect.Memoized; +import org.jclouds.compute.domain.OperatingSystem; +import org.jclouds.compute.domain.OperatingSystem.Builder; +import org.jclouds.compute.domain.OsFamily; +import org.jclouds.compute.reference.ComputeServiceConstants; +import org.jclouds.compute.util.ComputeServiceUtils; +import org.jclouds.logging.Logger; + +import com.google.common.base.Function; +import com.google.common.base.Supplier; + +/** + * + * @author Adrian Cole + */ +@Singleton +public class TemplateToOperatingSystem implements Function { + // CentOS 5.2 (32-bit) + public static final Pattern DEFAULT_PATTERN = Pattern.compile(".* ([0-9.]+) ?\\(.*"); + + @Resource + @Named(ComputeServiceConstants.COMPUTE_LOGGER) + protected Logger logger = Logger.NULL; + + private final Supplier> osTypes; + private final Supplier> osCategories; + private final Map> osVersionMap; + + @Inject + public TemplateToOperatingSystem(@Memoized Supplier> osTypes, + @Memoized Supplier> osCategories, Map> osVersionMap) { + this.osTypes = checkNotNull(osTypes, "osTypes"); + this.osCategories = checkNotNull(osCategories, "osCategories"); + this.osVersionMap = checkNotNull(osVersionMap, "osVersionMap"); + } + + public OperatingSystem apply(Template from) { + Builder builder = OperatingSystem.builder().description(from.getOSType()); + + OSType type = osTypes.get().get(from.getOSTypeId()); + if (type == null) { + logger.warn("type for template %s not found in %s", from, osTypes.get()); + return builder.build(); + } + builder.description(type.getDescription()); + builder.is64Bit(type.getDescription().indexOf("64-bit") != -1); + String osCategory = osCategories.get().get(type.getOSCategoryId()); + if (osCategory == null) { + logger.warn("category for OSType %s not found in %s", type, osCategories.get()); + return builder.build(); + } + builder.name(osCategory); + OsFamily family = OsFamily.fromValue(osCategory.toLowerCase()); + builder.family(family); + Matcher matcher = DEFAULT_PATTERN.matcher(type.getDescription()); + if (matcher.find()) { + builder.version(ComputeServiceUtils.parseVersionOrReturnEmptyString(family, matcher.group(1), osVersionMap)); + } + return builder.build(); + } +} diff --git a/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/VirtualMachineToNodeMetadata.java b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/VirtualMachineToNodeMetadata.java new file mode 100644 index 0000000000..502758465b --- /dev/null +++ b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/VirtualMachineToNodeMetadata.java @@ -0,0 +1,162 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.cloudstack.compute.functions; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.jclouds.compute.util.ComputeServiceUtils.parseGroupFromName; + +import java.util.Map; +import java.util.Set; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.jclouds.cloudstack.domain.VirtualMachine; +import org.jclouds.collect.FindResourceInSet; +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.domain.NodeState; +import org.jclouds.domain.Credentials; +import org.jclouds.domain.Location; +import org.jclouds.util.InetAddresses2; + +import com.google.common.base.Function; +import com.google.common.base.Supplier; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; + +/** + * @author Adrian Cole + */ +@Singleton +public class VirtualMachineToNodeMetadata implements Function { + + public static final Map vmStateToNodeState = ImmutableMap + . builder().put(VirtualMachine.State.STARTING, NodeState.PENDING) + .put(VirtualMachine.State.RUNNING, NodeState.RUNNING).put(VirtualMachine.State.STOPPING, NodeState.SUSPENDED) + .put(VirtualMachine.State.STOPPED, NodeState.PENDING) + .put(VirtualMachine.State.DESTROYED, NodeState.TERMINATED) + .put(VirtualMachine.State.EXPUNGING, NodeState.TERMINATED) + .put(VirtualMachine.State.MIGRATING, NodeState.PENDING).put(VirtualMachine.State.ERROR, NodeState.ERROR) + .put(VirtualMachine.State.UNKNOWN, NodeState.UNRECOGNIZED) + // TODO: is this really a state? + .put(VirtualMachine.State.SHUTDOWNED, NodeState.PENDING) + .put(VirtualMachine.State.UNRECOGNIZED, NodeState.UNRECOGNIZED).build(); + + private final Map credentialStore; + private final FindLocationForVirtualMachine findLocationForVirtualMachine; + private final FindHardwareForVirtualMachine findHardwareForVirtualMachine; + private final FindImageForVirtualMachine findImageForVirtualMachine; + + @Inject + VirtualMachineToNodeMetadata(Map credentialStore, + FindLocationForVirtualMachine findLocationForVirtualMachine, + FindHardwareForVirtualMachine findHardwareForVirtualMachine, + FindImageForVirtualMachine findImageForVirtualMachine) { + this.credentialStore = checkNotNull(credentialStore, "credentialStore"); + this.findLocationForVirtualMachine = checkNotNull(findLocationForVirtualMachine, "findLocationForVirtualMachine"); + this.findHardwareForVirtualMachine = checkNotNull(findHardwareForVirtualMachine, "findHardwareForVirtualMachine"); + this.findImageForVirtualMachine = checkNotNull(findImageForVirtualMachine, "findImageForVirtualMachine"); + } + + @Override + public NodeMetadata apply(VirtualMachine from) { + // convert the result object to a jclouds NodeMetadata + NodeMetadataBuilder builder = new NodeMetadataBuilder(); + String id = from.getZoneId() + "/" + from.getId(); + builder.id(id); + builder.providerId(from.getId() + ""); + builder.name(from.getName()); + builder.hostname(from.getHostname()); + builder.location(findLocationForVirtualMachine.apply(from)); + builder.group(parseGroupFromName(from.getHostname())); + Image image = findImageForVirtualMachine.apply(from); + if (image != null) { + builder.imageId(image.getId()); + builder.operatingSystem(image.getOperatingSystem()); + } + + Hardware hardware = findHardwareForVirtualMachine.apply(from); + if (hardware != null) + builder.hardware(hardware); + + builder.state(vmStateToNodeState.get(from.getState())); + + // TODO: check to see public or private + if (from.getIPAddress() != null) { + boolean isPrivate = InetAddresses2.isPrivateIPAddress(from.getIPAddress()); + Set addresses = ImmutableSet. of(from.getIPAddress()); + if (isPrivate) + builder.privateAddresses(addresses); + else + builder.publicAddresses(addresses); + } + builder.credentials(credentialStore.get("node#" + id)); + return builder.build(); + } + + @Singleton + public static class FindLocationForVirtualMachine extends FindResourceInSet { + + @Inject + public FindLocationForVirtualMachine(@Memoized Supplier> location) { + super(location); + } + + @Override + public boolean matches(VirtualMachine from, Location input) { + return input.getId().equals(Long.toString(from.getZoneId())); + } + } + + @Singleton + public static class FindHardwareForVirtualMachine extends FindResourceInSet { + + @Inject + public FindHardwareForVirtualMachine(@Memoized Supplier> location) { + super(location); + } + + @Override + public boolean matches(VirtualMachine from, Hardware input) { + return input.getProviderId().equals(Long.toString(from.getServiceOfferingId())); + } + } + + @Singleton + public static class FindImageForVirtualMachine extends FindResourceInSet { + + @Inject + public FindImageForVirtualMachine(@Memoized Supplier> location) { + super(location); + } + + @Override + public boolean matches(VirtualMachine from, Image input) { + return input.getProviderId().equals(from.getTemplateId() + "") + // either location free image (location is null) + // or in the same zone as the VM + && (input.getLocation() == null || input.getId().equals(from.getZoneId() + "")); + } + } + +} diff --git a/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/ZoneToLocation.java b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/ZoneToLocation.java new file mode 100644 index 0000000000..ee59064b4e --- /dev/null +++ b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/ZoneToLocation.java @@ -0,0 +1,54 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.cloudstack.compute.functions; + +import static com.google.common.base.Preconditions.checkNotNull; + +import javax.inject.Inject; + +import org.jclouds.cloudstack.domain.Zone; +import org.jclouds.domain.Location; +import org.jclouds.domain.LocationBuilder; +import org.jclouds.domain.LocationScope; +import org.jclouds.location.suppliers.JustProvider; + +import com.google.common.base.Function; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterables; + +/** + * Converts an Zone into a Location. + */ +public class ZoneToLocation implements Function { + private final JustProvider provider; + + // allow us to lazy discover the provider of a resource + @Inject + public ZoneToLocation(JustProvider provider) { + this.provider = checkNotNull(provider, "provider"); + } + + @Override + public Location apply(Zone zone) { + return new LocationBuilder().scope(LocationScope.ZONE).metadata(ImmutableMap. of()) + .description(zone.getName()).id(Long.toString(zone.getId())) + .parent(Iterables.getOnlyElement(provider.get())).build(); + } + +} diff --git a/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/options/CloudStackTemplateOptions.java b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/options/CloudStackTemplateOptions.java new file mode 100644 index 0000000000..e2125fc440 --- /dev/null +++ b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/options/CloudStackTemplateOptions.java @@ -0,0 +1,260 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.cloudstack.compute.options; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Map; +import java.util.Set; + +import org.jclouds.cloudstack.options.DeployVirtualMachineOptions; +import org.jclouds.compute.ComputeService; +import org.jclouds.compute.options.TemplateOptions; +import org.jclouds.io.Payload; + +import com.google.common.collect.Iterables; +import com.google.common.collect.Sets; + +/** + * Contains options supported by the + * {@link ComputeService#createNodesInGroup(String, int, TemplateOptions)} and + * {@link ComputeService#runNodesWithTag(String, int, TemplateOptions)} + * operations on the gogrid provider. + * + *

Usage

The recommended way to instantiate a + * {@link CloudStackTemplateOptions} object is to statically import + * {@code CloudStackTemplateOptions.*} and invoke a static creation method + * followed by an instance mutator (if needed): + *

+ * + *

+ * import static org.jclouds.compute.options.CloudStackTemplateOptions.Builder.*;
+ * ComputeService client = // get connection
+ * templateBuilder.options(inboundPorts(22, 80, 8080, 443));
+ * Set<? extends NodeMetadata> set = client.runNodesWithTag(tag, 2, templateBuilder.build());
+ * 
+ * + * @author Adrian Cole + */ +public class CloudStackTemplateOptions extends TemplateOptions implements Cloneable { + + protected Set securityGroupIds = Sets. newLinkedHashSet(); + + @Override + public CloudStackTemplateOptions clone() { + CloudStackTemplateOptions options = new CloudStackTemplateOptions(); + copyTo(options); + return options; + } + + @Override + public void copyTo(TemplateOptions to) { + super.copyTo(to); + if (to instanceof CloudStackTemplateOptions) { + CloudStackTemplateOptions eTo = CloudStackTemplateOptions.class.cast(to); + eTo.securityGroupIds(this.securityGroupIds); + } + } + + /** + * @see DeployVirtualMachineOptions#securityGroupId + */ + public CloudStackTemplateOptions securityGroupId(long securityGroupId) { + this.securityGroupIds.add(securityGroupId); + return this; + } + + /** + * @see DeployVirtualMachineOptions#securityGroupIds + */ + public CloudStackTemplateOptions securityGroupIds(Iterable securityGroupIds) { + Iterables.addAll(this.securityGroupIds, checkNotNull(securityGroupIds, "securityGroupIds was null")); + return this; + } + + public Set getSecurityGroupIds() { + return securityGroupIds; + } + + public static final CloudStackTemplateOptions NONE = new CloudStackTemplateOptions(); + + public static class Builder { + + /** + * @see CloudStackTemplateOptions#securityGroupId + */ + public static CloudStackTemplateOptions securityGroupId(long id) { + CloudStackTemplateOptions options = new CloudStackTemplateOptions(); + return options.securityGroupId(id); + } + + /** + * @see CloudStackTemplateOptions#securityGroupIds + */ + public static CloudStackTemplateOptions securityGroupIds(Iterable securityGroupIds) { + CloudStackTemplateOptions options = new CloudStackTemplateOptions(); + return options.securityGroupIds(securityGroupIds); + } + + // methods that only facilitate returning the correct object type + + /** + * @see TemplateOptions#inboundPorts(int...) + */ + public static CloudStackTemplateOptions inboundPorts(int... ports) { + CloudStackTemplateOptions options = new CloudStackTemplateOptions(); + return CloudStackTemplateOptions.class.cast(options.inboundPorts(ports)); + } + + /** + * @see TemplateOptions#blockOnPort(int, int) + */ + public static CloudStackTemplateOptions blockOnPort(int port, int seconds) { + CloudStackTemplateOptions options = new CloudStackTemplateOptions(); + return CloudStackTemplateOptions.class.cast(options.blockOnPort(port, seconds)); + } + + /** + * @see TemplateOptions#runScript(Payload) + */ + public static CloudStackTemplateOptions runScript(Payload script) { + CloudStackTemplateOptions options = new CloudStackTemplateOptions(); + return CloudStackTemplateOptions.class.cast(options.runScript(script)); + } + + /** + * @see TemplateOptions#installPrivateKey(Payload) + */ + @Deprecated + public static CloudStackTemplateOptions installPrivateKey(Payload rsaKey) { + CloudStackTemplateOptions options = new CloudStackTemplateOptions(); + return CloudStackTemplateOptions.class.cast(options.installPrivateKey(rsaKey)); + } + + /** + * @see TemplateOptions#authorizePublicKey(Payload) + */ + @Deprecated + public static CloudStackTemplateOptions authorizePublicKey(Payload rsaKey) { + CloudStackTemplateOptions options = new CloudStackTemplateOptions(); + return CloudStackTemplateOptions.class.cast(options.authorizePublicKey(rsaKey)); + } + + /** + * @see TemplateOptions#userMetadata(Map) + */ + public static CloudStackTemplateOptions userMetadata(Map userMetadata) { + CloudStackTemplateOptions options = new CloudStackTemplateOptions(); + return CloudStackTemplateOptions.class.cast(options.userMetadata(userMetadata)); + } + + /** + * @see TemplateOptions#userMetadata(String, String) + */ + public static CloudStackTemplateOptions userMetadata(String key, String value) { + CloudStackTemplateOptions options = new CloudStackTemplateOptions(); + return CloudStackTemplateOptions.class.cast(options.userMetadata(key, value)); + } + } + + // methods that only facilitate returning the correct object type + + /** + * @see TemplateOptions#blockOnPort(int, int) + */ + @Override + public CloudStackTemplateOptions blockOnPort(int port, int seconds) { + return CloudStackTemplateOptions.class.cast(super.blockOnPort(port, seconds)); + } + + /** + * @see TemplateOptions#inboundPorts(int...) + */ + @Override + public CloudStackTemplateOptions inboundPorts(int... ports) { + return CloudStackTemplateOptions.class.cast(super.inboundPorts(ports)); + } + + /** + * @see TemplateOptions#authorizePublicKey(String) + */ + @Override + public CloudStackTemplateOptions authorizePublicKey(String publicKey) { + return CloudStackTemplateOptions.class.cast(super.authorizePublicKey(publicKey)); + } + + /** + * @see TemplateOptions#authorizePublicKey(Payload) + */ + @Override + @Deprecated + public CloudStackTemplateOptions authorizePublicKey(Payload publicKey) { + return CloudStackTemplateOptions.class.cast(super.authorizePublicKey(publicKey)); + } + + /** + * @see TemplateOptions#installPrivateKey(String) + */ + @Override + public CloudStackTemplateOptions installPrivateKey(String privateKey) { + return CloudStackTemplateOptions.class.cast(super.installPrivateKey(privateKey)); + } + + /** + * @see TemplateOptions#installPrivateKey(Payload) + */ + @Override + @Deprecated + public CloudStackTemplateOptions installPrivateKey(Payload privateKey) { + return CloudStackTemplateOptions.class.cast(super.installPrivateKey(privateKey)); + } + + /** + * @see TemplateOptions#runScript(Payload) + */ + @Override + public CloudStackTemplateOptions runScript(Payload script) { + return CloudStackTemplateOptions.class.cast(super.runScript(script)); + } + + /** + * @see TemplateOptions#runScript(byte[]) + */ + @Override + @Deprecated + public CloudStackTemplateOptions runScript(byte[] script) { + return CloudStackTemplateOptions.class.cast(super.runScript(script)); + } + + /** + * {@inheritDoc} + */ + @Override + public CloudStackTemplateOptions userMetadata(Map userMetadata) { + return CloudStackTemplateOptions.class.cast(super.userMetadata(userMetadata)); + } + + /** + * {@inheritDoc} + */ + @Override + public CloudStackTemplateOptions userMetadata(String key, String value) { + return CloudStackTemplateOptions.class.cast(super.userMetadata(key, value)); + } +} diff --git a/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/strategy/CloudStackComputeServiceAdapter.java b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/strategy/CloudStackComputeServiceAdapter.java new file mode 100644 index 0000000000..a82712031e --- /dev/null +++ b/sandbox-apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/strategy/CloudStackComputeServiceAdapter.java @@ -0,0 +1,165 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.cloudstack.compute.strategy; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Iterables.filter; + +import java.util.Map; +import java.util.concurrent.ExecutionException; + +import javax.annotation.Resource; +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.jclouds.cloudstack.CloudStackClient; +import org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions; +import org.jclouds.cloudstack.domain.AsyncCreateResponse; +import org.jclouds.cloudstack.domain.AsyncJob; +import org.jclouds.cloudstack.domain.ServiceOffering; +import org.jclouds.cloudstack.domain.Template; +import org.jclouds.cloudstack.domain.VirtualMachine; +import org.jclouds.cloudstack.domain.Zone; +import org.jclouds.cloudstack.options.DeployVirtualMachineOptions; +import org.jclouds.cloudstack.predicates.TemplatePredicates; +import org.jclouds.compute.ComputeService; +import org.jclouds.compute.ComputeServiceAdapter; +import org.jclouds.compute.reference.ComputeServiceConstants; +import org.jclouds.domain.Credentials; +import org.jclouds.logging.Logger; + +import com.google.common.base.Predicate; +import com.google.common.base.Throwables; + +/** + * defines the connection between the {@link CloudStackClient} implementation + * and the jclouds {@link ComputeService} + * + */ +@Singleton +public class CloudStackComputeServiceAdapter implements + ComputeServiceAdapter { + + @Resource + @Named(ComputeServiceConstants.COMPUTE_LOGGER) + protected Logger logger = Logger.NULL; + + private final CloudStackClient client; + private final Predicate jobComplete; + + @Inject + public CloudStackComputeServiceAdapter(CloudStackClient client, Predicate jobComplete) { + this.client = checkNotNull(client, "client"); + this.jobComplete=checkNotNull(jobComplete, "jobComplete"); + } + + @Override + public VirtualMachine createNodeWithGroupEncodedIntoNameThenStoreCredentials(String group, String name, + org.jclouds.compute.domain.Template template, Map credentialStore) { + checkNotNull(template, "template was null"); + checkNotNull(template.getOptions(), "template options was null"); + checkArgument(template.getOptions().getClass().isAssignableFrom(CloudStackTemplateOptions.class), + "options class %s should have been assignable from CloudStackTemplateOptions", template.getOptions() + .getClass()); + CloudStackTemplateOptions templateOptions = template.getOptions().as(CloudStackTemplateOptions.class); + + DeployVirtualMachineOptions options = new DeployVirtualMachineOptions(); + if (templateOptions.getSecurityGroupIds().size() > 0) + options.securityGroupIds(templateOptions.getSecurityGroupIds()); + + long zoneId = Long.parseLong(template.getLocation().getId()); + long templateId = Long.parseLong(template.getImage().getProviderId()); + long serviceOfferingId = Long.parseLong(template.getHardware().getProviderId()); + + System.out.printf("serviceOfferingId %d, templateId %d, zoneId %d, options %s%n", serviceOfferingId, templateId, + zoneId, options); + AsyncCreateResponse job = client.getVirtualMachineClient().deployVirtualMachineInZone(zoneId, serviceOfferingId, + templateId, options); + assert jobComplete.apply(job.getJobId()); + AsyncJob jobWithResult = client.getAsyncJobClient(). getAsyncJob(job.getJobId()); + if (jobWithResult.getError() != null) + Throwables.propagate(new ExecutionException(String.format("job %s failed with exception %s", job.getId(), + jobWithResult.getError().toString())) { + private static final long serialVersionUID = 4371112085613620239L; + }); + VirtualMachine vm = jobWithResult.getResult(); + if (vm.isPasswordEnabled()) { + assert vm.getPassword() != null : vm; + Credentials credentials = new Credentials("root", vm.getPassword()); + credentialStore.put("node#" + zoneId + "/" + vm.getId(), credentials); + } else { + // TODO: look for ssh key? + } + return vm; + } + + @Override + public Iterable listHardwareProfiles() { + // TODO: we may need to filter these + return client.getOfferingClient().listServiceOfferings(); + } + + @Override + public Iterable