mirror of https://github.com/apache/jclouds.git
Issue 494:vCloud director 1.0.1 fixes guest customization bug
This commit is contained in:
parent
59c0dbb6cd
commit
d494c8a9f3
|
@ -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("<", "<").replace(">", ">").replace(""", "\"")
|
||||
.replace("'", "'").replace(" ", "\r\n").replace(" ", "\n").replace("&", "&");
|
||||
customizationScript = customizationScript.replace(">", ">");
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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 > /root/foo.txt<<EOF
|
||||
I '"love"' {asc|!}*&
|
||||
EOF
|
||||
</CustomizationScript>
|
||||
<ComputerName>RHEL5</ComputerName>
|
||||
<Link rel="edit"
|
||||
type="application/vnd.vmware.vcloud.guestCustomizationSection+xml"
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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 > /root/foo.txt<<EOF
|
||||
I '"love"' {asc|!}*&
|
||||
EOF
|
||||
</CustomizationScript>
|
||||
<ComputerName>RHEL5</ComputerName>
|
||||
<Link rel="edit"
|
||||
type="application/vnd.vmware.vcloud.guestCustomizationSection+xml"
|
||||
|
|
|
@ -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 > /root/foo.txt<<EOF
|
||||
I '"love"' {asc|!}*&
|
||||
EOF
|
||||
</CustomizationScript>
|
||||
<ComputerName>RHEL5</ComputerName>
|
||||
<Link rel="edit"
|
||||
type="application/vnd.vmware.vcloud.guestCustomizationSection+xml"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -34,4 +34,8 @@ public class BlueLockVCloudDirectorGuestCustomizationLiveTest extends VCloudGues
|
|||
provider = "bluelock-vcdirector";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void checkApiOutput(String apiOutput) {
|
||||
checkApiOutput1_0_0(apiOutput);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue