From c85453503ec8cb6704a6f047559f90c638e993b4 Mon Sep 17 00:00:00 2001 From: Andrew Bayer Date: Wed, 22 Oct 2014 10:09:09 -0700 Subject: [PATCH] JCLOUDS-756 - Adding tags/userMetadata to NodeMetadata transform --- .../VirtualMachineToNodeMetadata.java | 43 ++++++++---- .../CloudStackComputeServiceAdapter.java | 69 +++++++++++++------ .../CloudStackComputeServiceLiveTest.java | 12 +--- .../VirtualMachineToNodeMetadataTest.java | 12 +++- .../ListVirtualMachinesResponseTest.java | 22 +++++- .../listvirtualmachinesresponse.json | 67 +++++++++++++++++- 6 files changed, 173 insertions(+), 52 deletions(-) diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/VirtualMachineToNodeMetadata.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/VirtualMachineToNodeMetadata.java index cb78ece154..c234f9c818 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/VirtualMachineToNodeMetadata.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/functions/VirtualMachineToNodeMetadata.java @@ -17,20 +17,33 @@ package org.jclouds.cloudstack.compute.functions; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Predicates.equalTo; +import static com.google.common.base.Predicates.not; import static com.google.common.collect.Iterables.filter; import static com.google.common.collect.Iterables.transform; +import static com.google.common.collect.Maps.filterValues; import static com.google.common.collect.Sets.newHashSet; import static org.jclouds.location.predicates.LocationPredicates.idEquals; import static org.jclouds.util.InetAddresses2.isPrivateIPAddress; +import javax.inject.Inject; +import javax.inject.Singleton; import java.util.Map; import java.util.Set; -import javax.inject.Inject; -import javax.inject.Singleton; - +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.base.Supplier; +import com.google.common.base.Throwables; +import com.google.common.cache.LoadingCache; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterables; +import com.google.common.util.concurrent.UncheckedExecutionException; import org.jclouds.cloudstack.domain.IPForwardingRule; import org.jclouds.cloudstack.domain.NIC; +import org.jclouds.cloudstack.domain.Tag; import org.jclouds.cloudstack.domain.VirtualMachine; import org.jclouds.collect.Memoized; import org.jclouds.compute.domain.HardwareBuilder; @@ -44,17 +57,6 @@ import org.jclouds.domain.Location; import org.jclouds.rest.ResourceNotFoundException; import org.jclouds.util.Throwables2; -import com.google.common.base.Function; -import com.google.common.base.Predicate; -import com.google.common.base.Supplier; -import com.google.common.base.Throwables; -import com.google.common.cache.LoadingCache; -import com.google.common.collect.FluentIterable; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Iterables; -import com.google.common.util.concurrent.UncheckedExecutionException; - @Singleton public class VirtualMachineToNodeMetadata implements Function { @@ -119,10 +121,21 @@ public class VirtualMachineToNodeMetadata implements Function tagsBuilder = ImmutableMap.builder(); + + for (Tag tag : from.getTags()) { + tagsBuilder.put(tag.getKey(), tag.getValue()); + } + + Map tagMap = tagsBuilder.build(); + builder.tags(filterValues(tagMap, equalTo("jclouds-empty-tag-placeholder")).keySet()) + .userMetadata(filterValues(tagMap, not(equalTo("jclouds-empty-tag-placeholder")))); + } + builder.hardware(new HardwareBuilder() .ids(from.getServiceOfferingId() + "") .name(from.getServiceOfferingName() + "") - // .tags() TODO .processors(ImmutableList.of(new Processor(from.getCpuCount(), from.getCpuSpeed()))) .ram((int)from.getMemory())// .hypervisor(from.getHypervisor())// diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/strategy/CloudStackComputeServiceAdapter.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/strategy/CloudStackComputeServiceAdapter.java index 29f5c85452..e723f7f363 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/strategy/CloudStackComputeServiceAdapter.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/compute/strategy/CloudStackComputeServiceAdapter.java @@ -28,15 +28,22 @@ import static org.jclouds.cloudstack.predicates.TemplatePredicates.isReady; import static org.jclouds.cloudstack.predicates.ZonePredicates.supportsSecurityGroups; import static org.jclouds.ssh.SshKeys.fingerprintPrivateKey; -import java.util.List; -import java.util.Map; -import java.util.Set; - 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 com.google.common.base.Predicate; +import com.google.common.base.Supplier; +import com.google.common.cache.LoadingCache; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSet.Builder; +import com.google.common.collect.Sets; +import com.google.common.primitives.Ints; import org.jclouds.cloudstack.CloudStackApi; import org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions; import org.jclouds.cloudstack.domain.AsyncCreateResponse; @@ -50,6 +57,7 @@ import org.jclouds.cloudstack.domain.PublicIPAddress; import org.jclouds.cloudstack.domain.SecurityGroup; import org.jclouds.cloudstack.domain.ServiceOffering; import org.jclouds.cloudstack.domain.SshKeyPair; +import org.jclouds.cloudstack.domain.Tag; import org.jclouds.cloudstack.domain.Template; import org.jclouds.cloudstack.domain.VirtualMachine; import org.jclouds.cloudstack.domain.Zone; @@ -60,26 +68,20 @@ import org.jclouds.cloudstack.functions.CreateFirewallRulesForIP; import org.jclouds.cloudstack.functions.CreatePortForwardingRulesForIP; import org.jclouds.cloudstack.functions.StaticNATVirtualMachineInNetwork; import org.jclouds.cloudstack.functions.StaticNATVirtualMachineInNetwork.Factory; +import org.jclouds.cloudstack.options.CreateTagsOptions; import org.jclouds.cloudstack.options.DeployVirtualMachineOptions; import org.jclouds.cloudstack.options.ListFirewallRulesOptions; import org.jclouds.cloudstack.options.ListTemplatesOptions; import org.jclouds.cloudstack.strategy.BlockUntilJobCompletesAndReturnResult; import org.jclouds.collect.Memoized; import org.jclouds.compute.ComputeServiceAdapter; +import org.jclouds.compute.config.GetLoginForProviderFromPropertiesAndStoreCredentialsOrReturnNull; import org.jclouds.compute.functions.GroupNamingConvention; import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.domain.Credentials; import org.jclouds.domain.LoginCredentials; import org.jclouds.logging.Logger; -import com.google.common.base.Predicate; -import com.google.common.base.Supplier; -import com.google.common.cache.LoadingCache; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableSet.Builder; -import com.google.common.collect.Sets; -import com.google.common.primitives.Ints; - /** * defines the connection between the {@link CloudStackApi} implementation * and the jclouds {@link ComputeService} @@ -107,6 +109,7 @@ public class CloudStackComputeServiceAdapter implements private final LoadingCache securityGroupCache; private final LoadingCache keyPairCache; private final GroupNamingConvention.Factory namingConvention; + private final GetLoginForProviderFromPropertiesAndStoreCredentialsOrReturnNull credentialsProvider; @Inject public CloudStackComputeServiceAdapter(CloudStackApi client, Predicate jobComplete, @@ -122,7 +125,8 @@ public class CloudStackComputeServiceAdapter implements Supplier> zoneIdToZone, LoadingCache securityGroupCache, LoadingCache keyPairCache, - GroupNamingConvention.Factory namingConvention) { + GroupNamingConvention.Factory namingConvention, + GetLoginForProviderFromPropertiesAndStoreCredentialsOrReturnNull credentialsProvider) { this.client = checkNotNull(client, "client"); this.jobComplete = checkNotNull(jobComplete, "jobComplete"); this.networkSupplier = checkNotNull(networkSupplier, "networkSupplier"); @@ -139,6 +143,7 @@ public class CloudStackComputeServiceAdapter implements this.optionsConverters = optionsConverters; this.zoneIdToZone = zoneIdToZone; this.namingConvention = namingConvention; + this.credentialsProvider = credentialsProvider; } @Override @@ -233,17 +238,37 @@ public class CloudStackComputeServiceAdapter implements templateId, options); VirtualMachine vm = blockUntilJobCompletesAndReturnResult.apply(job); logger.debug("--- virtualmachine: %s", vm); - LoginCredentials.Builder credentialsBuilder = LoginCredentials.builder(); - if (templateOptions.getKeyPair() != null) { - SshKeyPair keyPair = keyPairCache.getUnchecked(templateOptions.getKeyPair()); - credentialsBuilder.privateKey(keyPair.getPrivateKey()); - } else if (vm.isPasswordEnabled()) { - assert vm.getPassword() != null : vm; - credentialsBuilder.password(vm.getPassword()); + LoginCredentials credentials = credentialsProvider.get(); + if (credentials == null || credentials.getUser() == null) { + LoginCredentials.Builder credentialsBuilder = LoginCredentials.builder(); + if (templateOptions.getKeyPair() != null) { + SshKeyPair keyPair = keyPairCache.getUnchecked(templateOptions.getKeyPair()); + credentialsBuilder.privateKey(keyPair.getPrivateKey()); + } else if (vm.isPasswordEnabled()) { + assert vm.getPassword() != null : vm; + credentialsBuilder.password(vm.getPassword()); + } + credentials = credentialsBuilder.build(); } try { - if (templateOptions.shouldSetupStaticNat()) { + ImmutableMap.Builder builder = ImmutableMap.builder(); + builder.putAll(template.getOptions().getUserMetadata()); + for (String tag : template.getOptions().getTags()) + builder.put(tag, "jclouds-empty-tag-placeholder"); + Map common = builder.build(); + + if (!common.isEmpty()) { + logger.debug(">> adding tags %s to virtualmachine(%s)", common, vm.getId()); + CreateTagsOptions tagOptions = CreateTagsOptions.Builder.resourceIds(vm.getId()) + .resourceType(Tag.ResourceType.USER_VM) + .tags(common); + AsyncCreateResponse tagJob = client.getTagApi().createTags(tagOptions); + awaitCompletion(tagJob.getJobId()); + logger.debug("<< tags added"); + vm = client.getVirtualMachineApi().getVirtualMachine(vm.getId()); + } + if (templateOptions.shouldSetupStaticNat()) { Capabilities capabilities = client.getConfigurationApi().listCapabilities(); // TODO: possibly not all network ids, do we want to do this for (String networkId : options.getNetworkIds()) { @@ -272,7 +297,7 @@ public class CloudStackComputeServiceAdapter implements } throw re; } - return new NodeAndInitialCredentials(vm, vm.getId() + "", credentialsBuilder.build()); + return new NodeAndInitialCredentials(vm, vm.getId() + "", credentials); } @Override diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/CloudStackComputeServiceLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/CloudStackComputeServiceLiveTest.java index 7cc6d40382..754396584c 100644 --- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/CloudStackComputeServiceLiveTest.java +++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/CloudStackComputeServiceLiveTest.java @@ -16,14 +16,11 @@ */ package org.jclouds.cloudstack.compute; -import org.jclouds.compute.domain.NodeMetadata; +import com.google.inject.Module; import org.jclouds.compute.internal.BaseComputeServiceLiveTest; import org.jclouds.sshj.config.SshjSshClientModule; import org.testng.annotations.Test; -import com.google.common.collect.ImmutableMap; -import com.google.inject.Module; - /** * * Generally disabled, as it incurs higher fees. @@ -39,13 +36,6 @@ public class CloudStackComputeServiceLiveTest extends BaseComputeServiceLiveTest return new SshjSshClientModule(); } - // cloudstack does not support metadata - @Override - protected void checkUserMetadataContains(NodeMetadata node, ImmutableMap userMetadata) { - assert node.getUserMetadata().equals(ImmutableMap. of()) : String.format( - "node userMetadata did not match %s %s", userMetadata, node); - } - @Override public void testOptionToNotBlock() { // start call blocks until we static nat, which is long enough to reach diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/functions/VirtualMachineToNodeMetadataTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/functions/VirtualMachineToNodeMetadataTest.java index 6c055f7f7f..01bf3b3bbc 100644 --- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/functions/VirtualMachineToNodeMetadataTest.java +++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/compute/functions/VirtualMachineToNodeMetadataTest.java @@ -21,6 +21,7 @@ import static org.testng.Assert.assertEquals; import java.net.UnknownHostException; import java.util.Set; +import com.google.common.collect.ImmutableMap; import org.jclouds.cloudstack.domain.GuestIPType; import org.jclouds.cloudstack.domain.IPForwardingRule; import org.jclouds.cloudstack.domain.NIC; @@ -81,7 +82,11 @@ public class VirtualMachineToNodeMetadataTest { .privateAddresses(ImmutableSet.of("10.1.1.18")).publicAddresses(ImmutableSet.of("1.1.1.1")) .hardware(addHypervisor(ServiceOfferingToHardwareTest.one, "XenServer")) .imageId(TemplateToImageTest.one.getId()) - .operatingSystem(TemplateToImageTest.one.getOperatingSystem()).build().toString()); + .operatingSystem(TemplateToImageTest.one.getOperatingSystem()) + .tags(ImmutableSet.of("another-tag")) + .userMetadata(ImmutableMap.of("some-tag", "some-value")) + .build().toString() + ); } @@ -177,7 +182,10 @@ public class VirtualMachineToNodeMetadataTest { .privateAddresses(ImmutableSet.of("10.1.1.18")) .hardware(addHypervisor(ServiceOfferingToHardwareTest.one, "XenServer")) .imageId(TemplateToImageTest.one.getId()) - .operatingSystem(TemplateToImageTest.one.getOperatingSystem()).build().toString()); + .operatingSystem(TemplateToImageTest.one.getOperatingSystem()) + .tags(ImmutableSet.of("another-tag")) + .userMetadata(ImmutableMap.of("some-tag", "some-value")) + .build().toString()); } protected Hardware addHypervisor(Hardware in, String hypervisor) { diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/parse/ListVirtualMachinesResponseTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/parse/ListVirtualMachinesResponseTest.java index d6b737b2d6..8d2d8e628f 100644 --- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/parse/ListVirtualMachinesResponseTest.java +++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/parse/ListVirtualMachinesResponseTest.java @@ -20,6 +20,7 @@ import java.util.Set; import org.jclouds.cloudstack.domain.GuestIPType; import org.jclouds.cloudstack.domain.NIC; +import org.jclouds.cloudstack.domain.Tag; import org.jclouds.cloudstack.domain.TrafficType; import org.jclouds.cloudstack.domain.VirtualMachine; import org.jclouds.date.internal.SimpleDateFormatDateService; @@ -69,7 +70,26 @@ public class ListVirtualMachinesResponseTest extends BaseSetParserTest