Issue 494:vCloud director 1.0.1 fixes guest customization bug

This commit is contained in:
Adrian Cole 2011-03-05 17:06:05 -05:00
parent 59c0dbb6cd
commit d494c8a9f3
8 changed files with 107 additions and 39 deletions

View File

@ -56,9 +56,9 @@ public class GuestCustomizationSectionHandler extends ParseSax.HandlerWithResult
public GuestCustomizationSection getResult() {
GuestCustomizationSection system = new GuestCustomizationSection(guest.getType(), guest.getHref(), info, enabled,
changeSid, virtualMachineId, joinDomainEnabled, useOrgSettings, domainName, domainUserName,
domainUserPassword, adminPasswordEnabled, adminPasswordAuto, adminPassword, resetPasswordRequired,
customizationScript, computerName, edit);
changeSid, virtualMachineId, joinDomainEnabled, useOrgSettings, domainName, domainUserName,
domainUserPassword, adminPasswordEnabled, adminPasswordAuto, adminPassword, resetPasswordRequired,
customizationScript, computerName, edit);
this.guest = null;
this.info = null;
this.edit = null;
@ -81,6 +81,7 @@ public class GuestCustomizationSectionHandler extends ParseSax.HandlerWithResult
public void startElement(String uri, String localName, String qName, Attributes attrs) {
Map<String, String> attributes = cleanseAttributes(attrs);
this.currentText = new StringBuilder();
if (qName.endsWith("GuestCustomizationSection")) {
guest = newReferenceType(attributes);
} else if (qName.endsWith("Link") && "edit".equals(attributes.get("rel"))) {
@ -119,8 +120,7 @@ public class GuestCustomizationSectionHandler extends ParseSax.HandlerWithResult
} else if (qName.endsWith("CustomizationScript")) {
this.customizationScript = currentOrNull();
if (this.customizationScript != null)
customizationScript = customizationScript.replace("&lt;", "<").replace(">", "&gt;").replace("&quot;", "\"")
.replace("&apos;", "'").replace("&#13;", "\r\n").replace("&#13;", "\n").replace("&amp;", "&");
customizationScript = customizationScript.replace("&gt;", ">");
} else if (qName.endsWith("ComputerName")) {
this.computerName = currentOrNull();
} else if (qName.endsWith("Name")) {
@ -134,7 +134,7 @@ public class GuestCustomizationSectionHandler extends ParseSax.HandlerWithResult
}
protected String currentOrNull() {
String returnVal = currentText.toString().trim();
String returnVal = currentText.toString();
return returnVal.equals("") ? null : returnVal;
}
}

View File

@ -22,6 +22,7 @@ package org.jclouds.vcloud;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Iterables.get;
import static com.google.common.collect.Iterables.getOnlyElement;
import static org.jclouds.crypto.CryptoStreams.base64;
import static org.testng.Assert.assertEquals;
import java.util.Properties;
@ -31,26 +32,31 @@ import org.jclouds.Constants;
import org.jclouds.compute.ComputeService;
import org.jclouds.compute.ComputeServiceContext;
import org.jclouds.compute.ComputeServiceContextFactory;
import org.jclouds.compute.domain.ExecResponse;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.options.TemplateOptions;
import org.jclouds.logging.log4j.config.Log4JLoggingModule;
import org.jclouds.net.IPSocket;
import org.jclouds.predicates.InetSocketAddressConnect;
import org.jclouds.predicates.RetryablePredicate;
import org.jclouds.rest.RestContextFactory;
import org.jclouds.ssh.SshClient;
import org.jclouds.ssh.SshClient.Factory;
import org.jclouds.ssh.jsch.config.JschSshClientModule;
import org.jclouds.vcloud.compute.options.VCloudTemplateOptions;
import org.jclouds.vcloud.domain.Vm;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeGroups;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.inject.Guice;
import com.google.inject.Module;
/**
* This tests that we can use guest customization as an alternative to bootstrapping with ssh. There
* are a few advangroupes to this, including the fact that it can work inside google appengine where
* are a few advantages to this, including the fact that it can work inside google appengine where
* network sockets (ssh:22) are prohibited.
*
* @author Adrian Cole
@ -58,7 +64,7 @@ import com.google.inject.Module;
@Test(groups = "live", enabled = true, sequential = true)
public class VCloudGuestCustomizationLiveTest {
public static final String PARSE_VMTOOLSD = "vmtoolsd --cmd=\"info-get guestinfo.ovfenv\" |grep vCloud_CustomizationInfo|sed 's/.*value=\"\\(.*\\)\".*/\\1/g'|base64 -d";
public static final String PARSE_VMTOOLSD = "vmtoolsd --cmd=\"info-get guestinfo.ovfenv\" |grep vCloud_CustomizationInfo|sed 's/.*value=\"\\(.*\\)\".*/\\1/g'";
protected ComputeServiceContext context;
protected ComputeService client;
@ -71,6 +77,8 @@ public class VCloudGuestCustomizationLiveTest {
protected String endpoint;
protected String apiversion;
protected NodeMetadata node;
protected void setupCredentials() {
identity = checkNotNull(System.getProperty("test." + provider + ".identity"), "test." + provider + ".identity");
credential = System.getProperty("test." + provider + ".credential");
@ -96,51 +104,89 @@ public class VCloudGuestCustomizationLiveTest {
public void setupClient() {
setupCredentials();
Properties overrides = setupProperties();
client = new ComputeServiceContextFactory().createContext(provider,
client = new ComputeServiceContextFactory(setupRestProperties()).createContext(provider,
ImmutableSet.<Module> of(new Log4JLoggingModule()), overrides).getComputeService();
socketTester = new RetryablePredicate<IPSocket>(new InetSocketAddressConnect(), 60, 1, TimeUnit.SECONDS);
socketTester = new RetryablePredicate<IPSocket>(new InetSocketAddressConnect(), 300, 1, TimeUnit.SECONDS);
sshFactory = Guice.createInjector(getSshModule()).getInstance(Factory.class);
}
protected Properties setupRestProperties() {
return RestContextFactory.getPropertiesFromResource("/rest.properties");
}
protected JschSshClientModule getSshModule() {
return new JschSshClientModule();
}
// make sure the script has a lot of screwy characters, knowing our parser throws-out \r
private String iLoveAscii = "I '\"love\"' {asc|!}*&";
String script = "cat > /root/foo.txt<<EOF\n" + iLoveAscii + "\nEOF\n";
@Test
public void testExtendedOptionsWithCustomizationScript() throws Exception {
String group = "customize";
String script = "cat > /root/foo.txt<<EOF\nI love candy\nEOF\n";
TemplateOptions options = client.templateOptions();
options.as(VCloudTemplateOptions.class).customizationScript(script);
node = getOnlyElement(client.createNodesInGroup(group, 1, options));
NodeMetadata node = null;
Vm vm = Iterables.get(
((VCloudClient) client.getContext().getProviderSpecificContext().getApi()).getVApp(node.getUri())
.getChildren(), 0);
String apiOutput = vm.getGuestCustomizationSection().getCustomizationScript();
checkApiOutput(apiOutput);
IPSocket socket = getSocket(node);
System.out.printf("%s:%s@%s", node.getCredentials().identity, node.getCredentials().credential, socket);
assert socketTester.apply(socket) : socket;
SshClient ssh = sshFactory.create(socket, node.getCredentials());
try {
node = getOnlyElement(client.createNodesInGroup(group, 1, options));
IPSocket socket = new IPSocket(get(node.getPublicAddresses(), 0), 22);
assert socketTester.apply(socket);
SshClient ssh = sshFactory.create(socket, node.getCredentials());
try {
ssh.connect();
assertEquals(ssh.exec(PARSE_VMTOOLSD).getOutput(), script.replaceAll("\n", "\r\n"));
assertEquals(ssh.exec("cat /root/foo.txt").getOutput().trim(), "I love candy");
} finally {
if (ssh != null)
ssh.disconnect();
}
ssh.connect();
ExecResponse vmTools = ssh.exec(PARSE_VMTOOLSD);
System.out.println(vmTools);
String fooTxt = ssh.exec("cat /root/foo.txt").getOutput();
String decodedVmToolsOutput = new String(base64(vmTools.getOutput().trim()));
checkVmOutput(fooTxt, decodedVmToolsOutput);
} finally {
if (node != null)
client.destroyNode(node.getId());
if (ssh != null)
ssh.disconnect();
}
}
protected void checkApiOutput(String apiOutput) {
checkApiOutput1_0_1(apiOutput);
}
protected void checkApiOutput1_0_1(String apiOutput) {
// in 1.0.1, vcloud director seems to pass through characters via api flawlessly
assertEquals(apiOutput, script);
}
protected void checkApiOutput1_0_0(String apiOutput) {
// in 1.0.0, vcloud director seems to remove all newlines
assertEquals(apiOutput, script.replace("\n", ""));
}
protected void checkVmOutput(String fooTxtContentsMadeByVMwareTools, String decodedVMwareToolsOutput) {
// note that vmwaretools throws in \r characters when executing scripts
assertEquals(fooTxtContentsMadeByVMwareTools, iLoveAscii + "\r\n");
assertEquals(decodedVMwareToolsOutput, script);
}
@AfterTest
public void tearDown() {
if (node != null)
client.destroyNode(node.getId());
if (context != null)
context.close();
}
protected IPSocket getSocket(NodeMetadata node) {
return new IPSocket(get(node.getPublicAddresses(), 0), 22);
}
}

View File

@ -12,7 +12,10 @@
<AdminPasswordEnabled>true</AdminPasswordEnabled>
<AdminPasswordAuto>true</AdminPasswordAuto>
<ResetPasswordRequired>false</ResetPasswordRequired>
<CustomizationScript>#!/bin/bash if [[ $1 == "postcustomization" ]]; then echo "post customization" touch /root/.postcustomization ping www.redhat.com -c 1 sleep 30 # register with RHN /usr/sbin/rhnreg_ks --profilename vic_`hostname`_`dmidecode -s system-uuid` --activationkey=XXXXXXXXXXXX --force echo "rhn registered" # make hostname fully qualified to speed up sendmail start perl -i -pe "s/`hostname`/`hostname`.victory.blk/g" /etc/sysconfig/network rm /etc/ssh/*_key* service sshd restart echo "completed" fi</CustomizationScript>
<CustomizationScript>cat &gt; /root/foo.txt&lt;&lt;EOF
I '"love"' {asc|!}*&amp;
EOF
</CustomizationScript>
<ComputerName>RHEL5</ComputerName>
<Link rel="edit"
type="application/vnd.vmware.vcloud.guestCustomizationSection+xml"

View File

@ -29,8 +29,8 @@
<ovf:Network ovf:name="none">
<ovf:Description>This is a special place-holder used for disconnected network interfaces.</ovf:Description>
</ovf:Network>
</ovf:NetworkSection> <NetworkConfigSection type="application/vnd.vmware.vcloud.networkConfigSection+xml" href="https://1.1.1.1/api/v1.0/vApp/vapp
-1/networkConfigSection/" ovf:required="false">
</ovf:NetworkSection>
<NetworkConfigSection type="application/vnd.vmware.vcloud.networkConfigSection+xml" href="https://1.1.1.1/api/v1.0/vApp/vapp-1/networkConfigSection/" ovf:required="false">
<ovf:Info>The configuration parameters for logical networks</ovf:Info>
<Link rel="edit" type="application/vnd.vmware.vcloud.networkConfigSection+xml" href="https://1.1.1.1/api/v1.0/vApp/vapp-1/networkConfigSection/"/>
<NetworkConfig networkName="none">

View File

@ -310,7 +310,10 @@
<AdminPasswordEnabled>true</AdminPasswordEnabled>
<AdminPasswordAuto>true</AdminPasswordAuto>
<ResetPasswordRequired>false</ResetPasswordRequired>
<CustomizationScript>#!/bin/bash if [[ $1 == "postcustomization" ]]; then echo "post customization" touch /root/.postcustomization ping www.redhat.com -c 1 sleep 30 # register with RHN /usr/sbin/rhnreg_ks --profilename vic_`hostname`_`dmidecode -s system-uuid` --activationkey=XXXXXXXXXXXX --force echo "rhn registered" # make hostname fully qualified to speed up sendmail start perl -i -pe "s/`hostname`/`hostname`.victory.blk/g" /etc/sysconfig/network rm /etc/ssh/*_key* service sshd restart echo "completed" fi</CustomizationScript>
<CustomizationScript>cat &gt; /root/foo.txt&lt;&lt;EOF
I '"love"' {asc|!}*&amp;
EOF
</CustomizationScript>
<ComputerName>RHEL5</ComputerName>
<Link rel="edit"
type="application/vnd.vmware.vcloud.guestCustomizationSection+xml"

View File

@ -184,7 +184,10 @@
<AdminPasswordEnabled>true</AdminPasswordEnabled>
<AdminPasswordAuto>true</AdminPasswordAuto>
<ResetPasswordRequired>false</ResetPasswordRequired>
<CustomizationScript>#!/bin/bash if [[ $1 == "postcustomization" ]]; then echo "post customization" touch /root/.postcustomization ping www.redhat.com -c 1 sleep 30 # register with RHN /usr/sbin/rhnreg_ks --profilename vic_`hostname`_`dmidecode -s system-uuid` --activationkey=XXXXXXXXXXXX --force echo "rhn registered" # make hostname fully qualified to speed up sendmail start perl -i -pe "s/`hostname`/`hostname`.victory.blk/g" /etc/sysconfig/network rm /etc/ssh/*_key* service sshd restart echo "completed" fi</CustomizationScript>
<CustomizationScript>cat &gt; /root/foo.txt&lt;&lt;EOF
I '"love"' {asc|!}*&amp;
EOF
</CustomizationScript>
<ComputerName>RHEL5</ComputerName>
<Link rel="edit"
type="application/vnd.vmware.vcloud.guestCustomizationSection+xml"

View File

@ -161,6 +161,15 @@ public abstract class BaseComputeServiceContextModule extends AbstractModule {
return template.osFamily(UBUNTU).osVersionMatches("10.04").os64Bit(true);
}
/**
* The default options if none are provided.
*/
@Provides
@Named("DEFAULT")
protected TemplateOptions provideTemplateOptions(Injector injector, TemplateOptions options) {
return options;
}
/**
* supplies how the tag is encoded into the name. A string of hex characters is the last argument
* and tag is the first

View File

@ -34,4 +34,8 @@ public class BlueLockVCloudDirectorGuestCustomizationLiveTest extends VCloudGues
provider = "bluelock-vcdirector";
}
@Override
protected void checkApiOutput(String apiOutput) {
checkApiOutput1_0_0(apiOutput);
}
}