/** * 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.ibm.smartcloud; import static com.google.common.collect.Iterables.filter; import static org.jclouds.ibm.smartcloud.options.CreateInstanceOptions.Builder.authorizePublicKey; import static org.jclouds.ibm.smartcloud.options.CreateInstanceOptions.Builder.secondaryIP; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertTrue; import java.io.IOException; import java.util.NoSuchElementException; import java.util.Set; import java.util.concurrent.TimeUnit; import org.jclouds.compute.domain.ExecResponse; import org.jclouds.domain.Credentials; import org.jclouds.http.HttpResponseException; import org.jclouds.http.handlers.BackoffLimitedRetryHandler; import org.jclouds.ibm.smartcloud.domain.Address; import org.jclouds.ibm.smartcloud.domain.Image; import org.jclouds.ibm.smartcloud.domain.Instance; import org.jclouds.ibm.smartcloud.domain.Instance.Software; import org.jclouds.ibm.smartcloud.domain.InstanceType; import org.jclouds.ibm.smartcloud.domain.Key; import org.jclouds.ibm.smartcloud.domain.Location; import org.jclouds.ibm.smartcloud.domain.Offering; import org.jclouds.ibm.smartcloud.domain.StorageOffering; import org.jclouds.ibm.smartcloud.domain.StorageOffering.Format; import org.jclouds.ibm.smartcloud.domain.Volume; import org.jclouds.ibm.smartcloud.predicates.AddressFree; import org.jclouds.ibm.smartcloud.predicates.InstanceActive; import org.jclouds.ibm.smartcloud.predicates.InstanceActiveOrFailed; import org.jclouds.ibm.smartcloud.predicates.InstanceRemovedOrNotFound; import org.jclouds.ibm.smartcloud.predicates.VolumeUnmounted; import org.jclouds.net.IPSocket; import org.jclouds.predicates.InetSocketAddressConnect; import org.jclouds.predicates.RetryablePredicate; import org.jclouds.ssh.SshClient; import org.jclouds.ssh.SshException; import org.jclouds.sshj.SshjSshClient; import org.testng.annotations.AfterTest; import org.testng.annotations.Test; import com.google.common.base.Predicate; import com.google.common.collect.ComparisonChain; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Ordering; import com.google.common.collect.Sets; /** * Tests behavior of {@code IBMSmartCloudClient} * * @author Adrian Cole */ @Test(groups = "live", singleThreaded = true, testName = "IBMSmartCloudClientLiveTest") public class IBMSmartCloudClientLiveTest extends BaseIBMSmartCloudClientLiveTest { private static final String OS = "SUSE Linux Enterprise Server"; private static final String VERSION = "11 SP1"; private static final String PLATFORM = OS + "/" + VERSION; private static final ImmutableSet<Software> SOFTWARE = ImmutableSet.<Software> of(new Software(OS, "OS", VERSION)); private static String FORMAT = "EXT3"; private Location location; private Address ip; private Key key; private Volume volume; private Instance instance2; private Instance instance; private InstanceType instanceType; private Image image; private StorageOffering cheapestStorage; private static final String GROUP = System.getProperty("user.name"); @Test public void testGetLocation() throws Exception { Set<? extends Location> response = connection.listLocations(); assertNotNull(response); if (response.size() > 0) { location = Iterables.find(response, new Predicate<Location>() { @Override public boolean apply(Location arg0) { // ontario is the least used return "101".equals(arg0.getId()); } }); assertEquals(connection.getLocation(location.getId()).getId(), location.getId()); } } @Test(dependsOnMethods = "testGetLocation") public void testAddPublicKey() throws Exception { try { connection.addPublicKey(GROUP, keyPair.get("public")); key = connection.getKey(GROUP); try { assert key.getInstanceIds().equals(ImmutableSet.<String> of()) : key; } catch (AssertionError e) { // inconsistency in the key api when recreating a key // http://www-180.ibm.com/cloud/enterprise/beta/ram/community/discussionTopic.faces?guid={DA689AEE-783C-6FE7-6F9F-DFEE9763F806}&v=1&fid=1068&tid=1528 } } catch (IllegalStateException e) { // must have been found connection.updatePublicKey(GROUP, keyPair.get("public")); key = connection.getKey(GROUP); for (String instanceId : key.getInstanceIds()) { Instance instance = connection.getInstance(instanceId); if (instance.getStatus() == Instance.Status.FAILED || instance.getStatus() == Instance.Status.ACTIVE) { killInstance(instance.getId()); } } } assertEquals(key.getName(), GROUP); assert keyPair.get("public").indexOf(key.getKeyMaterial()) > 0; assertNotNull(key.getLastModifiedTime()); } @Test(dependsOnMethods = "resolveImageAndInstanceType") public void testAllocateIpAddress() throws Exception { Offering offering = Iterables.find(connection.listAddressOfferings(), new Predicate<Offering>() { @Override public boolean apply(Offering arg0) { return arg0.getLocation().equals(location.getId()); } }); try { ip = connection.allocateAddressInLocation(location.getId(), offering.getId()); System.out.println(ip); assertEquals(ip.getIP(), null); // wait up to 30 seconds for this to become "free" new RetryablePredicate<Address>(new AddressFree(connection), 30, 2, TimeUnit.SECONDS).apply(ip); refreshIpAndReturnAllAddresses(); assertEquals(ip.getInstanceId(), null); } catch (IllegalStateException e) { if (HttpResponseException.class.cast(e.getCause()).getResponse().getStatusCode() == 409) { ip = Iterables.find(connection.listAddresses(), new Predicate<Address>() { @Override public boolean apply(Address input) { return input.getState() == Address.State.FREE; } }); } else { throw e; } } assertEquals(ip.getInstanceId(), null); assertEquals(ip.getLocation(), location.getId()); Set<? extends Address> allAddresses = refreshIpAndReturnAllAddresses(); assert (allAddresses.contains(ip)) : String.format("ip %s not in %s", ip, allAddresses); } @Test(dependsOnMethods = "testGetLocation") public void testResolveVolumeOffering() throws Exception { Ordering<StorageOffering> cheapestOrdering = new Ordering<StorageOffering>() { public int compare(StorageOffering left, StorageOffering right) { return ComparisonChain.start().compare(left.getPrice().getRate(), right.getPrice().getRate()).result(); } }.reverse(); Iterable<? extends StorageOffering> storageOfferingsThatAreInOurLocationAndCorrectFormat = filter(connection .listStorageOfferings(), new Predicate<StorageOffering>() { @Override public boolean apply(StorageOffering arg0) { return arg0.getLocation().equals(location.getId()) && Iterables.any(arg0.getFormats(), new Predicate<StorageOffering.Format>() { @Override public boolean apply(Format arg0) { return arg0.getId().equals(FORMAT); } }); } }); cheapestStorage = cheapestOrdering.max(storageOfferingsThatAreInOurLocationAndCorrectFormat); System.out.println(cheapestStorage); } @Test(dependsOnMethods = "testResolveVolumeOffering") public void testCreateVolume() throws Exception { try { volume = connection.createVolumeInLocation(location.getId(), GROUP, FORMAT, cheapestStorage.getName(), cheapestStorage.getId()); // wait up to 5 minutes for this to become "unmounted" assert new RetryablePredicate<Volume>(new VolumeUnmounted(connection), 300, 5, TimeUnit.SECONDS).apply(volume); } catch (IllegalStateException e) { int code = HttpResponseException.class.cast(e.getCause()).getResponse().getStatusCode(); if (code == 409 || code == 500) { Set<? extends Volume> volumes = connection.listVolumes(); try { volume = Iterables.find(volumes, new Predicate<Volume>() { @Override public boolean apply(Volume input) { return input.getState() == Volume.State.UNMOUNTED; } }); } catch (NoSuchElementException ex) { killInstance(GROUP + 1); } } else { throw e; } } assertEquals(volume.getInstanceId(), null); assertEquals(volume.getLocation(), location.getId()); final String id = volume.getId(); Set<? extends Volume> allVolumes = connection.listVolumes(); // refresh volume as it may have been just created volume = Iterables.find(allVolumes, new Predicate<Volume>() { @Override public boolean apply(Volume input) { return input.getId().equals(id); } }); assert (allVolumes.contains(volume)) : String.format("volume %s not in %s", volume, volume); } @Test(dependsOnMethods = "testGetLocation") public void resolveImageAndInstanceType() throws Exception { Iterable<? extends Image> imagesThatAreInOurLocationAndNotBYOL = filter(connection.listImages(), new Predicate<Image>() { @Override public boolean apply(Image arg0) { return arg0.getLocation().equals(location.getId()) && arg0.getPlatform().equals(PLATFORM) && arg0.getName().startsWith(OS); } }); Ordering<InstanceType> cheapestOrdering = new Ordering<InstanceType>() { public int compare(InstanceType left, InstanceType right) { return ComparisonChain.start().compare(left.getPrice().getRate(), right.getPrice().getRate()).result(); } }.reverse(); Set<InstanceType> instanceTypes = Sets.newLinkedHashSet(); for (Image image : imagesThatAreInOurLocationAndNotBYOL) Iterables.addAll(instanceTypes, image.getSupportedInstanceTypes()); instanceType = cheapestOrdering.max(instanceTypes); final InstanceType cheapestInstanceType = instanceType; System.err.println(cheapestInstanceType); image = Iterables.find(imagesThatAreInOurLocationAndNotBYOL, new Predicate<Image>() { @Override public boolean apply(Image arg0) { return arg0.getSupportedInstanceTypes().contains(cheapestInstanceType); } }); System.err.println(image); System.err.println(connection.getManifestOfImage(image.getId())); } @Test(dependsOnMethods = { "testAddPublicKey", "resolveImageAndInstanceType" }) public void testCreateInstance() throws Exception { killInstance(GROUP); instance = connection.createInstanceInLocation(location.getId(), GROUP, image.getId(), instanceType.getId(), authorizePublicKey(key.getName()).isMiniEphemeral(true)); assertBeginState(instance, GROUP); assertIpHostNullAndStatusNEW(instance); blockUntilRunning(instance); instance = assertRunning(instance, GROUP); sshAndDf(new IPSocket(instance.getPrimaryIP().getIP(), 22), new Credentials("idcuser", key.getKeyMaterial())); } private void killInstance(final String nameToKill) { Set<? extends Instance> instances = connection.listInstances(); try { Instance instance = Iterables.find(instances, new Predicate<Instance>() { @Override public boolean apply(Instance input) { return input.getName().equals(nameToKill); } }); if (instance.getStatus() != Instance.Status.DEPROVISIONING && instance.getStatus() != Instance.Status.DEPROVISION_PENDING) { System.out.println("deleting instance: " + instance); int timeout = (instance.getStatus() == Instance.Status.NEW || instance.getStatus() == Instance.Status.PROVISIONING) ? 300 : 30; assert new RetryablePredicate<Instance>(new InstanceActiveOrFailed(connection), timeout, 2, TimeUnit.SECONDS).apply(instance) : instance; connection.deleteInstance(instance.getId()); } assert new RetryablePredicate<Instance>(new InstanceRemovedOrNotFound(connection), 120, 2, TimeUnit.SECONDS) .apply(instance) : instance; } catch (NoSuchElementException ex) { } } private Instance assertRunning(Instance instance, String name) throws AssertionError { instance = connection.getInstance(instance.getId()); try { assertIpHostAndStatusACTIVE(instance); assertConsistent(instance, name); } catch (NullPointerException e) { System.err.println(instance); throw e; } catch (AssertionError e) { System.err.println(instance); throw e; } System.err.println("RUNNING: " + instance); return instance; } private void blockUntilRunning(Instance instance) { long start = System.currentTimeMillis(); assert new RetryablePredicate<Instance>(new InstanceActive(connection), 15 * 60 * 1000).apply(instance) : connection .getInstance(instance.getId()); System.out.println(((System.currentTimeMillis() - start) / 1000) + " seconds"); } private void assertBeginState(Instance instance, String name) throws AssertionError { try { assertConsistent(instance, name); } catch (NullPointerException e) { System.err.println(instance); throw e; } catch (AssertionError e) { killInstance(instance.getId()); throw e; } } private void assertConsistent(Instance instance, String name) { assert (instance.getId() != null) : instance; assertEquals(instance.getName(), name); assertEquals(instance.getInstanceType(), instanceType.getId()); assertEquals(instance.getLocation(), location.getId()); assertEquals(instance.getImageId(), image.getId()); assertEquals(instance.getSoftware(), SOFTWARE); assertEquals(instance.getKeyName(), key.getName()); assertNotNull(instance.getLaunchTime()); assertNotNull(instance.getExpirationTime()); assertEquals(instance.getOwner(), identity); assertEquals(instance.getProductCodes(), ImmutableSet.<String> of()); assertEquals(instance.getRequestName(), name); assertNotNull(instance.getRequestId()); } private void assertIpHostNullAndStatusNEW(Instance instance) { assertEquals(instance.getPrimaryIP(), null); assertEquals(instance.getStatus(), Instance.Status.NEW); } private void assertIpHostAndStatusNEW(Instance instance) { assertNotNull(instance.getPrimaryIP()); assertEquals(instance.getStatus(), Instance.Status.NEW); } private void assertIpHostAndStatusACTIVE(Instance instance) { assertNotNull(instance.getPrimaryIP()); assertEquals(instance.getStatus(), Instance.Status.ACTIVE); } @Test(dependsOnMethods = { "testAddPublicKey", "testAllocateIpAddress", "testCreateVolume", "resolveImageAndInstanceType" }) public void testCreateInstanceWithIpAndVolume() throws Exception { String name = GROUP + "1"; killInstance(name); instance2 = connection.createInstanceInLocation(location.getId(), name, image.getId(), instanceType.getId(), secondaryIP(ip.getId()).isMiniEphemeral(true).authorizePublicKey(key.getName()).mountVolume( volume.getId(), "/mnt")); assertBeginState(instance2, name); assertIpHostAndStatusNEW(instance2); blockUntilRunning(instance2); instance2 = assertRunning(instance2, name); volume = connection.getVolume(volume.getId()); assertEquals(volume.getInstanceId(), instance2.getId()); refreshIpAndReturnAllAddresses(); assertEquals(ip.getInstanceId(), instance2.getId()); assertEquals(ip.getIP(), instance2.getPrimaryIP().getIP()); sshAndDf(new IPSocket(instance2.getPrimaryIP().getIP(), 22), new Credentials("idcuser", keyPair.get("private"))); } private Set<? extends Address> refreshIpAndReturnAllAddresses() { Set<? extends Address> allAddresses = connection.listAddresses(); final String id = ip.getId(); // refresh address as it may have been just created ip = Iterables.find(allAddresses, new Predicate<Address>() { @Override public boolean apply(Address input) { return input.getId().equals(id); } }); return allAddresses; } @AfterTest(groups = { "live" }) void tearDown() { if (volume != null) try { connection.deleteVolume(volume.getId()); } catch (Exception e) { } if (ip != null) try { connection.releaseAddress(ip.getId()); } catch (Exception e) { } if (key != null) try { connection.deleteKey(key.getName()); } catch (Exception e) { } if (instance != null) try { connection.deleteInstance(instance.getId()); } catch (Exception e) { } if (instance2 != null) try { connection.deleteInstance(instance2.getId()); } catch (Exception e) { } } private void sshAndDf(IPSocket socket, Credentials credentials) throws IOException { for (int i = 0; i < 5; i++) {// retry loop TODO replace with predicate. try { _sshAndDf(socket, credentials); return; } catch (SshException e) { try { Thread.sleep(10 * 1000); } catch (InterruptedException e1) { } continue; } } } private void _sshAndDf(IPSocket socket, Credentials credentials) { RetryablePredicate<IPSocket> socketOpen = new RetryablePredicate<IPSocket>(new InetSocketAddressConnect(), 180, 5, TimeUnit.SECONDS); socketOpen.apply(socket); SshClient ssh = new SshjSshClient(new BackoffLimitedRetryHandler(), socket, 60000, credentials.identity, null, credentials.credential.getBytes()); try { ssh.connect(); ExecResponse hello = ssh.exec("echo hello"); assertEquals(hello.getOutput().trim(), "hello"); ExecResponse exec = ssh.exec("df"); assertTrue(exec.getOutput().contains("Filesystem"), "The output should've contained filesystem information, but it didn't. Output: " + exec); } finally { if (ssh != null) ssh.disconnect(); } } }