VMware Cloud Community
JLogan2016
Enthusiast
Enthusiast
Jump to solution

Capturing the progress of a task and outputting to another application

I have been working for a while now on a VM creation script for a customer, to allow them to add a VM through either a CSV import or through a GUI. The GUI is written in another language, and relies on capturing the Stdout from the powerCLI script and outputting to a edit control in the GUI so the technicians can follow the progress. Everything thus far is working just fine.

I had an addendum request that I add some sort of progress message to tasks that take a little longer, such as the New-VM call so the technicians (lower level) can see that the script is not hanging. I know that when running the PowerCLI script directly you get the typical powershell progress bar. I am wondering if there is any way to pull out of that just the percentage of completion, so I can output to the external GUI.

I could use something like Write-Progress if I were calling New-VM in a loop against a number of machines, but in the case of a single VM creation it doesn't look like it will do the job. I also looked at Wait-Task, but that does not seem output anything that I can capture. I would be interested if anyone has any suggestions.

Reply
0 Kudos
1 Solution

Accepted Solutions
LucD
Leadership
Leadership
Jump to solution

No, it's because you are not specifying the RunAsync switch.

Then the New-VM cmdlet will not return a Task object.
Add a RunAsync = $true to the splat hash table.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

View solution in original post

Reply
0 Kudos
14 Replies
jasnyder
Hot Shot
Hot Shot
Jump to solution

Can you add the -runasync parameter to the New-VM cmdlet and grab the output, which is a task?  Then you can check percentcomplete:

$task = new-vm -name test3 -runasync [otheroptions here]

WriteOutput $task.PercentComplete

The percent complete should update as the task continues, so you may want to add a sleep/poll cycle or whatever makes sense for your application.

Reply
0 Kudos
jasnyder
Hot Shot
Hot Shot
Jump to solution

And by the way the new VM would be in the $task.Result object, assuming the task completes successfully.

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

I'm afraid a Task object doesn't refresh automatically, so you'll have to do this in a While-loop.

The progress can be displayed from the Task object's '% Complete' property.

Something like this (you can eventually place a 'sleep' before the Get-Task in the While-loop

$t = New-VM -Name NewVM -Template stress -Datastore MyDS -VMHost MyEsx -RunAsync

while('Running','Queued' -contains $t.State){

    Write-Progress -Activity 'Creating VM' -PercentComplete $t.PercentComplete

    $t = Get-Task -Id $t.Id

}

Write-Output "Task '$($t.Description)/$($t.Id)' for $(Get-View -Id $t.ObjectId -Property Name | select -ExpandProperty Name) ended with status $($t.State)"


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
jasnyder
Hot Shot
Hot Shot
Jump to solution

I think this may be a difference in PowerCLI versions, but your script doesn't work because the $t.ObjectId is a GUID and the get-task -Id parameter wants a MOID.  I ran into the same problem when writing my first response.  I tested on 6.5R1 and 6.5.3 and got the same result, but noticed that the task is auto-refreshing.

Example error from your script:

Get-Task : 12/11/2017 1:26:10 PM    Get-Task            The identifier 9b29eecf-fa92-4799-968f-8caf0be408db resulted in no objects.

At newvmtest.ps1:9 char:10

+ $t = Get-Task -Id $t.Id
+      ~~~~~~~~~~~~~~~~~~
+ CategoryInfo      : ObjectNotFound: (:) [Get-Task], VimException
+ FullyQualifiedErrorId : Client20_OutputTracker_ReportNotFoundLocators_LocatorNotProduced,VMware.VimAutomation.Vi

   Core.Cmdlets.Commands.GetTask

Get-View : Cannot validate argument on parameter 'Id'. The argument is null or empty. Provide an argument that is not

null or empty, and then try the command again.

At newvmtest.ps1:13 char:68

+ ... k '$($t.Description)/$($t.Id)' for $(Get-View -Id $t.ObjectId -Proper ...

+                                                   ~~~~~~~~~~~
+ CategoryInfo      : InvalidData: (:) [Get-View], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,VMware.VimAutomation.ViCore.Cmdlets.Commands.DotNetInte

   rop.GetVIView

This works, however:

$t = New-VM -Name test11 -VMHost host -RunAsync

Write-Output $t.PercentComplete

while($t.PercentComplete -ne 100) {

    Write-Output $t.PercentComplete

    Start-Sleep -s 5

}

Write-Output $t.PercentComplete

Sample Output:

0

0

100

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

Not sure what exactly you are doing, but there is no auto-update on properties.

See the about_runasync text "...This state is not automatically updated and has one of the following values - Error, Queued, Running, and Success. To refresh the task status, pass the Task object to the Get-Task cmdlet...."

Also the Get-Task cmdlet is expecting a [String] representation of the Task MoRef on the Id parameter, and the Id property of a returned Task object is exactly that.

And it is not a PowerCLI version thing either.

I checked the Id parameter and the Id property all the way back to PowerCLI 5.5, it is a [string] in all those versions.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
jasnyder
Hot Shot
Hot Shot
Jump to solution

The issue isn't the type, but that the value passed back in the id field is a GUID (in string format).  So when you pass that into the -id parameter of the get-task call it doesn't find anything.  Passing the object results in the same problem.

What version of vSphere are you running?  I'm on 6.5 Update 1b - build 6816762

So maybe it's a bug there returning the wrong value for ID, thus breaking this chain.  This would be on the server side vs. client side.

As for why the PercentComplete updates itself... I assure you that I haven't hacked anything to cause this to happen and if I open the vSphere client side by side with the script, the status corresponds to what I receive in the task.PercentComplete value.  If I take off the sleep, I can see several incremental updates... 2.5, 50, 100, etc.

Everything that I'm doing is in the scripts I posted.

Reply
0 Kudos
JLogan2016
Enthusiast
Enthusiast
Jump to solution

Thanks for the suggestions. I get an error about ID being null or empty, whether I run it directly from PowerCLI or through the GUI. Out of curiosity, would this be due to it being contained in a try/catch block? Or perhaps the use of splat? Also, I have noticed (and this may well just be a limitation of the GUI script) that only what I write out with Write-Host is properly captured. Write-Output is often sporadic in whether it is caught. I will have to anonymize the code a bit to show the entire build function, but the relevant piece is below:

#Section 1 determine single datastore or cluster

   ...

#Section 2 If not in A.D. add

   ...

#Section 3 Create Customization Spec

   ...

#Section 4 Configure NIC Mappings on Customization Spec

   ...

#Section 5 Create the VM

            Write-Host "Creating the New VM....."

            Start-Sleep -Milliseconds 100

            $sVM = @{

                Name = $oVM.Name

                Template = Get-Template $oVM.Template -Location $oVM.Site

                DataStore = $sDStoreOutput

                OSCustomizationSpec = $oVM.Name

            }

            $esx = Get-VMHost -Name $oVM.Host -ErrorAction SilentlyContinue

            $cluster = Get-Cluster -Name $oVM.Host -ErrorAction SilentlyContinue

                if($esx){

                    $sVM.Add('VMHost',$esx)

                }elseif($cluster){

                    $sVM.Add('ResourcePool',$cluster)

                }else{

                    Write-Host "Cannot find Host or Cluster, please check spelling"

                }

            Try {

               $t = New-VM @sVM

                  While('Running','Queued' -contains $t.State) {

                     Write-Progress -Activity 'Creating VM' -PercentComplete $t.PercentComplete

                     $t = Get-Task -Id $t.ID

                  }

                Write-Host "New VM" $oVM.Name "created"

                Start-Sleep -Seconds 5

            } catch [Exception] {

                Write-Host "Unable to create VM. Error is: " $Error

                Exit

            }

The VM is created, though I see no output as to progress, but then it fails with the error below. It seems as if the object is never created, or at least the script cannot access the ID member:

Unable to create VM. Error is:  Cannot validate argument on parameter 'Id'. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again.

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

No, it's because you are not specifying the RunAsync switch.

Then the New-VM cmdlet will not return a Task object.
Add a RunAsync = $true to the splat hash table.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
JLogan2016
Enthusiast
Enthusiast
Jump to solution

Bah, sorry I missed that part. That does it. Thanks again

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

I'm on the same vSphere version.

Look at the following example.

After the task completes, the Task object still shows running and 0%

task.jpg

And the Id field is not a Guid, but a string representation of a MoRef to a Task object


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
JLogan2016
Enthusiast
Enthusiast
Jump to solution

That is odd. What I ended up doing was just grabbing the PercentComplete piece like so:

            Try {

               $t = New-VM @sVM -RunAsync

                  While('Running','Queued' -contains $t.State) {

                     Write-Host "...... $($t.PercentComplete)%"

                     Start-Sleep 20

                     $t = Get-Task -Id $t.ID

                  }

                Write-Host "...... 100%"

                Write-Host "New VM" $oVM.Name "created"

                Start-Sleep -Seconds 5

             ......

Seems to work well in both PowerCLI and the GUI.

Reply
0 Kudos
jasnyder
Hot Shot
Hot Shot
Jump to solution

OK so the reason for the difference is that I was not cloning from template, but simply creating a new VM.  PowerCLI returns a different type of task object depending on which route you take, either a client side task or (presumably) a server side task, although it is not named as such.

Here is a call to just create a VM:

PS> $t = New-VM -Name test14 -VMHost host -RunAsync

PS > $t.GetType()

IsPublic IsSerial Name                                     BaseType

-------- -------- ----                                     --------

True     False    ClientSideTaskImpl                       VMware.VimAutomation.Sdk.Util10.Task.ClientSideTaskImpl

Here is a call to clone (note it is intentionally a failure by calling to create a VM of a pre-existing name to make it happen more quickly):

PS> $x = New-VM -Name test14 -Template template -VMHost host -RunAsync

PS > $x.GetType()

IsPublic IsSerial Name                                     BaseType

-------- -------- ----                                     --------

True     False    TaskImpl                                 VMware.VimAutomation.ViCore.Impl.V1.VIObjectImpl

As you can see, the object types are different between the two calls to the same cmdlet.  Why there is a difference in these two, I can only surmise the reason being that the PowerCLI devs understand that some tasks run faster than others, and created a ClientSideTask implementation to handle this (i.e. automatically updates itself because of the quick return). 

You wouldn't expect it given that the vSphere Web Services API handles a VM creation using a call to CreateVM_Task() which returns a Managed Object reference to a task.  It handles a clone operation in the same manner, with a call to CloneVM_Task() which also returns a Managed Object Reference to a task.  In both cases, if you are coding against the API directly, you have to periodically check in with the server to get the task information using the Managed Object Reference to the task.

PowerCLI decided for some reason to make a difference between these two calls and return different object types, although I am sure their implementation is exactly as I described above.

So using the above example of $t being a CreateVM_Task (client side) and $x being a CloneVM_Task (server side, for lack of a better term), here are the properties:

PS > $t | select *

Client                  :

CmdletTaskInfo          :

ExtensionData           :

Id                      : 1d20d6be-e872-4a46-8a8d-83b0c9d71e79

IsCancelable            : False

Result                  : test14

Name                    : CreateVM_Task

Description             : Create virtual machine

State                   : Success

PercentComplete         : 100

StartTime               : 12/11/2017 2:28:51 PM

FinishTime              : 12/11/2017 2:28:52 PM

ObjectId                :

Uid                     : /Local=/ClientSideTask=1d20d6be-e872-4a46-8a8d-83b0c9d71e79/

NonTerminatingErrorList : {}

TerminatingError        :

PS > $x | select *

ServerId        : /VIServer=lab\jsnyder@hwlvcs01.lab.local:443/

State           : Running

IsCancelable    : True

PercentComplete : 0

StartTime       : 12/11/2017 2:30:49 PM

FinishTime      :

ObjectId        : VirtualMachine-vm-179

Result          :

Description     : Clone virtual machine

ExtensionData   : VMware.Vim.Task

Id              : Task-task-84405

Name            : CloneVM_Task

Uid             : /VIServer=lab\jsnyder@hwlvcs01.lab.local:443/Task=Task-task-84405/

Client          : VMware.VimAutomation.ViCore.Impl.V1.VimClient

CmdletTaskInfo  :

As you can see, the ID field in a "client side" task is a GUID and the ID field in the "not client side" task is a Managed Object Reference.

And as you say, the second one ($x) does not automatically update itself, whereas the first one ($t) does.

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

But you are doing a Get-Task as well in the While loop.

That's why the field is updated.

Which Gui do you mean? The Web Client?


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
JLogan2016
Enthusiast
Enthusiast
Jump to solution

No, sorry, I meant the GUI I wrote around this script. It has an output pane like so:

GUI.png

Reply
0 Kudos