blob: d08911c7d2aecce6b5687f1937a8730b09f53f16 [file] [log] [blame]
/**
* 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.vcloud.director.v1_5.internal;
import static org.jclouds.vcloud.director.v1_5.VCloudDirectorLiveTestConstants.ENTITY_NON_NULL;
import static org.jclouds.vcloud.director.v1_5.VCloudDirectorLiveTestConstants.REF_REQ_LIVE;
import static org.jclouds.vcloud.director.v1_5.VCloudDirectorLiveTestConstants.TASK_COMPLETE_TIMELY;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
import java.net.URI;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Properties;
import java.util.Random;
import java.util.Set;
import javax.annotation.Resource;
import javax.inject.Inject;
import org.jclouds.compute.BaseVersionedServiceLiveTest;
import org.jclouds.date.DateService;
import org.jclouds.logging.Logger;
import org.jclouds.logging.log4j.config.Log4JLoggingModule;
import org.jclouds.predicates.RetryablePredicate;
import org.jclouds.rest.RestContext;
import org.jclouds.rest.RestContextFactory;
import org.jclouds.sshj.config.SshjSshClientModule;
import org.jclouds.vcloud.director.testng.FormatApiResultsListener;
import org.jclouds.vcloud.director.v1_5.VCloudDirectorContext;
import org.jclouds.vcloud.director.v1_5.VCloudDirectorException;
import org.jclouds.vcloud.director.v1_5.VCloudDirectorMediaType;
import org.jclouds.vcloud.director.v1_5.admin.VCloudDirectorAdminAsyncClient;
import org.jclouds.vcloud.director.v1_5.admin.VCloudDirectorAdminClient;
import org.jclouds.vcloud.director.v1_5.domain.InstantiateVAppTemplateParams;
import org.jclouds.vcloud.director.v1_5.domain.InstantiationParams;
import org.jclouds.vcloud.director.v1_5.domain.Link;
import org.jclouds.vcloud.director.v1_5.domain.Network;
import org.jclouds.vcloud.director.v1_5.domain.NetworkConfigSection;
import org.jclouds.vcloud.director.v1_5.domain.NetworkConfiguration;
import org.jclouds.vcloud.director.v1_5.domain.Org;
import org.jclouds.vcloud.director.v1_5.domain.Reference;
import org.jclouds.vcloud.director.v1_5.domain.ResourceEntityType.Status;
import org.jclouds.vcloud.director.v1_5.domain.Role.DefaultRoles;
import org.jclouds.vcloud.director.v1_5.domain.RoleReferences;
import org.jclouds.vcloud.director.v1_5.domain.Session;
import org.jclouds.vcloud.director.v1_5.domain.Task;
import org.jclouds.vcloud.director.v1_5.domain.UndeployVAppParams;
import org.jclouds.vcloud.director.v1_5.domain.User;
import org.jclouds.vcloud.director.v1_5.domain.VApp;
import org.jclouds.vcloud.director.v1_5.domain.VAppNetworkConfiguration;
import org.jclouds.vcloud.director.v1_5.domain.VAppTemplate;
import org.jclouds.vcloud.director.v1_5.domain.Vdc;
import org.jclouds.vcloud.director.v1_5.features.TaskClient;
import org.jclouds.vcloud.director.v1_5.features.VAppClient;
import org.jclouds.vcloud.director.v1_5.features.VAppTemplateClient;
import org.jclouds.vcloud.director.v1_5.features.VdcClient;
import org.jclouds.vcloud.director.v1_5.predicates.ReferencePredicates;
import org.jclouds.vcloud.director.v1_5.predicates.TaskStatusEquals;
import org.jclouds.vcloud.director.v1_5.predicates.TaskSuccess;
import org.jclouds.vcloud.director.v1_5.user.VCloudDirectorAsyncClient;
import org.jclouds.vcloud.director.v1_5.user.VCloudDirectorClient;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.inject.Guice;
import com.google.inject.Module;
/**
* Tests behavior of {@link VCloudDirectorClient} and acts as parent for other client live tests.
*
* @author Adrian Cole
* @author grkvlt@apache.org
*/
@Listeners(FormatApiResultsListener.class)
@Test(groups = "live")
public abstract class BaseVCloudDirectorClientLiveTest extends BaseVersionedServiceLiveTest {
@Resource
protected Logger logger = Logger.CONSOLE;
protected static final long TASK_TIMEOUT_SECONDS = 100L;
protected static final long LONG_TASK_TIMEOUT_SECONDS = 300L;
public static final String VAPP = "vApp";
public static final String VAPP_TEMPLATE = "vAppTemplate";
public static final String VDC = "vdc";
public static final int REQUIRED_ADMIN_VM_QUOTA = 0;
public static final int REQUIRED_USER_VM_QUOTA = 0;
public Predicate<Task> retryTaskSuccess;
public Predicate<Task> retryTaskSuccessLong;
protected RestContext<VCloudDirectorAdminClient, VCloudDirectorAdminAsyncClient> adminContext;
protected RestContext<VCloudDirectorClient, VCloudDirectorAsyncClient> context; // FIXME: rename to userContext?
protected Session adminSession;
protected Session session;
protected String catalogId;
protected URI vAppTemplateURI;
protected URI mediaURI;
protected URI networkURI;
protected URI vdcURI;
protected URI userURI;
protected final Set<String> vAppNames = Sets.newLinkedHashSet();
protected static final Random random = new Random();
protected BaseVCloudDirectorClientLiveTest() {
provider = "vcloud-director";
}
protected DateService dateService;
@BeforeClass(alwaysRun = true)
protected void setupDateService() {
dateService = Guice.createInjector().getInstance(DateService.class);
assertNotNull(dateService);
}
// NOTE Implement as required to populate xxxClient fields, or NOP
protected abstract void setupRequiredClients() throws Exception;
@Inject
protected void initTaskSuccess(TaskSuccess taskSuccess) {
retryTaskSuccess = new RetryablePredicate<Task>(taskSuccess, TASK_TIMEOUT_SECONDS * 1000L);
}
@Inject
protected void initTaskSuccessLong(TaskSuccess taskSuccess) {
retryTaskSuccessLong = new RetryablePredicate<Task>(taskSuccess, LONG_TASK_TIMEOUT_SECONDS * 1000L);
}
@BeforeClass(alwaysRun = true)
protected void setupContext() throws Exception {
setupCredentials();
Properties overrides = setupProperties();
VCloudDirectorContext rootContext = VCloudDirectorContext.class.cast(
new RestContextFactory().createContext(provider, identity, credential, ImmutableSet.<Module> of(
new Log4JLoggingModule(), new SshjSshClientModule()), overrides));
adminContext = rootContext.getAdminContext();
rootContext.utils().injector().injectMembers(this);
Reference orgRef = Iterables.getFirst(rootContext.getApi().getOrgClient().getOrgList().getOrgs(), null)
.toAdminReference(endpoint);
assertNotNull(orgRef, String.format(REF_REQ_LIVE, "admin org"));
String adminIdentity = "testAdmin"+new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
String adminCredential = "testAdminPassword";
rootContext.getAdminContext().getApi().getUserClient().createUser(orgRef.getHref(), User.builder()
.name(adminIdentity)
.password(adminCredential)
.description("test user with user-level privileges") //TODO desc
.role(getRoleReferenceFor(DefaultRoles.ORG_ADMIN))
.deployedVmQuota(REQUIRED_ADMIN_VM_QUOTA)
.isEnabled(true)
.build());
rootContext.close(); rootContext = null;
adminContext = VCloudDirectorContext.class.cast(new RestContextFactory().createContext(provider, adminIdentity, adminCredential, ImmutableSet.<Module> of(
new Log4JLoggingModule(), new SshjSshClientModule()), overrides)).getAdminContext();
adminSession = adminContext.getApi().getCurrentSession();
adminContext.utils().injector().injectMembers(this);
String userIdentity = "test"+new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
String userCredential = "testPassword";
adminContext.getApi().getUserClient().createUser(orgRef.getHref(), User.builder()
.name(userIdentity)
.password(userCredential)
.description("test user with user-level privileges")
.role(getRoleReferenceFor(DefaultRoles.USER))
.deployedVmQuota(REQUIRED_USER_VM_QUOTA)
.isEnabled(true)
.build());
context = new RestContextFactory().createContext(provider, userIdentity, userCredential, ImmutableSet.<Module> of(
new Log4JLoggingModule(), new SshjSshClientModule()), overrides);
session = context.getApi().getCurrentSession();
context.utils().injector().injectMembers(this);
initTestParametersFromPropertiesOrLazyDiscover();
setupRequiredClients();
}
public Reference getRoleReferenceFor(String name) {
RoleReferences roles = adminContext.getApi().getQueryClient().roleReferencesQueryAll();
// wrapped in a builder to strip out unwanted xml cruft that the api chokes on
return Reference.builder().fromReference(Iterables.find(roles.getReferences(), ReferencePredicates.nameEquals(name))).build();
}
public User randomTestUser(String prefix) {
return randomTestUser(prefix, getRoleReferenceFor(DefaultRoles.USER));
}
public User randomTestUser(String prefix, Reference role) {
return User.builder()
.name(name(prefix)+random.nextInt(999999))
.fullName("testFullName")
.emailAddress("test@test.com")
.telephone("555-1234")
.isEnabled(false)
.im("testIM")
.isAlertEnabled(false)
.alertEmailPrefix("testPrefix")
.alertEmail("testAlert@test.com")
.isExternal(false)
.isGroupRole(false)
.role(role)
.password("password")
.build();
}
// TODO change properties to URI, not id
protected void initTestParametersFromPropertiesOrLazyDiscover() {
catalogId = Strings.emptyToNull(System.getProperty("test." + provider + ".catalog-id"));
String vAppTemplateId = Strings.emptyToNull(System.getProperty("test." + provider + ".vapptemplate-id"));
if (vAppTemplateId != null)
vAppTemplateURI = URI.create(endpoint + "/vAppTemplate/" + vAppTemplateId);
String vdcId = Strings.emptyToNull(System.getProperty("test." + provider + ".vdc-id"));
if (vdcId != null)
vdcURI = URI.create(endpoint + "/vdc/" + vdcId);
String mediaId = Strings.emptyToNull(System.getProperty("test." + provider + ".media-id"));
if (mediaId != null)
mediaURI = URI.create(endpoint + "/media/" + mediaId);
String networkId = Strings.emptyToNull(System.getProperty("test." + provider + ".network-id"));
if (networkId != null)
networkURI = URI.create(endpoint + "/network/" + networkId);
String userId = Strings.emptyToNull(System.getProperty("test." + provider + ".user-id"));
if (userId != null)
userURI = URI.create(endpoint + "/admin/user/" + userId);
if (Iterables.any(Lists.newArrayList(vAppTemplateURI, networkURI, vdcURI), Predicates.isNull())) {
Org thisOrg = context.getApi().getOrgClient().getOrg(
Iterables.find(context.getApi().getOrgClient().getOrgList().getOrgs(),
ReferencePredicates.<Reference> nameEquals(session.getOrg())).getHref());
if (vdcURI == null)
vdcURI = Iterables.find(thisOrg.getLinks(),
ReferencePredicates.<Link> typeEquals(VCloudDirectorMediaType.VDC)).getHref();
if (networkURI == null)
networkURI = Iterables.find(thisOrg.getLinks(),
ReferencePredicates.<Link> typeEquals(VCloudDirectorMediaType.ORG_NETWORK)).getHref();
// FIXME the URI should be opaque
if (Strings.isNullOrEmpty(catalogId)) {
String uri = Iterables.find(thisOrg.getLinks(),
ReferencePredicates.<Link> typeEquals(VCloudDirectorMediaType.CATALOG)).getHref().toASCIIString();
catalogId = Iterables.getLast(Splitter.on('/').split(uri));
}
}
}
@AfterClass(alwaysRun = true)
protected void tearDown() {
if (context != null)
context.close();
if (adminContext != null)
adminContext.close();
}
public URI toAdminUri(Reference ref) {
return toAdminUri(ref.getHref());
}
public URI toAdminUri(URI uri) {
return Reference.builder().href(uri).build().toAdminReference(endpoint).getHref();
}
protected void assertTaskSucceeds(Task task) {
assertTrue(retryTaskSuccess.apply(task), String.format(TASK_COMPLETE_TIMELY, task));
}
protected void assertTaskSucceedsLong(Task task) {
assertTrue(retryTaskSuccessLong.apply(task), String.format(TASK_COMPLETE_TIMELY, task));
}
protected void assertTaskStatusEventually(Task task, String expectedStatus, Collection<String> failingStatuses) {
TaskClient taskClient = context.getApi().getTaskClient();
TaskStatusEquals predicate = new TaskStatusEquals(taskClient, expectedStatus, failingStatuses);
RetryablePredicate<Task> retryablePredicate = new RetryablePredicate<Task>(predicate, TASK_TIMEOUT_SECONDS * 1000L);
assertTrue(retryablePredicate.apply(task), "Task must enter status "+expectedStatus);
}
protected void assertTaskDoneEventually(Task task) {
TaskClient taskClient = context.getApi().getTaskClient();
TaskStatusEquals predicate = new TaskStatusEquals(
taskClient,
ImmutableSet.of(Task.Status.ABORTED, Task.Status.CANCELED, Task.Status.ERROR, Task.Status.SUCCESS),
Collections.<String>emptySet());
RetryablePredicate<Task> retryablePredicate = new RetryablePredicate<Task>(predicate, LONG_TASK_TIMEOUT_SECONDS * 1000L);
assertTrue(retryablePredicate.apply(task), "Task must be done");
}
/**
* Instantiate a {@link VApp} in a {@link Vdc} using the {@link VAppTemplate} we have configured for the tests.
*
* @return the VApp that is being instantiated
*/
protected VApp instantiateVApp() {
return instantiateVApp(name("test-vapp-"));
}
protected VApp instantiateVApp(String name) {
InstantiateVAppTemplateParams instantiate = InstantiateVAppTemplateParams.builder()
.name(name)
.notDeploy()
.notPowerOn()
.description("Test VApp")
.instantiationParams(instantiationParams())
.source(Reference.builder().href(vAppTemplateURI).build())
.build();
VdcClient vdcClient = context.getApi().getVdcClient();
VApp vAppInstantiated = vdcClient.instantiateVApp(vdcURI, instantiate);
assertNotNull(vAppInstantiated, String.format(ENTITY_NON_NULL, VAPP));
Task instantiationTask = Iterables.getFirst(vAppInstantiated.getTasks(), null);
if (instantiationTask != null) assertTaskSucceedsLong(instantiationTask);
// Save VApp name for cleanUp
vAppNames.add(name);
return vAppInstantiated;
}
/** Build an {@link InstantiationParams} object. */
private InstantiationParams instantiationParams() {
InstantiationParams instantiationParams = InstantiationParams.builder()
.sections(ImmutableSet.of(networkConfigSection()))
.build();
return instantiationParams;
}
/** Build a {@link NetworkConfigSection} object. */
private NetworkConfigSection networkConfigSection() {
NetworkConfigSection networkConfigSection = NetworkConfigSection.builder()
.info("Configuration parameters for logical networks")
.networkConfigs(
ImmutableSet.of(
VAppNetworkConfiguration.builder()
.networkName("vAppNetwork")
.configuration(networkConfiguration())
.build()))
.build();
return networkConfigSection;
}
/** Build a {@link NetworkConfiguration} object. */
private NetworkConfiguration networkConfiguration() {
Vdc vdc = context.getApi().getVdcClient().getVdc(vdcURI);
assertNotNull(vdc, String.format(ENTITY_NON_NULL, VDC));
Set<Reference> networks = vdc.getAvailableNetworks();
// Look up the network in the Vdc with the id configured for the tests
Optional<Reference> parentNetwork = Iterables.tryFind(networks, new Predicate<Reference>() {
@Override
public boolean apply(Reference reference) {
return reference.getHref().equals(networkURI);
}
});
// Check we actually found a network reference
if (!parentNetwork.isPresent()) {
fail(String.format("Could not find network %s in vdc", networkURI.toASCIIString()));
}
// Build the configuration object
NetworkConfiguration networkConfiguration = NetworkConfiguration.builder()
.parentNetwork(parentNetwork.get())
.fenceMode(Network.FenceMode.ISOLATED)
.build();
return networkConfiguration;
}
protected void cleanUpVAppTemplate(VAppTemplate vAppTemplate) {
VAppTemplateClient vappTemplateClient = context.getApi().getVAppTemplateClient();
Task task = vappTemplateClient.deleteVappTemplate(vAppTemplate.getHref());
assertTaskSucceeds(task);
}
protected void cleanUpVApp(VApp vApp) {
cleanUpVApp(vApp.getHref());
}
// TODO code tidy for cleanUpVApp? Seems extremely verbose!
protected void cleanUpVApp(URI vAppURI) {
VAppClient vAppClient = context.getApi().getVAppClient();
VApp vApp;
try {
vApp = vAppClient.getVApp(vAppURI); // Refresh
logger.debug("Deleting VApp %s (%s)", vApp.getName(), vAppURI.getPath());
} catch (VCloudDirectorException e) {
// Presumably vApp has already been deleted. Ignore.
logger.info("Cannot find VApp at %s", vAppURI.getPath());
return;
}
// Wait for busy tasks to complete (don't care if it's failed or successful)
// Otherwise, get error on delete "entity is busy completing an operation.
if (vApp.getTasks() != null) {
for (Task task : vApp.getTasks()) {
assertTaskDoneEventually(task);
}
}
// Shutdown and power off the VApp if necessary
if (vApp.getStatus().equals(Status.POWERED_ON.getValue())) {
try {
Task shutdownTask = vAppClient.shutdown(vAppURI);
retryTaskSuccess.apply(shutdownTask);
} catch (Exception e) {
// keep going; cleanup as much as possible
logger.warn(e, "Continuing cleanup after error shutting down VApp %s", vApp.getName());
}
}
// Undeploy the VApp if necessary
if (vApp.isDeployed()) {
try {
UndeployVAppParams params = UndeployVAppParams.builder().build();
Task undeployTask = vAppClient.undeploy(vAppURI, params);
retryTaskSuccess.apply(undeployTask);
} catch (Exception e) {
// keep going; cleanup as much as possible
logger.warn(e, "Continuing cleanup after error undeploying VApp %s", vApp.getName());
}
}
try {
Task task = vAppClient.deleteVApp(vAppURI);
assertTaskSucceeds(task);
vAppNames.remove(vApp.getName());
logger.info("Deleted VApp %s", vApp.getName());
} catch (Exception e) {
try {
vApp = vAppClient.getVApp(vAppURI); // Refresh
} catch (Exception e2) {
// Ignore
}
logger.warn(e, "Deleting VApp %s failed (%s)", vApp.getName(), vAppURI.getPath());
}
}
public static String name(String prefix) {
return prefix + Integer.toString(random.nextInt(Integer.MAX_VALUE));
}
}