1. 程式人生 > >Hyper-V配置(四)

Hyper-V配置(四)

上一篇貼了建立的指令碼,這篇說一下怎麼在C#下呼叫:

此部分程式碼包含兩種方法: Invoke 和 BeginInvoke ,兩者的區別就是後者導致工作執行緒等待,前者不會。因為關機要等到他關機結束以後虛擬機器的狀態顯示才是off,所以要用BeginInvoke等待他關機結束;而開機的話不需要,因為從接受到start虛擬機器命令開始虛擬機器的狀態就是running。

private static string startVMTemplet = @"function StartVM
{
    param
    (
    [string]$requestVMName = $(throw ""param -requestVMName is required."")
    )
  Start-VM -Name $requestVMName
}";

private static string stopVMTemplet = @"function StopVM
{
  param
    (
    [string]$requestVMName = $(throw ""param -requestVMName is required."")
    )
  Stop-VM -Name $requestVMName -force
}";

/*
 * vmname: VM name
 * return: Running for success case and Off for fail
 */
public VMState StartVM(string vmname)
{
    if (vmname == null || vmname.Equals(""))
    {
        LogHelper.Write("error--StartHv: Name can not be empty!");
        return 0;
    }

    try
    {
        string runScript = startVMTemplet;
        using (PowerShell psInstance = PowerShell.Create())
        {
            Collection<PSObject> psOutput = null;

            // add hyperV management script, and invoke execution
            psInstance.AddScript(runScript);
            psOutput = psInstance.Invoke();

            // run specified command
            psInstance.AddCommand("StartVM");
            psInstance.AddParameter("requestVMName", vmname);
            psOutput = psInstance.Invoke();

            // check the other output streams (for example, the error stream)
            if (psInstance.Streams.Error.Count > 0)
            {
                // default return is false
                foreach (ErrorRecord errorItem in psInstance.Streams.Error)
                {
                    if (errorItem != null)
                    {
                        LogHelper.WriteWithDateTime("error--StartHv:{0}", errorItem.ToString());
                    }
                }                        
            }
            else
            {
                foreach (PSObject outputItem in psOutput)
                {
                    // if null object was dumped to the pipeline during the script then a null
                    // object may be present here. check for null to prevent potential NRE.
                    if (outputItem != null)
                    {
                        LogHelper.WriteWithDateTime("info--StartHv:{0}", outputItem.BaseObject.ToString());
                    }
                }                        
            }
        }
    }
    catch (Exception ex)
    {
        LogHelper.WriteWithDateTime("ERROR--STARTHV:{0}", ex.Message);
    }
    return 1;
}

/*
 * vmname: VM name
 * return: Off for success case and Running for fail
 */
public VMState StopVMOnce(string vmname)
{
    if (vmname == null || vmname.Equals(""))
    {
        LogHelper.Write("error--StopHv: Name can not be empty!");
        return 0;
    }
    
    try
    {
        int nPastTimeinMs = 0;
        string runScript = stopVMTemplet;
        using (PowerShell psInstance = PowerShell.Create())
        {
            // add hyperV management script, and invoke execution
            psInstance.AddScript(runScript);
            psInstance.Invoke();

            // run specified command
            psInstance.AddCommand("StopVM");
            psInstance.AddParameter("requestVMName", vmname);

            //psInstance.AddCommand("GetProc");
            LogHelper.Write("info--StopHv: Call begininvoke start");

            // prepare a new collection to store output stream objects
            PSDataCollection<PSObject> outputCollection = new PSDataCollection<PSObject>();

            IAsyncResult async = psInstance.BeginInvoke<PSObject, PSObject>(null, outputCollection);
            LogHelper.Write("info--StopHv: Call begininvoke end");

            while (async.IsCompleted == false)
            {
                LogHelper.Write("info--StopHv: Waiting for pipeline to finish...");
                Thread.Sleep(1000);
                nPastTimeinMs += 1000;
                if (nPastTimeinMs > TIMEOUTFORSTOP)
                {
                    LogHelper.Write("error--StopHv: StopHv timeout!");
                    throw (new TimeoutException("StopHv timeout"));
                }
            }

            LogHelper.Write("info--StopHv: Task finish");
            if (psInstance.Streams.Error.Count > 0)
            {
                // default return is false
                foreach (ErrorRecord errorItem in psInstance.Streams.Error)
                {
                    if (errorItem != null)
                    {
                        LogHelper.WriteWithDateTime("error--StopHv:{0}", errorItem.ToString());
                    }
                }                        
            }
            else
            {
                foreach (PSObject outputItem in outputCollection)
                {
                    // if null object was dumped to the pipeline during the script then a null
                    // object may be present here. check for null to prevent potential NRE.
                    if (outputItem != null)
                    {
                        LogHelper.WriteWithDateTime("info--StopHv:{0}", outputItem.BaseObject.ToString());
                    }
                }                        
            }
        }
    }
    catch (Exception ex)
    {
        LogHelper.WriteWithDateTime("ERROR--STOPHV:{0}", ex.Message);
    }

    return 1;
}

在執行StopVM時遇到一個很噁心的問題——卡在關機的過程中無法正常關機。對於這個問題懷疑過很多種可能,曾經一度認為網路服務未正常關閉導致的,後來發現並沒有那麼單純,到最後也沒有找到真正的原因。不過專案最後還是想到了一個歪門邪道的方法,那就是:先正常執行StopVM延時60秒結束(正常關機肯定能在60s內完成),檢查虛擬機器現行狀態,如果仍然是running,就用c#強制kill名為vmwp的程序(此程序為虛擬機器程序),不過這個程序會有保護,在被kill了之後會馬上重啟,此時虛擬機器裡的系統也再重啟,在這個時候再執行一次StopVM就能100%正常關機(因為此時虛擬機器的系統才剛剛啟動阻礙關機的服務還沒有起來)。這個辦法在想到的時候被同組的小夥伴黑成狗,不過最後還是用的這個辦法(再黑也要實現功能!!!)。