Handling Job Objects with Hyper-V WMI scripting

Handling Job Objects with Hyper-V WMI scripting

  • Comments 3

Many of the methods in the Hyper-V WMI have an odd way of returning status.  They can either immediately return a return value - or instead return a job object - and it can be hard to predict which one you are going to get.  The reason for this is that some methods are always synchronous (and return a return value), some methods are always asynchronous (and return a job object) while other methods can be executed synchronously or asynchronously on the back end by WMI, and could return either.

The job object returned by asynchronous methods allows you to track the progress of the operation, and get detailed error information in the case of a failure.

Given this you have two options when working with our WMI interfaces:

  1. Ignore method results.

    This is simple and for many scripts is acceptable, as you can figure out whether the method succeeded or not through observing the results.

  2. Do the work to handle the results.

    Thankfully, Hyper-V does help a little here.  All methods return two objects: a return value and a job path.  If the return value is anything but "4096" then you know the method executed synchronously - and a return value of "1" means success.  If the return value is "4096" then you need to go and look at the job object.

    Methods that always complete asynchronously will always return "4096" in the return value.

Digging into the second option - and starting with this script for mounting a virtual hard disk - let's look at how to add proper support for handling the method results.

This is relatively simple with PowerShell where:

#Specify the VHD to be mounted
$VHDName = "F:\Windows.vhd" 
 
#Get the MSVM_ImageManagementService
$VHDService = get-wmiobject -class "Msvm_ImageManagementService" -namespace "root\virtualization" -computername "." 
 
#Mount the VHD
$Result = $VHDService.Mount($VHDName)

Becomes:

#Specify the VHD to be mounted
$VHDName = "F:\Windows.vhd" 
 
#Get the MSVM_ImageManagementService
$VHDService = get-wmiobject -class "Msvm_ImageManagementService" -namespace "root\virtualization" -computername "." 
 
#Mount the VHD
$Result = $VHDService.Mount($VHDName)
 
#Return success if the return value is "0"
if ($Result.ReturnValue -eq 0)
   {write-host "Operation completed sucessfully"}
 
#If the return value is not "0" or "4096" then the operation failed
ElseIf ($Result.ReturnValue -ne 4096)
   {write-host "Operation failed"}
 
 
Else
   {#Get the job object
    $job=[WMI]$Result.job
    
    #Provide updates if the jobstate is "3" (starting) or "4" (running)
    while ($job.JobState -eq 3 -or $job.JobState -eq 4)
      {write-host $job.PercentComplete
       start-sleep 1
    
       #Refresh the job object
       $job=[WMI]$Result.job}
 
    #A jobstate of "7" means success
    if ($job.JobState -eq 7)
       {write-host "Operation completed sucessfully"}
      Else
       {write-host "Operation failed"
        write-host "ErrorCode:" $job.ErrorCode
        write-host "ErrorDescription" $job.ErrorDescription}
   }

As you can see, the only addition is code to inspect and respond to the return value and job object.

In VBScript life is harder:

Option Explicit 
 
Dim WMIService
Dim VHDService
Dim VHD 
 
'Specify the VHD to be mounted
VHD = "F:\Windows.vhd" 
 
'Get instance of 'virtualization' WMI service on the local computer
Set WMIService = GetObject("winmgmts:\\.\root\virtualization")  
 
'Get the MSVM_ImageManagementService
Set VHDService = WMIService.ExecQuery("SELECT * FROM Msvm_ImageManagementService").ItemIndex(0) 
 
'Mount the VHD
VHDService.Mount(VHD)

Becomes:

Option Explicit
 
Dim WMIService
Dim VHDService
Dim VHD 
Dim Result
Dim Job
Dim InParam
Dim OutParam
 
'Specify the VHD to be mounted
VHD = "C:\Hyper-V Virtual Machines\Virtual Hard Disks\SCVMM - Domain Controller.vhd" 
 
'Get instance of 'virtualization' WMI service on the local computer
Set WMIService = GetObject("winmgmts:\\.\root\virtualization")  
 
'Get the MSVM_ImageManagementServiceSet 
Set VHDService = WMIService.ExecQuery("SELECT * FROM Msvm_ImageManagementService").ItemIndex(0)
 
'Setup the input parameter list
Set InParam = VHDService.Methods_("mount").InParameters.SpawnInstance_()
InParam.Path = VHD 
 
'Execute the method and store the results in OutParam
Set OutParam = VHDService.ExecMethod_("mount", InParam)
 
'Check to see if the jub completed synchronously
if (OutParam.ReturnValue = 0) then
   Wscript.Echo "Operation succeeded"
elseif (OutParam.ReturnValue <> 4096) then
   Wscript.Echo "Operation failed"
else
   
   'Get the job object
   set Job = WMIService.Get(OutParam.Job)
 
   'Wait for the job to complete (3 == starting, 4 == running)
   while (Job.JobState = 3) or (Job.JobState = 4)
      Wscript.Echo Job.PercentComplete
      WScript.Sleep(1000)
      
      'Refresh the job object
      set Job = WMIService.Get(OutParam.Job)
   Wend
 
   'Provide details if the job fails (7 == complete)
   if (Job.JobState <> 7) then
      Wscript.Echo "Operation failed"
      Wscript.Echo "ErrorCode:" & Job.ErrorCode
      Wscript.Echo "ErrorDescription:" & Job.ErrorDescription
   else
      Wscript.Echo "Operation succeeded"
   end If
end if

Yikes!  The issue here is that when you call a method under VBScript directly you do not get both the return value and job path in the output.  You just get the return value.  In order to get both output parameters you need to use "ExecMethod_", which means you need to make an instance of the input parameters, populate it and pass it to ExecMethod - along with the name of the method you want to run.

Cheers,
Ben

Leave a Comment
  • Please add 1 and 4 and type the answer here:
  • Post
  • Sorry about the off-topic/side-topic... how do you do the layout of the code? I don't think you write all those span tags by hand (unless you know how to dilate time).

    Thanks in advance.

  • I get an error trying to to mount a disk:

    Operation failed

    ErrorCode: 32769

    ErrorDescription The system failed to mount 'C:\Hyper-V\base-xp-pro-sp3-eng-x86.vhd' with

    error 'General access denied error.' (0x80070005)

    I'm not sure why this is happening.

  • oreidomar -

    I use Live Writer and the "insert code snippet" plugin

    Joe -

    Try running your script from an elevated command prompt

    Cheers,

    Ben

Page 1 of 1 (3 items)