A better placement strategy for vCAC with Custom Properties

As an update to the previous post on doing a manual placement of VMs I would like to present a approach for a placement strategy in vCAC that is a little bit more flexible than just relying on the location field and a reservation policy. The idea is to have custom properties on various reservations that will be checked before placement. If and only if all custom properties match the input pattern that reservation should be selected for provisioning. As an example see the following figure where five custom properties have been defined on the reservation ‘GOLD02’:

Reservation Custom Properties

Reservation Custom Properties


So for your workflow to check on these properties you define them in a hashtable and pass them on to the placement script:

PS > $Policies.GetType()
IsPublic IsSerial Name      BaseType
-------- -------- ----      --------
True     True     Hashtable System.Object

PS > $Policies
Name                           Value
----                           -----
biz.dfch.Machine.NetZoneGroup  Internal
biz.dfch.Machine.ISO27001      1
biz.dfch.Level                 Gold
biz.dfch.Machine.StageValue    Prod
biz.dfch.Machine.Location      PremierSite

You would then use this ‘Policies’ object to pass it on to the placement script to determine possible resources (reservation and storagepath). Resources are taken on a first match policy which you could easily change to something more sophisticated.

PS > $m.GetType().FullName
DynamicOps.ManagementModel.ManagementModelEntities
PS > $Machine.GetType().FullName
DynamicOps.ManagementModel.VirtualMachine

# Required storage for this machine
$StorageRequired = [math]::Ceiling($Machine.VMUsedStorageGB) -as [int64];
Write-Host ("{0}: StorageRequired/Machine.VMUsedStorageGB: [{1}]." -f $Machine.VirtualMachineID, $StorageRequired);

# ResourcePlacementParameters holds parameters for actual placement
$ResourcePlacementParameters = @{};
# holds possible HostReservations to use
$hrCandidates = @{}
foreach($hr in $m.HostReservations) {
  $fReturn = $true;
  # Check if enabled
  if($fReturn -And !$hr.Enabled) { $fReturn = $false; }

  # Check hr against given Policies hash table to match - if no policy is defined this reservation is skipped
  $ahrProp = @{};
  $m.LoadProperty($hr, 'HostReservationProperties') | 
    Select-Object PropertyName, PropertyValue |% 
	{ $ahrProp.Add($_.PropertyName, $_.PropertyValue); };
  foreach($p in $Policies.GetEnumerator()) { 
    if($fReturn -And (!$ahrProp.ContainsKey($p.Name) -Or ($p.Value -ne $ahrProp.($p.Name))) {
      Write-Host ("{0}: HostReservation '{1}' does not match '{2}'. Skipping ..." -f 
	    $Machine.VirtualMachineID, $hr.HostReservationName, $p.Name);
      $fReturn = $false;
      break;
    } # if
  } # foreach

  # Check if enough storage on reservation
  $StorageFree = ($hr.ReservationStorageSizeGB - $hr.HostReservationStats.StorageAllocated);
  if($StorageFree -lt $StorageRequired) {
    Write-Host ("{0}: HostReservation '{1}' does not have enough free space '{2}'. Skipping ..." 
	  -f $Machine.VirtualMachineID, $hr.HostReservationName, $StorageFree);
    $fReturn = $false;
  } # if

  # Check if another reservation policy with the same priority already exists
  if($hrCandidates.ContainsKey($hr.ReservationPriority)) {
    Write-Host ("{0}: Processing hr '{1}' [{2}]. Other ReservationPriority '{3}' already exists. Skipping ..." 
	  -f $Machine.VirtualMachineID, $hr.HostReservationName, $hr.HostReservationID, $hr.ReservationPriority);
    continue; 
  } # if
  Write-Host ('ahrProp: {0}' -f ($ahrProp | Out-String));
  $null = $hrCandidates.Add($hr.ReservationPriority, $hr.HostReservationName);
} # foreach

# Get the first reservation policy with lowest numerical priority
$hrKey = $hrCandidates.Keys | Sort | Select-Object -First 1;
if(!$hrKey) {
  Write-Error ("{0}: No HostReservation found." -f $Machine.VirtualMachineID);
  throw($gotoFailure);
} # if
$ResourcePlacementParameters.HostReservation = $hrCandidates.$hrKey;

if(!$fReturn) {
  $msg = "{0}: No HostReservation meets requirement. Aborting ... " -f $Machine.VirtualMachineID;
  Log-Critical $fn $msg;
  $null = New-VcacUserLog -Message $msg -Username $Machine.Owner.UserName;
  $e = New-CustomErrorRecord -msg $msg -cat ObjectNotFound -o $Machine;
  throw($gotoError);
} # if
Write-Info ("{0}: HostReservation '{1}' meets requirements." -f $Machine.VirtualMachineID, $hr.HostReservationName);

$htsCandidates = @{}
$h = $hr.Host;
$ahts = $m.LoadProperty($h, 'HostToStorages');
foreach($hrts in $hr.HostReservationToStorages) { 
  Write-Host ("{0}: Processing hrts '{1}' with Priority '{2}' and MaxCapacity '{3}' ..." -f $Machine.VirtualMachineID, $hrts.HostReservationToStorageID, $hrts.ReservationPriority, $hrts.MaxCapacity);
  if(!$hrts.Enabled) { 
    Write-Warning ("{0}: Processing hrts '{1}'. HostReservationToStrage is disabled. Skipping ..." -f $Machine.VirtualMachineID, $hrts.HostReservationToStorageID);
    continue; 
  } # if
  if($hrts.MaxCapacity -le $StorageRequired) {
    Write-Warning ("{0}: Processing hrts '{1}'. MaxCapacity [{2}] is less than StorageRequired [{3}] ..." -f $Machine.VirtualMachineID, $hrts.HostReservationToStorageID, $hrts.MaxCapacity, $StorageRequired);
    continue;
  } # if
  $hrhts = $m.LoadProperty($hrts, 'HostToStorage'); 
  $hts = $ahts |? HostToStorageID -eq $hrhts.HostToStorageID;
  if($htsCandidates.ContainsKey($hrts.ReservationPriority)) {
    Write-Host ("{0}: Processing hrts '{1}'. Other HostReservationToStrage priority '{2}' already exists. Skipping ..." -f $Machine.VirtualMachineID, $hrts.HostReservationToStorageID, $hrts.ReservationPriority);
    continue; 
  } # if
  $null = $m.LoadProperty($hts, 'Storage');
  Write-Host ("{0}: Checking hts[{1}].Storage.StorageFreeCapacityGB against StorageRequired [{2}] ..." -f $Machine.VirtualMachineID, $hts.StoragePath, $StorageRequired);
  if($hts.Storage.StorageFreeCapacityGB -le $StorageRequired) {
    Write-Warning ("{0}: Checking hts[{1}].Storage.StorageFreeCapacityGB against StorageRequired [{2}] FAILED: [{3}]." -f $Machine.VirtualMachineID, $hts.StoragePath, $StorageRequired, $hts.Storage.StorageFreeCapacityGB);
    continue;
  } # if
  Write-Info ("{0}: Checking hts[{1}].Storage.StorageFreeCapacityGB against StorageRequired [{2}] SUCCEEDED: [{3}]." -f $Machine.VirtualMachineID, $hts.StoragePath, $StorageRequired, $hts.Storage.StorageFreeCapacityGB);
  $null = $htsCandidates.Add($hrts.ReservationPriority, $hts.StoragePath);
} # foreach

# Get the first storage path with lowest numerical priority
$htsKey = $htsCandidates.Keys | Sort | Select-Object -First 1;
if(!$htsKey) {
  Write-Error ("{0}: No HostReservationToStorages/HostToStorages found." -f $Machine.VirtualMachineID);
  throw($gotoFailure);
} # if
$ResourcePlacementParameters.StoragePath = $htsCandidates.$htsKey;

After running the code against the match script you might get back a result like the following which you can then use to place the machine on actual resources:

PS > $$ResourcePlacementParameters
Name            Value
----            -----
HostReservation GOLD02
StoragePath     vCenterResources2-storage-pool-3

As usual error handling, logging and resource cleanup has been removed from this sample code.

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 )

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s

%d bloggers like this: