Tuesday, May 8, 2007

Driving VMWare

I'm currently working on a project that consists of multiple subsystems. Integrating those subsystems is normally a matter of getting the installer for each and then running them on a machine that has been prepared to have all prerequisites. It's not really a lot of work, although it can be a bit cumbersome at times.

But since we build installers only once per week, we also only get one such integration build per week. And for some of the stuff I'm working with that just isn't good enough.

So I decided to do something about it and see if I could create a nightly integration build. Luckily we already run quite some unit tests every night and -as it happens- one of the teams is using VMWare as the basis of their unit tests in a way I hadn't seen before.

The team has a VMWare image with a snapshot just after they installed all the prerequisites on it. Every night, they revert the VMWare image to that snapshot and then install the software on it through a combination of NAnt files, batch files and custom made executables.

The key to installing the software without security issues is to have it run on the VMWare image itself, which is accomplished by using the wonderful psexec tool. Using psexec allows you to start a process on remote machine. Or a remote virtual machine in our case.

So the main NAnt file conists of this first instruction:

  • <exec program="vmrun" append="true" commandline="reverttosnapshot &quot;${vmware.image.file}"" />
Which reverts the VMWare image to its snapshot.

And then many of these instructions:
  • <exec program="psexec.exe" basedir="${pstools.dir}" commandline="\\${target.name} -u ${runas.username} -p ${runas.password} -i nant -logger:NAnt.Core.XmlLogger -f:"${host.name}\install_module_a.build" -D:database.type=mssql -D:database.cm.server=amssql2005 -D:database.cm.database=nightly_53_${target.name} -D:database.cm.username=dbuser53 -D:database.cm.password=dbuser53" />
I think you'll agree that this isn't the most readable NAnt instruction ever seen. But it basically starts NAnt on the remote (virtual) machine with a .build file that is on the (physical) host machine. All the -D arguments just tell the .build file what to do on which machine.

After a bunch of these psexec commands, we tell the virtual machine to reboot:
  • <exec program="psshutdown.exe" basedir="${pstools.dir}" commandline="-r -t 10 -f \\${target.name}" />
And voila, we have a fresh nightly build ready for testing.

I'm pretty sure we could also have accomplished that last step through some of VMWare's tools, but -like pretty much all sysinternals tools- psshutdown just happens to be part of our standard tool belt.

Now all that is left to do is run an automatic sysprep on the fresh build, so that multiple people can start it without getting into each others way.


BigPigVT said...

You're post describes exactly what I'm trying to do, but being new to VMWare (and having learned about psexec from your post) I'm hoping you might be able to help me figure out what I'm doing wrong.

Let's say my virtual machine is named "FOOBAR". I've tried running psexec as well as psinfo (another utility in pstools) but the connection always times out. For example:

psinfo \\FOOBAR -u uid -p pwd

results in:

Could not find FOOBAR:
The network path was not found.

If I do this against a non-virtual machine it works.

If I ping FOOBAR it resolves to the correct IP address but the requests all time out.

So I suspect my problem is related to the fact this is a virtual machine. Does the value of your NAnt script's target.name property use some special prefix for VM's I'm not aware of?

Any suggestions would be appreciated.

BigPigVT said...

Well, I had a few moments of clarity and some input from a co-worker. After turning off the Windows Firewall on the virtual machine and removing/readding that virtual machine to the domain it appears to be working.

Thanks again for the article, like I said, it's pretty much exactly what I needed.