C#调PowerShell在SCVMM中创建虚拟机时,实时显示创建进度

关于c#调用PowerShell来控制SCVMM,网上有很多例子,也比较简单,但创建虚拟机的过程,是一个很漫长的时间,所以一般来说,创建的时候都希望可以实时的显示当前虚拟机的创建进度。当时这个问题困扰了我了一段时间,但找到方法以后,发现其实很简单。

环境:

  Win server 2008 R2 + Hyper-v + SCVMM2008 R2

目的:

  C#调PowerShell在SCVMM中创建虚拟机时,实时显示创建进度

 

在SCVMM2008R2中手动创建一个vm(虚拟机)时,作业界面中会显示很详细的创建进度,包括有哪些子任务,每个任务的完成度、状态等信息。SCVMM的界面操作是基于Powershell的,所以肯定有ps脚本可以实现上述目的。

microsoft提供的创建虚拟机的ps脚本中,提到如下内容(为显示PS脚本中部分内容被回车

$NewVMTasks = [System.Array]::CreateInstance("Microsoft.SystemCenter.VirtualMachineManager.Task"$NumVMs)
$NewVMs = [System.Array]::CreateInstance("Microsoft.SystemCenter.VirtualMachineManager.VM"$NumVMs)
$i = 0
# Loop that creates each VM asynchronously.
while($NumVMs -gt 0)
{
# Generate a unique VM name.
$VMRnd = $Random.next()
$NewVMName = $VMName+$VMRnd
# Get the ratings for each host and sort the hosts by ratings.
$Ratings = @(Get-VMHostRating -Template $Template -VMHost $VMHosts -DiskSpaceGB $DiskSizeGB
 -VMName $NewVMName | where { $_.Rating -gt 0| Sort-Object -property Rating -descending)
if ($Ratings.Count -gt 0
{
$VMHost = $Ratings[0].VMHost
$VMPath = $Ratings[0].VMHost.VMPaths[0]
# Create a new VM from the template and add an additional VHD
# to the VM.
$NewVMJobGroup = [System.Guid]::NewGuid()
$VMAdditionalVhd | Add-VirtualHardDisk -Bus 0 -Lun 1 -IDE -JobGroup $NewVMJobGroup
$NewVMs[$i= New-VM -Template $Template -Name $NewVMName -Description $NewVMName
 -VMHost $VMHost -Path $VMPath -RunAsynchronously -StartVM -JobGroup $NewVMJobGroup
$NewVMTasks[$i= $NewVMs[$i].MostRecentTask
$i = $i + 1
}
$NumVMs = $NumVMs - 1
Start
-Sleep -seconds 5
}

脚本中的Task,其实就是我们所需要的,可以实时显示创建进度的类(Microsoft.SystemCenter.VirtualMachineManager.Task)。

也就是说在通过c#调用powershell创建虚拟时,如果可以得到此类的实例,即可解决问题。

 

在SCVMM提供的ps脚本中,创建虚机的cmdlet为“new-vm .....”后面带了很多参数,比如-name(虚机名字),-path(存储路径),-jobGroup(作业ID,作业ID一般通过 [System.Guid]::NewGuid()获得,唯一标示),-jobVariable(记录当前作业实例)等等,其中的-jobVariable即是我们所需要的。

关于-jobVariable这个参数的详细信息,及实例可以参考微软帮助文档

http://technet.microsoft.com/en-us/library/bb963731.aspx

例如下面的解释很详细了,即调用new-vm命令后,会将当前作业存在-jobvariable 后面指定的变量中。

####################################################################
#
Run a VMM cmdlet that creates a job - in this example script, the
#
cmdlet is New-V2V, so the job is the creation of a new VM from an
#
existing VMware VM.
#
###################################################################
$VM = New-V2V -VMXPath $LegacyVM -VMHost $VMHost -Name $VMName
 -Path $VMPath -Memory $Memory -Runasynchronously -Jobvariable "Job"


####################################################################
#
Track the status of the running job.
#
###################################################################
$JobNameString = $Job.CmdletName+" "+$Job.ResultName

# Loop while the job is running, writing progress using current step
#
and progress values from the job.
while ($job.status -eq "Running"


Write
-Progress -Activity "$JobNameString" -Status $Job.CurrentStep
 -PercentComplete $Job.ProgressValue;

Start
-Sleep -seconds 5
}

-jobvariable 一般跟-Runasynchronously参数一起使用,因为你获得了job变量当然希望ps异步执行了,

 

在c#中如何使用-jobvariable参数?请参考下面代码中红色部分

private void CreateVM(Host host
           , Template template
           , HardwareProfile hardwareProfile
           , Runspace runspace)
{
  Command newVM = new Command("new-vm");
newVM.Parameters.Add(
"vmhost", host);
newVM.Parameters.Add(
"Template", template);
newVM.Parameters.Add(
"hardwareprofile", hardwareProfile);
newVM.Parameters.Add(
"name", newVMName);
newVM.Parameters.Add(
"Description"
            , string.Format("vm created by {0} on {1}"
                      , Environment.UserName, DateTime.Now));
  newVM.Parameters.Add(
"path", host.VMPaths[0]);
  newVM.Parameters.Add("owner"this.owner);
  newVM.Parameters.Add("startvm"true);
  newVM.Parameters.Add(
"jobgroup"this.jobGroupID);
  newVM.Parameters.Add(
"jobvariable""newvmtask");
  newVM.Parameters.Add(
"runAsynchronously"true);  
  using (Pipeline pipeline = GetCommandPipe(runspace))
  {
    pipeline.Commands.Add(newVM);

Collection
<PSObject> result = pipeline.Invoke();
pipeline.Stop();

Task task
= runspace.SessionStateProxy.GetVariable("newvmtask"as Task;
task.Updated
+= new EventHandler<ObjectChangedEventArgs>(task_Updated);
}
}

private
void task_Updated(object sender, ObjectChangedEventArgs e)
  if (OnTaskProcess != null)
{
Task taskTemp
= e.Object as Task;
this.OnTaskProcess(taskTemp);
}
}

上述代码中,很多东西不全,只是介绍如何取到-jobvariable参数指定的Task实例,在Command中添加-jobvariable

//Task对象放于名为newvmtask的变量中,此处随便定义。
newVM.Parameters.Add(
"jobvariable""newvmtask");
newVM.Parameters.Add("runAsynchronously"true);//指定cmdlet异步执行

在异步执行完后,在Runspace的Session中可以取到之前指定的Task实例,(此处为newvmtask)

 Task task = runspace.SessionStateProxy.GetVariable("newvmtask") as Task;
此处的newvmtask是依据在command中-jobvariable参数指定的实例名。

取到Task对象实例后,即可通过其update事件,来实时显示Task的处理进度。

task.Updated += new EventHandler<ObjectChangedEventArgs>(task_Updated);

在task_Updated中可以作自己想做的事情。

Microsoft.SystemCenter.VirtualMachineManager.Task

Task类中存在一个属性Steps,类型为List<Step>,可以通过此属性,浏览一个作业的子过程的运行状况。

Step为Microsoft.SystemCenter.VirtualMachineManager.Step类

 

顺便提一下,SCVMM2008R2中powershell的Snapin为"Microsoft.SystemCenter.VirtualMachineManager"。

posted @ 2014-06-08 10:39  zzg168  阅读(218)  评论(0)    收藏  举报