如何在 asp.net mvc 中执行 Powershell 和 Powercli 命令时设置超时
how to set a timeout when executing Powershell & Powercli commands inside asp.net mvc
我正在开发一个 asp.net mvc web 应用程序,我有以下代码定义了服务器列表的循环并在我的 asp.net mvc 中为每个服务器执行 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 分钟后如果没有返回结果则跳过命令...
您将不得不推出自己的超时命令。下面是我根据 MSDN Blog entry by Keith Babinec - Executing PowerShell scripts from C# 编写的代码。我在控制台应用程序中编写示例仅用于演示目的。我发现更容易看到发生了什么。您可以通过删除控制台输出和其他调整将其转换为 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.");
}
}
}
我更喜欢这种简短的结构:
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");
}
}
我正在开发一个 asp.net mvc web 应用程序,我有以下代码定义了服务器列表的循环并在我的 asp.net mvc 中为每个服务器执行 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 分钟后如果没有返回结果则跳过命令...
您将不得不推出自己的超时命令。下面是我根据 MSDN Blog entry by Keith Babinec - Executing PowerShell scripts from C# 编写的代码。我在控制台应用程序中编写示例仅用于演示目的。我发现更容易看到发生了什么。您可以通过删除控制台输出和其他调整将其转换为 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.");
}
}
}
我更喜欢这种简短的结构:
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");
}
}