如何在ASP.NET MVC中执行Powershell和Powercli命令时设置超时时间

5

我正在开发一个asp.net mvc Web应用程序,以下代码是循环遍历服务器列表,并在每个服务器上执行PowerCli命令:

//Start Loop
    var shell = PowerShell.Create();
    var shell2 = PowerShell.Create();
    var shell3 = PowerShell.Create();

    string PsCmd = "add-pssnapin VMware.VimAutomation.Core; $vCenterServer = '" + vCenterName + "';$vCenterAdmin = '" + vCenterUsername + "' ;$vCenterPassword = '" + vCenterPassword + "';" + System.Environment.NewLine;

    PsCmd = PsCmd + "$VIServer = Connect-VIServer -Server $vCenterServer -User $vCenterAdmin -Password $vCenterPassword;" + System.Environment.NewLine;

    PsCmd = PsCmd + "Get-VMHost " + System.Environment.NewLine;

    string PsCmd2 = "add-pssnapin VMware.VimAutomation.Core; $vCenterServer = '" + vCenterName + "';$vCenterAdmin = '" + vCenterUsername + "' ;$vCenterPassword = '" + vCenterPassword + "';" + System.Environment.NewLine;

    PsCmd2 = PsCmd2 + "$VIServer = Connect-VIServer -Server $vCenterServer -User $vCenterAdmin -Password $vCenterPassword;" + System.Environment.NewLine;

    PsCmd2 = PsCmd2 + " Get-VMHost " + vCenterName + "| Get-VMHostNetworkAdapter -VMKernel" + System.Environment.NewLine;

    shell.Commands.AddScript(PsCmd);
    shell2.Commands.AddScript(PsCmd2);

    dynamic results = shell.Invoke(); 
    dynamic results2 = shell2.Invoke();
// end of loop

但我注意到有时候Shell命令会挂起,执行永远不会结束,所以我能不能定义一个超时行为,即在5分钟内跳过命令,如果没有返回结果...


为什么会被踩!!!甚至没有解释!!!当有人踩你的时候不指明原因,我就无法理解!!! - John John
我真的更想知道为什么我被踩得比得到任何答案还要多 :( - John John
3个回答

4
你需要自己编写超时命令。以下是我根据Keith Babinec的MSDN博客条目-从C#执行PowerShell脚本编写的代码。我只是为了演示目的而在控制台应用程序中编写了示例。我发现这样更容易看到发生了什么。您可以通过删除控制台输出和其他调整将其转换为Asp.Net应用程序。
以下是Program.cs
using System;
using System.Management.Automation;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string script = "Write-Host \"Testing lopping...\"" + Environment.NewLine
                            + "for ($i=1; $i -le 5; $i++)" + Environment.NewLine
                            + "{" + Environment.NewLine
                            + "Write-Output $i" + Environment.NewLine
                            + "Start-Sleep -s 3" + Environment.NewLine
                            + "}" + Environment.NewLine
                            + "Write-Host \"Done!\"" + Environment.NewLine;

            PowerShell shell = PowerShell.Create();
            shell.AddScript(script);

            PowerShellHelper helper = new PowerShellHelper(shell);
            try
            {
                // the script above should take 15 seconds to execute

                // do timeout of 10 minutes
                helper.ExecuteAsynchronously(new TimeSpan(0, 10, 0));

                // do a really short timeout - 2 seconds
                helper.ExecuteAsynchronously(new TimeSpan(0, 0, 2));
            }
            catch(TimeoutException te)
            {
                Console.WriteLine("\n\nScript took long!");
            }

            Console.WriteLine("Demo Finish");
            Console.ReadLine();
        }
    }
}

这里是PowerShellHelper.cs

using System;
using System.Management.Automation;
using System.Threading;

// This code was build from MSDN Blogs entry by Keith Babinec
// http://blogs.msdn.com/b/kebab/archive/2014/04/28/executing-powershell-scripts-from-c.aspx

namespace ConsoleApplication1
{
    class PowerShellHelper
    {
        private PowerShell shell_;

        public PowerShellHelper(PowerShell shell)
        {
            shell_ = shell;
        }
        public void ExecuteAsynchronously(TimeSpan timeout)
        {
            // prepare a new collection to store output stream objects
            PSDataCollection<PSObject> outputCollection = new PSDataCollection<PSObject>();
            outputCollection.DataAdded += outputCollection_DataAdded;

            // begin invoke execution on the pipeline
            // use this overload to specify an output stream buffer
            IAsyncResult result = shell_.BeginInvoke<PSObject, PSObject>(null, outputCollection);

            // start the timer
            DateTime startTime = DateTime.Now;

            // do something else until execution has completed.
            // this could be sleep/wait, or perhaps some other work
            while (result.IsCompleted == false)
            {
                Console.WriteLine("Waiting for pipeline to finish...");
                Thread.Sleep(100);

                // we check on our timeout here
                TimeSpan elasped = DateTime.Now.Subtract(startTime);
                if (elasped > timeout)
                {
                    // we can do a few things here, I like to throw exception
                    throw new TimeoutException("Powershell script taking too long");
                }
            }

            Console.WriteLine("Execution has stopped. The pipeline state: " + shell_.InvocationStateInfo.State);

            foreach (PSObject outputItem in outputCollection)
            {
                //TODO: handle/process the output items if required
                if (outputItem != null)
                {
                    Console.WriteLine(outputItem.BaseObject.ToString());
                }
            }
        }

        /// <summary>
        /// Event handler for when data is added to the output stream.
        /// </summary>
        /// <param name="sender">Contains the complete PSDataCollection of all output items.</param>
        /// <param name="e">Contains the index ID of the added collection item and the ID of the PowerShell instance this event belongs to.</param>
        private void outputCollection_DataAdded(object sender, DataAddedEventArgs e)
        {
            // do something when an object is written to the output stream
            Console.WriteLine("Object added to output.");
        }
    }
}

2

我更喜欢这个简短的结构:

using (PowerShell ps = PowerShell.Create())
{
    ps.AddScript(script);
    var psAsyncResult = ps.BeginInvoke();
    if (psAsyncResult.AsyncWaitHandle.WaitOne(timeoutMilliseconds))
    {
        // Execution finished
        var results = ps.EndInvoke(psAsyncResult);
    }
    else
    {
        // Execution terminated by timeout
        Console.WriteLine($"Unable to complete running powershell script within {timeoutMilliseconds} milliseconds");
    }
}

0

Powershell带超时的调用

有一个更短的解决方案(因此更少出错):

using (PowerShell ps = PowerShell.Create())
{
    ps.AddScript(script);

    Task invocationTask = ps.InvokeAsync();
    try
    {
        // Ensure the task is waited for the timeout duration.
        // As documentation says if the timeout is reached then the task is faulted
        if (!invocationTask.Wait(timeout))
        {
            isTimeouted = true;
        }
    }
    finally
    {
        // task may not be completed here 
        // and disposal of not completed task will raise an exception
        if (invocationTask != null && invocationTask.IsCompleted)
        {
            invocationTask.Dispose();
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接