Monday, December 16, 2013

Running the Microsoft ALM TFS 2013 (Hyper-V) Virtual Machine in VMWare without Persisting Changes

This blog describes how to run the Microsoft TFS/ALM Hyper-V virtual machine in one of the VMWare hypervisors (Player/Workstation/Fusion). You can use the same concepts for any other virtual machine/appliance.

The 2013 RTM ALM image comes with a set of TFS and Visual studio labs.  The virtual machine and the labs  have a couple expectations:
  1. It comes as a a Hyper-V VHD running an evaluation copy of Windows Server 2012.
  2. The work items and other information are "point in time" information.  This means sprint, iteration or other information can only be used in the labs if the VMs time is set to a specific date(s). 
  3. The machine resets the time to the same specific date and time on every reboot. This lets you re-run the exercises.  Pay attention to the places the way the time is set if you wish to use this process for Virtual Machines anchored at dates and times other than the ones used for the TFS 2013 RTM ALM image used in this post.
  4. The labs work best if the machine is restored back to its initial state after each lab.
We're going to run this virtual disk as a VMWare guest in a machine that automatically sets the Guest RTC to the correct time for the labs.  We will also restore the VM to its original state every time VMWare is re-launched.

VMWare Configuration

You can create a new virtual machine that uses the ALM virtual disk.  VMWare can consume the disk directly without conversion.  Configure the virtual machine but do not start it until you have manually edited the VMWare vmx file to set up the clock and disks snapshots.

  1. Create a directory to hold the virtual machine with some reasonable name. We will have VMWare use that directory for all its files later.
  2. Move the vhd virtual disk into the previously created directory.
  3. Run VMWare
  4. Create a new virtual machine
  5. Select Install System Later
  6. Select Microsoft Windows on the Guest operating screen. 
  7. Select the Windows version of Hyper-V (unsupported)
  8. Pick a location for the virtual machine.  Select the directory we created above
  9. Pick Store virtual disk as a single file.  We're not really going to use this disk. We're going to reconfigure the disks later to use the ALM VHD.
  10. Select Finish.  This should take you back to the screen that lists all the VMs including the one we just defined.
  11. Select the virtual machine and select Edit virtual machine settings
    1. Add a new IDE disk.  
    2. Select Use existing...
    3. Select the VHD
    4. Delete the old virtual disk.  We only need one disk and it should be the ALM disk
  12. Do not install VMWare tools
  13. Do not boot the machine
  14. Quit VMWare
Manually edit the VM configuration, vmx, file before booting the machine.

Manual Changes

Disabling Disk Persistence 

We want  the machine to roll back all session changes so that the VM always starts in exactly the same state.  We need to add 3 settings to the VMX file.  
ide0:0.mode = "independent-nonpersistent"
snapshot.action = "autoRevert"
snapshot.disabled = "TRUE"
We can remove the settings if we want to do some  work that we want to work across restarts. Be careful though. You often cant push time backwards if you've already done work on the current time.

Time at Startup.

The TFS 2013 RTM ALM VM resets its clock to 7/9/2013 1:30PM ever time it starts via a Task. This puts the machine in a known state and makes time based labs work.  Ex: Think iteration dates here. We want to be inside an iteration when using the sprint planning dashboards. 

You can have problem with this approach though.  A virtual machine's RTC is set to NOW when it boots up. This means the machine boots with the current time before it is reset by the startup tasks.  System or SQL changes can occur in thevirtual machine before the clock reset occurs creating timestamps later than 7/9/2013 1:30 PM. We need to set the virtual hardware clock before the operating system boots and reads the time.

The virtual RTC is used by the VM every time it runs. VMWare lets you set the virtual machine RTC at startup with a vmx setting. This web entry describes how we can set the VM time using the VM's virtual bios and RTC.

VMWare sets the RTC on the Unix Epoch. There are web sites that let you calculate the epoch time from a date and time that you enter. We want to choose an RTC time to just before the time that is set by the ALM vm on startup.  This insures that time moves only forward between bios boot, OS startup, SQL startup and task execution.

For this TFS 2013 RTM ALM virtual machine I have chosen 7/9/2013 1:30 AM which is earlier than,7/9/2013 1:30 PM, the ALM machine's OS will be set to after boot. 7/9/2013 1:30 AM maps to 1373334621 in Epoch Time. We add the following vmx entry based on that calculation:
rtc.starttime = "1373334621"
The time here is a unix style epoch time. There are web pages out there that can convert time between human readable and the Unix time format. The TFS 2012  and TFS 2013 RC ALM virtual machines used other dates so their rtc.startime should be set accordingly.  You can use this same reset process for other lab oriented virtual machines. 

Time Synchronization

VMWare has a set of tools that strive to synchronize time between the guest and the host. The Microsoft ALM Virtual Machine operates as if it is still July 2013 so we need to disable most of the Host/Guest time synchronization 
tools.syncTime = "FALSE"
time.synchronize.continue = "FALSE"
time.synchronize.restore = "FALSE"
time.synchronize.resume.disk = "FALSE"
time.synchronize.shrink = "FALSE"
time.synchronize.tools.startup = "FALSE"
time.synchronize.tools.enable = "FALSE"
time.synchronize.resume.host = "FALSE"
This document describes how VMWare manages time.

Sample VMX File

Here is my vmx configuration file that works with this service This VM described by this file operates on its own concept of time without syncing to any official time server. It also restores the C: drive disk on every restarts.

You can create completely new VM based the vmx describe below.

  1. Create a directory
  2. Put the vhd in the directory.
  3. Create a new vmx file, put it in the created directory and paste the contents from below into the file
  4. You should be able to now just double click on the vmx file to launch the ALM virtual machine.


.encoding = "windows-1252"
config.version = "8"
virtualHW.version = "10"
vcpu.hotadd = "TRUE"
scsi0.present = "TRUE"
scsi0.virtualDev = "lsisas1068"
sata0.present = "TRUE"
memsize = "4096"
mem.hotadd = "TRUE"
sata0:1.present = "TRUE"
sata0:1.autodetect = "TRUE"
sata0:1.deviceType = "cdrom-raw"
ethernet0.present = "TRUE"
ethernet0.connectionType = "nat"
ethernet0.virtualDev = "e1000"
ethernet0.wakeOnPcktRcv = "FALSE"
ethernet0.addressType = "generated"
usb.present = "TRUE"
ehci.present = "TRUE"
ehci.pciSlotNumber = "35"
usb_xhci.present = "TRUE"
sound.present = "TRUE"
sound.virtualDev = "hdaudio"
sound.fileName = "-1"
sound.autodetect = "TRUE"
serial0.present = "TRUE"
serial0.fileType = "thinprint"
pciBridge0.present = "TRUE"
pciBridge4.present = "TRUE"
pciBridge4.virtualDev = "pcieRootPort"
pciBridge4.functions = "8"
pciBridge5.present = "TRUE"
pciBridge5.virtualDev = "pcieRootPort"
pciBridge5.functions = "8"
pciBridge6.present = "TRUE"
pciBridge6.virtualDev = "pcieRootPort"
pciBridge6.functions = "8"
pciBridge7.present = "TRUE"
pciBridge7.virtualDev = "pcieRootPort"
pciBridge7.functions = "8"
vmci0.present = "TRUE"
hpet0.present = "TRUE"
usb.vbluetooth.startConnected = "TRUE"
displayName = "2013 ALM Hyper-V (unsupported)"
guestOS = "winhyperv"
nvram = "2013 ALM Hyper-V (unsupported).nvram"
virtualHW.productCompatibility = "hosted"
gui.exitOnCLIHLT = "FALSE"
powerType.powerOff = "soft"
powerType.powerOn = "soft"
powerType.suspend = "soft"
powerType.reset = "soft"
extendedConfigFile = "2013 ALM Hyper-V (unsupported).vmxf"
ide0:0.present = "TRUE"
ide0:0.fileName = "TD02WS12SFx64.vhd"
scsi0:0.present = "FALSE"
floppy0.present = "FALSE"
uuid.bios = "56 4d 6b 23 25 1b 6f 2d-b7 ca e8 22 47 3b ad a3"
uuid.location = "56 4d 6b 23 25 1b 6f 2d-b7 ca e8 22 47 3b ad a3"
replay.supported = "FALSE"
replay.filename = ""
ide0:0.redo = ""
pciBridge0.pciSlotNumber = "17"
pciBridge4.pciSlotNumber = "21"
pciBridge5.pciSlotNumber = "22"
pciBridge6.pciSlotNumber = "23"
pciBridge7.pciSlotNumber = "24"
scsi0.pciSlotNumber = "160"
usb.pciSlotNumber = "32"
ethernet0.pciSlotNumber = "33"
sound.pciSlotNumber = "34"
usb_xhci.pciSlotNumber = "192"
vmci0.pciSlotNumber = "36"
sata0.pciSlotNumber = "37"
scsi0.sasWWID = "50 05 05 63 25 1b 6f 20"
ethernet0.generatedAddress = "00:0c:29:3b:ad:a3"
ethernet0.generatedAddressOffset = "0"
vmci0.id = "1195093411"
vmotion.checkpointFBSize = "33554432"
cleanShutdown = "TRUE"
softPowerOff = "TRUE"
usb_xhci:1.speed = "2"
usb_xhci:1.present = "TRUE"
usb_xhci:1.deviceType = "hub"
usb_xhci:1.port = "1"
usb_xhci:1.parent = "-1"
usb_xhci:3.speed = "4"
usb_xhci:3.present = "TRUE"
usb_xhci:3.deviceType = "hub"
usb_xhci:3.port = "3"
usb_xhci:3.parent = "-1"
toolsInstallManager.updateCounter = "8"
sata0:1.startConnected = "FALSE"
unity.wasCapable = "FALSE"
tools.remindInstall = "TRUE"

ide0:0.mode = "independent-nonpersistent"
snapshot.action = "autoRevert"
snapshot.disabled = "TRUE"

rtc.starttime = "1373362200"
tools.syncTime = "FALSE"
time.synchronize.continue = "FALSE"
time.synchronize.restore = "FALSE"
time.synchronize.resume.disk = "FALSE"
time.synchronize.shrink = "FALSE"
time.synchronize.tools.startup = "FALSE"
time.synchronize.tools.enable = "FALSE"
time.synchronize.resume.host = "FALSE"

usb_xhci:4.present = "TRUE"
usb_xhci:4.deviceType = "hid"
usb_xhci:4.port = "0"
usb_xhci:4.parent = "1"

Conclusion

You can use these techniques to create/manage Virtual Machines that need to restore back to the previous state on every restart.  You can configure these same machines to put the hardware back at that same or other times.

Last edited 12/29/2013

Sunday, December 8, 2013

Microsoft Code Analysis results differ based on Configuration

Background

Code Analysis (CA) is a very useful Visual Studio feature that applies good practice type static code analysis to your code base.  You can run CA manually at any time or configure it to run automatically on each build. Automatic execution can be configured per configuration per project. This means you can enable automatic CA on Release builds while ignoring it during builds in Debug mode.  Some teams do this to speed up the debug compilation cycle.  I'm not sure what in CA makes it so slow that you can't run it all the time :-(

You can view which CA rules apply for any configuration or CPU type via the Solution Properties window:




You can set the automatic execution of CA on a per project basis in the Code Analysis pane of the Project Properties:


The method of Code Analysis  menu item and the position of the results varies by Visual Studio Version. The menu item that executes CA is located on the Build menu in VS2013.

Unexpected Behavior

Microsoft Code Analysis will sometimes generate different results based on the configuration.  This may be the result of rule configuration, flag values and/or the level of optimization. I've used static analysis tools on Java for years but this is the first time I've seen different results.  That may be because we only compiled Java one way.

This very simple method generates different results with Microsoft All Rules depending on the configuration.

        public void DummyMethod(){
            HttpStyleUriParser foo = new HttpStyleUriParser();
        }

You can see here that the last Code Analysis error is different. Make sure you are testing your code with CA in the proper configuration for your project's standards.

Release ConfigurationDebug Configuration


We found this when using nested C# using blocks to insure the correct clean up behavior in stream and memory buffer operations.  A CA rule fires in Debug configuration that flags the way using blocks map to close and Dispose() behavor. This Code Analysis error disappears in Release configuration , possibly because the system intelligently calculates the correct behavior.

Conclusion

Make sure you run your CA rules in the same configuration as your build servers.  You don't want to think you are clean and then have the CI builds fail because of a CA related issue.