added cloudstack support to launch servers in a network with ip forwarding

This commit is contained in:
Adrian Cole 2011-11-14 19:23:41 +02:00
parent 4329129c25
commit be7cf42f04
28 changed files with 899 additions and 201 deletions

View File

@ -34,6 +34,8 @@ public class CloudStackPropertiesBuilder extends PropertiesBuilder {
protected Properties defaultProperties() { protected Properties defaultProperties() {
Properties properties = super.defaultProperties(); Properties properties = super.defaultProperties();
properties.setProperty(PROPERTY_API_VERSION, "2.2"); properties.setProperty(PROPERTY_API_VERSION, "2.2");
properties.setProperty("jclouds.ssh.max-retries", "7");
properties.setProperty("jclouds.ssh.retry-auth", "true");
return properties; return properties;
} }

View File

@ -18,11 +18,13 @@
*/ */
package org.jclouds.cloudstack.compute.config; package org.jclouds.cloudstack.compute.config;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL; import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import javax.inject.Singleton; import javax.inject.Singleton;
@ -35,13 +37,19 @@ import org.jclouds.cloudstack.compute.functions.VirtualMachineToNodeMetadata;
import org.jclouds.cloudstack.compute.functions.ZoneToLocation; import org.jclouds.cloudstack.compute.functions.ZoneToLocation;
import org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions; import org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions;
import org.jclouds.cloudstack.compute.strategy.CloudStackComputeServiceAdapter; import org.jclouds.cloudstack.compute.strategy.CloudStackComputeServiceAdapter;
import org.jclouds.cloudstack.domain.IPForwardingRule;
import org.jclouds.cloudstack.domain.Network;
import org.jclouds.cloudstack.domain.OSType; import org.jclouds.cloudstack.domain.OSType;
import org.jclouds.cloudstack.domain.ServiceOffering; import org.jclouds.cloudstack.domain.ServiceOffering;
import org.jclouds.cloudstack.domain.Template; import org.jclouds.cloudstack.domain.Template;
import org.jclouds.cloudstack.domain.User;
import org.jclouds.cloudstack.domain.VirtualMachine; import org.jclouds.cloudstack.domain.VirtualMachine;
import org.jclouds.cloudstack.domain.Zone; import org.jclouds.cloudstack.domain.Zone;
import org.jclouds.cloudstack.features.GuestOSClient; import org.jclouds.cloudstack.features.GuestOSClient;
import org.jclouds.cloudstack.functions.StaticNATVirtualMachineInNetwork;
import org.jclouds.cloudstack.predicates.JobComplete; import org.jclouds.cloudstack.predicates.JobComplete;
import org.jclouds.cloudstack.suppliers.GetCurrentUser;
import org.jclouds.cloudstack.suppliers.NetworksForCurrentUser;
import org.jclouds.collect.Memoized; import org.jclouds.collect.Memoized;
import org.jclouds.compute.ComputeServiceAdapter; import org.jclouds.compute.ComputeServiceAdapter;
import org.jclouds.compute.config.ComputeServiceAdapterContextModule; import org.jclouds.compute.config.ComputeServiceAdapterContextModule;
@ -51,14 +59,19 @@ import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.domain.Location; import org.jclouds.domain.Location;
import org.jclouds.location.suppliers.OnlyLocationOrFirstZone; import org.jclouds.location.suppliers.OnlyLocationOrFirstZone;
import org.jclouds.predicates.RetryablePredicate; import org.jclouds.predicates.RetryablePredicate;
import org.jclouds.rest.ResourceNotFoundException;
import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier; import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.inject.Provides; import com.google.inject.Provides;
import com.google.inject.TypeLiteral; import com.google.inject.TypeLiteral;
import com.google.inject.assistedinject.FactoryModuleBuilder;
/** /**
* *
@ -90,6 +103,9 @@ public class CloudStackComputeServiceContextModule
bind(TemplateOptions.class).to(CloudStackTemplateOptions.class); bind(TemplateOptions.class).to(CloudStackTemplateOptions.class);
bind(new TypeLiteral<Function<Template, OperatingSystem>>() { bind(new TypeLiteral<Function<Template, OperatingSystem>>() {
}).to(TemplateToOperatingSystem.class); }).to(TemplateToOperatingSystem.class);
install(new FactoryModuleBuilder().build(StaticNATVirtualMachineInNetwork.Factory.class));
bind(new TypeLiteral<CacheLoader<Long, IPForwardingRule>>() {
}).to(GetIPForwardingRuleByVirtualMachine.class);
} }
@Provides @Provides
@ -128,10 +144,58 @@ public class CloudStackComputeServiceContextModule
}); });
} }
@Provides
@Singleton
@Memoized
public Supplier<Map<Long, Network>> listNetworks(@Named(PROPERTY_SESSION_INTERVAL) long seconds,
final NetworksForCurrentUser networksForCurrentUser) {
return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<Map<Long, Network>>(authException,
seconds, networksForCurrentUser);
}
@Provides
@Singleton
@Memoized
public Supplier<User> getCurrentUser(@Named(PROPERTY_SESSION_INTERVAL) long seconds,
final GetCurrentUser getCurrentUser) {
return new MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier<User>(authException, seconds,
getCurrentUser);
}
@Provides @Provides
@Singleton @Singleton
protected Predicate<Long> jobComplete(JobComplete jobComplete) { protected Predicate<Long> jobComplete(JobComplete jobComplete) {
// TODO: parameterize // TODO: parameterize
return new RetryablePredicate<Long>(jobComplete, 1200, 1, 5, TimeUnit.SECONDS); return new RetryablePredicate<Long>(jobComplete, 1200, 1, 5, TimeUnit.SECONDS);
} }
@Provides
@Singleton
protected Cache<Long, IPForwardingRule> getIPForwardingRuleByVirtualMachine(
CacheLoader<Long, IPForwardingRule> getIPForwardingRule) {
return CacheBuilder.newBuilder().build(getIPForwardingRule);
}
@Singleton
public static class GetIPForwardingRuleByVirtualMachine extends CacheLoader<Long, IPForwardingRule> {
private final CloudStackClient client;
@Inject
public GetIPForwardingRuleByVirtualMachine(CloudStackClient client) {
this.client = checkNotNull(client, "client");
}
/**
* @throws ResourceNotFoundException
* when there is no ip forwarding rule available for the VM
*/
@Override
public IPForwardingRule load(Long input) {
IPForwardingRule rule = client.getNATClient().getIPForwardingRuleForVirtualMachine(input);
if (rule == null)
throw new ResourceNotFoundException("no ip forwarding rule for: " + input);
return rule;
}
}
} }

View File

@ -27,6 +27,7 @@ import java.util.Set;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import org.jclouds.cloudstack.domain.IPForwardingRule;
import org.jclouds.cloudstack.domain.VirtualMachine; import org.jclouds.cloudstack.domain.VirtualMachine;
import org.jclouds.collect.FindResourceInSet; import org.jclouds.collect.FindResourceInSet;
import org.jclouds.collect.Memoized; import org.jclouds.collect.Memoized;
@ -37,12 +38,17 @@ import org.jclouds.compute.domain.NodeMetadataBuilder;
import org.jclouds.compute.domain.NodeState; import org.jclouds.compute.domain.NodeState;
import org.jclouds.domain.Credentials; import org.jclouds.domain.Credentials;
import org.jclouds.domain.Location; import org.jclouds.domain.Location;
import org.jclouds.rest.ResourceNotFoundException;
import org.jclouds.util.InetAddresses2; import org.jclouds.util.InetAddresses2;
import org.jclouds.util.Throwables2;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.base.Throwables;
import com.google.common.cache.Cache;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.UncheckedExecutionException;
/** /**
* @author Adrian Cole * @author Adrian Cole
@ -66,16 +72,20 @@ public class VirtualMachineToNodeMetadata implements Function<VirtualMachine, No
private final FindLocationForVirtualMachine findLocationForVirtualMachine; private final FindLocationForVirtualMachine findLocationForVirtualMachine;
private final FindHardwareForVirtualMachine findHardwareForVirtualMachine; private final FindHardwareForVirtualMachine findHardwareForVirtualMachine;
private final FindImageForVirtualMachine findImageForVirtualMachine; private final FindImageForVirtualMachine findImageForVirtualMachine;
private final Cache<Long, IPForwardingRule> getIPForwardingRuleByVirtualMachine;
@Inject @Inject
VirtualMachineToNodeMetadata(Map<String, Credentials> credentialStore, VirtualMachineToNodeMetadata(Map<String, Credentials> credentialStore,
FindLocationForVirtualMachine findLocationForVirtualMachine, FindLocationForVirtualMachine findLocationForVirtualMachine,
FindHardwareForVirtualMachine findHardwareForVirtualMachine, FindHardwareForVirtualMachine findHardwareForVirtualMachine,
FindImageForVirtualMachine findImageForVirtualMachine) { FindImageForVirtualMachine findImageForVirtualMachine,
Cache<Long, IPForwardingRule> getIPForwardingRuleByVirtualMachine) {
this.credentialStore = checkNotNull(credentialStore, "credentialStore"); this.credentialStore = checkNotNull(credentialStore, "credentialStore");
this.findLocationForVirtualMachine = checkNotNull(findLocationForVirtualMachine, "findLocationForVirtualMachine"); this.findLocationForVirtualMachine = checkNotNull(findLocationForVirtualMachine, "findLocationForVirtualMachine");
this.findHardwareForVirtualMachine = checkNotNull(findHardwareForVirtualMachine, "findHardwareForVirtualMachine"); this.findHardwareForVirtualMachine = checkNotNull(findHardwareForVirtualMachine, "findHardwareForVirtualMachine");
this.findImageForVirtualMachine = checkNotNull(findImageForVirtualMachine, "findImageForVirtualMachine"); this.findImageForVirtualMachine = checkNotNull(findImageForVirtualMachine, "findImageForVirtualMachine");
this.getIPForwardingRuleByVirtualMachine = checkNotNull(getIPForwardingRuleByVirtualMachine,
"getIPForwardingRuleByVirtualMachine");
} }
@Override @Override
@ -108,6 +118,15 @@ public class VirtualMachineToNodeMetadata implements Function<VirtualMachine, No
else else
builder.publicAddresses(addresses); builder.publicAddresses(addresses);
} }
try {
IPForwardingRule rule = getIPForwardingRuleByVirtualMachine.getUnchecked(from.getId());
builder.publicAddresses(ImmutableSet.<String> of(rule.getIPAddress()));
} catch (UncheckedExecutionException e) {
if (Throwables2.getFirstThrowableOfType(e, ResourceNotFoundException.class) == null) {
Throwables.propagateIfPossible(e.getCause());
throw e;
}
}
builder.credentials(credentialStore.get("node#" + from.getId())); builder.credentials(credentialStore.get("node#" + from.getId()));
return builder.build(); return builder.build();
} }

View File

@ -55,6 +55,7 @@ import com.google.common.collect.Sets;
public class CloudStackTemplateOptions extends TemplateOptions implements Cloneable { public class CloudStackTemplateOptions extends TemplateOptions implements Cloneable {
protected Set<Long> securityGroupIds = Sets.<Long> newLinkedHashSet(); protected Set<Long> securityGroupIds = Sets.<Long> newLinkedHashSet();
protected Set<Long> networkIds = Sets.<Long> newLinkedHashSet();
protected String keyPair; protected String keyPair;
@Override @Override
@ -70,6 +71,7 @@ public class CloudStackTemplateOptions extends TemplateOptions implements Clonea
if (to instanceof CloudStackTemplateOptions) { if (to instanceof CloudStackTemplateOptions) {
CloudStackTemplateOptions eTo = CloudStackTemplateOptions.class.cast(to); CloudStackTemplateOptions eTo = CloudStackTemplateOptions.class.cast(to);
eTo.securityGroupIds(this.securityGroupIds); eTo.securityGroupIds(this.securityGroupIds);
eTo.keyPair(this.keyPair);
} }
} }
@ -93,6 +95,26 @@ public class CloudStackTemplateOptions extends TemplateOptions implements Clonea
return securityGroupIds; return securityGroupIds;
} }
/**
* @see DeployVirtualMachineOptions#networkId
*/
public CloudStackTemplateOptions networkId(long networkId) {
this.networkIds.add(networkId);
return this;
}
/**
* @see DeployVirtualMachineOptions#networkIds
*/
public CloudStackTemplateOptions networkIds(Iterable<Long> networkIds) {
Iterables.addAll(this.networkIds, checkNotNull(networkIds, "networkIds was null"));
return this;
}
public Set<Long> getNetworkIds() {
return networkIds;
}
/** /**
* @see DeployVirtualMachineOptions#keyPair(String) * @see DeployVirtualMachineOptions#keyPair(String)
*/ */
@ -125,6 +147,22 @@ public class CloudStackTemplateOptions extends TemplateOptions implements Clonea
return options.securityGroupIds(securityGroupIds); return options.securityGroupIds(securityGroupIds);
} }
/**
* @see CloudStackTemplateOptions#networkId
*/
public static CloudStackTemplateOptions networkId(long id) {
CloudStackTemplateOptions options = new CloudStackTemplateOptions();
return options.networkId(id);
}
/**
* @see CloudStackTemplateOptions#networkIds
*/
public static CloudStackTemplateOptions networkIds(Iterable<Long> networkIds) {
CloudStackTemplateOptions options = new CloudStackTemplateOptions();
return options.networkIds(networkIds);
}
/** /**
* @see CloudStackTemplateOptions#keyPair * @see CloudStackTemplateOptions#keyPair
*/ */

View File

@ -20,7 +20,9 @@ package org.jclouds.cloudstack.compute.strategy;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Throwables.propagate;
import static com.google.common.collect.Iterables.filter; import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Iterables.getOnlyElement;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
@ -30,17 +32,21 @@ import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import javax.inject.Singleton; import javax.inject.Singleton;
import com.google.common.base.Predicates;
import org.jclouds.cloudstack.CloudStackClient; import org.jclouds.cloudstack.CloudStackClient;
import org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions; import org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions;
import org.jclouds.cloudstack.domain.AsyncCreateResponse; import org.jclouds.cloudstack.domain.AsyncCreateResponse;
import org.jclouds.cloudstack.domain.AsyncJob; import org.jclouds.cloudstack.domain.AsyncJob;
import org.jclouds.cloudstack.domain.Network;
import org.jclouds.cloudstack.domain.PublicIPAddress;
import org.jclouds.cloudstack.domain.ServiceOffering; import org.jclouds.cloudstack.domain.ServiceOffering;
import org.jclouds.cloudstack.domain.Template; import org.jclouds.cloudstack.domain.Template;
import org.jclouds.cloudstack.domain.VirtualMachine; import org.jclouds.cloudstack.domain.VirtualMachine;
import org.jclouds.cloudstack.domain.Zone; import org.jclouds.cloudstack.domain.Zone;
import org.jclouds.cloudstack.functions.StaticNATVirtualMachineInNetwork;
import org.jclouds.cloudstack.functions.StaticNATVirtualMachineInNetwork.Factory;
import org.jclouds.cloudstack.options.DeployVirtualMachineOptions; import org.jclouds.cloudstack.options.DeployVirtualMachineOptions;
import org.jclouds.cloudstack.predicates.TemplatePredicates; import org.jclouds.cloudstack.predicates.TemplatePredicates;
import org.jclouds.collect.Memoized;
import org.jclouds.compute.ComputeService; import org.jclouds.compute.ComputeService;
import org.jclouds.compute.ComputeServiceAdapter; import org.jclouds.compute.ComputeServiceAdapter;
import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.compute.reference.ComputeServiceConstants;
@ -48,7 +54,7 @@ import org.jclouds.domain.Credentials;
import org.jclouds.logging.Logger; import org.jclouds.logging.Logger;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.base.Throwables; import com.google.common.base.Supplier;
/** /**
* defines the connection between the {@link CloudStackClient} implementation * defines the connection between the {@link CloudStackClient} implementation
@ -65,63 +71,96 @@ public class CloudStackComputeServiceAdapter implements
private final CloudStackClient client; private final CloudStackClient client;
private final Predicate<Long> jobComplete; private final Predicate<Long> jobComplete;
private final Supplier<Map<Long, Network>> networkSupplier;
private final Factory staticNATVMInNetwork;
private final Map<String, Credentials> credentialStore;
@Inject @Inject
public CloudStackComputeServiceAdapter(CloudStackClient client, Predicate<Long> jobComplete) { public CloudStackComputeServiceAdapter(CloudStackClient client, Predicate<Long> jobComplete,
@Memoized Supplier<Map<Long, Network>> networkSupplier,
StaticNATVirtualMachineInNetwork.Factory staticNATVMInNetwork, Map<String, Credentials> credentialStore) {
this.client = checkNotNull(client, "client"); this.client = checkNotNull(client, "client");
this.jobComplete=checkNotNull(jobComplete, "jobComplete"); this.jobComplete = checkNotNull(jobComplete, "jobComplete");
this.networkSupplier = checkNotNull(networkSupplier, "networkSupplier");
this.staticNATVMInNetwork = checkNotNull(staticNATVMInNetwork, "staticNATVMInNetwork");
this.credentialStore = checkNotNull(credentialStore, "credentialStore");
} }
@Override @Override
public VirtualMachine createNodeWithGroupEncodedIntoNameThenStoreCredentials(String group, String name, public NodeAndInitialCredentials<VirtualMachine> createNodeWithGroupEncodedIntoName(String group, String name,
org.jclouds.compute.domain.Template template, Map<String, Credentials> credentialStore) { org.jclouds.compute.domain.Template template) {
checkNotNull(template, "template was null"); checkNotNull(template, "template was null");
checkNotNull(template.getOptions(), "template options was null"); checkNotNull(template.getOptions(), "template options was null");
checkArgument(template.getOptions().getClass().isAssignableFrom(CloudStackTemplateOptions.class), checkArgument(template.getOptions().getClass().isAssignableFrom(CloudStackTemplateOptions.class),
"options class %s should have been assignable from CloudStackTemplateOptions", template.getOptions() "options class %s should have been assignable from CloudStackTemplateOptions", template.getOptions()
.getClass()); .getClass());
Map<Long, Network> networks = networkSupplier.get();
final long zoneId = Long.parseLong(template.getLocation().getId());
CloudStackTemplateOptions templateOptions = template.getOptions().as(CloudStackTemplateOptions.class); CloudStackTemplateOptions templateOptions = template.getOptions().as(CloudStackTemplateOptions.class);
DeployVirtualMachineOptions options = new DeployVirtualMachineOptions(); DeployVirtualMachineOptions options = new DeployVirtualMachineOptions();
if (templateOptions.getSecurityGroupIds().size() > 0) if (templateOptions.getSecurityGroupIds().size() > 0) {
options.securityGroupIds(templateOptions.getSecurityGroupIds()); options.securityGroupIds(templateOptions.getSecurityGroupIds());
} else if (templateOptions.getNetworkIds().size() > 0) {
options.networkIds(templateOptions.getNetworkIds());
} else if (networks.size() > 0) {
try {
options.networkId(getOnlyElement(filter(networks.values(), new Predicate<Network>() {
@Override
public boolean apply(Network arg0) {
return arg0.getZoneId() == zoneId;
}
})).getId());
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("please choose a specific network in zone " + zoneId + ": " + networks);
}
} else {
throw new IllegalArgumentException("please setup a network or security group for zone: " + zoneId);
}
if (templateOptions.getKeyPair() != null) { if (templateOptions.getKeyPair() != null) {
options.keyPair(templateOptions.getKeyPair()); options.keyPair(templateOptions.getKeyPair());
if (templateOptions.getRunScript() != null) { if (templateOptions.getRunScript() != null) {
checkArgument( checkArgument(
credentialStore.containsKey("keypair#" + templateOptions.getKeyPair()), credentialStore.containsKey("keypair#" + templateOptions.getKeyPair()),
"no private key configured for: %s; please use options.overrideLoginCredentialWith(rsa_private_text)", "no private key configured for: %s; please use options.overrideLoginCredentialWith(rsa_private_text)",
templateOptions.getKeyPair()); templateOptions.getKeyPair());
} }
} }
long zoneId = Long.parseLong(template.getLocation().getId());
long templateId = Long.parseLong(template.getImage().getId()); long templateId = Long.parseLong(template.getImage().getId());
long serviceOfferingId = Long.parseLong(template.getHardware().getId()); long serviceOfferingId = Long.parseLong(template.getHardware().getId());
System.out.printf("serviceOfferingId %d, templateId %d, zoneId %d, options %s%n", serviceOfferingId, templateId, logger.info("serviceOfferingId %d, templateId %d, zoneId %d, options %s%n", serviceOfferingId, templateId,
zoneId, options); zoneId, options);
AsyncCreateResponse job = client.getVirtualMachineClient().deployVirtualMachineInZone(zoneId, serviceOfferingId, AsyncCreateResponse job = client.getVirtualMachineClient().deployVirtualMachineInZone(zoneId, serviceOfferingId,
templateId, options); templateId, options);
assert jobComplete.apply(job.getJobId()); boolean completed = jobComplete.apply(job.getJobId());
AsyncJob<VirtualMachine> jobWithResult = client.getAsyncJobClient().<VirtualMachine> getAsyncJob(job.getJobId()); AsyncJob<VirtualMachine> jobWithResult = client.getAsyncJobClient().<VirtualMachine> getAsyncJob(job.getJobId());
assert completed : jobWithResult;
if (jobWithResult.getError() != null) if (jobWithResult.getError() != null)
Throwables.propagate(new ExecutionException(String.format("job %s failed with exception %s", job.getId(), propagate(new ExecutionException(String.format("job %s failed with exception %s", job.getId(), jobWithResult
jobWithResult.getError().toString())) { .getError().toString())) {
private static final long serialVersionUID = 4371112085613620239L; private static final long serialVersionUID = 4371112085613620239L;
}); });
VirtualMachine vm = jobWithResult.getResult(); VirtualMachine vm = jobWithResult.getResult();
Credentials credentials = null;
if (vm.isPasswordEnabled()) { if (vm.isPasswordEnabled()) {
assert vm.getPassword() != null : vm; assert vm.getPassword() != null : vm;
Credentials credentials = new Credentials("root", vm.getPassword()); credentials = new Credentials(null, vm.getPassword());
credentialStore.put("node#" + vm.getId(), credentials);
} else { } else {
// assert templateOptions.getKeyPair() != null : vm; credentials = credentialStore.get("keypair#" + templateOptions.getKeyPair());
Credentials credentials = credentialStore.get("keypair#" + templateOptions.getKeyPair());
credentialStore.put("node#" + vm.getId(), credentials);
} }
return vm; // TODO: possibly not all network ids, do we want to do this
for (long networkId : options.getNetworkIds()) {
// TODO: log this
PublicIPAddress ip = staticNATVMInNetwork.create(networks.get(networkId)).apply(vm);
}
return new NodeAndInitialCredentials<VirtualMachine>(vm, vm.getId() + "", credentials);
} }
@Override @Override

View File

@ -56,4 +56,34 @@ public class AsyncCreateResponse {
return jobId; return jobId;
} }
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (int) (id ^ (id >>> 32));
result = prime * result + (int) (jobId ^ (jobId >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
AsyncCreateResponse other = (AsyncCreateResponse) obj;
if (id != other.id)
return false;
if (jobId != other.jobId)
return false;
return true;
}
@Override
public String toString() {
return "[id=" + id + ", jobId=" + jobId + "]";
}
} }

View File

@ -32,7 +32,7 @@ import org.jclouds.concurrent.Timeout;
* @see <a href="http://download.cloud.com/releases/2.2.0/api_2.2.12/TOC_User.html" /> * @see <a href="http://download.cloud.com/releases/2.2.0/api_2.2.12/TOC_User.html" />
* @author Adrian Cole * @author Adrian Cole
*/ */
@Timeout(duration = 60, timeUnit = TimeUnit.SECONDS) @Timeout(duration = 120, timeUnit = TimeUnit.SECONDS)
public interface AccountClient { public interface AccountClient {
/** /**
* Lists Accounts * Lists Accounts

View File

@ -46,7 +46,9 @@ import com.google.common.util.concurrent.ListenableFuture;
* <p/> * <p/>
* *
* @see NATClient * @see NATClient
* @see <a href="http://download.cloud.com/releases/2.2.0/api_2.2.12/TOC_User.html" /> * @see <a
* href="http://download.cloud.com/releases/2.2.0/api_2.2.12/TOC_User.html"
* />
* @author Adrian Cole * @author Adrian Cole
*/ */
@RequestFilters(QuerySigner.class) @RequestFilters(QuerySigner.class)
@ -74,6 +76,28 @@ public interface NATAsyncClient {
@ExceptionParser(ReturnNullOnNotFoundOr404.class) @ExceptionParser(ReturnNullOnNotFoundOr404.class)
ListenableFuture<IPForwardingRule> getIPForwardingRule(@QueryParam("id") long id); ListenableFuture<IPForwardingRule> getIPForwardingRule(@QueryParam("id") long id);
/**
* @see NATClient#getIPForwardingRuleForIPAddress
*/
@GET
@QueryParams(keys = "command", values = "listIpForwardingRules")
@SelectJson("ipforwardingrule")
@OnlyElement
@Consumes(MediaType.APPLICATION_JSON)
@ExceptionParser(ReturnNullOnNotFoundOr404.class)
ListenableFuture<IPForwardingRule> getIPForwardingRuleForIPAddress(@QueryParam("ipaddressid") long id);
/**
* @see NATClient#getIPForwardingRuleForVirtualMachine
*/
@GET
@QueryParams(keys = "command", values = "listIpForwardingRules")
@SelectJson("ipforwardingrule")
@OnlyElement
@Consumes(MediaType.APPLICATION_JSON)
@ExceptionParser(ReturnNullOnNotFoundOr404.class)
ListenableFuture<IPForwardingRule> getIPForwardingRuleForVirtualMachine(@QueryParam("virtualmachineid") long id);
/** /**
* @see NATClient#createIPForwardingRule * @see NATClient#createIPForwardingRule
*/ */

View File

@ -32,7 +32,9 @@ import org.jclouds.concurrent.Timeout;
* <p/> * <p/>
* *
* @see IPForwardingRuleAsyncClient * @see IPForwardingRuleAsyncClient
* @see <a href="http://download.cloud.com/releases/2.2.0/api_2.2.12/TOC_User.html" /> * @see <a
* href="http://download.cloud.com/releases/2.2.0/api_2.2.12/TOC_User.html"
* />
* @author Adrian Cole * @author Adrian Cole
*/ */
@Timeout(duration = 60, timeUnit = TimeUnit.SECONDS) @Timeout(duration = 60, timeUnit = TimeUnit.SECONDS)
@ -56,6 +58,24 @@ public interface NATClient {
*/ */
IPForwardingRule getIPForwardingRule(long id); IPForwardingRule getIPForwardingRule(long id);
/**
* get a specific IPForwardingRule by ipaddress id
*
* @param id
* IPAddress of rule to get
* @return IPForwardingRule or null if not found
*/
IPForwardingRule getIPForwardingRuleForIPAddress(long id);
/**
* get a specific IPForwardingRule by virtual machine id
*
* @param id
* virtual machine of rule to get
* @return IPForwardingRule or null if not found
*/
IPForwardingRule getIPForwardingRuleForVirtualMachine(long id);
/** /**
* Creates an ip forwarding rule * Creates an ip forwarding rule
* *

View File

@ -50,16 +50,18 @@ import org.jclouds.rest.annotations.SkipEncoding;
import org.jclouds.rest.annotations.Unwrap; import org.jclouds.rest.annotations.Unwrap;
import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404; import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404; import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404;
/** /**
* Provides asynchronous access to cloudstack via their REST API. * Provides asynchronous access to cloudstack via their REST API.
* <p/> * <p/>
* *
* @see TemplateClient * @see TemplateClient
* @see <a href="http://download.cloud.com/releases/2.2.0/api_2.2.12/TOC_User.html" /> * @see <a
* href="http://download.cloud.com/releases/2.2.0/api_2.2.12/TOC_User.html"
* />
* @author Adrian Cole * @author Adrian Cole
*/ */
@RequestFilters(QuerySigner.class) @RequestFilters(QuerySigner.class)
@ -74,7 +76,9 @@ public interface TemplateAsyncClient {
@QueryParams(keys = "command", values = "createTemplate") @QueryParams(keys = "command", values = "createTemplate")
@Unwrap @Unwrap
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
ListenableFuture<AsyncCreateResponse> createTemplate(@BinderParam(BindTemplateMetadataToQueryParams.class) TemplateMetadata templateMetadata, CreateTemplateOptions... options); ListenableFuture<AsyncCreateResponse> createTemplate(
@BinderParam(BindTemplateMetadataToQueryParams.class) TemplateMetadata templateMetadata,
CreateTemplateOptions... options);
/** /**
* @see TemplateClient#registerTemplate * @see TemplateClient#registerTemplate
@ -83,7 +87,10 @@ public interface TemplateAsyncClient {
@QueryParams(keys = "command", values = "registerTemplate") @QueryParams(keys = "command", values = "registerTemplate")
@SelectJson("template") @SelectJson("template")
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
ListenableFuture<Template> registerTemplate(@BinderParam(BindTemplateMetadataToQueryParams.class) TemplateMetadata templateMetadata, @QueryParam("format") String format, @QueryParam("hypervisor") String hypervisor, @QueryParam("url") String url, @QueryParam("zoneid") long zoneId, RegisterTemplateOptions... options); ListenableFuture<Template> registerTemplate(
@BinderParam(BindTemplateMetadataToQueryParams.class) TemplateMetadata templateMetadata,
@QueryParam("format") String format, @QueryParam("hypervisor") String hypervisor,
@QueryParam("url") String url, @QueryParam("zoneid") long zoneId, RegisterTemplateOptions... options);
/** /**
* @see TemplateClient#updateTemplate * @see TemplateClient#updateTemplate
@ -101,7 +108,8 @@ public interface TemplateAsyncClient {
@QueryParams(keys = "command", values = "copyTemplate") @QueryParams(keys = "command", values = "copyTemplate")
@Unwrap @Unwrap
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
ListenableFuture<AsyncCreateResponse> copyTemplateToZone(@QueryParam("id") long id, @QueryParam("sourcezoneid") long sourceZoneId, @QueryParam("destzoneid") long destZoneId); ListenableFuture<AsyncCreateResponse> copyTemplateToZone(@QueryParam("id") long id,
@QueryParam("sourcezoneid") long sourceZoneId, @QueryParam("destzoneid") long destZoneId);
/** /**
* @see TemplateClient#deleteTemplate * @see TemplateClient#deleteTemplate
@ -135,6 +143,7 @@ public interface TemplateAsyncClient {
* @see TemplateClient#getTemplate * @see TemplateClient#getTemplate
*/ */
@GET @GET
// templatefilter required in at least 2.2.8 version
@QueryParams(keys = { "command", "templatefilter" }, values = { "listTemplates", "executable" }) @QueryParams(keys = { "command", "templatefilter" }, values = { "listTemplates", "executable" })
@SelectJson("template") @SelectJson("template")
@OnlyElement @OnlyElement
@ -147,7 +156,8 @@ public interface TemplateAsyncClient {
*/ */
@GET @GET
@QueryParams(keys = "command", values = "updateTemplatePermissions") @QueryParams(keys = "command", values = "updateTemplatePermissions")
ListenableFuture<Void> updateTemplatePermissions(@QueryParam("id") long id, UpdateTemplatePermissionsOptions... options); ListenableFuture<Void> updateTemplatePermissions(@QueryParam("id") long id,
UpdateTemplatePermissionsOptions... options);
/** /**
* @see TemplateClient#listTemplatePermissions * @see TemplateClient#listTemplatePermissions
@ -156,7 +166,8 @@ public interface TemplateAsyncClient {
@QueryParams(keys = "command", values = "listTemplatePermissions") @QueryParams(keys = "command", values = "listTemplatePermissions")
@Unwrap @Unwrap
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
ListenableFuture<Set<TemplatePermission>> listTemplatePermissions(@QueryParam("id") long id, AccountInDomainOptions... options); ListenableFuture<Set<TemplatePermission>> listTemplatePermissions(@QueryParam("id") long id,
AccountInDomainOptions... options);
/** /**
* @see TemplateClient#extractTemplate * @see TemplateClient#extractTemplate
@ -165,5 +176,6 @@ public interface TemplateAsyncClient {
@QueryParams(keys = "command", values = "extractTemplate") @QueryParams(keys = "command", values = "extractTemplate")
@Unwrap @Unwrap
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
ListenableFuture<AsyncCreateResponse> extractTemplate(@QueryParam("id") long id, @QueryParam("mode") ExtractMode mode, @QueryParam("zoneid") long zoneId, ExtractTemplateOptions... options); ListenableFuture<AsyncCreateResponse> extractTemplate(@QueryParam("id") long id,
@QueryParam("mode") ExtractMode mode, @QueryParam("zoneid") long zoneId, ExtractTemplateOptions... options);
} }

View File

@ -0,0 +1,92 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.cloudstack.functions;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.jclouds.cloudstack.CloudStackClient;
import org.jclouds.cloudstack.domain.AsyncCreateResponse;
import org.jclouds.cloudstack.domain.AsyncJob;
import org.jclouds.cloudstack.domain.IPForwardingRule;
import org.jclouds.cloudstack.domain.Network;
import org.jclouds.cloudstack.domain.PublicIPAddress;
import org.jclouds.cloudstack.domain.VirtualMachine;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.cache.Cache;
import com.google.inject.assistedinject.Assisted;
/**
*
* @author Adrian Cole
*/
@Singleton
public class StaticNATVirtualMachineInNetwork implements Function<VirtualMachine, PublicIPAddress> {
public static interface Factory {
StaticNATVirtualMachineInNetwork create(Network in);
}
private final CloudStackClient client;
private final ReuseOrAssociateNewPublicIPAddress reuseOrAssociate;
private final Network network;
private final Predicate<Long> jobComplete;
private final Cache<Long, IPForwardingRule> getIPForwardingRuleByVirtualMachine;
@Inject
public StaticNATVirtualMachineInNetwork(CloudStackClient client,
ReuseOrAssociateNewPublicIPAddress reuseOrAssociate, Predicate<Long> jobComplete,
Cache<Long, IPForwardingRule> getIPForwardingRuleByVirtualMachine, @Assisted Network network) {
this.client = checkNotNull(client, "client");
this.reuseOrAssociate = checkNotNull(reuseOrAssociate, "reuseOrAssociate");
this.jobComplete = checkNotNull(jobComplete, "jobComplete");
this.getIPForwardingRuleByVirtualMachine = checkNotNull(getIPForwardingRuleByVirtualMachine,
"getIPForwardingRuleByVirtualMachine");
this.network = checkNotNull(network, "network");
}
public PublicIPAddress apply(VirtualMachine vm) {
PublicIPAddress ip;
for (ip = reuseOrAssociate.apply(network); (!ip.isStaticNAT() || ip.getVirtualMachineId() != vm.getId()); ip = reuseOrAssociate
.apply(network)) {
// check to see if someone already grabbed this ip
if (ip.getVirtualMachineId() > 0 && ip.getVirtualMachineId() != vm.getId())
continue;
try {
client.getNATClient().enableStaticNATForVirtualMachine(vm.getId(), ip.getId());
ip = client.getAddressClient().getPublicIPAddress(ip.getId());
if (ip.isStaticNAT() && ip.getVirtualMachineId() == vm.getId())
break;
} catch (IllegalStateException e) {
// very likely an ip conflict, so retry;
}
return ip;
}
AsyncCreateResponse job = client.getNATClient().createIPForwardingRule(ip.getId(), "tcp", 22);
checkState(jobComplete.apply(job.getJobId()), "Timeout creating IP forwarding rule: ", job);
AsyncJob<IPForwardingRule> response = client.getAsyncJobClient().getAsyncJob(job.getJobId());
checkState(response.getResult() != null, "No result after creating IP forwarding rule: ", response);
getIPForwardingRuleByVirtualMachine.asMap().put(vm.getId(), response.getResult());
return ip;
}
}

View File

@ -70,7 +70,12 @@ public class CloudStackErrorHandler implements HttpErrorHandler {
break; break;
case 409: case 409:
case 431: case 431:
exception = new IllegalStateException(message, exception); if (command.getCurrentRequest().getRequestLine().indexOf("delete") != -1
&& message.indexOf("does not exist") != -1) {
exception = new ResourceNotFoundException(message, exception);
} else {
exception = new IllegalStateException(message, exception);
}
break; break;
} }
} finally { } finally {

View File

@ -23,8 +23,11 @@ import static com.google.common.base.Preconditions.checkNotNull;
import org.jclouds.encryption.internal.Base64; import org.jclouds.encryption.internal.Base64;
import com.google.common.base.Function;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
/** /**
* Options used to control what disk offerings are returned * Options used to control what disk offerings are returned
@ -119,6 +122,23 @@ public class DeployVirtualMachineOptions extends AccountInDomainOptions {
return this; return this;
} }
public Iterable<Long> getNetworkIds() {
if (queryParameters.get("networkids").size() == 1) {
return Iterables.transform(
Splitter.on(",").split(Iterables.getOnlyElement(queryParameters.get("networkids"))),
new Function<String, Long>() {
@Override
public Long apply(String arg0) {
return Long.parseLong(arg0);
}
});
} else {
return ImmutableSet.<Long> of();
}
}
/** /**
* @param securityGroupId * @param securityGroupId
* security group applied to the virtual machine. Should be passed * security group applied to the virtual machine. Should be passed

View File

@ -0,0 +1,69 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.cloudstack.suppliers;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.NoSuchElementException;
import javax.inject.Inject;
import org.jclouds.cloudstack.CloudStackClient;
import org.jclouds.cloudstack.domain.Account;
import org.jclouds.cloudstack.domain.User;
import org.jclouds.cloudstack.predicates.UserPredicates;
import org.jclouds.rest.annotations.Identity;
import com.google.common.base.Predicate;
import com.google.common.base.Supplier;
import com.google.common.collect.Iterables;
/**
*
* @author Adrian Cole
*/
public class GetCurrentUser implements Supplier<User> {
private final CloudStackClient client;
private final String identity;
@Inject
public GetCurrentUser(CloudStackClient client, @Identity String identity) {
this.client = checkNotNull(client, "client");
this.identity = checkNotNull(identity, "identity");
}
@Override
public User get() {
Iterable<User> users = Iterables.concat(client.getAccountClient().listAccounts());
Predicate<User> apiKeyMatches = UserPredicates.apiKeyEquals(identity);
User currentUser = null;
try {
currentUser = Iterables.find(users, apiKeyMatches);
} catch (NoSuchElementException e) {
throw new NoSuchElementException(String.format("none of the following users match %s: %s", apiKeyMatches,
users));
}
if (currentUser.getAccountType() != Account.Type.USER)
throw new IllegalArgumentException(String.format(
"invalid account type: %s, please specify an apiKey of a USER, for example: %s",
currentUser.getAccountType(), Iterables.filter(users, UserPredicates.isUserAccount())));
return currentUser;
}
}

View File

@ -0,0 +1,66 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.cloudstack.suppliers;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.jclouds.cloudstack.options.ListNetworksOptions.Builder.accountInDomain;
import java.util.Map;
import javax.inject.Inject;
import org.jclouds.cloudstack.CloudStackClient;
import org.jclouds.cloudstack.domain.Network;
import org.jclouds.cloudstack.domain.User;
import org.jclouds.cloudstack.features.NetworkClient;
import org.jclouds.collect.Memoized;
import com.google.common.base.Function;
import com.google.common.base.Supplier;
import com.google.common.collect.Maps;
/**
*
* @author Adrian Cole
*/
public class NetworksForCurrentUser implements Supplier<Map<Long, Network>> {
private final CloudStackClient client;
private final Supplier<User> currentUserSupplier;
@Inject
public NetworksForCurrentUser(CloudStackClient client, @Memoized Supplier<User> currentUserSupplier) {
this.client = checkNotNull(client, "client");
this.currentUserSupplier = checkNotNull(currentUserSupplier, "currentUserSupplier");
}
@Override
public Map<Long, Network> get() {
User currentUser = currentUserSupplier.get();
NetworkClient networkClient = client.getNetworkClient();
return Maps.uniqueIndex(
networkClient.listNetworks(accountInDomain(currentUser.getAccount(), currentUser.getDomainId())),
new Function<Network, Long>() {
@Override
public Long apply(Network arg0) {
return arg0.getId();
}
});
}
}

View File

@ -25,35 +25,47 @@ import static org.testng.Assert.fail;
import java.io.IOException; import java.io.IOException;
import java.util.Map; import java.util.Map;
import java.util.Properties;
import java.util.Random; import java.util.Random;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.inject.Singleton; import javax.inject.Singleton;
import org.jclouds.cloudstack.CloudStackClient; import org.jclouds.cloudstack.CloudStackClient;
import org.jclouds.cloudstack.CloudStackPropertiesBuilder; import org.jclouds.cloudstack.compute.config.CloudStackComputeServiceContextModule.GetIPForwardingRuleByVirtualMachine;
import org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions; import org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions;
import org.jclouds.cloudstack.compute.strategy.CloudStackComputeServiceAdapter; import org.jclouds.cloudstack.compute.strategy.CloudStackComputeServiceAdapter;
import org.jclouds.cloudstack.domain.IPForwardingRule;
import org.jclouds.cloudstack.domain.Network;
import org.jclouds.cloudstack.domain.ServiceOffering; import org.jclouds.cloudstack.domain.ServiceOffering;
import org.jclouds.cloudstack.domain.SshKeyPair; import org.jclouds.cloudstack.domain.User;
import org.jclouds.cloudstack.domain.VirtualMachine; import org.jclouds.cloudstack.domain.VirtualMachine;
import org.jclouds.cloudstack.features.BaseCloudStackClientLiveTest; import org.jclouds.cloudstack.features.BaseCloudStackClientLiveTest;
import org.jclouds.cloudstack.functions.StaticNATVirtualMachineInNetwork;
import org.jclouds.cloudstack.predicates.JobComplete; import org.jclouds.cloudstack.predicates.JobComplete;
import org.jclouds.cloudstack.predicates.TemplatePredicates; import org.jclouds.cloudstack.predicates.TemplatePredicates;
import org.jclouds.cloudstack.suppliers.GetCurrentUser;
import org.jclouds.cloudstack.suppliers.NetworksForCurrentUser;
import org.jclouds.collect.Memoized;
import org.jclouds.compute.ComputeServiceAdapter.NodeAndInitialCredentials;
import org.jclouds.compute.ComputeTestUtils; import org.jclouds.compute.ComputeTestUtils;
import org.jclouds.compute.domain.ExecResponse; import org.jclouds.compute.domain.ExecResponse;
import org.jclouds.compute.domain.Template; import org.jclouds.compute.domain.Template;
import org.jclouds.compute.functions.DefaultCredentialsFromImageOrOverridingCredentials;
import org.jclouds.compute.strategy.PrioritizeCredentialsFromTemplate;
import org.jclouds.domain.Credentials; import org.jclouds.domain.Credentials;
import org.jclouds.logging.log4j.config.Log4JLoggingModule; import org.jclouds.logging.log4j.config.Log4JLoggingModule;
import org.jclouds.net.IPSocket; import org.jclouds.net.IPSocket;
import org.jclouds.predicates.RetryablePredicate; import org.jclouds.predicates.RetryablePredicate;
import org.jclouds.rest.annotations.Identity;
import org.jclouds.ssh.SshClient; import org.jclouds.ssh.SshClient;
import org.testng.annotations.AfterGroups; import org.testng.annotations.AfterGroups;
import org.testng.annotations.BeforeGroups; import org.testng.annotations.BeforeGroups;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.base.Supplier;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.net.InetAddresses; import com.google.common.net.InetAddresses;
@ -61,15 +73,19 @@ import com.google.inject.AbstractModule;
import com.google.inject.Guice; import com.google.inject.Guice;
import com.google.inject.Module; import com.google.inject.Module;
import com.google.inject.Provides; import com.google.inject.Provides;
import com.google.inject.Scopes;
import com.google.inject.TypeLiteral;
import com.google.inject.assistedinject.FactoryModuleBuilder;
@Test(groups = "live", singleThreaded = true, testName = "CloudStackComputeServiceAdapterLiveTest") @Test(groups = "live", singleThreaded = true, testName = "CloudStackComputeServiceAdapterLiveTest")
public class CloudStackComputeServiceAdapterLiveTest extends BaseCloudStackClientLiveTest { public class CloudStackComputeServiceAdapterLiveTest extends BaseCloudStackClientLiveTest {
private CloudStackComputeServiceAdapter adapter; private CloudStackComputeServiceAdapter adapter;
private VirtualMachine vm; private NodeAndInitialCredentials<VirtualMachine> vm;
private String keyPairName; private String keyPairName;
private Map<String, String> keyPair; private Map<String, String> keyPair;
Map<String, Credentials> credentialStore = Maps.newLinkedHashMap();
@BeforeGroups(groups = { "live" }) @BeforeGroups(groups = { "live" })
public void setupClient() { public void setupClient() {
@ -78,8 +94,16 @@ public class CloudStackComputeServiceAdapterLiveTest extends BaseCloudStackClien
@Override @Override
protected void configure() { protected void configure() {
bindProperties(binder(), CloudStackComputeServiceAdapterLiveTest.this.setupProperties()); bindProperties(binder(), setupProperties());
bind(String.class).annotatedWith(Identity.class).toInstance(identity);
bind(new TypeLiteral<Supplier<User>>() {
}).annotatedWith(Memoized.class).to(GetCurrentUser.class).in(Scopes.SINGLETON);
bind(new TypeLiteral<Supplier<Map<Long, Network>>>() {
}).annotatedWith(Memoized.class).to(NetworksForCurrentUser.class).in(Scopes.SINGLETON);
bind(new TypeLiteral<Map<String, Credentials>>() {
}).toInstance(credentialStore);
bind(CloudStackClient.class).toInstance(context.getApi()); bind(CloudStackClient.class).toInstance(context.getApi());
install(new FactoryModuleBuilder().build(StaticNATVirtualMachineInNetwork.Factory.class));
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
@ -88,6 +112,14 @@ public class CloudStackComputeServiceAdapterLiveTest extends BaseCloudStackClien
protected Predicate<Long> jobComplete(JobComplete jobComplete) { protected Predicate<Long> jobComplete(JobComplete jobComplete) {
return new RetryablePredicate<Long>(jobComplete, 1200, 1, 5, TimeUnit.SECONDS); return new RetryablePredicate<Long>(jobComplete, 1200, 1, 5, TimeUnit.SECONDS);
} }
@SuppressWarnings("unused")
@Provides
@Singleton
protected Cache<Long, IPForwardingRule> getIPForwardingRuleByVirtualMachine(
GetIPForwardingRuleByVirtualMachine getIPForwardingRule) {
return CacheBuilder.newBuilder().build(getIPForwardingRule);
}
}; };
adapter = Guice.createInjector(module, new Log4JLoggingModule()).getInstance( adapter = Guice.createInjector(module, new Log4JLoggingModule()).getInstance(
CloudStackComputeServiceAdapter.class); CloudStackComputeServiceAdapter.class);
@ -105,35 +137,47 @@ public class CloudStackComputeServiceAdapterLiveTest extends BaseCloudStackClien
assertFalse(Iterables.isEmpty(adapter.listLocations())); assertFalse(Iterables.isEmpty(adapter.listLocations()));
} }
private static final PrioritizeCredentialsFromTemplate prioritizeCredentialsFromTemplate = new PrioritizeCredentialsFromTemplate(
new DefaultCredentialsFromImageOrOverridingCredentials());
@Test @Test
public void testCreateNodeWithGroupEncodedIntoNameThenStoreCredentialsWithSecurityGroup() { public void testCreateNodeWithGroupEncodedIntoNameThenStoreCredentialsWithSecurityGroup()
throws InterruptedException {
String group = "foo"; String group = "foo";
String name = "node" + new Random().nextInt(); String name = "node" + new Random().nextInt();
Template template = computeContext.getComputeService().templateBuilder().build(); Template template = computeContext.getComputeService().templateBuilder().build();
client.getSSHKeyPairClient().deleteSSHKeyPair(keyPairName); if (!client
client.getSSHKeyPairClient().registerSSHKeyPair(keyPairName, keyPair.get("public")); .getTemplateClient()
.getTemplateInZone(Long.parseLong(template.getImage().getId()),
Long.parseLong(template.getLocation().getId())).isPasswordEnabled()) {
client.getSSHKeyPairClient().deleteSSHKeyPair(keyPairName);
client.getSSHKeyPairClient().registerSSHKeyPair(keyPairName, keyPair.get("public"));
Map<String, Credentials> credentialStore = Maps.newLinkedHashMap(); credentialStore.put("keypair#" + keyPairName, new Credentials("root", keyPair.get("private")));
credentialStore.put("keypair#" + keyPairName, new Credentials("root", keyPair.get("private")));
// TODO: look at SecurityGroupClientLiveTest for how to do this // TODO: look at SecurityGroupClientLiveTest for how to do this
template.getOptions().as(CloudStackTemplateOptions.class).keyPair(keyPairName); template.getOptions().as(CloudStackTemplateOptions.class).keyPair(keyPairName);
}
vm = adapter.createNodeWithGroupEncodedIntoNameThenStoreCredentials(group, name, template, credentialStore); vm = adapter.createNodeWithGroupEncodedIntoName(group, name, template);
// TODO: check security groups vm.getSecurityGroups(), // TODO: check security groups vm.getSecurityGroups(),
// check other things, like cpu correct, mem correct, image/os is correct // check other things, like cpu correct, mem correct, image/os is correct
// (as possible) // (as possible)
assert credentialStore.containsKey("node#" + vm.getId()) : "credentials to log into vm not found " + vm; // check to see if we setup a NAT rule (conceding we could check this from
assert InetAddresses.isInetAddress(vm.getIPAddress()) : vm; // cache)
IPForwardingRule rule = client.getNATClient().getIPForwardingRuleForVirtualMachine(vm.getNode().getId());
doConnectViaSsh(vm, credentialStore.get("node#" + vm.getId())); String address = rule != null ? rule.getIPAddress() : vm.getNode().getIPAddress();
assert InetAddresses.isInetAddress(address) : vm;
IPSocket socket = new IPSocket(address, 22);
doConnectViaSsh(socket, prioritizeCredentialsFromTemplate.apply(template, vm.getCredentials()));
} }
protected void doConnectViaSsh(VirtualMachine vm, Credentials creds) { protected void doConnectViaSsh(IPSocket socket, Credentials creds) {
SshClient ssh = computeContext.utils().sshFactory().create(new IPSocket(vm.getIPAddress(), 22), creds); SshClient ssh = computeContext.utils().sshFactory().create(socket, creds);
try { try {
ssh.connect(); ssh.connect();
ExecResponse hello = ssh.exec("echo hello"); ExecResponse hello = ssh.exec("echo hello");
@ -170,7 +214,7 @@ public class CloudStackComputeServiceAdapterLiveTest extends BaseCloudStackClien
@AfterGroups(groups = "live") @AfterGroups(groups = "live")
protected void tearDown() { protected void tearDown() {
if (vm != null) if (vm != null)
adapter.destroyNode(vm.getId() + ""); adapter.destroyNode(vm.getNodeId());
super.tearDown(); super.tearDown();
} }
} }

View File

@ -28,6 +28,7 @@ import org.jclouds.sshj.config.SshjSshClientModule;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Module; import com.google.inject.Module;
/** /**
@ -49,8 +50,9 @@ public class CloudStackComputeServiceLiveTest extends BaseComputeServiceLiveTest
public void testAssignability() throws Exception { public void testAssignability() throws Exception {
@SuppressWarnings("unused") @SuppressWarnings("unused")
RestContext<CloudStackClient, CloudStackAsyncClient> tmContext = new ComputeServiceContextFactory() RestContext<CloudStackClient, CloudStackAsyncClient> tmContext = new ComputeServiceContextFactory(
.createContext(provider, identity, credential).getProviderSpecificContext(); setupRestProperties()).createContext(provider, identity, credential, ImmutableSet.<Module> of(),
setupProperties()).getProviderSpecificContext();
} }
// cloudstack does not support metadata // cloudstack does not support metadata
@ -60,4 +62,3 @@ public class CloudStackComputeServiceLiveTest extends BaseComputeServiceLiveTest
"node userMetadata did not match %s %s", userMetadata, node); "node userMetadata did not match %s %s", userMetadata, node);
} }
} }

View File

@ -27,6 +27,7 @@ import java.util.Set;
import org.jclouds.cloudstack.compute.functions.VirtualMachineToNodeMetadata.FindHardwareForVirtualMachine; import org.jclouds.cloudstack.compute.functions.VirtualMachineToNodeMetadata.FindHardwareForVirtualMachine;
import org.jclouds.cloudstack.compute.functions.VirtualMachineToNodeMetadata.FindImageForVirtualMachine; import org.jclouds.cloudstack.compute.functions.VirtualMachineToNodeMetadata.FindImageForVirtualMachine;
import org.jclouds.cloudstack.compute.functions.VirtualMachineToNodeMetadata.FindLocationForVirtualMachine; import org.jclouds.cloudstack.compute.functions.VirtualMachineToNodeMetadata.FindLocationForVirtualMachine;
import org.jclouds.cloudstack.domain.IPForwardingRule;
import org.jclouds.cloudstack.domain.VirtualMachine; import org.jclouds.cloudstack.domain.VirtualMachine;
import org.jclouds.cloudstack.parse.ListVirtualMachinesResponseTest; import org.jclouds.cloudstack.parse.ListVirtualMachinesResponseTest;
import org.jclouds.compute.domain.Hardware; import org.jclouds.compute.domain.Hardware;
@ -36,10 +37,13 @@ import org.jclouds.compute.domain.NodeMetadataBuilder;
import org.jclouds.compute.domain.NodeState; import org.jclouds.compute.domain.NodeState;
import org.jclouds.domain.Credentials; import org.jclouds.domain.Credentials;
import org.jclouds.domain.Location; import org.jclouds.domain.Location;
import org.jclouds.rest.ResourceNotFoundException;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.base.Suppliers; import com.google.common.base.Suppliers;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
@ -50,6 +54,51 @@ import com.google.common.collect.Iterables;
@Test(groups = "unit", testName = "VirtualMachineToNodeMetadataTest") @Test(groups = "unit", testName = "VirtualMachineToNodeMetadataTest")
public class VirtualMachineToNodeMetadataTest { public class VirtualMachineToNodeMetadataTest {
@Test
public void testApplyWhereVirtualMachineWithIPForwardingRule() throws UnknownHostException {
// note we are testing when no credentials are here. otherwise would be
// ("node#416696", new
// Credentials("root", "password"))
Map<String, Credentials> credentialStore = ImmutableMap.<String, Credentials> of();
Supplier<Set<? extends Location>> locationSupplier = Suppliers.<Set<? extends Location>> ofInstance(ImmutableSet
.<Location> of(ZoneToLocationTest.one, ZoneToLocationTest.two));
Supplier<Set<? extends Hardware>> hardwareSupplier = Suppliers.<Set<? extends Hardware>> ofInstance(ImmutableSet
.<Hardware> of(ServiceOfferingToHardwareTest.one, ServiceOfferingToHardwareTest.two));
Supplier<Set<? extends Image>> imageSupplier = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet
.<Image> of(TemplateToImageTest.one, TemplateToImageTest.two));
VirtualMachineToNodeMetadata parser = new VirtualMachineToNodeMetadata(credentialStore,
new FindLocationForVirtualMachine(locationSupplier), new FindHardwareForVirtualMachine(hardwareSupplier),
new FindImageForVirtualMachine(imageSupplier), CacheBuilder.newBuilder().<Long, IPForwardingRule> build(
new CacheLoader<Long, IPForwardingRule>() {
@Override
public IPForwardingRule load(Long arg0) throws Exception {
return IPForwardingRule.builder().id(1234l).IPAddress("1.1.1.1").build();
}
}));
// notice if we've already parsed this properly here, we can rely on it.
VirtualMachine guest = Iterables.get(new ListVirtualMachinesResponseTest().expected(), 0);
NodeMetadata node = parser.apply(guest);
assertEquals(
node.toString(),
new NodeMetadataBuilder().id("54").providerId("54").name("i-3-54-VM").location(ZoneToLocationTest.one)
.state(NodeState.PENDING).privateAddresses(ImmutableSet.of("10.1.1.18"))
.publicAddresses(ImmutableSet.of("1.1.1.1")).hardware(ServiceOfferingToHardwareTest.one)
.imageId(TemplateToImageTest.one.getId())
.operatingSystem(TemplateToImageTest.one.getOperatingSystem()).build().toString());
// because it wasn't present in the credential store.
assertEquals(node.getCredentials(), null);
}
@Test @Test
public void testApplyWhereVirtualMachineWithNoPassword() throws UnknownHostException { public void testApplyWhereVirtualMachineWithNoPassword() throws UnknownHostException {
@ -66,10 +115,17 @@ public class VirtualMachineToNodeMetadataTest {
Supplier<Set<? extends Image>> imageSupplier = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet Supplier<Set<? extends Image>> imageSupplier = Suppliers.<Set<? extends Image>> ofInstance(ImmutableSet
.<Image> of(TemplateToImageTest.one, TemplateToImageTest.two)); .<Image> of(TemplateToImageTest.one, TemplateToImageTest.two));
VirtualMachineToNodeMetadata parser = new VirtualMachineToNodeMetadata(credentialStore, VirtualMachineToNodeMetadata parser = new VirtualMachineToNodeMetadata(credentialStore,
new FindLocationForVirtualMachine(locationSupplier), new FindHardwareForVirtualMachine(hardwareSupplier), new FindLocationForVirtualMachine(locationSupplier), new FindHardwareForVirtualMachine(hardwareSupplier),
new FindImageForVirtualMachine(imageSupplier)); new FindImageForVirtualMachine(imageSupplier), CacheBuilder.newBuilder().<Long, IPForwardingRule> build(
new CacheLoader<Long, IPForwardingRule>() {
@Override
public IPForwardingRule load(Long arg0) throws Exception {
throw new ResourceNotFoundException("no ip forwarding rule for: " + arg0);
}
}));
// notice if we've already parsed this properly here, we can rely on it. // notice if we've already parsed this properly here, we can rely on it.
VirtualMachine guest = Iterables.get(new ListVirtualMachinesResponseTest().expected(), 0); VirtualMachine guest = Iterables.get(new ListVirtualMachinesResponseTest().expected(), 0);
@ -104,7 +160,15 @@ public class VirtualMachineToNodeMetadataTest {
VirtualMachineToNodeMetadata parser = new VirtualMachineToNodeMetadata(credentialStore, VirtualMachineToNodeMetadata parser = new VirtualMachineToNodeMetadata(credentialStore,
new FindLocationForVirtualMachine(locationSupplier), new FindHardwareForVirtualMachine(hardwareSupplier), new FindLocationForVirtualMachine(locationSupplier), new FindHardwareForVirtualMachine(hardwareSupplier),
new FindImageForVirtualMachine(imageSupplier)); new FindImageForVirtualMachine(imageSupplier), CacheBuilder.newBuilder().<Long, IPForwardingRule> build(
new CacheLoader<Long, IPForwardingRule>() {
@Override
public IPForwardingRule load(Long arg0) throws Exception {
throw new ResourceNotFoundException("no ip forwarding rule for: " + arg0);
}
}));
// notice if we've already parsed this properly here, we can rely on it. // notice if we've already parsed this properly here, we can rely on it.
VirtualMachine guest = Iterables.get(new ListVirtualMachinesResponseTest().expected(), 0); VirtualMachine guest = Iterables.get(new ListVirtualMachinesResponseTest().expected(), 0);
@ -121,5 +185,4 @@ public class VirtualMachineToNodeMetadataTest {
assertEquals(node.getCredentials(), new Credentials("root", "password")); assertEquals(node.getCredentials(), new Credentials("root", "password"));
} }
} }

View File

@ -19,6 +19,8 @@
package org.jclouds.cloudstack.compute.options; package org.jclouds.cloudstack.compute.options;
import static org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions.Builder.keyPair; import static org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions.Builder.keyPair;
import static org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions.Builder.networkId;
import static org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions.Builder.networkIds;
import static org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions.Builder.securityGroupId; import static org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions.Builder.securityGroupId;
import static org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions.Builder.securityGroupIds; import static org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions.Builder.securityGroupIds;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
@ -74,6 +76,36 @@ public class CloudStackTemplateOptionsTest {
assertEquals(options.as(CloudStackTemplateOptions.class).getSecurityGroupIds(), ImmutableSet.of(3l)); assertEquals(options.as(CloudStackTemplateOptions.class).getSecurityGroupIds(), ImmutableSet.of(3l));
} }
@Test
public void testDefaultNetworkIds() {
TemplateOptions options = new CloudStackTemplateOptions();
assertEquals(options.as(CloudStackTemplateOptions.class).getNetworkIds(), ImmutableSet.of());
}
@Test
public void testNetworkId() {
TemplateOptions options = new CloudStackTemplateOptions().networkId(3l);
assertEquals(options.as(CloudStackTemplateOptions.class).getNetworkIds(), ImmutableSet.of(3l));
}
@Test
public void testNetworkIdStatic() {
TemplateOptions options = networkId(3l);
assertEquals(options.as(CloudStackTemplateOptions.class).getNetworkIds(), ImmutableSet.of(3l));
}
@Test
public void testNetworkIds() {
TemplateOptions options = new CloudStackTemplateOptions().networkIds(ImmutableSet.of(3l));
assertEquals(options.as(CloudStackTemplateOptions.class).getNetworkIds(), ImmutableSet.of(3l));
}
@Test
public void testNetworkIdsStatic() {
TemplateOptions options = networkIds(ImmutableSet.of(3l));
assertEquals(options.as(CloudStackTemplateOptions.class).getNetworkIds(), ImmutableSet.of(3l));
}
@Test @Test
public void testKeyPair() { public void testKeyPair() {
TemplateOptions options = keyPair("test"); TemplateOptions options = keyPair("test");

View File

@ -18,19 +18,27 @@
*/ */
package org.jclouds.cloudstack.features; package org.jclouds.cloudstack.features;
import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Iterables.get;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import java.util.Properties; import java.util.Properties;
import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.jclouds.cloudstack.CloudStackAsyncClient; import org.jclouds.cloudstack.CloudStackAsyncClient;
import org.jclouds.cloudstack.CloudStackClient; import org.jclouds.cloudstack.CloudStackClient;
import org.jclouds.cloudstack.domain.Account; import org.jclouds.cloudstack.domain.Account;
import org.jclouds.cloudstack.domain.Template;
import org.jclouds.cloudstack.domain.User; import org.jclouds.cloudstack.domain.User;
import org.jclouds.cloudstack.domain.VirtualMachine; import org.jclouds.cloudstack.domain.VirtualMachine;
import org.jclouds.cloudstack.functions.ReuseOrAssociateNewPublicIPAddress; import org.jclouds.cloudstack.functions.ReuseOrAssociateNewPublicIPAddress;
import org.jclouds.cloudstack.options.ListTemplatesOptions;
import org.jclouds.cloudstack.predicates.CorrectHypervisorForZone;
import org.jclouds.cloudstack.predicates.JobComplete; import org.jclouds.cloudstack.predicates.JobComplete;
import org.jclouds.cloudstack.predicates.OSCategoryIn;
import org.jclouds.cloudstack.predicates.TemplatePredicates;
import org.jclouds.cloudstack.predicates.UserPredicates; import org.jclouds.cloudstack.predicates.UserPredicates;
import org.jclouds.cloudstack.predicates.VirtualMachineDestroyed; import org.jclouds.cloudstack.predicates.VirtualMachineDestroyed;
import org.jclouds.cloudstack.predicates.VirtualMachineRunning; import org.jclouds.cloudstack.predicates.VirtualMachineRunning;
@ -50,6 +58,7 @@ import org.testng.annotations.AfterGroups;
import org.testng.annotations.BeforeGroups; import org.testng.annotations.BeforeGroups;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.inject.Guice; import com.google.inject.Guice;
@ -65,6 +74,33 @@ public class BaseCloudStackClientLiveTest extends BaseVersionedServiceLiveTest {
provider = "cloudstack"; provider = "cloudstack";
} }
public static long defaultTemplateOrPreferredInZone(Long defaultTemplate, CloudStackClient client, long zoneId) {
long templateId = defaultTemplate != null ? defaultTemplate : getTemplateForZone(client, zoneId);
return templateId;
}
public static long getTemplateForZone(CloudStackClient client, long zoneId) {
// TODO enum, as this is way too easy to mess up.
Set<String> acceptableCategories = ImmutableSet.of("Ubuntu", "CentOS");
final Predicate<Template> hypervisorPredicate = new CorrectHypervisorForZone(client).apply(zoneId);
final Predicate<Template> osTypePredicate = new OSCategoryIn(client).apply(acceptableCategories);
@SuppressWarnings("unchecked")
Predicate<Template> templatePredicate = Predicates.<Template> and(TemplatePredicates.isReady(),
hypervisorPredicate, osTypePredicate);
Iterable<Template> templates = filter(
client.getTemplateClient().listTemplates(ListTemplatesOptions.Builder.zoneId(zoneId)), templatePredicate);
if (Iterables.any(templates, TemplatePredicates.isPasswordEnabled())) {
templates = filter(templates, TemplatePredicates.isPasswordEnabled());
}
if (Iterables.size(templates) == 0) {
throw new NoSuchElementException(templatePredicate.toString());
}
long templateId = get(templates, 0).getId();
return templateId;
}
protected String prefix = System.getProperty("user.name"); protected String prefix = System.getProperty("user.name");
protected CloudStackClient client; protected CloudStackClient client;
@ -83,7 +119,6 @@ public class BaseCloudStackClientLiveTest extends BaseVersionedServiceLiveTest {
protected ComputeServiceContext computeContext; protected ComputeServiceContext computeContext;
protected void checkSSH(IPSocket socket) { protected void checkSSH(IPSocket socket) {
socketTester.apply(socket); socketTester.apply(socket);
SshClient client = sshFactory.create(socket, new Credentials("root", password)); SshClient client = sshFactory.create(socket, new Credentials("root", password));
@ -97,7 +132,7 @@ public class BaseCloudStackClientLiveTest extends BaseVersionedServiceLiveTest {
client.disconnect(); client.disconnect();
} }
} }
@BeforeGroups(groups = "live") @BeforeGroups(groups = "live")
public void setupClient() { public void setupClient() {
setupCredentials(); setupCredentials();

View File

@ -57,7 +57,9 @@ public class FirewallClientLiveTest extends BaseCloudStackClientLiveTest {
prefix += "rule"; prefix += "rule";
try { try {
network = find(client.getNetworkClient().listNetworks(), NetworkPredicates.supportsPortForwarding()); network = find(client.getNetworkClient().listNetworks(), NetworkPredicates.supportsPortForwarding());
vm = VirtualMachineClientLiveTest.createVirtualMachineInNetwork(network, client, jobComplete, Long defaultTemplate = (imageId != null && !"".equals(imageId)) ? new Long(imageId) : null;
vm = VirtualMachineClientLiveTest.createVirtualMachineInNetwork(network,
defaultTemplateOrPreferredInZone(defaultTemplate, client, network.getZoneId()), client, jobComplete,
virtualMachineRunning); virtualMachineRunning);
if (vm.getPassword() != null) if (vm.getPassword() != null)
password = vm.getPassword(); password = vm.getPassword();

View File

@ -67,7 +67,9 @@ public class LoadBalancerClientLiveTest extends BaseCloudStackClientLiveTest {
prefix += "rule"; prefix += "rule";
try { try {
network = find(client.getNetworkClient().listNetworks(), NetworkPredicates.hasLoadBalancerService()); network = find(client.getNetworkClient().listNetworks(), NetworkPredicates.hasLoadBalancerService());
vm = VirtualMachineClientLiveTest.createVirtualMachineInNetwork(network, client, jobComplete, Long defaultTemplate = (imageId != null && !"".equals(imageId)) ? new Long(imageId) : null;
vm = VirtualMachineClientLiveTest.createVirtualMachineInNetwork(network,
defaultTemplateOrPreferredInZone(defaultTemplate, client, network.getZoneId()), client, jobComplete,
virtualMachineRunning); virtualMachineRunning);
if (vm.getPassword() != null) if (vm.getPassword() != null)
password = vm.getPassword(); password = vm.getPassword();

View File

@ -27,7 +27,6 @@ import org.jclouds.http.HttpRequest;
import org.jclouds.http.functions.ParseFirstJsonValueNamed; import org.jclouds.http.functions.ParseFirstJsonValueNamed;
import org.jclouds.http.functions.UnwrapOnlyJsonValue; import org.jclouds.http.functions.UnwrapOnlyJsonValue;
import org.jclouds.http.functions.UnwrapOnlyNestedJsonValue; import org.jclouds.http.functions.UnwrapOnlyNestedJsonValue;
import org.jclouds.http.functions.UnwrapOnlyNestedJsonValueInSet;
import org.jclouds.rest.functions.MapHttp4xxCodesToExceptions; import org.jclouds.rest.functions.MapHttp4xxCodesToExceptions;
import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404; import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404;
import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404; import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;

View File

@ -18,27 +18,14 @@
*/ */
package org.jclouds.cloudstack.features; package org.jclouds.cloudstack.features;
import static com.google.common.collect.Iterables.find;
import static com.google.common.collect.Iterables.getOnlyElement; import static com.google.common.collect.Iterables.getOnlyElement;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue; import static org.testng.Assert.assertTrue;
import java.util.NoSuchElementException;
import java.util.Set; import java.util.Set;
import org.jclouds.cloudstack.domain.AsyncCreateResponse;
import org.jclouds.cloudstack.domain.IPForwardingRule; import org.jclouds.cloudstack.domain.IPForwardingRule;
import org.jclouds.cloudstack.domain.Network;
import org.jclouds.cloudstack.domain.PublicIPAddress;
import org.jclouds.cloudstack.domain.VirtualMachine;
import org.jclouds.cloudstack.options.ListIPForwardingRulesOptions; import org.jclouds.cloudstack.options.ListIPForwardingRulesOptions;
import org.jclouds.cloudstack.predicates.NetworkPredicates;
import org.jclouds.compute.domain.ExecResponse;
import org.jclouds.domain.Credentials;
import org.jclouds.net.IPSocket;
import org.jclouds.ssh.SshClient;
import org.testng.annotations.AfterGroups;
import org.testng.annotations.BeforeGroups;
import org.testng.annotations.Test; import org.testng.annotations.Test;
/** /**
@ -48,79 +35,6 @@ import org.testng.annotations.Test;
*/ */
@Test(groups = "live", singleThreaded = true, testName = "NATClientLiveTest") @Test(groups = "live", singleThreaded = true, testName = "NATClientLiveTest")
public class NATClientLiveTest extends BaseCloudStackClientLiveTest { public class NATClientLiveTest extends BaseCloudStackClientLiveTest {
private PublicIPAddress ip = null;
private VirtualMachine vm;
private IPForwardingRule rule;
private Network network;
private boolean networksDisabled;
@BeforeGroups(groups = "live")
public void setupClient() {
super.setupClient();
prefix += "nat";
try {
network = find(client.getNetworkClient().listNetworks(), NetworkPredicates.supportsStaticNAT());
vm = VirtualMachineClientLiveTest.createVirtualMachineInNetwork(network, client, jobComplete,
virtualMachineRunning);
if (vm.getPassword() != null)
password = vm.getPassword();
} catch (NoSuchElementException e) {
networksDisabled = true;
}
}
public void testCreateIPForwardingRule() throws Exception {
if (networksDisabled)
return;
for (ip = reuseOrAssociate.apply(network); (!ip.isStaticNAT() || ip.getVirtualMachineId() != vm.getId()); ip = reuseOrAssociate
.apply(network)) {
// check to see if someone already grabbed this ip
if (ip.getVirtualMachineId() > 0 && ip.getVirtualMachineId() != vm.getId())
continue;
try {
client.getNATClient().enableStaticNATForVirtualMachine(vm.getId(), ip.getId());
ip = client.getAddressClient().getPublicIPAddress(ip.getId());
if (ip.isStaticNAT() && ip.getVirtualMachineId() == vm.getId())
break;
} catch (IllegalStateException e) {
// very likely an ip conflict, so retry;
}
}
AsyncCreateResponse job = client.getNATClient().createIPForwardingRule(ip.getId(), "tcp", 22);
assert jobComplete.apply(job.getJobId());
rule = client.getNATClient().getIPForwardingRule(job.getId());
assertEquals(rule.getIPAddressId(), ip.getId());
assertEquals(rule.getVirtualMachineId(), vm.getId());
assertEquals(rule.getStartPort(), 22);
assertEquals(rule.getProtocol(), "tcp");
checkRule(rule);
IPSocket socket = new IPSocket(ip.getIPAddress(), 22);
socketTester.apply(socket);
SshClient client = sshFactory.create(socket, new Credentials("root", password));
try {
client.connect();
ExecResponse exec = client.exec("echo hello");
assertEquals(exec.getOutput().trim(), "hello");
} finally {
if (client != null)
client.disconnect();
}
}
@AfterGroups(groups = "live")
protected void tearDown() {
if (rule != null) {
client.getNATClient().deleteIPForwardingRule(rule.getId());
}
if (vm != null) {
jobComplete.apply(client.getVirtualMachineClient().destroyVirtualMachine(vm.getId()));
}
if (ip != null) {
client.getAddressClient().disassociateIPAddress(ip.getId());
}
super.tearDown();
}
@Test(enabled = false) @Test(enabled = false)
// takes too long // takes too long

View File

@ -169,8 +169,10 @@ public class SecurityGroupClientLiveTest extends BaseCloudStackClientLiveTest {
public void testCreateVMInSecurityGroup() throws Exception { public void testCreateVMInSecurityGroup() throws Exception {
if (!securityGroupsSupported) if (!securityGroupsSupported)
return; return;
vm = VirtualMachineClientLiveTest.createVirtualMachineWithSecurityGroupInZone(zone.getId(), group.getId(), Long defaultTemplate = (imageId != null && !"".equals(imageId)) ? new Long(imageId) : null;
client, jobComplete, virtualMachineRunning); vm = VirtualMachineClientLiveTest.createVirtualMachineWithSecurityGroupInZone(zone.getId(),
defaultTemplateOrPreferredInZone(defaultTemplate, client, zone.getId()), group.getId(), client,
jobComplete, virtualMachineRunning);
if (vm.getPassword() != null) if (vm.getPassword() != null)
password = vm.getPassword(); password = vm.getPassword();
// ingress port 22 // ingress port 22

View File

@ -20,14 +20,12 @@ package org.jclouds.cloudstack.features;
import static com.google.common.base.Predicates.equalTo; import static com.google.common.base.Predicates.equalTo;
import static com.google.common.base.Predicates.or; import static com.google.common.base.Predicates.or;
import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Iterables.find; import static com.google.common.collect.Iterables.find;
import static com.google.common.collect.Iterables.get; import static com.google.common.collect.Iterables.get;
import static com.google.common.collect.Iterables.getOnlyElement; import static com.google.common.collect.Iterables.getOnlyElement;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue; import static org.testng.Assert.assertTrue;
import java.util.NoSuchElementException;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
@ -37,15 +35,10 @@ import org.jclouds.cloudstack.domain.AsyncJob;
import org.jclouds.cloudstack.domain.NIC; import org.jclouds.cloudstack.domain.NIC;
import org.jclouds.cloudstack.domain.Network; import org.jclouds.cloudstack.domain.Network;
import org.jclouds.cloudstack.domain.ServiceOffering; import org.jclouds.cloudstack.domain.ServiceOffering;
import org.jclouds.cloudstack.domain.Template;
import org.jclouds.cloudstack.domain.VirtualMachine; import org.jclouds.cloudstack.domain.VirtualMachine;
import org.jclouds.cloudstack.domain.Zone; import org.jclouds.cloudstack.domain.Zone;
import org.jclouds.cloudstack.options.DeployVirtualMachineOptions; import org.jclouds.cloudstack.options.DeployVirtualMachineOptions;
import org.jclouds.cloudstack.options.ListTemplatesOptions;
import org.jclouds.cloudstack.options.ListVirtualMachinesOptions; import org.jclouds.cloudstack.options.ListVirtualMachinesOptions;
import org.jclouds.cloudstack.predicates.CorrectHypervisorForZone;
import org.jclouds.cloudstack.predicates.OSCategoryIn;
import org.jclouds.cloudstack.predicates.TemplatePredicates;
import org.jclouds.net.IPSocket; import org.jclouds.net.IPSocket;
import org.jclouds.predicates.RetryablePredicate; import org.jclouds.predicates.RetryablePredicate;
import org.jclouds.util.InetAddresses2; import org.jclouds.util.InetAddresses2;
@ -53,11 +46,8 @@ import org.testng.annotations.AfterGroups;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.base.Throwables; import com.google.common.base.Throwables;
import com.google.common.collect.ComparisonChain; import com.google.common.collect.ComparisonChain;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Ordering; import com.google.common.collect.Ordering;
import com.google.common.net.HostSpecifier; import com.google.common.net.HostSpecifier;
@ -77,61 +67,50 @@ public class VirtualMachineClientLiveTest extends BaseCloudStackClientLiveTest {
} }
}; };
public static VirtualMachine createVirtualMachine(CloudStackClient client, RetryablePredicate<Long> jobComplete, public static VirtualMachine createVirtualMachine(CloudStackClient client, Long defaultTemplate,
RetryablePredicate<VirtualMachine> virtualMachineRunning) { RetryablePredicate<Long> jobComplete, RetryablePredicate<VirtualMachine> virtualMachineRunning) {
Set<Network> networks = client.getNetworkClient().listNetworks(); Set<Network> networks = client.getNetworkClient().listNetworks();
if (networks.size() > 0) { if (networks.size() > 0) {
return createVirtualMachineInNetwork(get(networks, 0), client, jobComplete, virtualMachineRunning); Network network = get(networks, 0);
return createVirtualMachineInNetwork(network,
defaultTemplateOrPreferredInZone(defaultTemplate, client, network.getZoneId()), client, jobComplete,
virtualMachineRunning);
} else { } else {
return createVirtualMachineWithSecurityGroupInZone( long zoneId = find(client.getZoneClient().listZones(), new Predicate<Zone>() {
find(client.getZoneClient().listZones(), new Predicate<Zone>() {
@Override @Override
public boolean apply(Zone arg0) { public boolean apply(Zone arg0) {
return arg0.isSecurityGroupsEnabled(); return arg0.isSecurityGroupsEnabled();
} }
}).getId(), get(client.getSecurityGroupClient().listSecurityGroups(), 0).getId(), client, jobComplete, }).getId();
return createVirtualMachineWithSecurityGroupInZone(zoneId,
defaultTemplateOrPreferredInZone(defaultTemplate, client, zoneId),
get(client.getSecurityGroupClient().listSecurityGroups(), 0).getId(), client, jobComplete,
virtualMachineRunning); virtualMachineRunning);
} }
} }
public static VirtualMachine createVirtualMachineWithSecurityGroupInZone(long zoneId, long groupId, public static VirtualMachine createVirtualMachineWithSecurityGroupInZone(long zoneId, long templateId, long groupId,
CloudStackClient client, RetryablePredicate<Long> jobComplete, CloudStackClient client, RetryablePredicate<Long> jobComplete,
RetryablePredicate<VirtualMachine> virtualMachineRunning) { RetryablePredicate<VirtualMachine> virtualMachineRunning) {
return createVirtualMachineWithOptionsInZone(new DeployVirtualMachineOptions().securityGroupId(groupId), zoneId, return createVirtualMachineWithOptionsInZone(new DeployVirtualMachineOptions().securityGroupId(groupId), zoneId,
client, jobComplete, virtualMachineRunning); templateId, client, jobComplete, virtualMachineRunning);
} }
public static VirtualMachine createVirtualMachineInNetwork(Network network, CloudStackClient client, public static VirtualMachine createVirtualMachineInNetwork(Network network, long templateId,
RetryablePredicate<Long> jobComplete, RetryablePredicate<VirtualMachine> virtualMachineRunning) { CloudStackClient client, RetryablePredicate<Long> jobComplete,
RetryablePredicate<VirtualMachine> virtualMachineRunning) {
DeployVirtualMachineOptions options = new DeployVirtualMachineOptions(); DeployVirtualMachineOptions options = new DeployVirtualMachineOptions();
long zoneId = network.getZoneId(); long zoneId = network.getZoneId();
options.networkId(network.getId()); options.networkId(network.getId());
return createVirtualMachineWithOptionsInZone(options, zoneId, client, jobComplete, virtualMachineRunning); return createVirtualMachineWithOptionsInZone(options, zoneId, templateId, client, jobComplete,
virtualMachineRunning);
} }
public static VirtualMachine createVirtualMachineWithOptionsInZone(DeployVirtualMachineOptions options, public static VirtualMachine createVirtualMachineWithOptionsInZone(DeployVirtualMachineOptions options, long zoneId,
final long zoneId, CloudStackClient client, RetryablePredicate<Long> jobComplete, long templateId, CloudStackClient client, RetryablePredicate<Long> jobComplete,
RetryablePredicate<VirtualMachine> virtualMachineRunning) { RetryablePredicate<VirtualMachine> virtualMachineRunning) {
// TODO enum, as this is way too easy to mess up.
Set<String> acceptableCategories = ImmutableSet.of("Ubuntu", "CentOS");
final Predicate<Template> hypervisorPredicate = new CorrectHypervisorForZone(client).apply(zoneId);
final Predicate<Template> osTypePredicate = new OSCategoryIn(client).apply(acceptableCategories);
@SuppressWarnings("unchecked")
Predicate<Template> templatePredicate = Predicates.<Template> and(TemplatePredicates.isReady(),
hypervisorPredicate, osTypePredicate);
Iterable<Template> templates = filter(
client.getTemplateClient().listTemplates(ListTemplatesOptions.Builder.zoneId(zoneId)), templatePredicate);
if (Iterables.any(templates, TemplatePredicates.isPasswordEnabled())) {
templates = filter(templates, TemplatePredicates.isPasswordEnabled());
}
if (Iterables.size(templates) == 0) {
throw new NoSuchElementException(templatePredicate.toString());
}
long templateId = get(templates, 0).getId();
long serviceOfferingId = DEFAULT_SIZE_ORDERING.min(client.getOfferingClient().listServiceOfferings()).getId(); long serviceOfferingId = DEFAULT_SIZE_ORDERING.min(client.getOfferingClient().listServiceOfferings()).getId();
System.out.printf("serviceOfferingId %d, templateId %d, zoneId %d, options %s%n", serviceOfferingId, templateId, System.out.printf("serviceOfferingId %d, templateId %d, zoneId %d, options %s%n", serviceOfferingId, templateId,
@ -158,7 +137,8 @@ public class VirtualMachineClientLiveTest extends BaseCloudStackClientLiveTest {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void testCreateVirtualMachine() throws Exception { public void testCreateVirtualMachine() throws Exception {
vm = createVirtualMachine(client, jobComplete, virtualMachineRunning); Long templateId = (imageId != null && !"".equals(imageId)) ? new Long(imageId) : null;
vm = createVirtualMachine(client, templateId, jobComplete, virtualMachineRunning);
if (vm.getPassword() != null) { if (vm.getPassword() != null) {
conditionallyCheckSSH(); conditionallyCheckSSH();
} }

View File

@ -0,0 +1,113 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.cloudstack.functions;
import static com.google.common.collect.Iterables.find;
import static org.testng.Assert.assertEquals;
import java.util.NoSuchElementException;
import org.jclouds.cloudstack.compute.config.CloudStackComputeServiceContextModule.GetIPForwardingRuleByVirtualMachine;
import org.jclouds.cloudstack.domain.IPForwardingRule;
import org.jclouds.cloudstack.domain.Network;
import org.jclouds.cloudstack.domain.PublicIPAddress;
import org.jclouds.cloudstack.domain.VirtualMachine;
import org.jclouds.cloudstack.features.NATClientLiveTest;
import org.jclouds.cloudstack.features.VirtualMachineClientLiveTest;
import org.jclouds.cloudstack.predicates.NetworkPredicates;
import org.jclouds.compute.domain.ExecResponse;
import org.jclouds.domain.Credentials;
import org.jclouds.net.IPSocket;
import org.jclouds.ssh.SshClient;
import org.testng.annotations.AfterGroups;
import org.testng.annotations.BeforeGroups;
import org.testng.annotations.Test;
import com.google.common.cache.CacheBuilder;
/**
* Tests behavior of {@code StaticNATVirtualMachineInNetwork}
*
* @author Adrian Cole
*/
@Test(groups = "live", singleThreaded = true, testName = "StaticNATVirtualMachineInNetworkLiveTest")
public class StaticNATVirtualMachineInNetworkLiveTest extends NATClientLiveTest {
private PublicIPAddress ip = null;
private VirtualMachine vm;
private IPForwardingRule rule;
private Network network;
private boolean networksDisabled;
@BeforeGroups(groups = "live")
public void setupClient() {
super.setupClient();
prefix += "nat";
try {
network = find(client.getNetworkClient().listNetworks(), NetworkPredicates.supportsStaticNAT());
Long defaultTemplate = (imageId != null && !"".equals(imageId)) ? new Long(imageId) : null;
vm = VirtualMachineClientLiveTest.createVirtualMachineInNetwork(network,
defaultTemplateOrPreferredInZone(defaultTemplate, client, network.getZoneId()), client, jobComplete,
virtualMachineRunning);
if (vm.getPassword() != null)
password = vm.getPassword();
} catch (NoSuchElementException e) {
networksDisabled = true;
}
}
public void testCreateIPForwardingRule() throws Exception {
if (networksDisabled)
return;
ip = new StaticNATVirtualMachineInNetwork(client, reuseOrAssociate, jobComplete, CacheBuilder.newBuilder()
.<Long, IPForwardingRule> build(new GetIPForwardingRuleByVirtualMachine(client)), network).apply(vm);
rule = client.getNATClient().getIPForwardingRuleForIPAddress(ip.getId());
assertEquals(rule.getIPAddressId(), ip.getId());
assertEquals(rule.getVirtualMachineId(), vm.getId());
assertEquals(rule.getStartPort(), 22);
assertEquals(rule.getProtocol(), "tcp");
checkRule(rule);
IPSocket socket = new IPSocket(ip.getIPAddress(), 22);
socketTester.apply(socket);
SshClient client = sshFactory.create(socket, new Credentials("root", password));
try {
client.connect();
ExecResponse exec = client.exec("echo hello");
assertEquals(exec.getOutput().trim(), "hello");
} finally {
if (client != null)
client.disconnect();
}
}
@AfterGroups(groups = "live")
protected void tearDown() {
if (rule != null) {
client.getNATClient().deleteIPForwardingRule(rule.getId());
}
if (vm != null) {
jobComplete.apply(client.getVirtualMachineClient().destroyVirtualMachine(vm.getId()));
}
if (ip != null) {
client.getAddressClient().disassociateIPAddress(ip.getId());
}
super.tearDown();
}
}

View File

@ -75,6 +75,17 @@ public class CloudStackErrorHandlerTest {
IllegalStateException.class); IllegalStateException.class);
} }
@Test
public void test431MakesResourceNotFoundExceptionOnDelete() {
assertCodeMakes(
"GET",
URI.create("https://api.ninefold.com/compute/v1.0/?response=json&command=deleteSSHKeyPair"),
431,
"",
"{ \"deletekeypairresponse\" : {\"errorcode\" : 431, \"errortext\" : \"A key pair with name 'adriancole-adapter-test-keypair' does not exist for account jclouds in domain id=457\"} }",
ResourceNotFoundException.class);
}
@Test @Test
public void test409MakesIllegalStateException() { public void test409MakesIllegalStateException() {
assertCodeMakes("GET", URI.create("https://cloudstack.com/foo"), 409, "", "Conflict", IllegalStateException.class); assertCodeMakes("GET", URI.create("https://cloudstack.com/foo"), 409, "", "Conflict", IllegalStateException.class);