[UPDATE 2014-02-16] For a different approach on how to change HostReservation and StorageReservation have a look at the method via RegisterExistingVirtualMachine().]
One of the limitations in vCAC 5.2 is the fact that you can only use one predefined field (“Reservation Policy”) per Reservation and another field (“Location”) per “Compute Resource” for having vCAC doing the resource selection and placement for a virtual machine automatically. (And if you are using reservations for physical deployment you do not even have a Compute Resource.) Furthermore you cannot specify a “Reservation Policy” via a “Custom Property” and therefore cannot override the policy specified in a blueprint when needed. When one of our customers wanted to have a more flexible placement strategy than possible with vCAC out-of-the-box features, this somehow left us with the challenge to find a way to do a “re-location” or manual placement after the user submitted the machine request.
With some C# coding and a CDK license it is actually possible (thanks to Duncan from VMware SDK Support). Our particular customer however decided against using a CDK license. So one possible approach was to rewrite the information in the “BuildingMachine” workflow step.

As vCAC already made a placement choice when entering the “BuildingMachine” step you actually cannot just specify custom properties like ‘Vrm.Admin.ClusterName’ to override the Compute Resource. Instead you have to re-arrange all the links from the virtual machine to its resources. The following picture shows the relationship of a virtual machine to its resources like “Reservation”, “Compute Resource” (“Host”), “Storage” (attached to Hosts and assigned to Reservations”) and “Networks”:

Machine Resource Relationship
Machine Resource Relationship

In order to do a placement (based on whatever logic you choose – I prefer custom attributes on the reservation and not using reservation policies at all) you only need to know a target reservation and a storage path (Datastore) to re-map the machine. The basic steps can be described like this:

1a. Delete link to existing HostReservation
1b. Resolve target reservation by name and find its Host / Compute Resource. Set link to new HostReservation
2a. Delete link to existing Host
2b. Set link to new Host
3. Update Properties
3a. StoragePath
3b. VirtualMachine.Storage.Name
4a. Delete link to existing HostReservationToStorage
4b. Resolve target HostReservationToStorage by looking at the Reservation and the name of the StoragePath for HostToStorage. Set link to new HostReservationToStorage
5. Update StoragePath value on all attached disks
6a. Delete link to existing VMDiskHardware for every attached disk
6b. Set link to new HostStorage (from 4b).
7a. Delete link to existing network
7b. Set link to new network
8. Update location field (if needed)

This is actually all it takes and in PowerShell this might look a little bit like this.
Some error and sanity checks certainly might come in handy …

# What we need
PS > $m.GetType().FullName
DynamicOps.ManagementModel.ManagementModelEntities
PS > $Machine.GetType().FullName
DynamicOps.ManagementModel.VirtualMachine
PS > $ReservationName
GOLD02
PS > $StoragePath
GOLD02-storage-pool-1
PS > $Network
VLAN42

# 1a. Delete link to existing HostReservation
$hrOld = $m.HostReservations |?
  HostReservationName -eq $Machine.HostReservation.HostReservationName;
$m.DeleteLink($hrOld, 'VirtualMachines', $Machine);
$m.UpdateObject($hrOld);
$r = $m.SaveChanges();

# 1b. Resolve target reservation by name
# and find its Host / Compute Resource.
# Set link to new HostReservation
$hr = $m.HostReservations |? HostReservationName -eq $ReservationName;
$null = $m.LoadProperty($hr, 'Host');
$m.SetLink($Machine, 'HostReservation', $hr);
$m.UpdateObject($Machine);
$r = $m.SaveChanges();

# 2a. Delete link to existing Host
$hOld = $Machine.Host;
$null = $m.LoadProperty($hOld, 'VirtualMachines');
$m.DeleteLink($hOld, 'VirtualMachines', $Machine);
$m.UpdateObject($hOld);
$r = $m.SaveChanges();

# 2b. Set link to new Host
$h = $hr.Host;
$null = $m.LoadProperty($h, 'HostToStorages');
$null = $m.LoadProperty($h, 'HostProperties');
$m.SetLink($Machine, 'Host', $h);
$m.UpdateObject($Machine);
$r = $m.SaveChanges();

# 3. Update Properties
# 3a. StoragePath
$Machine.StoragePath = $StoragePath
$m.UpdateObject($Machine)
$r = $m.SaveChanges();

# 3b. VirtualMachine.Storage.Name
$p = $Machine.VirtualMachineProperties |?
  PropertyName -eq 'VirtualMachine.Storage.Name'
$m.AttachIfNeeded($p.GetType().FullName, $p);
$p.PropertyValue = $Machine.StoragePath;
$m.UpdateObject($p);
$r = $m.SaveChanges();

# 4a. Delete link to existing HostReservationToStorage
$hrtsOld = $m.HostReservationToStorages |?
  HostReservationToStorageID -eq $Machine.HostReservationToStorage
  .HostReservationToStorageID;
$m.DeleteLink($hrtsOld, 'VirtualMachines', $Machine);
$m.UpdateObject($hrtsOld);
$r = $m.SaveChanges();

# 4b. Resolve target HostReservationToStorage
# by looking at the Reservation and the name of the StoragePath
# for HostToStorage. Set link to new HostReservationToStorage
foreach( $hts in ($h.HostToStorages |? StoragePath -eq $StoragePath) ) {
  $htsid = $hts.HostToStorageID;
  break;
} # foreach
$hts = $m.HostToStorage |? HostToStorageID -eq $htsid;
$null = $m.LoadProperty($hts, 'HostReservationToStorages');
foreach($hrts in $hts.HostReservationToStorages) {
  $hrtsid = $hrts.HostReservationToStorageID;
  break;
} # foreach
$hrts = $m.HostReservationToStorages |?
  HostReservationToStorageID -eq $hrtsid;

$m.SetLink($Machine, 'HostReservationToStorage', $hrts);
$m.UpdateObject($Machine);
$r = $m.SaveChanges();

# 5. Update StoragePath value on all attached disks
foreach($p in $Machine.VirtualMachineProperties |?
  PropertyName -match 'VirtualMachine.(Disk\d).Storage') {
	$m.AttachIfNeeded($p.GetType().FullName, $p);
	$p.PropertyValue = $Machine.StoragePath;
	$m.UpdateObject($p);
	$r = $m.SaveChanges();
} # foreach

# 6a. Delete / Set link to existing VMDiskHardware for every attached disk
foreach($d in $Machine.VMDiskHardware) {

	# 6a. Delete link to existing VMDiskHardware for every attached disk
	$MachineToStorageID = $d.VMToStorageID;
	$null = $m.Detach($d);
	$d = $m.VMDiskHardwares |? VMToStorageID -eq $MachineToStorageID;
	$null = $m.LoadProperty($d, 'HostToStorage');

	$htsOld = $d.HostToStorage;
	$m.DeleteLink($htsOld, 'VMDiskHardware', $d);
	$m.UpdateObject($htsOld);
	$r = $m.SaveChanges();

	# 6b. Set link to new HostStorage (from 4b).
	$m.SetLink($d, 'HostToStorage', $hts);
	$m.UpdateObject($d);
	$r = $m.SaveChanges();

} # foreach disk

# 8. Update location field (if needed)
$p = $h.HostProperties |?
  PropertyName -eq -eq 'Vrm.DataCenter.Location';
$Location = $p.PropertyValue;
$p = $Machine.VirtualMachineProperties |?
  PropertyName -eq 'Vrm.DataCenter.Location';
$m.AttachIfNeeded($p.GetType().FullName, $p);
$p.PropertyValue = $Location;
$m.UpdateObject($p);
$r = $m.SaveChanges();

This code can be placed into a PowerShell script that is called during the BuildingMachine step as described in Simplify your life while testing and debugging PowerShell scripts in vCAC.

Footnote 1: when doing a “re-location” of the virtual machine in the “BuildingMachine” step, please keep in mind that vCAC already made a resource allocation against some possibly other Reservation. So in order for that initial request not to fail you should have a Reservation with enough (temporary) space. You can use the “Test Agent” with additional “Compute Resources” to work around this issue.

Another side effect of this approach is the fact, that you might run into a situation where the request fails as you might not find enough space to place a machine. From a usability standpoint this is changing the behaviour of vCAC as the user does not get an error message while submitting the request (but only afterwards). You can work around this by writing a message into the user log as described in Notifiying Users in vCAC via ‘Recent Events’.

1 Comment »

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.