diff --git a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/DeployVirtualMachineOptions.java b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/DeployVirtualMachineOptions.java index 33bbfab7f7..281e40d434 100644 --- a/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/DeployVirtualMachineOptions.java +++ b/apis/cloudstack/src/main/java/org/jclouds/cloudstack/options/DeployVirtualMachineOptions.java @@ -29,6 +29,8 @@ import com.google.common.base.Splitter; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; +import java.util.Map; + /** * Options used to control what disk offerings are returned * @@ -50,7 +52,7 @@ public class DeployVirtualMachineOptions extends AccountInDomainOptions { * parameter passed is from an ISO object, the diskOfferingId refers to a * ROOT Disk Volume created. * - * @param id + * @param diskofferingid * the ID of the disk offering */ public DeployVirtualMachineOptions diskOfferingId(long diskofferingid) { @@ -104,6 +106,35 @@ public class DeployVirtualMachineOptions extends AccountInDomainOptions { return this; } + /** + * @param ipAddress + * the requested ip address (2.2.12 only option) + */ + public DeployVirtualMachineOptions ipAddress(String ipAddress) { + this.queryParameters.replaceValues("ipaddress", ImmutableSet.of(ipAddress)); + return this; + } + + /** + * @param ipToNetworkList + * mapping ip addresses to network ids (2.2.12 only option) + */ + public DeployVirtualMachineOptions ipToNetworkList(Map ipToNetworkList) { + int count = 0; + for(String ip : ipToNetworkList.keySet()) { + this.queryParameters.replaceValues( + String.format("ipnetworklist[%d].ip", count), + ImmutableSet.of(ip) + ); + this.queryParameters.replaceValues( + String.format("ipnetworklist[%d].networkid", count), + ImmutableSet.of("" + ipToNetworkList.get(ip)) + ); + count += 1; + } + return this; + } + /** * @param networkId * network id used by virtual machine @@ -236,6 +267,22 @@ public class DeployVirtualMachineOptions extends AccountInDomainOptions { return options.name(name); } + /** + * @see DeployVirtualMachineOptions#ipAddress + */ + public static DeployVirtualMachineOptions ipAddress(String ipAddress) { + DeployVirtualMachineOptions options = new DeployVirtualMachineOptions(); + return options.ipAddress(ipAddress); + } + + /** + * @see DeployVirtualMachineOptions#ipToNetworkList + */ + public static DeployVirtualMachineOptions ipToNetworkList(Map ipToNetworkList) { + DeployVirtualMachineOptions options = new DeployVirtualMachineOptions(); + return options.ipToNetworkList(ipToNetworkList); + } + /** * @see DeployVirtualMachineOptions#networkId */ diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/BaseCloudStackClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/BaseCloudStackClientLiveTest.java index 9af6961b06..db8b01c056 100644 --- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/BaseCloudStackClientLiveTest.java +++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/BaseCloudStackClientLiveTest.java @@ -136,8 +136,10 @@ public class BaseCloudStackClientLiveTest extends BaseVersionedServiceLiveTest { protected RetryablePredicate jobComplete; protected RetryablePredicate adminJobComplete; protected RetryablePredicate virtualMachineRunning; + protected RetryablePredicate adminVirtualMachineRunning; protected RetryablePredicate virtualMachineDestroyed; - protected SshClient.Factory sshFactory; + protected RetryablePredicate adminVirtualMachineDestroyed; + protected SshClient.Factory sshFactory; protected String password = "password"; protected Injector injector; @@ -196,9 +198,15 @@ public class BaseCloudStackClientLiveTest extends BaseVersionedServiceLiveTest { virtualMachineRunning = new RetryablePredicate(new VirtualMachineRunning(client), 600, 5, 5, TimeUnit.SECONDS); injector.injectMembers(virtualMachineRunning); + adminVirtualMachineRunning = new RetryablePredicate(new VirtualMachineRunning(adminClient), 600, 5, 5, + TimeUnit.SECONDS); + injector.injectMembers(adminVirtualMachineRunning); virtualMachineDestroyed = new RetryablePredicate(new VirtualMachineDestroyed(client), 600, 5, 5, TimeUnit.SECONDS); injector.injectMembers(virtualMachineDestroyed); + adminVirtualMachineDestroyed = new RetryablePredicate(new VirtualMachineDestroyed(adminClient), 600, 5, 5, + TimeUnit.SECONDS); + injector.injectMembers(adminVirtualMachineDestroyed); reuseOrAssociate = new ReuseOrAssociateNewPublicIPAddress(client, jobComplete); injector.injectMembers(reuseOrAssociate); } diff --git a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VirtualMachineClientLiveTest.java b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VirtualMachineClientLiveTest.java index 86a1ca4578..be292bd729 100644 --- a/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VirtualMachineClientLiveTest.java +++ b/apis/cloudstack/src/test/java/org/jclouds/cloudstack/features/VirtualMachineClientLiveTest.java @@ -18,28 +18,44 @@ */ package org.jclouds.cloudstack.features; +import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Predicates.equalTo; import static com.google.common.base.Predicates.in; import static com.google.common.base.Predicates.or; import static com.google.common.collect.Iterables.find; import static com.google.common.collect.Iterables.get; +import static com.google.common.collect.Iterables.getFirst; import static com.google.common.collect.Iterables.getOnlyElement; +import static com.google.common.collect.Sets.filter; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; +import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutionException; +import java.util.logging.Logger; +import com.google.common.base.Function; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.common.collect.Maps; import org.jclouds.cloudstack.CloudStackClient; import org.jclouds.cloudstack.domain.AsyncCreateResponse; import org.jclouds.cloudstack.domain.AsyncJob; +import org.jclouds.cloudstack.domain.GuestIPType; import org.jclouds.cloudstack.domain.NIC; import org.jclouds.cloudstack.domain.Network; +import org.jclouds.cloudstack.domain.NetworkOffering; 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.CreateNetworkOptions; import org.jclouds.cloudstack.options.DeployVirtualMachineOptions; +import org.jclouds.cloudstack.options.ListNetworkOfferingsOptions; +import org.jclouds.cloudstack.options.ListNetworksOptions; +import org.jclouds.cloudstack.options.ListTemplatesOptions; import org.jclouds.cloudstack.options.ListVirtualMachinesOptions; import org.jclouds.net.IPSocket; import org.jclouds.predicates.RetryablePredicate; @@ -52,7 +68,8 @@ import com.google.common.base.Throwables; import com.google.common.collect.ComparisonChain; import com.google.common.collect.Ordering; import com.google.common.net.HostSpecifier; -import org.testng.collections.Sets; + +import javax.annotation.Nullable; /** * Tests behavior of {@code VirtualMachineClientLiveTest} @@ -61,6 +78,8 @@ import org.testng.collections.Sets; */ @Test(groups = "live", singleThreaded = true, testName = "VirtualMachineClientLiveTest") public class VirtualMachineClientLiveTest extends BaseCloudStackClientLiveTest { + private final static Logger logger = Logger.getAnonymousLogger(); + private VirtualMachine vm = null; static final Ordering DEFAULT_SIZE_ORDERING = new Ordering() { @@ -111,6 +130,25 @@ public class VirtualMachineClientLiveTest extends BaseCloudStackClientLiveTest { virtualMachineRunning); } + public static VirtualMachine createVirtualMachineInNetworkWithIp( + CloudStackClient client, long templateId, Set networks, Map ipToNetwork, + RetryablePredicate jobComplete, RetryablePredicate virtualMachineRunning) { + + DeployVirtualMachineOptions options = new DeployVirtualMachineOptions(); + + long zoneId = getFirst(networks, null).getZoneId(); + options.networkIds(Iterables.transform(networks, new Function() { + @Override + public Long apply(@Nullable Network network) { + return network.getId(); + } + })); + options.ipToNetworkList(ipToNetwork); + + return createVirtualMachineWithOptionsInZone(options, zoneId, templateId, + client, jobComplete, virtualMachineRunning); + } + public static VirtualMachine createVirtualMachineWithOptionsInZone(DeployVirtualMachineOptions options, long zoneId, long templateId, CloudStackClient client, RetryablePredicate jobComplete, RetryablePredicate virtualMachineRunning) { @@ -150,6 +188,99 @@ public class VirtualMachineClientLiveTest extends BaseCloudStackClientLiveTest { checkVm(vm); } + public void testCreateVirtualMachineWithSpecificIp() throws Exception { + Long templateId = (imageId != null && !"".equals(imageId)) ? new Long(imageId) : null; + Network network = null; + + try { + Template template = getOnlyElement( + client.getTemplateClient().listTemplates(ListTemplatesOptions.Builder.id(templateId))); + logger.info("Using template: " + template); + + Set allSafeNetworksInZone = adminClient.getNetworkClient().listNetworks( + ListNetworksOptions.Builder.zoneId(template.getZoneId()).isSystem(false)); + for(Network net : allSafeNetworksInZone) { + if(net.getName().equals(prefix + "-ip-network")) { + logger.info("Deleting VMs in network: " + net); + + Set machinesInNetwork = adminClient.getVirtualMachineClient().listVirtualMachines( + ListVirtualMachinesOptions.Builder.networkId(net.getId())); + + for(VirtualMachine machine : machinesInNetwork) { + if (machine.getState().equals(VirtualMachine.State.RUNNING)) { + logger.info("Deleting VM: " + machine); + destroyMachine(machine); + } + } + + assert adminJobComplete.apply( + adminClient.getNetworkClient().deleteNetwork(net.getId())) : net; + } + } + + NetworkOffering offering = getFirst( + client.getOfferingClient().listNetworkOfferings( + ListNetworkOfferingsOptions.Builder.zoneId(template.getZoneId()).specifyVLAN(true)), null); + checkNotNull(offering, "No network offering found"); + logger.info("Using network offering: " + offering); + + network = adminClient.getNetworkClient().createNetworkInZone( + template.getZoneId(), offering.getId(), prefix + "-ip-network", "", + CreateNetworkOptions.Builder.startIP("192.168.0.1").endIP("192.168.0.5") + .netmask("255.255.255.0").gateway("192.168.0.1").vlan("21")); + logger.info("Created network: " + network); + + Network requiredNetwork = getOnlyElement(filter(adminClient.getNetworkClient().listNetworks( + ListNetworksOptions.Builder.zoneId(template.getZoneId())), new Predicate() { + @Override + public boolean apply(@Nullable Network network) { + return network.isDefault() && + network.getGuestIPType() == GuestIPType.VIRTUAL && + network.getNetworkOfferingId() == 6 && + network.getId() == 204; + } + })); + logger.info("Required network: " + requiredNetwork); + + String ipAddress = "192.168.0.4"; + + Map ipToNetwork = Maps.newHashMap(); + ipToNetwork.put(ipAddress, network.getId()); + + vm = createVirtualMachineInNetworkWithIp( + adminClient, templateId, ImmutableSet.of(requiredNetwork, network), + ipToNetwork, adminJobComplete, adminVirtualMachineRunning); + logger.info("Created VM: " + vm); + + boolean hasStaticIpNic = false; + for(NIC nic : vm.getNICs()) { + if (nic.getNetworkId() == network.getId()) { + hasStaticIpNic = true; + assertEquals(nic.getIPAddress(), ipAddress); + } + } + assert hasStaticIpNic; + checkVm(vm); + + } finally { + if (vm != null) { + destroyMachine(vm); + vm = null; + } + if (network != null) { + long jobId = adminClient.getNetworkClient().deleteNetwork(network.getId()); + adminJobComplete.apply(jobId); + network = null; + } + } + } + + private void destroyMachine(VirtualMachine virtualMachine) { + assert adminJobComplete.apply( + adminClient.getVirtualMachineClient().destroyVirtualMachine(virtualMachine.getId())) : virtualMachine; + assert adminVirtualMachineDestroyed.apply(virtualMachine); + } + private void conditionallyCheckSSH() { password = vm.getPassword(); assert HostSpecifier.isValid(vm.getIPAddress()); @@ -194,9 +325,8 @@ public class VirtualMachineClientLiveTest extends BaseCloudStackClientLiveTest { @AfterGroups(groups = "live") protected void tearDown() { if (vm != null) { - assert jobComplete.apply(client.getVirtualMachineClient().stopVirtualMachine(vm.getId())) : vm; - assert jobComplete.apply(client.getVirtualMachineClient().destroyVirtualMachine(vm.getId())) : vm; - assert virtualMachineDestroyed.apply(vm); + destroyMachine(vm); + vm = null; } super.tearDown(); }