http://git-wip-us.apache.org/repos/asf/stratos/blob/86fd5cf2/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/functions/VirtualMachineToNodeMetadata.java ---------------------------------------------------------------------- diff --git a/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/functions/VirtualMachineToNodeMetadata.java b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/functions/VirtualMachineToNodeMetadata.java new file mode 100644 index 0000000..cb78ece --- /dev/null +++ b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/functions/VirtualMachineToNodeMetadata.java @@ -0,0 +1,179 @@ +/* + * 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.cloudstack.compute.functions; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Iterables.filter; +import static com.google.common.collect.Iterables.transform; +import static com.google.common.collect.Sets.newHashSet; +import static org.jclouds.location.predicates.LocationPredicates.idEquals; +import static org.jclouds.util.InetAddresses2.isPrivateIPAddress; + +import java.util.Map; +import java.util.Set; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.jclouds.cloudstack.domain.IPForwardingRule; +import org.jclouds.cloudstack.domain.NIC; +import org.jclouds.cloudstack.domain.VirtualMachine; +import org.jclouds.collect.Memoized; +import org.jclouds.compute.domain.HardwareBuilder; +import org.jclouds.compute.domain.Image; +import org.jclouds.compute.domain.NodeMetadata; +import org.jclouds.compute.domain.NodeMetadata.Status; +import org.jclouds.compute.domain.NodeMetadataBuilder; +import org.jclouds.compute.domain.Processor; +import org.jclouds.compute.functions.GroupNamingConvention; +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<VirtualMachine, NodeMetadata> { + + public static final Map<VirtualMachine.State, Status> vmStateToNodeStatus = ImmutableMap + .<VirtualMachine.State, Status> builder().put(VirtualMachine.State.STARTING, Status.PENDING) + .put(VirtualMachine.State.RUNNING, Status.RUNNING).put(VirtualMachine.State.STOPPING, Status.PENDING) + .put(VirtualMachine.State.STOPPED, Status.SUSPENDED) + .put(VirtualMachine.State.DESTROYED, Status.TERMINATED) + .put(VirtualMachine.State.EXPUNGING, Status.TERMINATED) + .put(VirtualMachine.State.MIGRATING, Status.PENDING).put(VirtualMachine.State.ERROR, Status.ERROR) + .put(VirtualMachine.State.UNKNOWN, Status.UNRECOGNIZED) + // TODO: is this really a state? + .put(VirtualMachine.State.SHUTDOWNED, Status.PENDING) + .put(VirtualMachine.State.UNRECOGNIZED, Status.UNRECOGNIZED).build(); + + private final Supplier<Set<? extends Location>> locations; + private final Supplier<Set<? extends Image>> images; + private final LoadingCache<String, Set<IPForwardingRule>> getIPForwardingRulesByVirtualMachine; + private final GroupNamingConvention nodeNamingConvention; + + @Inject + VirtualMachineToNodeMetadata(@Memoized Supplier<Set<? extends Location>> locations, + @Memoized Supplier<Set<? extends Image>> images, + LoadingCache<String, Set<IPForwardingRule>> getIPForwardingRulesByVirtualMachine, + GroupNamingConvention.Factory namingConvention) { + this.nodeNamingConvention = checkNotNull(namingConvention, "namingConvention").createWithoutPrefix(); + this.locations = checkNotNull(locations, "locations"); + this.images = checkNotNull(images, "images"); + this.getIPForwardingRulesByVirtualMachine = checkNotNull(getIPForwardingRulesByVirtualMachine, + "getIPForwardingRulesByVirtualMachine"); + } + + @Override + public NodeMetadata apply(final VirtualMachine from) { + // convert the result object to a jclouds NodeMetadata + NodeMetadataBuilder builder = new NodeMetadataBuilder(); + builder.ids(from.getId() + ""); + builder.name(from.getName()); + // TODO: in cloudstack 2.2.12, when "name" was set fine on the backend, + // but wrong API response was returned to the user + // http://bugs.cloud.com/show_bug.cgi?id=11664 + // + // we set displayName to the same value as name, but this could be wrong + // on hosts not started with jclouds + builder.hostname(from.getDisplayName()); + builder.location(FluentIterable.from(locations.get()).firstMatch(idEquals(from.getZoneId())).orNull()); + if (from.getGroup() != null) { + builder.group(from.getGroup()); + } else if (from.getDisplayName() != null) { + builder.group(nodeNamingConvention.groupInUniqueNameOrNull(from.getDisplayName())); + } + Image image = FluentIterable.from(images.get()).firstMatch(new Predicate<Image>() { + @Override + public boolean apply(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() + "")); + } + }).orNull(); + if (image != null) { + builder.imageId(image.getId()); + builder.operatingSystem(image.getOperatingSystem()); + } + + 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())// + .build()); + + builder.status(vmStateToNodeStatus.get(from.getState())); + + Set<String> publicAddresses = newHashSet(); + Set<String> privateAddresses = newHashSet(); + if (from.getIPAddress() != null) { + boolean isPrivate = isPrivateIPAddress(from.getIPAddress()); + if (isPrivate) { + privateAddresses.add(from.getIPAddress()); + } else { + publicAddresses.add(from.getIPAddress()); + } + } + if (from.getPublicIP() != null) { + publicAddresses.add(from.getPublicIP()); + } + for (NIC nic : from.getNICs()) { + if (nic.getIPAddress() != null) { + if (isPrivateIPAddress(nic.getIPAddress())) { + privateAddresses.add(nic.getIPAddress()); + } else { + publicAddresses.add(nic.getIPAddress()); + } + } + } + try { + /* Also add to the list of public IPs any public IP address that has a + forwarding rule that links to this machine */ + Iterables.addAll(publicAddresses, transform( + filter(getIPForwardingRulesByVirtualMachine.getUnchecked(from.getId()), + new Predicate<IPForwardingRule>() { + @Override + public boolean apply(IPForwardingRule rule) { + return !"Deleting".equals(rule.getState()); + } + }), new Function<IPForwardingRule, String>() { + @Override + public String apply(IPForwardingRule rule) { + return rule.getIPAddress(); + } + })); + } catch (UncheckedExecutionException e) { + if (Throwables2.getFirstThrowableOfType(e, ResourceNotFoundException.class) == null) { + Throwables.propagateIfPossible(e.getCause()); + throw e; + } + } + return builder.privateAddresses(privateAddresses).publicAddresses(publicAddresses).build(); + } +}
http://git-wip-us.apache.org/repos/asf/stratos/blob/86fd5cf2/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/functions/ZoneToLocation.java ---------------------------------------------------------------------- diff --git a/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/functions/ZoneToLocation.java b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/functions/ZoneToLocation.java new file mode 100644 index 0000000..ae46a67 --- /dev/null +++ b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/functions/ZoneToLocation.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.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.all.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<Zone, Location> { + 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.<String, Object> of()) + .description(zone.getName()).id(zone.getId()) + .parent(Iterables.getOnlyElement(provider.get())).build(); + } + +} http://git-wip-us.apache.org/repos/asf/stratos/blob/86fd5cf2/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/loaders/CreateUniqueKeyPair.java ---------------------------------------------------------------------- diff --git a/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/loaders/CreateUniqueKeyPair.java b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/loaders/CreateUniqueKeyPair.java new file mode 100644 index 0000000..81d51d8 --- /dev/null +++ b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/loaders/CreateUniqueKeyPair.java @@ -0,0 +1,64 @@ +/* + * 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.cloudstack.compute.loaders; + +import static com.google.common.base.Preconditions.checkNotNull; + +import javax.annotation.Resource; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.jclouds.compute.reference.ComputeServiceConstants; +import org.jclouds.logging.Logger; +import org.jclouds.cloudstack.CloudStackApi; +import org.jclouds.cloudstack.domain.SshKeyPair; + +import com.google.common.base.Throwables; +import com.google.common.cache.CacheLoader; +import com.google.inject.Inject; + +@Singleton +public class CreateUniqueKeyPair extends CacheLoader<String, SshKeyPair> { + @Resource + @Named(ComputeServiceConstants.COMPUTE_LOGGER) + protected Logger logger = Logger.NULL; + protected final CloudStackApi client; + + @Inject + public CreateUniqueKeyPair(CloudStackApi client) { + this.client = checkNotNull(client, "client"); + } + + @Override + public SshKeyPair load(String input) { + SshKeyPair keyPair = null; + while (keyPair == null) { + try { + keyPair = client.getSSHKeyPairApi().createSSHKeyPair(input); + logger.debug(">> creating SSH key pair with name %s", input); + } catch (IllegalStateException e) { + logger.error(e, "<< error creating SSH key pair with name %s: ", + Throwables.getRootCause(e).getMessage()); + throw Throwables.propagate(e); + } + } + + logger.debug("<< created keyPair(%s)", keyPair.getName()); + return keyPair; + } + +} http://git-wip-us.apache.org/repos/asf/stratos/blob/86fd5cf2/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/loaders/FindSecurityGroupOrCreate.java ---------------------------------------------------------------------- diff --git a/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/loaders/FindSecurityGroupOrCreate.java b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/loaders/FindSecurityGroupOrCreate.java new file mode 100644 index 0000000..93f32a9 --- /dev/null +++ b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/loaders/FindSecurityGroupOrCreate.java @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.cloudstack.compute.loaders; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + + +import javax.annotation.Resource; +import javax.inject.Inject; +import javax.inject.Named; + +import org.jclouds.compute.reference.ComputeServiceConstants; +import org.jclouds.logging.Logger; +import org.jclouds.cloudstack.CloudStackApi; +import org.jclouds.cloudstack.domain.SecurityGroup; +import org.jclouds.cloudstack.domain.ZoneAndName; +import org.jclouds.cloudstack.domain.ZoneSecurityGroupNamePortsCidrs; + +import com.google.common.base.Function; +import com.google.common.cache.CacheLoader; + +public class FindSecurityGroupOrCreate extends CacheLoader<ZoneAndName, SecurityGroup> { + @Resource + @Named(ComputeServiceConstants.COMPUTE_LOGGER) + protected Logger logger = Logger.NULL; + protected final CloudStackApi client; + protected final Function<ZoneSecurityGroupNamePortsCidrs, SecurityGroup> groupCreator; + + @Inject + public FindSecurityGroupOrCreate(CloudStackApi client, + Function<ZoneSecurityGroupNamePortsCidrs, SecurityGroup> groupCreator) { + this.client = checkNotNull(client, "client"); + this.groupCreator = checkNotNull(groupCreator, "groupCreator"); + } + + @Override + public SecurityGroup load(ZoneAndName in) { + SecurityGroup group = client.getSecurityGroupApi().getSecurityGroupByName(in.getName()); + if (group != null) { + return group; + } else { + return createNewSecurityGroup(in); + } + } + + private SecurityGroup createNewSecurityGroup(ZoneAndName in) { + checkState( + checkNotNull(in, "ZoneSecurityGrioupNamePortsCidrs") instanceof ZoneSecurityGroupNamePortsCidrs, + "programming error: when issuing get to this cacheloader, you need to pass an instance of ZoneSecurityGroupNamePortsCidrs, not %s", + in); + ZoneSecurityGroupNamePortsCidrs zoneSecurityGroupNamePortsCidrs = ZoneSecurityGroupNamePortsCidrs.class.cast(in); + return groupCreator.apply(zoneSecurityGroupNamePortsCidrs); + } + + @Override + public String toString() { + return "returnExistingSecurityGroupInZoneOrCreateAsNeeded()"; + } + +} http://git-wip-us.apache.org/repos/asf/stratos/blob/86fd5cf2/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/options/CloudStackTemplateOptions.java ---------------------------------------------------------------------- diff --git a/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/options/CloudStackTemplateOptions.java b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/options/CloudStackTemplateOptions.java new file mode 100644 index 0000000..1bcad0c --- /dev/null +++ b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/options/CloudStackTemplateOptions.java @@ -0,0 +1,509 @@ +/* + * 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.cloudstack.compute.options; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Map; +import java.util.Set; + +import org.jclouds.compute.options.TemplateOptions; + +import com.google.common.collect.Iterables; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; + +/** + * Contains options supported by the + * {@link ComputeService#createNodesInGroup(String, int, TemplateOptions)} and + * {@link ComputeService#createNodesInGroup(String, int, TemplateOptions)} + * operations on the <em>gogrid</em> provider. + * + * <h2>Usage</h2> 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): + * <p> + * + * <pre> + * import static org.jclouds.compute.options.CloudStackTemplateOptions.Builder.*; + * ComputeService client = // get connection + * templateBuilder.options(inboundPorts(22, 80, 8080, 443)); + * Set<? extends NodeMetadata> set = client.createNodesInGroup(tag, 2, templateBuilder.build()); + * </pre> + */ +public class CloudStackTemplateOptions extends TemplateOptions implements Cloneable { + + protected Set<String> securityGroupIds = Sets.<String> newLinkedHashSet(); + protected Map<String, String> ipsToNetworks = Maps.<String, String>newLinkedHashMap(); + protected String ipOnDefaultNetwork; + protected String keyPair; + protected boolean setupStaticNat = true; + protected String account; + protected String domainId; + protected boolean generateKeyPair = false; + protected boolean generateSecurityGroup = false; + protected String diskOfferingId; + protected int dataDiskSize; + + @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); + eTo.ipsToNetworks(this.ipsToNetworks); + eTo.ipOnDefaultNetwork(this.ipOnDefaultNetwork); + eTo.keyPair(this.keyPair); + eTo.generateKeyPair(shouldGenerateKeyPair()); + eTo.generateSecurityGroup(shouldGenerateSecurityGroup()); + eTo.account(this.account); + eTo.domainId(this.domainId); + eTo.setupStaticNat(setupStaticNat); + eTo.diskOfferingId(diskOfferingId); + eTo.dataDiskSize(dataDiskSize); + } + } + + /** + * @see org.jclouds.cloudstack.options.DeployVirtualMachineOptions#diskOfferingId + */ + public CloudStackTemplateOptions diskOfferingId(String diskOfferingId) { + this.diskOfferingId = diskOfferingId; + return this; + } + + public String getDiskOfferingId() { + return diskOfferingId; + } + + /** + * @see DeployVirtualMachineOptions#dataDiskSize + */ + public CloudStackTemplateOptions dataDiskSize(int dataDiskSize) { + this.dataDiskSize = dataDiskSize; + return this; + } + + public int getDataDiskSize() { + return dataDiskSize; + } + + /** + * @see DeployVirtualMachineOptions#securityGroupId + */ + public CloudStackTemplateOptions securityGroupId(String securityGroupId) { + this.securityGroupIds.add(securityGroupId); + return this; + } + + /** + * @see DeployVirtualMachineOptions#securityGroupIds + */ + public CloudStackTemplateOptions securityGroupIds(Iterable<String> securityGroupIds) { + Iterables.addAll(this.securityGroupIds, checkNotNull(securityGroupIds, "securityGroupIds was null")); + return this; + } + + public Set<String> getSecurityGroupIds() { + return securityGroupIds; + } + + /** + * @see #shouldGenerateKeyPair() + */ + public CloudStackTemplateOptions generateSecurityGroup(boolean enable) { + this.generateSecurityGroup = enable; + return this; + } + + /** + * @return true if auto generation of keypairs is enabled + */ + public boolean shouldGenerateSecurityGroup() { + return generateSecurityGroup; + } + + /** + * @deprecated See TemplateOptions#networks + * @see DeployVirtualMachineOptions#networkId + */ + @Deprecated + public CloudStackTemplateOptions networkId(String networkId) { + this.networks.add(networkId); + return this; + } + + /** + * @deprecated See TemplateOptions#networks + * @see DeployVirtualMachineOptions#networkIds + */ + @Deprecated + public CloudStackTemplateOptions networkIds(Iterable<String> networkIds) { + Iterables.addAll(this.networks, checkNotNull(networkIds, "networkIds was null")); + return this; + } + + /** + * @deprecated See TemplateOptions#getNetworks + */ + @Deprecated + public Set<String> getNetworkIds() { + return this.getNetworks(); + } + + public CloudStackTemplateOptions setupStaticNat(boolean setupStaticNat) { + this.setupStaticNat = setupStaticNat; + return this; + } + + public boolean shouldSetupStaticNat() { + return this.setupStaticNat; + } + + /** + * @see DeployVirtualMachineOptions#ipOnDefaultNetwork + */ + public CloudStackTemplateOptions ipOnDefaultNetwork(String ipOnDefaultNetwork) { + this.ipOnDefaultNetwork = ipOnDefaultNetwork; + return this; + } + + public String getIpOnDefaultNetwork() { + return ipOnDefaultNetwork; + } + + /** + * @see DeployVirtualMachineOptions#ipOnDefaultNetwork(String) + */ + public CloudStackTemplateOptions ipsToNetworks(Map<String, String> ipsToNetworks) { + this.ipsToNetworks.putAll(ipsToNetworks); + return this; + } + + public Map<String, String> getIpsToNetworks() { + return ipsToNetworks; + } + + /** + * @see DeployVirtualMachineOptions#keyPair(String) + */ + public CloudStackTemplateOptions keyPair(String keyPair) { + this.keyPair = keyPair; + return this; + } + + public String getKeyPair() { + return keyPair; + } + + /** + * @see #shouldGenerateKeyPair() + */ + public CloudStackTemplateOptions generateKeyPair(boolean enable) { + this.generateKeyPair = enable; + return this; + } + + /** + * @return true if auto generation of keypairs is enabled + */ + public boolean shouldGenerateKeyPair() { + return generateKeyPair; + } + + /** + * @see DeployVirtualMachineOptions#accountInDomain(String,String) + */ + public CloudStackTemplateOptions account(String account) { + this.account = account; + return this; + } + + public String getAccount() { + return account; + } + + /** + * @see DeployVirtualMachineOptions#accountInDomain(String,String) + * @see DeployVirtualMachineOptions#domainId(String) + */ + public CloudStackTemplateOptions domainId(String domainId) { + this.domainId = domainId; + return this; + } + + public String getDomainId() { + return domainId; + } + + public static final CloudStackTemplateOptions NONE = new CloudStackTemplateOptions(); + + public static class Builder { + + /** + * @see CloudStackTemplateOptions#diskOfferingId + */ + public static CloudStackTemplateOptions diskOfferingId(String diskOfferingId) { + CloudStackTemplateOptions options = new CloudStackTemplateOptions(); + return options.diskOfferingId(diskOfferingId); + } + + /** + * @see CloudStackTemplateOptions#dataDiskSize + */ + public static CloudStackTemplateOptions dataDiskSize(int dataDiskSize) { + CloudStackTemplateOptions options = new CloudStackTemplateOptions(); + return options.dataDiskSize(dataDiskSize); + } + + /** + * @see CloudStackTemplateOptions#securityGroupId + */ + public static CloudStackTemplateOptions securityGroupId(String id) { + CloudStackTemplateOptions options = new CloudStackTemplateOptions(); + return options.securityGroupId(id); + } + + /** + * @see CloudStackTemplateOptions#securityGroupIds + */ + public static CloudStackTemplateOptions securityGroupIds(Iterable<String> securityGroupIds) { + CloudStackTemplateOptions options = new CloudStackTemplateOptions(); + return options.securityGroupIds(securityGroupIds); + } + + /** + * @see CloudStackTemplateOptions#shouldGenerateSecurityGroup() + */ + public static CloudStackTemplateOptions generateSecurityGroup(boolean enable) { + return new CloudStackTemplateOptions().generateSecurityGroup(enable); + } + + /** + * @deprecated See TemplateOptions#networks + * @see CloudStackTemplateOptions#networkId + */ + @Deprecated + public static CloudStackTemplateOptions networkId(String id) { + return networks(id); + } + + /** + * @deprecated see TemplateOptions#networks + * @see CloudStackTemplateOptions#networkIds + */ + @Deprecated + public static CloudStackTemplateOptions networkIds(Iterable<String> networkIds) { + return networks(networkIds); + } + + /** + * @see CloudStackTemplateOptions#ipOnDefaultNetwork + */ + public static CloudStackTemplateOptions ipOnDefaultNetwork(String ipAddress) { + CloudStackTemplateOptions options = new CloudStackTemplateOptions(); + return options.ipOnDefaultNetwork(ipAddress); + } + + /** + * @see CloudStackTemplateOptions#ipsToNetworks + */ + public static CloudStackTemplateOptions ipsToNetworks(Map<String, String> ipToNetworkMap) { + CloudStackTemplateOptions options = new CloudStackTemplateOptions(); + return options.ipsToNetworks(ipToNetworkMap); + } + + /** + * @see CloudStackTemplateOptions#setupStaticNat + */ + public static CloudStackTemplateOptions setupStaticNat(boolean setupStaticNat) { + CloudStackTemplateOptions options = new CloudStackTemplateOptions(); + return options.setupStaticNat(setupStaticNat); + } + + /** + * @see CloudStackTemplateOptions#keyPair + */ + public static CloudStackTemplateOptions keyPair(String keyPair) { + CloudStackTemplateOptions options = new CloudStackTemplateOptions(); + return options.keyPair(keyPair); + } + + /** + * @see CloudStackTemplateOptions#shouldGenerateKeyPair() + */ + public static CloudStackTemplateOptions generateKeyPair(boolean enable) { + return new CloudStackTemplateOptions().generateKeyPair(enable); + } + + /** + * @see CloudStackTemplateOptions#account + */ + public static CloudStackTemplateOptions account(String account) { + CloudStackTemplateOptions options = new CloudStackTemplateOptions(); + return options.account(account); + } + + /** + * @see CloudStackTemplateOptions#domainId + */ + public static CloudStackTemplateOptions domainId(String domainId) { + CloudStackTemplateOptions options = new CloudStackTemplateOptions(); + return options.domainId(domainId); + } + + // 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#userMetadata(Map) + */ + public static CloudStackTemplateOptions userMetadata(Map<String, String> 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)); + } + + /** + * @see TemplateOptions#nodeNames(Iterable) + */ + public static CloudStackTemplateOptions nodeNames(Iterable<String> nodeNames) { + CloudStackTemplateOptions options = new CloudStackTemplateOptions(); + return CloudStackTemplateOptions.class.cast(options.nodeNames(nodeNames)); + } + + /** + * @see TemplateOptions#networks(Iterable) + */ + public static CloudStackTemplateOptions networks(Iterable<String> networks) { + CloudStackTemplateOptions options = new CloudStackTemplateOptions(); + return CloudStackTemplateOptions.class.cast(options.networks(networks)); + } + + /** + * @see TemplateOptions#networks(String...) + */ + public static CloudStackTemplateOptions networks(String... networks) { + CloudStackTemplateOptions options = new CloudStackTemplateOptions(); + return CloudStackTemplateOptions.class.cast(options.networks(networks)); + } + } + + // 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#installPrivateKey(String) + */ + @Override + public CloudStackTemplateOptions installPrivateKey(String privateKey) { + return CloudStackTemplateOptions.class.cast(super.installPrivateKey(privateKey)); + } + + /** + * {@inheritDoc} + */ + @Override + public CloudStackTemplateOptions userMetadata(Map<String, String> 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)); + } + + /** + * {@inheritDoc} + */ + @Override + public CloudStackTemplateOptions nodeNames(Iterable<String> nodeNames) { + return CloudStackTemplateOptions.class.cast(super.nodeNames(nodeNames)); + } + + /** + * {@inheritDoc} + */ + @Override + public CloudStackTemplateOptions networks(Iterable<String> networks) { + return CloudStackTemplateOptions.class.cast(super.networks(networks)); + } + + /** + * {@inheritDoc} + */ + @Override + public CloudStackTemplateOptions networks(String... networks) { + return CloudStackTemplateOptions.class.cast(super.networks(networks)); + } +} http://git-wip-us.apache.org/repos/asf/stratos/blob/86fd5cf2/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/predicates/AllNodesInGroupTerminated.java ---------------------------------------------------------------------- diff --git a/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/predicates/AllNodesInGroupTerminated.java b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/predicates/AllNodesInGroupTerminated.java new file mode 100644 index 0000000..6a4bd89 --- /dev/null +++ b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/predicates/AllNodesInGroupTerminated.java @@ -0,0 +1,53 @@ +/* + * 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.cloudstack.compute.predicates; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Predicates.and; +import static com.google.common.collect.Iterables.all; +import static org.jclouds.compute.predicates.NodePredicates.TERMINATED; +import static org.jclouds.compute.predicates.NodePredicates.inGroup; +import static org.jclouds.compute.predicates.NodePredicates.locationId; +import static org.jclouds.compute.predicates.NodePredicates.parentLocationId; + +import javax.inject.Inject; + +import org.jclouds.compute.ComputeService; +import org.jclouds.compute.domain.ComputeMetadata; +import org.jclouds.cloudstack.domain.ZoneAndName; + +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; + +public class AllNodesInGroupTerminated implements Predicate<ZoneAndName> { + private final ComputeService computeService; + + + //TODO: TESTME + @Inject + public AllNodesInGroupTerminated(ComputeService computeService) { + this.computeService = checkNotNull(computeService, "computeService"); + } + + @Override + public boolean apply(ZoneAndName input) { + // new nodes can have the zone as their location, existing nodes, the parent is the + // location + return all(computeService.listNodesDetailsMatching(Predicates.<ComputeMetadata> or(locationId(input.getZone()), + parentLocationId(input.getZone()))), and(inGroup(input.getName()), TERMINATED)); + } +} http://git-wip-us.apache.org/repos/asf/stratos/blob/86fd5cf2/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/strategy/AdvancedNetworkOptionsConverter.java ---------------------------------------------------------------------- diff --git a/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/strategy/AdvancedNetworkOptionsConverter.java b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/strategy/AdvancedNetworkOptionsConverter.java new file mode 100644 index 0000000..efbce39 --- /dev/null +++ b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/strategy/AdvancedNetworkOptionsConverter.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.cloudstack.compute.strategy; + +import com.google.common.collect.Iterables; +import org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions; +import org.jclouds.cloudstack.domain.Network; +import org.jclouds.cloudstack.options.DeployVirtualMachineOptions; + +import java.util.Map; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Predicates.and; +import static com.google.common.collect.Iterables.filter; +import static org.jclouds.cloudstack.predicates.NetworkPredicates.defaultNetworkInZone; +import static org.jclouds.cloudstack.predicates.NetworkPredicates.isIsolatedNetwork; +import static org.jclouds.cloudstack.predicates.NetworkPredicates.supportsStaticNAT; + +/** + * Convert template options into DeployVirtualMachineOptions, when the target zone has advanced networking. + */ +public class AdvancedNetworkOptionsConverter implements OptionsConverter { + @Override + public DeployVirtualMachineOptions apply(CloudStackTemplateOptions templateOptions, Map<String, Network> networks, String zoneId, DeployVirtualMachineOptions options) { + // security groups not allowed. + // at least one network must be given to CloudStack, + // but jclouds will try to autodetect an appropriate network if none given. + checkArgument(templateOptions.getSecurityGroupIds().isEmpty(), "security groups cannot be specified for locations (zones) that use advanced networking"); + if (!templateOptions.getNetworks().isEmpty()) { + options.networkIds(templateOptions.getNetworks()); + } else if (templateOptions.getIpsToNetworks().isEmpty()) { + checkArgument(!networks.isEmpty(), "please setup a network for zone: " + zoneId); + Network defaultNetworkInZone = Iterables.getFirst(filter(networks.values(), and(defaultNetworkInZone(zoneId), supportsStaticNAT())), null); + if (defaultNetworkInZone == null) { + defaultNetworkInZone = Iterables.getFirst(filter(networks.values(), isIsolatedNetwork()), null); + } + if (defaultNetworkInZone == null) { + throw new IllegalArgumentException("please choose a specific network in zone " + zoneId + ": " + networks); + } else { + options.networkId(defaultNetworkInZone.getId()); + } + } + return options; + } +} http://git-wip-us.apache.org/repos/asf/stratos/blob/86fd5cf2/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/strategy/BasicNetworkOptionsConverter.java ---------------------------------------------------------------------- diff --git a/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/strategy/BasicNetworkOptionsConverter.java b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/strategy/BasicNetworkOptionsConverter.java new file mode 100644 index 0000000..181ddc7 --- /dev/null +++ b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/strategy/BasicNetworkOptionsConverter.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.cloudstack.compute.strategy; + +import org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions; +import org.jclouds.cloudstack.domain.Network; +import org.jclouds.cloudstack.options.DeployVirtualMachineOptions; + +import java.util.Map; + +/** + * Convert template options into DeployVirtualMachineOptions, when the target zone has basic networking. + */ +public class BasicNetworkOptionsConverter implements OptionsConverter { + @Override + public DeployVirtualMachineOptions apply(CloudStackTemplateOptions templateOptions, Map<String, Network> networks, String zoneId, DeployVirtualMachineOptions options) { + // both security groups and networks are optional, and CloudStack will + // use the zone/user's default network/security group if none given + if (!templateOptions.getSecurityGroupIds().isEmpty()) { + options.securityGroupIds(templateOptions.getSecurityGroupIds()); + } + if (!templateOptions.getNetworks().isEmpty()) { + options.networkIds(templateOptions.getNetworks()); + } + return options; + } +} http://git-wip-us.apache.org/repos/asf/stratos/blob/86fd5cf2/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/strategy/CloudStackComputeServiceAdapter.java ---------------------------------------------------------------------- diff --git a/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/strategy/CloudStackComputeServiceAdapter.java b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/strategy/CloudStackComputeServiceAdapter.java new file mode 100644 index 0000000..29f5c85 --- /dev/null +++ b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/strategy/CloudStackComputeServiceAdapter.java @@ -0,0 +1,476 @@ +/* + * 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.cloudstack.compute.strategy; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.collect.Iterables.contains; +import static com.google.common.collect.Iterables.filter; +import static com.google.common.collect.Iterables.get; +import static org.jclouds.cloudstack.options.DeployVirtualMachineOptions.Builder.displayName; +import static org.jclouds.cloudstack.options.ListTemplatesOptions.Builder.id; +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 org.jclouds.cloudstack.CloudStackApi; +import org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions; +import org.jclouds.cloudstack.domain.AsyncCreateResponse; +import org.jclouds.cloudstack.domain.Capabilities; +import org.jclouds.cloudstack.domain.FirewallRule; +import org.jclouds.cloudstack.domain.IPForwardingRule; +import org.jclouds.cloudstack.domain.Network; +import org.jclouds.cloudstack.domain.NetworkType; +import org.jclouds.cloudstack.domain.Project; +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.Template; +import org.jclouds.cloudstack.domain.VirtualMachine; +import org.jclouds.cloudstack.domain.Zone; +import org.jclouds.cloudstack.domain.ZoneAndName; +import org.jclouds.cloudstack.domain.ZoneSecurityGroupNamePortsCidrs; +import org.jclouds.cloudstack.features.TemplateApi; +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.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.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} + */ +@Singleton +public class CloudStackComputeServiceAdapter implements + ComputeServiceAdapter<VirtualMachine, ServiceOffering, Template, Zone> { + + @Resource + @Named(ComputeServiceConstants.COMPUTE_LOGGER) + protected Logger logger = Logger.NULL; + + private final CloudStackApi client; + private final Predicate<String> jobComplete; + private final Supplier<Map<String, Network>> networkSupplier; + private final Supplier<Map<String, Project>> projectSupplier; + private final BlockUntilJobCompletesAndReturnResult blockUntilJobCompletesAndReturnResult; + private final Factory staticNATVMInNetwork; + private final CreatePortForwardingRulesForIP setupPortForwardingRulesForIP; + private final CreateFirewallRulesForIP setupFirewallRulesForIP; + private final LoadingCache<String, Set<IPForwardingRule>> vmToRules; + private final Map<String, Credentials> credentialStore; + private final Map<NetworkType, ? extends OptionsConverter> optionsConverters; + private final Supplier<LoadingCache<String, Zone>> zoneIdToZone; + private final LoadingCache<ZoneAndName, SecurityGroup> securityGroupCache; + private final LoadingCache<String, SshKeyPair> keyPairCache; + private final GroupNamingConvention.Factory namingConvention; + + @Inject + public CloudStackComputeServiceAdapter(CloudStackApi client, Predicate<String> jobComplete, + @Memoized Supplier<Map<String, Network>> networkSupplier, + @Memoized Supplier<Map<String, Project>> projectSupplier, + BlockUntilJobCompletesAndReturnResult blockUntilJobCompletesAndReturnResult, + StaticNATVirtualMachineInNetwork.Factory staticNATVMInNetwork, + CreatePortForwardingRulesForIP setupPortForwardingRulesForIP, + CreateFirewallRulesForIP setupFirewallRulesForIP, + LoadingCache<String, Set<IPForwardingRule>> vmToRules, + Map<String, Credentials> credentialStore, + Map<NetworkType, ? extends OptionsConverter> optionsConverters, + Supplier<LoadingCache<String, Zone>> zoneIdToZone, + LoadingCache<ZoneAndName, SecurityGroup> securityGroupCache, + LoadingCache<String, SshKeyPair> keyPairCache, + GroupNamingConvention.Factory namingConvention) { + this.client = checkNotNull(client, "client"); + this.jobComplete = checkNotNull(jobComplete, "jobComplete"); + this.networkSupplier = checkNotNull(networkSupplier, "networkSupplier"); + this.projectSupplier = checkNotNull(projectSupplier, "projectSupplier"); + this.blockUntilJobCompletesAndReturnResult = checkNotNull(blockUntilJobCompletesAndReturnResult, + "blockUntilJobCompletesAndReturnResult"); + this.staticNATVMInNetwork = checkNotNull(staticNATVMInNetwork, "staticNATVMInNetwork"); + this.setupPortForwardingRulesForIP = checkNotNull(setupPortForwardingRulesForIP, "setupPortForwardingRulesForIP"); + this.setupFirewallRulesForIP = checkNotNull(setupFirewallRulesForIP, "setupFirewallRulesForIP"); + this.vmToRules = checkNotNull(vmToRules, "vmToRules"); + this.credentialStore = checkNotNull(credentialStore, "credentialStore"); + this.securityGroupCache = checkNotNull(securityGroupCache, "securityGroupCache"); + this.keyPairCache = checkNotNull(keyPairCache, "keyPairCache"); + this.optionsConverters = optionsConverters; + this.zoneIdToZone = zoneIdToZone; + this.namingConvention = namingConvention; + } + + @Override + public NodeAndInitialCredentials<VirtualMachine> createNodeWithGroupEncodedIntoName(String group, String name, + org.jclouds.compute.domain.Template template) { + + 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()); + Map<String, Network> networks = networkSupplier.get(); + + final String zoneId = template.getLocation().getId(); + Zone zone = zoneIdToZone.get().getUnchecked(zoneId); + + CloudStackTemplateOptions templateOptions = template.getOptions().as(CloudStackTemplateOptions.class); + + checkState(optionsConverters.containsKey(zone.getNetworkType()), "no options converter configured for network type %s", zone.getNetworkType()); + DeployVirtualMachineOptions options = displayName(name).name(name); + if (templateOptions.getAccount() != null) { + options.accountInDomain(templateOptions.getAccount(), templateOptions.getDomainId()); + } else if (templateOptions.getDomainId() != null) { + options.domainId(templateOptions.getDomainId()); + } + + OptionsConverter optionsConverter = optionsConverters.get(zone.getNetworkType()); + options = optionsConverter.apply(templateOptions, networks, zoneId, options); + + options.group(group); + + if (templateOptions.getIpOnDefaultNetwork() != null) { + options.ipOnDefaultNetwork(templateOptions.getIpOnDefaultNetwork()); + } + + if (!templateOptions.getIpsToNetworks().isEmpty()) { + options.ipsToNetworks(templateOptions.getIpsToNetworks()); + } + + if (templateOptions.getKeyPair() != null) { + SshKeyPair keyPair = null; + if (templateOptions.getLoginPrivateKey() != null) { + String pem = templateOptions.getLoginPrivateKey(); + keyPair = SshKeyPair.builder().name(templateOptions.getKeyPair()) + .fingerprint(fingerprintPrivateKey(pem)).privateKey(pem).build(); + keyPairCache.asMap().put(keyPair.getName(), keyPair); + options.keyPair(keyPair.getName()); + } else if (client.getSSHKeyPairApi().getSSHKeyPair(templateOptions.getKeyPair()) != null) { + keyPair = client.getSSHKeyPairApi().getSSHKeyPair(templateOptions.getKeyPair()); + } + if (keyPair != null) { + keyPairCache.asMap().put(keyPair.getName(), keyPair); + options.keyPair(keyPair.getName()); + } + } else if (templateOptions.shouldGenerateKeyPair()) { + SshKeyPair keyPair = keyPairCache.getUnchecked(namingConvention.create() + .sharedNameForGroup(group)); + keyPairCache.asMap().put(keyPair.getName(), keyPair); + templateOptions.keyPair(keyPair.getName()); + options.keyPair(keyPair.getName()); + } + + if (templateOptions.getDiskOfferingId() != null) { + options.diskOfferingId(templateOptions.getDiskOfferingId()); + if (templateOptions.getDataDiskSize() > 0) { + options.dataDiskSize(templateOptions.getDataDiskSize()); + } + } + + if (supportsSecurityGroups().apply(zone)) { + List<Integer> inboundPorts = Ints.asList(templateOptions.getInboundPorts()); + + if (templateOptions.getSecurityGroupIds().isEmpty() + && !inboundPorts.isEmpty() + && templateOptions.shouldGenerateSecurityGroup()) { + String securityGroupName = namingConvention.create().sharedNameForGroup(group); + SecurityGroup sg = securityGroupCache.getUnchecked(ZoneSecurityGroupNamePortsCidrs.builder() + .zone(zone.getId()) + .name(securityGroupName) + .ports(ImmutableSet.copyOf(inboundPorts)) + .cidrs(ImmutableSet.<String> of()).build()); + options.securityGroupId(sg.getId()); + } + } + + String templateId = template.getImage().getId(); + String serviceOfferingId = template.getHardware().getId(); + + logger.debug("serviceOfferingId %s, templateId %s, zoneId %s, options %s%n", serviceOfferingId, templateId, + zoneId, options); + AsyncCreateResponse job = client.getVirtualMachineApi().deployVirtualMachineInZone(zoneId, serviceOfferingId, + templateId, options); + VirtualMachine vm = blockUntilJobCompletesAndReturnResult.<VirtualMachine>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()); + } + + try { + 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()) { + logger.debug(">> creating static NAT for virtualMachine(%s) in network(%s)", vm.getId(), networkId); + PublicIPAddress ip = staticNATVMInNetwork.create(networks.get(networkId)).apply(vm); + logger.trace("<< static NATed IPAddress(%s) to virtualMachine(%s)", ip.getId(), vm.getId()); + vm = client.getVirtualMachineApi().getVirtualMachine(vm.getId()); + List<Integer> ports = Ints.asList(templateOptions.getInboundPorts()); + if (capabilities.getCloudStackVersion().startsWith("2")) { + logger.debug(">> setting up IP forwarding for IPAddress(%s) rules(%s)", ip.getId(), ports); + Set<IPForwardingRule> rules = setupPortForwardingRulesForIP.apply(ip, ports); + logger.trace("<< setup %d IP forwarding rules on IPAddress(%s)", rules.size(), ip.getId()); + } else { + logger.debug(">> setting up firewall rules for IPAddress(%s) rules(%s)", ip.getId(), ports); + Set<FirewallRule> rules = setupFirewallRulesForIP.apply(ip, ports); + logger.trace("<< setup %d firewall rules on IPAddress(%s)", rules.size(), ip.getId()); + } + } + } + } catch (RuntimeException re) { + logger.error("-- exception after node has been created, trying to destroy the created virtualMachine(%s)", vm.getId()); + try { + destroyNode(vm.getId()); + } catch (RuntimeException re2) { + logger.debug("-- exception in exceptionHandler while executing destroyNode for virtualMachine(%s), ignoring and rethrowing original exception", vm.getId()); + } + throw re; + } + return new NodeAndInitialCredentials<VirtualMachine>(vm, vm.getId() + "", credentialsBuilder.build()); + } + + @Override + public Iterable<ServiceOffering> listHardwareProfiles() { + // TODO: we may need to filter these + return client.getOfferingApi().listServiceOfferings(); + } + + @Override + public Iterable<Template> listImages() { + TemplateApi templateApi = client.getTemplateApi(); + ImmutableSet.Builder<Template> templates = ImmutableSet.builder(); + templates.addAll(templateApi.listTemplates()); + for (String project : projectSupplier.get().keySet()) { + templates.addAll(templateApi.listTemplates(ListTemplatesOptions.Builder.projectId(project))); + } + + return filter(templates.build(), isReady()); + } + + @Override + public Template getImage(String id) { + return get(client.getTemplateApi().listTemplates(id(id)), 0, null); + } + + @Override + public Iterable<VirtualMachine> listNodes() { + return client.getVirtualMachineApi().listVirtualMachines(); + } + + @Override + public Iterable<VirtualMachine> listNodesByIds(final Iterable<String> ids) { + return filter(listNodes(), new Predicate<VirtualMachine>() { + + @Override + public boolean apply(VirtualMachine vm) { + return contains(ids, vm.getId()); + } + }); + } + + @Override + public Iterable<Zone> listLocations() { + // TODO: we may need to filter these + return client.getZoneApi().listZones(); + } + + @Override + public VirtualMachine getNode(String id) { + String virtualMachineId = id; + return client.getVirtualMachineApi().getVirtualMachine(virtualMachineId); + } + + @Override + public void destroyNode(String id) { + String virtualMachineId = id; + // There was a bug in 2.2.12 release happening when static nat IP address + // was being released, and corresponding firewall rules were left behind. + // So next time the same IP is allocated, it might be not be static nat + // enabled, but there are still rules associated with it. And when you try + // to release this IP, the release will fail. + // + // The bug was fixed in 2.2.13 release only, and the current system wasn't + // updated yet. + // + // To avoid the issue, every time you release a static nat ip address, do + // the following: + + // 1) Delete IP forwarding rules associated with IP. + Set<String> ipAddresses = deleteIPForwardingRulesForVMAndReturnDistinctIPs(virtualMachineId); + + // 2) Delete firewall rules associated with IP. + ipAddresses.addAll(deleteFirewallRulesForVMAndReturnDistinctIPs(virtualMachineId)); + + // 3) Disable static nat rule for the IP. + disableStaticNATOnIPAddresses(ipAddresses); + + // 4) Only after 1 and 2 release the IP address. + disassociateIPAddresses(ipAddresses); + + destroyVirtualMachine(virtualMachineId); + + vmToRules.invalidate(virtualMachineId); + } + + public void disassociateIPAddresses(Set<String> ipAddresses) { + for (String ipAddress : ipAddresses) { + logger.debug(">> disassociating IPAddress(%s)", ipAddress); + client.getAddressApi().disassociateIPAddress(ipAddress); + } + } + + public void destroyVirtualMachine(String virtualMachineId) { + + String destroyVirtualMachine = client.getVirtualMachineApi().destroyVirtualMachine(virtualMachineId); + if (destroyVirtualMachine != null) { + logger.debug(">> destroying virtualMachine(%s) job(%s)", virtualMachineId, destroyVirtualMachine); + awaitCompletion(destroyVirtualMachine); + } else { + logger.trace("<< virtualMachine(%s) not found", virtualMachineId); + } + + } + + public void disableStaticNATOnIPAddresses(Set<String> ipAddresses) { + Builder<String> jobsToTrack = ImmutableSet.builder(); + for (String ipAddress : ipAddresses) { + String disableStaticNAT = client.getNATApi().disableStaticNATOnPublicIP(ipAddress); + if (disableStaticNAT != null) { + logger.debug(">> disabling static NAT IPAddress(%s) job(%s)", ipAddress, disableStaticNAT); + jobsToTrack.add(disableStaticNAT); + } + } + awaitCompletion(jobsToTrack.build()); + } + + public Set<String> deleteIPForwardingRulesForVMAndReturnDistinctIPs(String virtualMachineId) { + Builder<String> jobsToTrack = ImmutableSet.builder(); + + // immutable doesn't permit duplicates + Set<String> ipAddresses = Sets.newLinkedHashSet(); + + Set<IPForwardingRule> forwardingRules = client.getNATApi().getIPForwardingRulesForVirtualMachine( + virtualMachineId); + for (IPForwardingRule rule : forwardingRules) { + if (!"Deleting".equals(rule.getState())) { + ipAddresses.add(rule.getIPAddressId()); + String deleteForwardingRule = client.getNATApi().deleteIPForwardingRule(rule.getId()); + if (deleteForwardingRule != null) { + logger.debug(">> deleting IPForwardingRule(%s) job(%s)", rule.getId(), deleteForwardingRule); + jobsToTrack.add(deleteForwardingRule); + } + } + } + awaitCompletion(jobsToTrack.build()); + return ipAddresses; + } + + public Set<String> deleteFirewallRulesForVMAndReturnDistinctIPs(String virtualMachineId) { + // immutable doesn't permit duplicates + Set<String> ipAddresses = Sets.newLinkedHashSet(); + + String publicIpId = client.getVirtualMachineApi().getVirtualMachine(virtualMachineId).getPublicIPId(); + if (publicIpId != null) { + Set<FirewallRule> firewallRules = client.getFirewallApi() + .listFirewallRules(ListFirewallRulesOptions.Builder.ipAddressId(client.getVirtualMachineApi().getVirtualMachine(virtualMachineId).getPublicIPId())); + + for (FirewallRule rule : firewallRules) { + if (rule.getState() != FirewallRule.State.DELETING) { + ipAddresses.add(rule.getIpAddressId()); + client.getFirewallApi().deleteFirewallRule(rule.getId()); + logger.debug(">> deleting FirewallRule(%s)", rule.getId()); + } + } + } + return ipAddresses; + } + + public void awaitCompletion(Iterable<String> jobs) { + logger.debug(">> awaiting completion of jobs(%s)", jobs); + for (String job : jobs) + awaitCompletion(job); + logger.trace("<< completed jobs(%s)", jobs); + } + + public void awaitCompletion(String job) { + boolean completed = jobComplete.apply(job); + logger.trace("<< job(%s) complete(%s)", job, completed); + } + + @Override + public void rebootNode(String id) { + String virtualMachineId = id; + String job = client.getVirtualMachineApi().rebootVirtualMachine(virtualMachineId); + if (job != null) { + logger.debug(">> rebooting virtualMachine(%s) job(%s)", virtualMachineId, job); + awaitCompletion(job); + } + } + + @Override + public void resumeNode(String id) { + String virtualMachineId = id; + String job = client.getVirtualMachineApi().startVirtualMachine(id); + if (job != null) { + logger.debug(">> starting virtualMachine(%s) job(%s)", virtualMachineId, job); + awaitCompletion(job); + } + } + + @Override + public void suspendNode(String id) { + String virtualMachineId = id; + String job = client.getVirtualMachineApi().stopVirtualMachine(id); + if (job != null) { + logger.debug(">> stopping virtualMachine(%s) job(%s)", virtualMachineId, job); + awaitCompletion(job); + } + } + +} http://git-wip-us.apache.org/repos/asf/stratos/blob/86fd5cf2/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/strategy/OptionsConverter.java ---------------------------------------------------------------------- diff --git a/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/strategy/OptionsConverter.java b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/strategy/OptionsConverter.java new file mode 100644 index 0000000..4cc6b9f --- /dev/null +++ b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/compute/strategy/OptionsConverter.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.cloudstack.compute.strategy; + +import java.util.Map; + +import org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions; +import org.jclouds.cloudstack.domain.Network; +import org.jclouds.cloudstack.options.DeployVirtualMachineOptions; + +/** + * Convert template options into DeployVirtualMachineOptions. Expressed as an interface, because in + * CloudStack different zone network types have different requirements when it comes to networks and + * security groups. + */ +public interface OptionsConverter { + + /** + * Convert a CloudStackTemplateOptions and apply to a DeployVirtualMachineOptions instance. + * + * @param templateOptions the input set of options + * @param networks the networks available + * @param zoneId the zone of the new virtual machine + * @param options where the resulting set of options will be applied + * @return same as "options" parameter + */ + DeployVirtualMachineOptions apply(CloudStackTemplateOptions templateOptions, Map<String, Network> networks, String zoneId, DeployVirtualMachineOptions options); + +} http://git-wip-us.apache.org/repos/asf/stratos/blob/86fd5cf2/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/config/CloudStackHttpApiModule.java ---------------------------------------------------------------------- diff --git a/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/config/CloudStackHttpApiModule.java b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/config/CloudStackHttpApiModule.java new file mode 100644 index 0000000..95f186e --- /dev/null +++ b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/config/CloudStackHttpApiModule.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.cloudstack.config; + +import static org.jclouds.rest.config.BinderUtils.bindHttpApi; + +import java.util.concurrent.TimeUnit; + +import org.jclouds.Constants; +import org.jclouds.cloudstack.CloudStackApi; +import org.jclouds.cloudstack.CloudStackDomainApi; +import org.jclouds.cloudstack.CloudStackGlobalApi; +import org.jclouds.cloudstack.domain.LoginResponse; +import org.jclouds.cloudstack.features.SessionApi; +import org.jclouds.cloudstack.filters.AddSessionKeyAndJSessionIdToRequest; +import org.jclouds.cloudstack.filters.AuthenticationFilter; +import org.jclouds.cloudstack.filters.QuerySigner; +import org.jclouds.cloudstack.handlers.CloudStackErrorHandler; +import org.jclouds.cloudstack.handlers.InvalidateSessionAndRetryOn401AndLogoutOnClose; +import org.jclouds.cloudstack.loaders.LoginWithPasswordCredentials; +import org.jclouds.domain.Credentials; +import org.jclouds.http.HttpErrorHandler; +import org.jclouds.http.HttpRetryHandler; +import org.jclouds.http.annotation.ClientError; +import org.jclouds.http.annotation.Redirection; +import org.jclouds.http.annotation.ServerError; +import org.jclouds.location.Provider; +import org.jclouds.location.suppliers.ImplicitLocationSupplier; +import org.jclouds.location.suppliers.implicit.OnlyLocationOrFirstZone; +import org.jclouds.rest.ConfiguresHttpApi; +import org.jclouds.rest.ApiContext; +import org.jclouds.rest.config.HttpApiModule; +import org.jclouds.rest.internal.ApiContextImpl; + +import com.google.common.base.Supplier; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.LoadingCache; +import com.google.inject.Inject; +import com.google.inject.Provides; +import com.google.inject.Scopes; +import com.google.inject.Singleton; +import com.google.inject.TypeLiteral; +import com.google.inject.name.Named; + +/** + * Configures the cloudstack connection. + */ +@ConfiguresHttpApi +public class CloudStackHttpApiModule extends HttpApiModule<CloudStackApi> { + + @Override + protected void configure() { + bind(new TypeLiteral<ApiContext<CloudStackDomainApi>>() { + }).to(new TypeLiteral<ApiContextImpl<CloudStackDomainApi>>() { + }); + bind(new TypeLiteral<ApiContext<CloudStackGlobalApi>>() { + }).to(new TypeLiteral<ApiContextImpl<CloudStackGlobalApi>>() { + }); + bind(CredentialType.class).toProvider(CredentialTypeFromPropertyOrDefault.class); + // session client is used directly for filters and retry handlers, so let's bind it explicitly + bindHttpApi(binder(), SessionApi.class); + bindHttpApi(binder(), CloudStackDomainApi.class); + bindHttpApi(binder(), CloudStackGlobalApi.class); + bind(HttpRetryHandler.class).annotatedWith(ClientError.class).to(InvalidateSessionAndRetryOn401AndLogoutOnClose.class); + + super.configure(); + } + + @Override + protected void installLocations() { + super.installLocations(); + bind(ImplicitLocationSupplier.class).to(OnlyLocationOrFirstZone.class).in(Scopes.SINGLETON); + } + + @Override + protected void bindErrorHandlers() { + bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to(CloudStackErrorHandler.class); + bind(HttpErrorHandler.class).annotatedWith(ClientError.class).to(CloudStackErrorHandler.class); + bind(HttpErrorHandler.class).annotatedWith(ServerError.class).to(CloudStackErrorHandler.class); + } + + @Singleton + static class CredentialTypeFromPropertyOrDefault implements javax.inject.Provider<CredentialType> { + /** + * use optional injection to supply a default value for credential type. so that we don't have + * to set a default property. + */ + @Inject(optional = true) + @Named(CloudStackProperties.CREDENTIAL_TYPE) + String credentialType = CredentialType.API_ACCESS_KEY_CREDENTIALS.toString(); + + @Override + public CredentialType get() { + return CredentialType.fromValue(credentialType); + } + } + + /** + * we use the type of credentials specified at login to determine which way we want to filter the + * request. <br/> + * for ex, if we are getting passwords, we know we will need to login/logout. Otherwise we are + * signing requests. + */ + @Provides + @Singleton + protected AuthenticationFilter authenticationFilterForCredentialType(CredentialType credentialType, + AddSessionKeyAndJSessionIdToRequest addSessionKeyAndJSessionIdToRequest, QuerySigner querySigner) { + switch (credentialType) { + case PASSWORD_CREDENTIALS: + return addSessionKeyAndJSessionIdToRequest; + case API_ACCESS_KEY_CREDENTIALS: + return querySigner; + default: + throw new IllegalArgumentException("credential type not supported: " + credentialType); + } + } + + // PROPERTY_SESSION_INTERVAL is default to 60 seconds + @Provides + @Singleton + protected LoadingCache<Credentials, LoginResponse> provideLoginResponseCache( + LoginWithPasswordCredentials getLoginResponse, + @Named(Constants.PROPERTY_SESSION_INTERVAL) int seconds) { + return CacheBuilder.newBuilder().expireAfterWrite(seconds, TimeUnit.SECONDS).build(getLoginResponse); + } + + // Temporary conversion of a cache to a supplier until there is a single-element cache + // http://code.google.com/p/guava-libraries/issues/detail?id=872 + @Provides + @Singleton + protected Supplier<LoginResponse> provideLoginResponseSupplier(final LoadingCache<Credentials, LoginResponse> cache, + @Provider final Supplier<Credentials> creds) { + return new Supplier<LoginResponse>() { + @Override + public LoginResponse get() { + return cache.getUnchecked(creds.get()); + } + }; + } +} http://git-wip-us.apache.org/repos/asf/stratos/blob/86fd5cf2/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/config/CloudStackParserModule.java ---------------------------------------------------------------------- diff --git a/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/config/CloudStackParserModule.java b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/config/CloudStackParserModule.java new file mode 100644 index 0000000..ab1fac0 --- /dev/null +++ b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/config/CloudStackParserModule.java @@ -0,0 +1,105 @@ +/* + * 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.cloudstack.config; + +import java.io.IOException; +import java.util.Date; + +import javax.inject.Inject; + +import org.jclouds.date.DateService; +import org.jclouds.json.config.GsonModule.DateAdapter; +import org.jclouds.json.config.GsonModule.Iso8601DateAdapter; +import org.jclouds.json.internal.NullFilteringTypeAdapterFactories.IterableTypeAdapter; +import org.jclouds.json.internal.NullFilteringTypeAdapterFactories.IterableTypeAdapterFactory; + +import com.google.common.base.Splitter; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableSet; +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import com.google.inject.AbstractModule; + +public class CloudStackParserModule extends AbstractModule { + + @Override + protected void configure() { + bind(DateAdapter.class).to(CloudStackDateAdapter.class); + bind(IterableTypeAdapterFactory.class).to(CommaDelimitedOKIterableTypeAdapterFactory.class); + } + + /** + * Data adapter for the date formats used by CloudStack. + * + * Essentially this is a workaround for the CloudStack getUsage() API call returning a corrupted form of ISO-8601 + * dates, which have an unexpected pair of apostrophes, like 2011-12-12'T'00:00:00+00:00 + * + */ + public static class CloudStackDateAdapter extends Iso8601DateAdapter { + + @Inject + private CloudStackDateAdapter(DateService dateService) { + super(dateService); + } + + public Date read(JsonReader reader) throws IOException { + return parseDate(reader.nextString().replaceAll("'T'", "T")); + } + + } + + /** + * Handles types that were previously strings and now arrays (ex. tags) + * + */ + public static class CommaDelimitedOKIterableTypeAdapterFactory extends IterableTypeAdapterFactory { + + @Override + @SuppressWarnings("unchecked") + protected <E, I> TypeAdapter<I> newAdapter(TypeAdapter<E> elementAdapter) { + return (TypeAdapter<I>) new Adapter<E>(elementAdapter); + } + + public static final class Adapter<E> extends TypeAdapter<Iterable<E>> { + + private final IterableTypeAdapter<E> delegate; + + public Adapter(TypeAdapter<E> elementAdapter) { + this.delegate = new IterableTypeAdapter<E>(elementAdapter); + nullSafe(); + } + + public void write(JsonWriter out, Iterable<E> value) throws IOException { + this.delegate.write(out, value); + } + + @SuppressWarnings("unchecked") + @Override + public Iterable<E> read(JsonReader in) throws IOException { + // HACK as cloudstack changed a field from String to Set! + if (in.peek() == JsonToken.STRING) { + String val = Strings.emptyToNull(in.nextString()); + return (Iterable<E>) (val != null ? Splitter.on(',').split(val) : ImmutableSet.of()); + } else { + return delegate.read(in); + } + } + } + } +} http://git-wip-us.apache.org/repos/asf/stratos/blob/86fd5cf2/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/config/CloudStackProperties.java ---------------------------------------------------------------------- diff --git a/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/config/CloudStackProperties.java b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/config/CloudStackProperties.java new file mode 100644 index 0000000..e123847 --- /dev/null +++ b/dependencies/jclouds/apis/cloudstack/1.8.0-stratos/src/main/java/org/jclouds/cloudstack/config/CloudStackProperties.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jclouds.cloudstack.config; + + +/** + * Configuration properties and constants used in CloudStack connections. + */ +public final class CloudStackProperties { + + /** + * Type of credentials specified during {@link ComputeServiceContextBuilder#overrides}. If + * {@link CredentialType#API_ACCESS_KEY_CREDENTIALS}, the request signing is used. If + * {@link CredentialType#PASSWORD_CREDENTIALS}, login will happen and a session will be + * persisted. + * + * <h3>valid values</h3> + * <ul> + * <li>apiAccessKeyCredentials</li> + * <li>passwordCredentials</li> + * </ul> + * + * @see CredentialType + * @see <a href="http://docs.cloud.com/CloudStack_Documentation/Customizing_the_CloudStack_UI#Cross_Site_Request_Forgery_%28CSRF%29" + * /> + */ + public static final String CREDENTIAL_TYPE = "jclouds.cloudstack.credential-type"; + + /** + * Whenever a node is created, automatically generate keypairs for groups, as needed, also + * delete the keypair(s) when the last node in the group is destroyed. + */ + public static final String AUTO_GENERATE_KEYPAIRS = "jclouds.cloudstack.auto-generate-keypairs"; + + private CloudStackProperties() { + throw new AssertionError("intentionally unimplemented"); + } +}
