VMHost Impact Recovery

com.apcc.ISXO.PRO.VMHostImpact.Recovery (WriteActionModuleType)

Recovery action which migrates virtual machines away from impacted hosts

Element properties:

TypeWriteActionModuleType
IsolationAny
AccessibilityInternal
RunAsDefault
InputTypeSystem.BaseData
OutputTypeSystem.BaseData

Member Modules:

ID Module Type TypeId RunAs 
Recovery WriteAction Microsoft.Windows.PowerShellWriteAction Default

Overrideable Parameters:

IDParameterTypeSelectorDisplay NameDescription
EventDescriptionstring$Config/EventDescription$Event DescriptionThe description to create in the event log

Source Code:

<WriteActionModuleType ID="com.apcc.ISXO.PRO.VMHostImpact.Recovery" Accessibility="Internal" Batching="false">
<Configuration>
<xsd:element name="EventDescription" type="xsd:string"/>
</Configuration>
<OverrideableParameters>
<OverrideableParameter ID="EventDescription" Selector="$Config/EventDescription$" ParameterType="string"/>
</OverrideableParameters>
<ModuleImplementation Isolation="Any">
<Composite>
<MemberModules>
<WriteAction ID="Recovery" TypeID="Windows!Microsoft.Windows.PowerShellWriteAction">
<ScriptName>com.apcc.ISXO.PRO.VMHostImpact.RecoveryScript.ps1</ScriptName>
<ScriptBody><Script>#############################################################################
# Filename: com.apcc.ITImpact.RecoveryScript.ps1
#############################################################################
Param($eventContent);

$isDebug = $false;

function LogInfo($logMessage, $doLog = $isDebug) {
if ($doLog) {
[Diagnostics.EventLog]::WriteEntry("ITImpact Recovery", "Info: " + $logMessage);
}
}

function LogError($logMessage) {
[Diagnostics.EventLog]::WriteEntry("ITImpact Recovery", "Error: " + $logMessage);
}

if ($eventContent -eq $null) {
eventcreate /T Error /ID 100 /L APPLICATION /SO VMMPro /D 'No parameters are passed to the script.';
return;
}

#Parse the contents and put the parameters into an array
$eventContentArray = $eventContent.Split(':');

#Check to ensure all of the expected parameters have been passed
if ($eventContentArray.Count -lt 5) {
eventcreate /T Error /ID 100 /L APPLICATION /SO VMMPro /D 'Invalid number of parameters is passed to the script.';
return;
}

#Put respective parameters into expected variables
$vmHostName = $eventContentArray[$eventContentArray.Count - 5].Replace('\n','').Trim();
$vmmServerName = $eventContentArray[$eventContentArray.Count - 4].Trim();
$vmmServerPort = $eventCOntentArray[$eventCOntentArray.Count - 3].Trim();
$protipId = $eventContentArray[$eventContentArray.Count - 2].Trim();
$vmErrorCode = $eventContentArray[$eventContentArray.Count - 1].Trim();

LogInfo("Request: Recovery for: " + $vmHostName + "@" + $vmmServerName + ":" + $vmmServerPort + " using PRO tip ID: " + $protipId + " and error code: " + $vmErrorCode);

if ($vmErrorCode -ne 76) {
# Don't handle
return;
}

$Error.Clear();

function new-array { $args }

function FailGenericError{
Set-PROTip -PROTipId $args[0] -LastError $args[1] -TipStatus Failed;
}

function FailHostNotFound {
$params = new-array '$MPElement[Name='ProMessage.SourceHostNotfound']$' $args[1];
Set-PROTip -PROTipId $args[0] -LastErrorOpsMgrString $params -TipStatus Failed;
}

function FailNoVMFound {
$params = new-array '$MPElement[Name='ProMessage.NoVMFound']$' $args[1];
Set-PROTip -PROTipId $args[0] -LastErrorOpsMgrString $params -TipStatus Failed;
}

function FailNoVMToMigrate {
$params = new-array '$MPElement[Name='ProMessage.NoVMFound.MigrationEligible']$' $args[1];
Set-PROTip -PROTipId $args[0] -LastErrorOpsMgrString $params -TipStatus Failed;
}

function FailNoHostToMigrateTo {
$params = new-array '$MPElement[Name='ProMessage.TargetHostNotfound']$' $args[1];
Set-PROTip -PROTipId $args[0] -LastErrorOpsMgrString $params -TipStatus Failed;
}

function FailInvalidErrorCode {
$params = new-array '$MPElement[Name='ProMessage.InvalidErrorCode']$';
Set-PROTip -PROTipId $args[0] -LastErrorOpsMgrString $params -TipStatus Failed;
}

function FailVMHostMaintenanceHost {
$params = new-array '$MPElement[Name='ProMessage.VMHostMaintenanceHost']$' $args[1];
Set-PROTip -PROTipId $args[0] -LastErrorOpsMgrString $params -TipStatus Failed;
}

function Main {
$Error.Clear();

$vmmServer = get-vmmserver $vmmServerName -TCPPort $vmmServerPort;
if ($Error.Count -ne 0) {
FailGenericError $protipId $Error[0];
$vmmServer.Disconnect();
return;
}

$vmHost = get-vmhost -computername $vmHostName -vmmserver $vmmServer;
if ($vmHost -eq $null) {
FailHostNotFound $protipId $vmHostName;
$vmmServer.Disconnect();
return;
}

# We have a VM host on the machine, so we Set Host into Maintenance Mode and we start the ProTip
Set-PROTip -PROTipId $protipId -TipStatus Running;
LogInfo("Setting PROTip status to running");

$vmmsList = Get-VM -VMHost $vmHost;
if($vmHost.HostCluster -ne $null){
$vmHostsInCluster = Get-VMHost -VMHostCluster $vmHost.HostCluster;
}

$availableHostsForPlacemen = @();
# Getting the List of Hosts available for placement.
foreach ($availableHost in $vmHostsInCluster){
if(($availableHost.MaintenanceHost -ne $true) -and ($availableHost.FQDN -ne $vmHost.FQDN)){
if($availableHost.AvailableForPlacement -eq $true){
$availableHostsForPlacemen = $availableHostsForPlacemen + $availableHost;
}
}
}

if ($vmHost.HostCluster -ne $null) {
if(($availableHostsForPlacemen -eq $null) -or ($availableHostsForPlacemen.Count -eq 0)){
if($vmHost.MaintenanceHost -eq $true){
FailVMHostMaintenanceHost $protipId $vmHostName;
$vmmServer.Disconnect();
return;
}
else{
Disable-VMHost $vmHost -RunAsynchronously;
}
}else{
if ($vmHost.MaintenanceHost -eq $true){
FailVMHostMaintenanceHost $protipId $vmHostName;
$vmmServer.Disconnect();
return;
}
elseif(($vmmsList -ne $null) -or ($vmmsList.Count -ne 0)){
Disable-VMHost $vmHost -MoveWithinCluster -RunAsynchronously;
}
}
}
elseif($vmHost.HostCluster -eq $null){
$candidateVMs = @();
$hostObjVMs = Get-VM -VMHost $vmHost

# Find all VMs on the host that have not been excluded from PRO
foreach ($cVM in $hostObjVMs){
if($cVM.ExcludeFromPRO -ne $true) {
$candidateVMs = $candidateVMs + $cVM;
}
}

# Checking if QuickMigration is possible and getting all VMHosts available (in same group)
$isQuickMigration = $false;
$hostList = @();
$vmHostGrout = @();
$allhosts = Get-VMHost -VMHostGroup $vmHost.VMHostGroup;

foreach ($tmpHost in $vmHost.VMHostGroup.Hosts) {
if (($tmpHost.FQDN -ne $vmHost.FQDN) -and ($tmpHost.AvailableForPlacement -ne $false)) {
$hostList = $hostList + $tmpHost;
}
}

# Stop if no hosts to migrate to
if (($hostList -eq $null) -or ($hostList.Count -eq 0)) {
FailNoHostToMigrateTo $protipId $vmHostName;
$vmmServer.Disconnect();
return;
}

# Stop if VMs are Excluded from PRO level actions.
if(($candidateVMs.Count -eq 0) -or ($candidateVMs -eq $null)){
FailNoVMToMigrate $protipId $vmHostName;
$vmmServer.Disconnect();
return;
}

# Setting AvailableForPlacement property to false.
if($vmHost.AvailableForPlacement -ne $false){
Set-VMHost $vmHost -AvailableForPlacement $false;
}

foreach( $cVM in $candidateVMs ) {
if(($cVM -eq $null)){
FailNoVMFound $protipId $vmHostName;
$vmmServer.Disconnect();
return;
}
else{
$vmHostRatings = @();
$vmHostRatings = $vmHostRatings + ( @(Get-VMHostRating -VM $cVM -VMHost $hostList -IsMigration -UseDefaultPath) | Sort-Object -property Rating -descending | where { $_.Rating -ne 0 } );
if ( $vmHostRatings.Count -eq $null) {
FailNoHostToMigrateTo $protipId $cVM;
$vmmServer.Disconnect();
return;
}

$selectedVMHost = $hostList | where { ($_.Name -eq $vmHostRatings[0].VMHost.Name) };
if ($selectedVMHost -eq $null) {
FailNoHostToMigrateTo $protipId $vmHostName;
$vmmServer.Disconnect();
return;
}

$Error.Clear();
if ($isQuickMigration) {
if(($selectedVMHost.HostCluster -eq $null) -and ($vmHost.VMHostGroup -eq $selectedVMHost.VMHostGroup) -and ($cVM.ExcludeFromPRO -ne $true )){
Move-VM -VM $cVM -VMHost $selectedVMHost -PROTipId $protipId -RunAsynchronously;
}
}
else {
$defaultPaths = @($selectedVMHost.VMPaths | where { $_.StartsWith($vmHostRatings[0].PreferredVolume) -ge 0 });
if ($defaultPaths.count -ne $null) {
if ($defaultPaths[0] -eq $null) {
FailNoHostToMigrateTo $protipId $vmHostName;
$vmmServer.Disconnect();
return;
}

if(($selectedVMHost.HostCluster -eq $null) -and ($vmHost.VMHostGroup -eq $selectedVMHost.VMHostGroup) -and ($cVM.ExcludeFromPRO -ne $true )){
Move-VM -VM $cVM -VMHost $selectedVMHost -Path $defaultPaths[0] -PROTipId $protipId -RunAsynchronously;;
}
} else {
$defaultPaths = @($selectedVMHost.VMPaths);
if ($defaultPaths[0] -eq $null) {
FailNoHostToMigrateTo $protipId $vmHostName;
$vmmServer.Disconnect();
return;
}

if(($selectedVMHost.HostCluster -eq $null) -and ($vmHost.VMHostGroup -eq $selectedVMHost.VMHostGroup) -and ($cVM.ExcludeFromPRO -ne $true )){
Move-VM -VM $cVM -VMHost $selectedVMHost -Path $defaultPaths[0] -PROTipId $protipId -RunAsynchronously;
}

if ($Error.Count -ne 0) {
FailGenericError $protipId $Error[0];
$vmmServer.Disconnect();
return;
}
}
}
}
}
}

LogInfo("Setting PROTip status to resolved");
#only setting the PROTip to resolved once all Vms has been migrated
Set-PROTip -PROTipId $protipId -TipStatus Resolved;
$vmmServer.Disconnect();
}
#Calling the main method
Main;</Script></ScriptBody>
<SnapIns>
<SnapIn>Microsoft.SystemCenter.VirtualMachineManager</SnapIn>
</SnapIns>
<Parameters>
<Parameter>
<Name>eventContent</Name>
<Value>$Config/EventDescription$</Value>
</Parameter>
</Parameters>
<TimeoutSeconds>90</TimeoutSeconds>
</WriteAction>
</MemberModules>
<Composition>
<Node ID="Recovery"/>
</Composition>
</Composite>
</ModuleImplementation>
<OutputType>System!System.BaseData</OutputType>
<InputType>System!System.BaseData</InputType>
</WriteActionModuleType>