mirror of https://github.com/apache/jclouds.git
added cloudstack support to launch servers in a network with ip forwarding
This commit is contained in:
parent
4329129c25
commit
be7cf42f04
|
@ -34,6 +34,8 @@ public class CloudStackPropertiesBuilder extends PropertiesBuilder {
|
|||
protected Properties defaultProperties() {
|
||||
Properties properties = super.defaultProperties();
|
||||
properties.setProperty(PROPERTY_API_VERSION, "2.2");
|
||||
properties.setProperty("jclouds.ssh.max-retries", "7");
|
||||
properties.setProperty("jclouds.ssh.retry-auth", "true");
|
||||
return properties;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,11 +18,13 @@
|
|||
*/
|
||||
package org.jclouds.cloudstack.compute.config;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
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.options.CloudStackTemplateOptions;
|
||||
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.ServiceOffering;
|
||||
import org.jclouds.cloudstack.domain.Template;
|
||||
import org.jclouds.cloudstack.domain.User;
|
||||
import org.jclouds.cloudstack.domain.VirtualMachine;
|
||||
import org.jclouds.cloudstack.domain.Zone;
|
||||
import org.jclouds.cloudstack.features.GuestOSClient;
|
||||
import org.jclouds.cloudstack.functions.StaticNATVirtualMachineInNetwork;
|
||||
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.compute.ComputeServiceAdapter;
|
||||
import org.jclouds.compute.config.ComputeServiceAdapterContextModule;
|
||||
|
@ -51,14 +59,19 @@ import org.jclouds.compute.options.TemplateOptions;
|
|||
import org.jclouds.domain.Location;
|
||||
import org.jclouds.location.suppliers.OnlyLocationOrFirstZone;
|
||||
import org.jclouds.predicates.RetryablePredicate;
|
||||
import org.jclouds.rest.ResourceNotFoundException;
|
||||
import org.jclouds.rest.suppliers.MemoizedRetryOnTimeOutButNotOnAuthorizationExceptionSupplier;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.inject.Provides;
|
||||
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(new TypeLiteral<Function<Template, OperatingSystem>>() {
|
||||
}).to(TemplateToOperatingSystem.class);
|
||||
install(new FactoryModuleBuilder().build(StaticNATVirtualMachineInNetwork.Factory.class));
|
||||
bind(new TypeLiteral<CacheLoader<Long, IPForwardingRule>>() {
|
||||
}).to(GetIPForwardingRuleByVirtualMachine.class);
|
||||
}
|
||||
|
||||
@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
|
||||
@Singleton
|
||||
protected Predicate<Long> jobComplete(JobComplete jobComplete) {
|
||||
// TODO: parameterize
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -27,6 +27,7 @@ import java.util.Set;
|
|||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.cloudstack.domain.IPForwardingRule;
|
||||
import org.jclouds.cloudstack.domain.VirtualMachine;
|
||||
import org.jclouds.collect.FindResourceInSet;
|
||||
import org.jclouds.collect.Memoized;
|
||||
|
@ -37,12 +38,17 @@ import org.jclouds.compute.domain.NodeMetadataBuilder;
|
|||
import org.jclouds.compute.domain.NodeState;
|
||||
import org.jclouds.domain.Credentials;
|
||||
import org.jclouds.domain.Location;
|
||||
import org.jclouds.rest.ResourceNotFoundException;
|
||||
import org.jclouds.util.InetAddresses2;
|
||||
import org.jclouds.util.Throwables2;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
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.ImmutableSet;
|
||||
import com.google.common.util.concurrent.UncheckedExecutionException;
|
||||
|
||||
/**
|
||||
* @author Adrian Cole
|
||||
|
@ -66,16 +72,20 @@ public class VirtualMachineToNodeMetadata implements Function<VirtualMachine, No
|
|||
private final FindLocationForVirtualMachine findLocationForVirtualMachine;
|
||||
private final FindHardwareForVirtualMachine findHardwareForVirtualMachine;
|
||||
private final FindImageForVirtualMachine findImageForVirtualMachine;
|
||||
private final Cache<Long, IPForwardingRule> getIPForwardingRuleByVirtualMachine;
|
||||
|
||||
@Inject
|
||||
VirtualMachineToNodeMetadata(Map<String, Credentials> credentialStore,
|
||||
FindLocationForVirtualMachine findLocationForVirtualMachine,
|
||||
FindHardwareForVirtualMachine findHardwareForVirtualMachine,
|
||||
FindImageForVirtualMachine findImageForVirtualMachine) {
|
||||
FindImageForVirtualMachine findImageForVirtualMachine,
|
||||
Cache<Long, IPForwardingRule> getIPForwardingRuleByVirtualMachine) {
|
||||
this.credentialStore = checkNotNull(credentialStore, "credentialStore");
|
||||
this.findLocationForVirtualMachine = checkNotNull(findLocationForVirtualMachine, "findLocationForVirtualMachine");
|
||||
this.findHardwareForVirtualMachine = checkNotNull(findHardwareForVirtualMachine, "findHardwareForVirtualMachine");
|
||||
this.findImageForVirtualMachine = checkNotNull(findImageForVirtualMachine, "findImageForVirtualMachine");
|
||||
this.getIPForwardingRuleByVirtualMachine = checkNotNull(getIPForwardingRuleByVirtualMachine,
|
||||
"getIPForwardingRuleByVirtualMachine");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -108,6 +118,15 @@ public class VirtualMachineToNodeMetadata implements Function<VirtualMachine, No
|
|||
else
|
||||
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()));
|
||||
return builder.build();
|
||||
}
|
||||
|
|
|
@ -55,6 +55,7 @@ import com.google.common.collect.Sets;
|
|||
public class CloudStackTemplateOptions extends TemplateOptions implements Cloneable {
|
||||
|
||||
protected Set<Long> securityGroupIds = Sets.<Long> newLinkedHashSet();
|
||||
protected Set<Long> networkIds = Sets.<Long> newLinkedHashSet();
|
||||
protected String keyPair;
|
||||
|
||||
@Override
|
||||
|
@ -70,6 +71,7 @@ public class CloudStackTemplateOptions extends TemplateOptions implements Clonea
|
|||
if (to instanceof CloudStackTemplateOptions) {
|
||||
CloudStackTemplateOptions eTo = CloudStackTemplateOptions.class.cast(to);
|
||||
eTo.securityGroupIds(this.securityGroupIds);
|
||||
eTo.keyPair(this.keyPair);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -93,6 +95,26 @@ public class CloudStackTemplateOptions extends TemplateOptions implements Clonea
|
|||
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)
|
||||
*/
|
||||
|
@ -125,6 +147,22 @@ public class CloudStackTemplateOptions extends TemplateOptions implements Clonea
|
|||
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
|
||||
*/
|
||||
|
|
|
@ -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.checkNotNull;
|
||||
import static com.google.common.base.Throwables.propagate;
|
||||
import static com.google.common.collect.Iterables.filter;
|
||||
import static com.google.common.collect.Iterables.getOnlyElement;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
@ -30,17 +32,21 @@ import javax.inject.Inject;
|
|||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import com.google.common.base.Predicates;
|
||||
import org.jclouds.cloudstack.CloudStackClient;
|
||||
import org.jclouds.cloudstack.compute.options.CloudStackTemplateOptions;
|
||||
import org.jclouds.cloudstack.domain.AsyncCreateResponse;
|
||||
import org.jclouds.cloudstack.domain.AsyncJob;
|
||||
import org.jclouds.cloudstack.domain.Network;
|
||||
import org.jclouds.cloudstack.domain.PublicIPAddress;
|
||||
import org.jclouds.cloudstack.domain.ServiceOffering;
|
||||
import org.jclouds.cloudstack.domain.Template;
|
||||
import org.jclouds.cloudstack.domain.VirtualMachine;
|
||||
import org.jclouds.cloudstack.domain.Zone;
|
||||
import org.jclouds.cloudstack.functions.StaticNATVirtualMachineInNetwork;
|
||||
import org.jclouds.cloudstack.functions.StaticNATVirtualMachineInNetwork.Factory;
|
||||
import org.jclouds.cloudstack.options.DeployVirtualMachineOptions;
|
||||
import org.jclouds.cloudstack.predicates.TemplatePredicates;
|
||||
import org.jclouds.collect.Memoized;
|
||||
import org.jclouds.compute.ComputeService;
|
||||
import org.jclouds.compute.ComputeServiceAdapter;
|
||||
import org.jclouds.compute.reference.ComputeServiceConstants;
|
||||
|
@ -48,7 +54,7 @@ import org.jclouds.domain.Credentials;
|
|||
import org.jclouds.logging.Logger;
|
||||
|
||||
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
|
||||
|
@ -65,26 +71,56 @@ public class CloudStackComputeServiceAdapter implements
|
|||
|
||||
private final CloudStackClient client;
|
||||
private final Predicate<Long> jobComplete;
|
||||
private final Supplier<Map<Long, Network>> networkSupplier;
|
||||
private final Factory staticNATVMInNetwork;
|
||||
private final Map<String, Credentials> credentialStore;
|
||||
|
||||
@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.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
|
||||
public VirtualMachine createNodeWithGroupEncodedIntoNameThenStoreCredentials(String group, String name,
|
||||
org.jclouds.compute.domain.Template template, Map<String, Credentials> credentialStore) {
|
||||
public NodeAndInitialCredentials<VirtualMachine> createNodeWithGroupEncodedIntoName(String group, String name,
|
||||
org.jclouds.compute.domain.Template template) {
|
||||
checkNotNull(template, "template was null");
|
||||
checkNotNull(template.getOptions(), "template options was null");
|
||||
checkArgument(template.getOptions().getClass().isAssignableFrom(CloudStackTemplateOptions.class),
|
||||
"options class %s should have been assignable from CloudStackTemplateOptions", template.getOptions()
|
||||
.getClass());
|
||||
Map<Long, Network> networks = networkSupplier.get();
|
||||
|
||||
final long zoneId = Long.parseLong(template.getLocation().getId());
|
||||
|
||||
CloudStackTemplateOptions templateOptions = template.getOptions().as(CloudStackTemplateOptions.class);
|
||||
|
||||
DeployVirtualMachineOptions options = new DeployVirtualMachineOptions();
|
||||
if (templateOptions.getSecurityGroupIds().size() > 0)
|
||||
if (templateOptions.getSecurityGroupIds().size() > 0) {
|
||||
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) {
|
||||
options.keyPair(templateOptions.getKeyPair());
|
||||
|
@ -96,32 +132,35 @@ public class CloudStackComputeServiceAdapter implements
|
|||
}
|
||||
}
|
||||
|
||||
long zoneId = Long.parseLong(template.getLocation().getId());
|
||||
long templateId = Long.parseLong(template.getImage().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);
|
||||
AsyncCreateResponse job = client.getVirtualMachineClient().deployVirtualMachineInZone(zoneId, serviceOfferingId,
|
||||
templateId, options);
|
||||
assert jobComplete.apply(job.getJobId());
|
||||
boolean completed = jobComplete.apply(job.getJobId());
|
||||
AsyncJob<VirtualMachine> jobWithResult = client.getAsyncJobClient().<VirtualMachine> getAsyncJob(job.getJobId());
|
||||
assert completed : jobWithResult;
|
||||
if (jobWithResult.getError() != null)
|
||||
Throwables.propagate(new ExecutionException(String.format("job %s failed with exception %s", job.getId(),
|
||||
jobWithResult.getError().toString())) {
|
||||
propagate(new ExecutionException(String.format("job %s failed with exception %s", job.getId(), jobWithResult
|
||||
.getError().toString())) {
|
||||
private static final long serialVersionUID = 4371112085613620239L;
|
||||
});
|
||||
VirtualMachine vm = jobWithResult.getResult();
|
||||
Credentials credentials = null;
|
||||
if (vm.isPasswordEnabled()) {
|
||||
assert vm.getPassword() != null : vm;
|
||||
Credentials credentials = new Credentials("root", vm.getPassword());
|
||||
credentialStore.put("node#" + vm.getId(), credentials);
|
||||
credentials = new Credentials(null, vm.getPassword());
|
||||
} else {
|
||||
// assert templateOptions.getKeyPair() != null : vm;
|
||||
Credentials credentials = credentialStore.get("keypair#" + templateOptions.getKeyPair());
|
||||
credentialStore.put("node#" + vm.getId(), credentials);
|
||||
credentials = credentialStore.get("keypair#" + templateOptions.getKeyPair());
|
||||
}
|
||||
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
|
||||
|
|
|
@ -56,4 +56,34 @@ public class AsyncCreateResponse {
|
|||
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 + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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" />
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Timeout(duration = 60, timeUnit = TimeUnit.SECONDS)
|
||||
@Timeout(duration = 120, timeUnit = TimeUnit.SECONDS)
|
||||
public interface AccountClient {
|
||||
/**
|
||||
* Lists Accounts
|
||||
|
|
|
@ -46,7 +46,9 @@ import com.google.common.util.concurrent.ListenableFuture;
|
|||
* <p/>
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
@RequestFilters(QuerySigner.class)
|
||||
|
@ -74,6 +76,28 @@ public interface NATAsyncClient {
|
|||
@ExceptionParser(ReturnNullOnNotFoundOr404.class)
|
||||
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
|
||||
*/
|
||||
|
|
|
@ -32,7 +32,9 @@ import org.jclouds.concurrent.Timeout;
|
|||
* <p/>
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
@Timeout(duration = 60, timeUnit = TimeUnit.SECONDS)
|
||||
|
@ -56,6 +58,24 @@ public interface NATClient {
|
|||
*/
|
||||
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
|
||||
*
|
||||
|
|
|
@ -50,16 +50,18 @@ import org.jclouds.rest.annotations.SkipEncoding;
|
|||
import org.jclouds.rest.annotations.Unwrap;
|
||||
import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404;
|
||||
import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
|
||||
import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404;
|
||||
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404;
|
||||
|
||||
/**
|
||||
* Provides asynchronous access to cloudstack via their REST API.
|
||||
* <p/>
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
@RequestFilters(QuerySigner.class)
|
||||
|
@ -74,7 +76,9 @@ public interface TemplateAsyncClient {
|
|||
@QueryParams(keys = "command", values = "createTemplate")
|
||||
@Unwrap
|
||||
@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
|
||||
|
@ -83,7 +87,10 @@ public interface TemplateAsyncClient {
|
|||
@QueryParams(keys = "command", values = "registerTemplate")
|
||||
@SelectJson("template")
|
||||
@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
|
||||
|
@ -101,7 +108,8 @@ public interface TemplateAsyncClient {
|
|||
@QueryParams(keys = "command", values = "copyTemplate")
|
||||
@Unwrap
|
||||
@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
|
||||
|
@ -135,6 +143,7 @@ public interface TemplateAsyncClient {
|
|||
* @see TemplateClient#getTemplate
|
||||
*/
|
||||
@GET
|
||||
// templatefilter required in at least 2.2.8 version
|
||||
@QueryParams(keys = { "command", "templatefilter" }, values = { "listTemplates", "executable" })
|
||||
@SelectJson("template")
|
||||
@OnlyElement
|
||||
|
@ -147,7 +156,8 @@ public interface TemplateAsyncClient {
|
|||
*/
|
||||
@GET
|
||||
@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
|
||||
|
@ -156,7 +166,8 @@ public interface TemplateAsyncClient {
|
|||
@QueryParams(keys = "command", values = "listTemplatePermissions")
|
||||
@Unwrap
|
||||
@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
|
||||
|
@ -165,5 +176,6 @@ public interface TemplateAsyncClient {
|
|||
@QueryParams(keys = "command", values = "extractTemplate")
|
||||
@Unwrap
|
||||
@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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -70,7 +70,12 @@ public class CloudStackErrorHandler implements HttpErrorHandler {
|
|||
break;
|
||||
case 409:
|
||||
case 431:
|
||||
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;
|
||||
}
|
||||
} finally {
|
||||
|
|
|
@ -23,8 +23,11 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
|||
|
||||
import org.jclouds.encryption.internal.Base64;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
/**
|
||||
* Options used to control what disk offerings are returned
|
||||
|
@ -119,6 +122,23 @@ public class DeployVirtualMachineOptions extends AccountInDomainOptions {
|
|||
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
|
||||
* security group applied to the virtual machine. Should be passed
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -25,35 +25,47 @@ import static org.testng.Assert.fail;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
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.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.SshKeyPair;
|
||||
import org.jclouds.cloudstack.domain.User;
|
||||
import org.jclouds.cloudstack.domain.VirtualMachine;
|
||||
import org.jclouds.cloudstack.features.BaseCloudStackClientLiveTest;
|
||||
import org.jclouds.cloudstack.functions.StaticNATVirtualMachineInNetwork;
|
||||
import org.jclouds.cloudstack.predicates.JobComplete;
|
||||
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.domain.ExecResponse;
|
||||
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.logging.log4j.config.Log4JLoggingModule;
|
||||
import org.jclouds.net.IPSocket;
|
||||
import org.jclouds.predicates.RetryablePredicate;
|
||||
import org.jclouds.rest.annotations.Identity;
|
||||
import org.jclouds.ssh.SshClient;
|
||||
import org.testng.annotations.AfterGroups;
|
||||
import org.testng.annotations.BeforeGroups;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
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.Maps;
|
||||
import com.google.common.net.InetAddresses;
|
||||
|
@ -61,15 +73,19 @@ import com.google.inject.AbstractModule;
|
|||
import com.google.inject.Guice;
|
||||
import com.google.inject.Module;
|
||||
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")
|
||||
public class CloudStackComputeServiceAdapterLiveTest extends BaseCloudStackClientLiveTest {
|
||||
|
||||
private CloudStackComputeServiceAdapter adapter;
|
||||
private VirtualMachine vm;
|
||||
private NodeAndInitialCredentials<VirtualMachine> vm;
|
||||
|
||||
private String keyPairName;
|
||||
private Map<String, String> keyPair;
|
||||
Map<String, Credentials> credentialStore = Maps.newLinkedHashMap();
|
||||
|
||||
@BeforeGroups(groups = { "live" })
|
||||
public void setupClient() {
|
||||
|
@ -78,8 +94,16 @@ public class CloudStackComputeServiceAdapterLiveTest extends BaseCloudStackClien
|
|||
|
||||
@Override
|
||||
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());
|
||||
install(new FactoryModuleBuilder().build(StaticNATVirtualMachineInNetwork.Factory.class));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
|
@ -88,6 +112,14 @@ public class CloudStackComputeServiceAdapterLiveTest extends BaseCloudStackClien
|
|||
protected Predicate<Long> jobComplete(JobComplete jobComplete) {
|
||||
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(
|
||||
CloudStackComputeServiceAdapter.class);
|
||||
|
@ -105,35 +137,47 @@ public class CloudStackComputeServiceAdapterLiveTest extends BaseCloudStackClien
|
|||
assertFalse(Iterables.isEmpty(adapter.listLocations()));
|
||||
}
|
||||
|
||||
private static final PrioritizeCredentialsFromTemplate prioritizeCredentialsFromTemplate = new PrioritizeCredentialsFromTemplate(
|
||||
new DefaultCredentialsFromImageOrOverridingCredentials());
|
||||
|
||||
@Test
|
||||
public void testCreateNodeWithGroupEncodedIntoNameThenStoreCredentialsWithSecurityGroup() {
|
||||
public void testCreateNodeWithGroupEncodedIntoNameThenStoreCredentialsWithSecurityGroup()
|
||||
throws InterruptedException {
|
||||
String group = "foo";
|
||||
String name = "node" + new Random().nextInt();
|
||||
Template template = computeContext.getComputeService().templateBuilder().build();
|
||||
|
||||
if (!client
|
||||
.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")));
|
||||
|
||||
// TODO: look at SecurityGroupClientLiveTest for how to do this
|
||||
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(),
|
||||
// check other things, like cpu correct, mem correct, image/os is correct
|
||||
// (as possible)
|
||||
|
||||
assert credentialStore.containsKey("node#" + vm.getId()) : "credentials to log into vm not found " + vm;
|
||||
assert InetAddresses.isInetAddress(vm.getIPAddress()) : vm;
|
||||
// check to see if we setup a NAT rule (conceding we could check this from
|
||||
// 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) {
|
||||
SshClient ssh = computeContext.utils().sshFactory().create(new IPSocket(vm.getIPAddress(), 22), creds);
|
||||
protected void doConnectViaSsh(IPSocket socket, Credentials creds) {
|
||||
SshClient ssh = computeContext.utils().sshFactory().create(socket, creds);
|
||||
try {
|
||||
ssh.connect();
|
||||
ExecResponse hello = ssh.exec("echo hello");
|
||||
|
@ -170,7 +214,7 @@ public class CloudStackComputeServiceAdapterLiveTest extends BaseCloudStackClien
|
|||
@AfterGroups(groups = "live")
|
||||
protected void tearDown() {
|
||||
if (vm != null)
|
||||
adapter.destroyNode(vm.getId() + "");
|
||||
adapter.destroyNode(vm.getNodeId());
|
||||
super.tearDown();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ import org.jclouds.sshj.config.SshjSshClientModule;
|
|||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.inject.Module;
|
||||
|
||||
/**
|
||||
|
@ -49,8 +50,9 @@ public class CloudStackComputeServiceLiveTest extends BaseComputeServiceLiveTest
|
|||
|
||||
public void testAssignability() throws Exception {
|
||||
@SuppressWarnings("unused")
|
||||
RestContext<CloudStackClient, CloudStackAsyncClient> tmContext = new ComputeServiceContextFactory()
|
||||
.createContext(provider, identity, credential).getProviderSpecificContext();
|
||||
RestContext<CloudStackClient, CloudStackAsyncClient> tmContext = new ComputeServiceContextFactory(
|
||||
setupRestProperties()).createContext(provider, identity, credential, ImmutableSet.<Module> of(),
|
||||
setupProperties()).getProviderSpecificContext();
|
||||
}
|
||||
|
||||
// cloudstack does not support metadata
|
||||
|
@ -60,4 +62,3 @@ public class CloudStackComputeServiceLiveTest extends BaseComputeServiceLiveTest
|
|||
"node userMetadata did not match %s %s", userMetadata, node);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ import java.util.Set;
|
|||
import org.jclouds.cloudstack.compute.functions.VirtualMachineToNodeMetadata.FindHardwareForVirtualMachine;
|
||||
import org.jclouds.cloudstack.compute.functions.VirtualMachineToNodeMetadata.FindImageForVirtualMachine;
|
||||
import org.jclouds.cloudstack.compute.functions.VirtualMachineToNodeMetadata.FindLocationForVirtualMachine;
|
||||
import org.jclouds.cloudstack.domain.IPForwardingRule;
|
||||
import org.jclouds.cloudstack.domain.VirtualMachine;
|
||||
import org.jclouds.cloudstack.parse.ListVirtualMachinesResponseTest;
|
||||
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.domain.Credentials;
|
||||
import org.jclouds.domain.Location;
|
||||
import org.jclouds.rest.ResourceNotFoundException;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.base.Supplier;
|
||||
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.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
@ -50,6 +54,51 @@ import com.google.common.collect.Iterables;
|
|||
@Test(groups = "unit", testName = "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
|
||||
public void testApplyWhereVirtualMachineWithNoPassword() throws UnknownHostException {
|
||||
|
||||
|
@ -66,10 +115,17 @@ public class VirtualMachineToNodeMetadataTest {
|
|||
|
||||
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));
|
||||
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.
|
||||
VirtualMachine guest = Iterables.get(new ListVirtualMachinesResponseTest().expected(), 0);
|
||||
|
@ -104,7 +160,15 @@ public class VirtualMachineToNodeMetadataTest {
|
|||
|
||||
VirtualMachineToNodeMetadata parser = new VirtualMachineToNodeMetadata(credentialStore,
|
||||
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.
|
||||
VirtualMachine guest = Iterables.get(new ListVirtualMachinesResponseTest().expected(), 0);
|
||||
|
@ -121,5 +185,4 @@ public class VirtualMachineToNodeMetadataTest {
|
|||
|
||||
assertEquals(node.getCredentials(), new Credentials("root", "password"));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
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.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.securityGroupIds;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
@ -74,6 +76,36 @@ public class CloudStackTemplateOptionsTest {
|
|||
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
|
||||
public void testKeyPair() {
|
||||
TemplateOptions options = keyPair("test");
|
||||
|
|
|
@ -18,19 +18,27 @@
|
|||
*/
|
||||
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 java.util.NoSuchElementException;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.jclouds.cloudstack.CloudStackAsyncClient;
|
||||
import org.jclouds.cloudstack.CloudStackClient;
|
||||
import org.jclouds.cloudstack.domain.Account;
|
||||
import org.jclouds.cloudstack.domain.Template;
|
||||
import org.jclouds.cloudstack.domain.User;
|
||||
import org.jclouds.cloudstack.domain.VirtualMachine;
|
||||
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.OSCategoryIn;
|
||||
import org.jclouds.cloudstack.predicates.TemplatePredicates;
|
||||
import org.jclouds.cloudstack.predicates.UserPredicates;
|
||||
import org.jclouds.cloudstack.predicates.VirtualMachineDestroyed;
|
||||
import org.jclouds.cloudstack.predicates.VirtualMachineRunning;
|
||||
|
@ -50,6 +58,7 @@ import org.testng.annotations.AfterGroups;
|
|||
import org.testng.annotations.BeforeGroups;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.inject.Guice;
|
||||
|
@ -65,6 +74,33 @@ public class BaseCloudStackClientLiveTest extends BaseVersionedServiceLiveTest {
|
|||
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 CloudStackClient client;
|
||||
|
@ -83,7 +119,6 @@ public class BaseCloudStackClientLiveTest extends BaseVersionedServiceLiveTest {
|
|||
|
||||
protected ComputeServiceContext computeContext;
|
||||
|
||||
|
||||
protected void checkSSH(IPSocket socket) {
|
||||
socketTester.apply(socket);
|
||||
SshClient client = sshFactory.create(socket, new Credentials("root", password));
|
||||
|
|
|
@ -57,7 +57,9 @@ public class FirewallClientLiveTest extends BaseCloudStackClientLiveTest {
|
|||
prefix += "rule";
|
||||
try {
|
||||
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);
|
||||
if (vm.getPassword() != null)
|
||||
password = vm.getPassword();
|
||||
|
|
|
@ -67,7 +67,9 @@ public class LoadBalancerClientLiveTest extends BaseCloudStackClientLiveTest {
|
|||
prefix += "rule";
|
||||
try {
|
||||
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);
|
||||
if (vm.getPassword() != null)
|
||||
password = vm.getPassword();
|
||||
|
|
|
@ -27,7 +27,6 @@ import org.jclouds.http.HttpRequest;
|
|||
import org.jclouds.http.functions.ParseFirstJsonValueNamed;
|
||||
import org.jclouds.http.functions.UnwrapOnlyJsonValue;
|
||||
import org.jclouds.http.functions.UnwrapOnlyNestedJsonValue;
|
||||
import org.jclouds.http.functions.UnwrapOnlyNestedJsonValueInSet;
|
||||
import org.jclouds.rest.functions.MapHttp4xxCodesToExceptions;
|
||||
import org.jclouds.rest.functions.ReturnEmptySetOnNotFoundOr404;
|
||||
import org.jclouds.rest.functions.ReturnNullOnNotFoundOr404;
|
||||
|
|
|
@ -18,27 +18,14 @@
|
|||
*/
|
||||
package org.jclouds.cloudstack.features;
|
||||
|
||||
import static com.google.common.collect.Iterables.find;
|
||||
import static com.google.common.collect.Iterables.getOnlyElement;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jclouds.cloudstack.domain.AsyncCreateResponse;
|
||||
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.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;
|
||||
|
||||
/**
|
||||
|
@ -48,79 +35,6 @@ import org.testng.annotations.Test;
|
|||
*/
|
||||
@Test(groups = "live", singleThreaded = true, testName = "NATClientLiveTest")
|
||||
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)
|
||||
// takes too long
|
||||
|
|
|
@ -169,8 +169,10 @@ public class SecurityGroupClientLiveTest extends BaseCloudStackClientLiveTest {
|
|||
public void testCreateVMInSecurityGroup() throws Exception {
|
||||
if (!securityGroupsSupported)
|
||||
return;
|
||||
vm = VirtualMachineClientLiveTest.createVirtualMachineWithSecurityGroupInZone(zone.getId(), group.getId(),
|
||||
client, jobComplete, virtualMachineRunning);
|
||||
Long defaultTemplate = (imageId != null && !"".equals(imageId)) ? new Long(imageId) : null;
|
||||
vm = VirtualMachineClientLiveTest.createVirtualMachineWithSecurityGroupInZone(zone.getId(),
|
||||
defaultTemplateOrPreferredInZone(defaultTemplate, client, zone.getId()), group.getId(), client,
|
||||
jobComplete, virtualMachineRunning);
|
||||
if (vm.getPassword() != null)
|
||||
password = vm.getPassword();
|
||||
// ingress port 22
|
||||
|
|
|
@ -20,14 +20,12 @@ package org.jclouds.cloudstack.features;
|
|||
|
||||
import static com.google.common.base.Predicates.equalTo;
|
||||
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.get;
|
||||
import static com.google.common.collect.Iterables.getOnlyElement;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
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.Network;
|
||||
import org.jclouds.cloudstack.domain.ServiceOffering;
|
||||
import org.jclouds.cloudstack.domain.Template;
|
||||
import org.jclouds.cloudstack.domain.VirtualMachine;
|
||||
import org.jclouds.cloudstack.domain.Zone;
|
||||
import org.jclouds.cloudstack.options.DeployVirtualMachineOptions;
|
||||
import org.jclouds.cloudstack.options.ListTemplatesOptions;
|
||||
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.predicates.RetryablePredicate;
|
||||
import org.jclouds.util.InetAddresses2;
|
||||
|
@ -53,11 +46,8 @@ import org.testng.annotations.AfterGroups;
|
|||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.base.Throwables;
|
||||
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.net.HostSpecifier;
|
||||
|
||||
|
@ -77,61 +67,50 @@ public class VirtualMachineClientLiveTest extends BaseCloudStackClientLiveTest {
|
|||
}
|
||||
};
|
||||
|
||||
public static VirtualMachine createVirtualMachine(CloudStackClient client, RetryablePredicate<Long> jobComplete,
|
||||
RetryablePredicate<VirtualMachine> virtualMachineRunning) {
|
||||
public static VirtualMachine createVirtualMachine(CloudStackClient client, Long defaultTemplate,
|
||||
RetryablePredicate<Long> jobComplete, RetryablePredicate<VirtualMachine> virtualMachineRunning) {
|
||||
Set<Network> networks = client.getNetworkClient().listNetworks();
|
||||
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 {
|
||||
return createVirtualMachineWithSecurityGroupInZone(
|
||||
find(client.getZoneClient().listZones(), new Predicate<Zone>() {
|
||||
long zoneId = find(client.getZoneClient().listZones(), new Predicate<Zone>() {
|
||||
|
||||
@Override
|
||||
public boolean apply(Zone arg0) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public static VirtualMachine createVirtualMachineWithSecurityGroupInZone(long zoneId, long groupId,
|
||||
public static VirtualMachine createVirtualMachineWithSecurityGroupInZone(long zoneId, long templateId, long groupId,
|
||||
CloudStackClient client, RetryablePredicate<Long> jobComplete,
|
||||
RetryablePredicate<VirtualMachine> virtualMachineRunning) {
|
||||
return createVirtualMachineWithOptionsInZone(new DeployVirtualMachineOptions().securityGroupId(groupId), zoneId,
|
||||
client, jobComplete, virtualMachineRunning);
|
||||
templateId, client, jobComplete, virtualMachineRunning);
|
||||
}
|
||||
|
||||
public static VirtualMachine createVirtualMachineInNetwork(Network network, CloudStackClient client,
|
||||
RetryablePredicate<Long> jobComplete, RetryablePredicate<VirtualMachine> virtualMachineRunning) {
|
||||
public static VirtualMachine createVirtualMachineInNetwork(Network network, long templateId,
|
||||
CloudStackClient client, RetryablePredicate<Long> jobComplete,
|
||||
RetryablePredicate<VirtualMachine> virtualMachineRunning) {
|
||||
DeployVirtualMachineOptions options = new DeployVirtualMachineOptions();
|
||||
long zoneId = network.getZoneId();
|
||||
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,
|
||||
final long zoneId, CloudStackClient client, RetryablePredicate<Long> jobComplete,
|
||||
public static VirtualMachine createVirtualMachineWithOptionsInZone(DeployVirtualMachineOptions options, long zoneId,
|
||||
long templateId, CloudStackClient client, RetryablePredicate<Long> jobComplete,
|
||||
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();
|
||||
|
||||
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")
|
||||
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) {
|
||||
conditionallyCheckSSH();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -75,6 +75,17 @@ public class CloudStackErrorHandlerTest {
|
|||
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
|
||||
public void test409MakesIllegalStateException() {
|
||||
assertCodeMakes("GET", URI.create("https://cloudstack.com/foo"), 409, "", "Conflict", IllegalStateException.class);
|
||||
|
|
Loading…
Reference in New Issue