VMware Cloud Community
ssheikhgpo
Contributor
Contributor

Scheduled task for cross-site vMotion

I am trying to write a script that would create a scheduled task for a cross-site vMotion. I can create that scheduled task through the GUI and it works. The environment is 3 linked 7.0 sites. From PowerShell there are connected VISessions to all three VC servers.   

When trying to do it through the script below, If I make sure sure that $scheduledTaskManager instance is from the VC where the VM is currently located then it throws errors for objects that are on the destination site. Errors like:

Exception calling "CreateScheduledTask" with "2" argument(s): "The object 'vim.ResourcePool:resgroup-1002' has already been deleted or has not been completely created"
At C:\Scripts\MigrateTo-IMDC\Create-VMMoveScheduledTask.ps1:123 char:12
+ return ($scheduledTaskManager.CreateScheduledTask($vmView.MoRef, ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : VimException

If I remove the resource pool then it complains about the datastore:

Exception calling "CreateScheduledTask" with "2" argument(s): "The object 'vim.Datastore:datastore-1411' has already been deleted or has not been completely created"
At C:\Scripts\MigrateTo-IMDC\Create-VMMoveScheduledTask.ps1:123 char:5
+ $scheduledTaskManager.CreateScheduledTask($vmView.MoRef, $spec)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : VimException

If I remove the datastore then it complains about the destination Host. And so on.

If I let $scheduledTaskManager enumerate ScheduledTaskManagers from all 3 sites then it cant find the VM:

Exception calling "CreateScheduledTask" with "2" argument(s): "The object 'vim.VirtualMachine:vm-107863' has already been deleted or has not been completely created"
At C:\Scripts\MigrateTo-IMDC\Create-VMMoveScheduledTask.ps1:123 char:12
+ return ($scheduledTaskManager.CreateScheduledTask($vmView.MoRef, ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : VimException

 

My script is as follows. Any help would be much appreciated.

function Create-VMMoveScheduledTask {
    Param(
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine]
        $VM,

        [Alias("Folder")]
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [VMware.VimAutomation.ViCore.Types.V1.Inventory.Folder]
        $DestinationFolder,

        [Alias("VMHost")]
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [VMware.VimAutomation.ViCore.Types.V1.Inventory.VMHost]
        $DestinationHost,

        [Alias("Datastore")]
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [VMware.VimAutomation.ViCore.Types.V1.DatastoreManagement.Datastore]
        $DestinationDatastore,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        # Array of Hashes. Keys = MACAddress. Values = PortGroup (object).
        [ValidateScript({
            if ($_.keys | Where-Object {$_ -notmatch "^[a-fA-F0-9]{2}([:-]?[a-fA-F0-9]{2}){5}$"}) {return $false}
            if ($_.values | Where-Object {!($_ -is [VMware.VimAutomation.Vds.Types.V1.VDPortgroup])}) {return $false}
            return $true;
        })]
      $vNICPortGroups,

        [
Alias("Name")]
        [Parameter(Mandatory = $true)]
        [String]
        $TaskName = "Create-VMMoveScheduledTask $(Get-Date -Format G)",      

        [
Alias("Description")]
        [Parameter(Mandatory = $true)]
        [String]
        $TaskDescription = "Create-VMMoveScheduledTask $(Get-Date -Format G)",

        [Parameter(Mandatory = $true)]
        [ValidateScript({([DateTime]$_) -gt (([DateTime]::Now).AddSeconds(15)) -and ([DateTime]$_) -lt (([DateTime]::Now).AddDays(7))})]
        [DateTime]
        $StartTime,

        [Alias("Email")]
        [Parameter()]
        [ValidatePattern("^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$")]
        [string]
        $AlertEmail
    )

    #$si = Get-View ServiceInstance -Server ($VM.Uid -replace "^.*@(.*):.*$",'$1')
    $si = Get-View ServiceInstance
    #$scheduledTaskManager = Get-View $si.Content.ScheduledTaskManager -Server ($VM.Uid -replace "^.*@(.*):.*$",'$1')
    $scheduledTaskManager = Get-View $si.Content.ScheduledTaskManager

    $vmView = $VM | Get-View

    # ScheduledTask specs
    $spec = New-Object VMware.Vim.ScheduledTaskSpec
    $spec.Name = $TaskName
    $spec.Description = $TaskDescription
    $spec.Enabled = $true
    if ($AlertEmail) {$spec.Notification = $AlertEmail}
    $spec.Scheduler = New-Object VMware.Vim.OnceTaskScheduler
    $spec.Scheduler.runat = $StartTime
    $spec.Action = New-Object VMware.Vim.MethodAction
    $spec.Action.Name = "RelocateVM_Task"

    # vMotion specs
    $arg1 = New-Object VMware.Vim.MethodActionArgument
    $arg1.Value = New-Object VMware.Vim.VirtualMachineRelocateSpec

    # ServiceLocator for destination - we should not need this
    #$arg1.Value.service = New-Object VMware.Vim.ServiceLocator
    #$arg1.Value.service.instanceUuid = ($global:defaultviservers | Where-Object {$DestinationHost.Uid -match  $_.Name}).InstanceUuid.toUpper()

    # Destination Datastore
    $arg1.Value.datastore = $DestinationDatastore.Id

   
# Destination Host
    $arg1.Value.host = $DestinationHost.Id

    # Destination ResourcePool
    if ($DestinationHost.ParentId -match "Cluster") {
        $arg1.Value.pool = ($DestinationHost | Get-Cluster).ExtensionData.ResourcePool
    }
    else {
        $arg1.Value.pool = $DestinationHost.ExtensionData.ResourcePool
    } #

   
# Destination Folder
    $arg1.Value.folder = ($DestinationFolder | Get-View).MoRef

    # vNIC PortGroups
    $arg1.Value.deviceChange = New-Object VMware.Vim.VirtualDeviceConfigSpec
    $VMNICs = $VM | Get-NetworkAdapter
    $VMNICs | ForEach-Object {$i = 0} {
        $arg1.Value.deviceChange[$i] = New-Object VMware.Vim.VirtualDeviceConfigSpec
        $arg1.Value.deviceChange[$i].Operation = "edit"
        $arg1.Value.deviceChange[$i].device = $_.ExtensionData
        $arg1.Value.deviceChange[$i].device.backing = New-Object VMware.Vim.VirtualEthernetCardDistributedVirtualPortBackingInfo
        $arg1.Value.deviceChange[$i].device.backing.port = New-Object VMware.Vim.DistributedVirtualSwitchPortConnection
        $arg1.Value.deviceChange[$i].device.backing.port.SwitchUuid = ($vNICPortGroups.($_.MacAddress) | Get-VDSwitch | Get-View).Uuid
        $arg1.Value.deviceChange[$i].device.backing.port.PortgroupKey = $vNICPortGroups.($_.MacAddress).Key
        $i++
    }

    $spec.Action.Argument += $arg1

    # Set vMotion priority
    $arg2 = New-Object VMware.Vim.MethodActionArgument
    $arg2.Value = [VMware.Vim.VirtualMachineMovePriority]"defaultPriority"
    $spec.Action.Argument += $arg2

    # Create scheduledTask
    return ($scheduledTaskManager.CreateScheduledTask($vmView.MoRef, $spec))
}

 

thx.

0 Kudos
4 Replies
LucD
Leadership
Leadership

You say that you are connected to the 3 vCenters, but that would mean that lines like

Get-View ServiceInstance

return 3 objects.
You could try using the Server parameter to point to the correct vCenter.

On another note, when you run the actual vMotion code alone (not as a scheduled task) does it work?


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

0 Kudos
ssheikhgpo
Contributor
Contributor

Thanks for the quick reply. 

Just tried to invoke RelocateVM_Task method on the VMview object and still get the same error.

Exception calling "RelocateVM_Task" with "2" argument(s): "The object 'vim.ResourcePool:resgroup-1002' has already been deleted or has not been completely created"
At C:\Scripts\MigrateTo-IMDC\Create-VMMoveScheduledTask.ps1:124 char:5
+ $vmView.RelocateVM_Task(($arg1.Value), 'defaultPriority')
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : VimException

 

Wondering if I have to create a ServiceLocator for the destination VC and give the script credentials to connect to it before it will see the objects at the destination site. Would be a bummer if thats the case.  

 

Is it possible to get the ServiceLocator object from the connected VISession?

0 Kudos
LucD
Leadership
Leadership

Not sure.
Can you check the vpxd log on the source vCenter.
There might be additional info in there


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

0 Kudos
ssheikhgpo
Contributor
Contributor

Thank you for your quick help. I think I've got it working. It seems like when moving a VM to another site one must provide the ServiceLocator for the destination vCenter even if it the destination is a linked site. And sadly to create the ServiceLocator object one must provide it with the credentials to connect to that vCenter even if its already connected.   

Function that seems to be working for me now is:

function Create-VMMoveScheduledTask {
    Param(
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [VMware.VimAutomation.ViCore.Types.V1.Inventory.VirtualMachine]
        $VM,

        [Alias("Folder")]
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [VMware.VimAutomation.ViCore.Types.V1.Inventory.Folder]
        $DestinationFolder,

        [Alias("VMHost")]
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [VMware.VimAutomation.ViCore.Types.V1.Inventory.VMHost]
        $DestinationHost,

        [Alias("Datastore")]
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [VMware.VimAutomation.ViCore.Types.V1.DatastoreManagement.Datastore]
        $DestinationDatastore,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        # Array of Hashes. Keys = MACAddress. Values = PortGroup (object).
        [ValidateScript({
            if ($_.keys | Where-Object {$_ -notmatch "^[a-fA-F0-9]{2}([:-]?[a-fA-F0-9]{2}){5}$"}) {return $false}
            if ($_.values | Where-Object {!($_ -is [VMware.VimAutomation.Vds.Types.V1.VDPortgroup])}) {return $false}
            return $true;
        })]
        $vNICPortGroups,

        [Alias("Name")]
        [Parameter(Mandatory = $true)]
        [String]
        $TaskName = "Create-VMMoveScheduledTask $(Get-Date -Format G)",      
        [Alias("Description")]

        [Parameter(Mandatory = $true)]
        [String]
        $TaskDescription = "Create-VMMoveScheduledTask $(Get-Date -Format G)",
        [Parameter(Mandatory = $true)]
        [ValidateScript({([DateTime]$_) -gt (([DateTime]::Now).AddSeconds(15)) -and ([DateTime]$_) -lt (([DateTime]::Now).AddDays(7))})]
        [DateTime]
        $StartTime,

        [Alias("Email")]
        [Parameter()]
        [ValidatePattern("^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$")]
        [string]
        $AlertEmail,

        [Alias("Credential")]
        [Parameter(Mandatory = $true)]
        [pscredential]
        $DestinationVCCredential
    )

    $SourceVC = $VM.Uid -replace "^.*@(.*):.*$",'$1'
    $DestinationVC = $DestinationHost.Uid -replace "^.*@(.*):.*$",'$1'
    $si = Get-View ServiceInstance -Server $SourceVC
    $scheduledTaskManager = Get-View $si.Content.ScheduledTaskManager -Server $SourceVC

    $vmView = $VM | Get-View

    # ScheduledTask specs
    $spec = New-Object VMware.Vim.ScheduledTaskSpec
    $spec.Name = $TaskName
    $spec.Description = $TaskDescription
    $spec.Enabled = $true
    if ($AlertEmail) {$spec.Notification = $AlertEmail}
    $spec.Scheduler = New-Object VMware.Vim.OnceTaskScheduler
    $spec.Scheduler.runat = $StartTime
    $spec.Action = New-Object VMware.Vim.MethodAction
    $spec.Action.Name = "RelocateVM_Task"

    # vMotion specs
    $arg1 = New-Object VMware.Vim.MethodActionArgument
    $arg1.Value = New-Object VMware.Vim.VirtualMachineRelocateSpec

    # ServiceLocator for destination - we should not need this ?
    $arg1.Value.service = New-Object VMware.Vim.ServiceLocator
    $arg1.Value.service.instanceUuid = ($global:defaultviservers | Where-Object {$DestinationHost.Uid -match  $_.Name}).InstanceUuid.toUpper()
    $ServiceCred = New-Object VMware.Vim.ServiceLocatorNamePassword
    $ServiceCred.username = $DestinationVCCredential.UserName
    $ServiceCred.password = $DestinationVCCredential.GetNetworkCredential().Password
    $arg1.Value.service.credential = $ServiceCred
    $arg1.Value.service.InstanceUuid = ($global:defaultviservers | Where-Object {$_.Name -like $DestinationVC}).InstanceUuid
    # $arg1.Value.service.sslThumbprint = $thumbprint
    $arg1.Value.service.url = ("https://" + $DestinationVC)

    # Destination Datastore
    $arg1.Value.datastore = $DestinationDatastore.Id

    # Destination Host
    $arg1.Value.host = $DestinationHost.Id

    # Destination ResourcePool
    if ($DestinationHost.ParentId -match "Cluster") {
        $arg1.Value.pool = ($DestinationHost | Get-Cluster).ExtensionData.ResourcePool
    }
    else {
        $arg1.Value.pool = $DestinationHost.ExtensionData.ResourcePool
    } #

    # Destination Folder
    $arg1.Value.folder = ($DestinationFolder | Get-View).MoRef

    # vNIC PortGroups
    $arg1.Value.deviceChange = New-Object VMware.Vim.VirtualDeviceConfigSpec
    $VMNICs = $VM | Get-NetworkAdapter
    $VMNICs | ForEach-Object {$i = 0} {
        if ($i) {$arg1.Value.deviceChange += New-Object VMware.Vim.VirtualDeviceConfigSpec}
        $arg1.Value.deviceChange[$i].Operation = "edit"
        $arg1.Value.deviceChange[$i].device = $_.ExtensionData
        $arg1.Value.deviceChange[$i].device.backing = New-Object VMware.Vim.VirtualEthernetCardDistributedVirtualPortBackingInfo
        $arg1.Value.deviceChange[$i].device.backing.port = New-Object VMware.Vim.DistributedVirtualSwitchPortConnection
        $arg1.Value.deviceChange[$i].device.backing.port.SwitchUuid = ($vNICPortGroups.($_.MacAddress) | Get-VDSwitch | Get-View).Uuid
        $arg1.Value.deviceChange[$i].device.backing.port.PortgroupKey = $vNICPortGroups.($_.MacAddress).Key
        $i++
    }

    $spec.Action.Argument += $arg1

    # Set vMotion priority
    $arg2 = New-Object VMware.Vim.MethodActionArgument
    $arg2.Value = [VMware.Vim.VirtualMachineMovePriority]"defaultPriority"

    $spec.Action.Argument += $arg2

    # Create scheduledTask
    return ($scheduledTaskManager.CreateScheduledTask($vmView.MoRef, $spec))
}

 

0 Kudos