Пример #1
0
        /// <summary>
        /// Create powershell instance for each Job and run it on Ruspacepool.
        /// Use Tasks to create threads and collect the results.
        /// If Force parameter is not used and first Job, perform Error check before Queuing remaining Jobs
        /// </summary>
        protected override void ProcessRecord()
        {
            try
            {
                jobNum++;

                do
                {
                    try
                    {
                        //Create a powershell instance for each input object and it will be processed on a seperate thread
                        PowerShell  powerShell = PowerShell.Create();
                        IDictionary psParams   = RunspaceMethods.FindParams(proxyFunction, ScriptToRun, commandName, InputObject);

                        //If command supplied doesn't take any input, bail out.
                        //This check can be removed if this cmdlet needs to run 'something' in parallel, but just didn't find a use case yet
                        if (psParams.Count <= 0)
                        {
                            throw new Exception("No Parameters were used on the command, Please verify the command.");
                        }

                        if (jobNum == 1)
                        {
                            LogHelper.LogProgress("Queuing First Job", this, quiet: Quiet.IsPresent);
                            LogHelper.Log(fileVerboseLogTypes, "Paramaters used for the first job:", this, NoFileLogging.IsPresent);
                            foreach (DictionaryEntry param in psParams)
                            {
                                string paramValues = null;
                                if (param.Value is Array)
                                {
                                    foreach (var val in param.Value as Array)
                                    {
                                        paramValues += val?.ToString() + ", ";
                                    }
                                }
                                else
                                {
                                    paramValues = param.Value?.ToString();
                                }
                                LogHelper.Log(fileVerboseLogTypes, string.Format("{0} - {1}", param.Key.ToString(), paramValues), this, NoFileLogging.IsPresent);
                            }
                        }

                        if (cmdInfo.CommandType == CommandTypes.ExternalScript)
                        {
                            powerShell.AddScript(((ExternalScriptInfo)cmdInfo).ScriptContents).AddParameters(psParams);
                        }
                        else
                        {
                            powerShell.AddCommand(commandName).AddParameters(psParams);
                        }

                        powerShell.RunspacePool = runspacePool;
                        //Creates the task and the continuation tasks
                        Task <PSDataCollection <PSObject> > task = Task <PSDataCollection <PSObject> > .Factory.FromAsync(powerShell.BeginInvoke(), r => powerShell.EndInvoke(r));

                        task.ContinueWith(t =>
                        {
                            var currentJob = CheckandCreateJob(
                                jobName: InputObject.ToString(),
                                task: t,
                                jobID: jobNum,
                                ps: powerShell);
                            currentJob.IsFaulted  = true;
                            currentJob.Exceptions = t.Exception;
                        }, TaskContinuationOptions.OnlyOnFaulted);

                        //Continuation tasks may have already created the Job, check before creating it.
                        Job job = CheckandCreateJob(
                            jobName: InputObject.ToString(),
                            task: task,
                            jobID: jobNum,
                            ps: powerShell);

                        if (!jobs.TryAdd(job.ID, job))
                        {
                            if (jobNum == 1)
                            {
                                //We might retry the Job 1 during Error Check, so ignore the error and replace the first job
                                jobs.TryRemove(jobs.First().Key, out Job removedJob);
                                jobs.TryAdd(job.ID, job);
                            }
                            else
                            {
                                throw new Exception(string.Format("Unable to add job ID: {0}", job.ID));
                            }
                        }

                        if (!Async.IsPresent &&
                            (jobs.Count == BatchSize || (!this.Force.IsPresent && (jobNum == 1))))
                        {
                            CollectJobs(this, jobs, jobNum, ProgressBarStage, Force, ReturnasJobObject, AppendJobNameToResult, jobNum);
                        }

                        if (Async.IsPresent)
                        {
                            WriteObject(job);
                        }
                    }
                    catch (AggregateException ae) when(jobNum == 1 && ae.InnerExceptions.Where(ie => ie is CommandNotFoundException).Count() > 0)
                    {
                        IEnumerable <Exception> exceptions   = ae.InnerExceptions.Where(ie => ie is CommandNotFoundException);
                        List <string>           debugStrings = new List <string>();

                        foreach (Exception exception in exceptions)
                        {
                            CommandInfo commandInfo = RunspaceMethods.GetCommandInfo(((CommandNotFoundException)exception).CommandName);
                            if (commandInfo == null)
                            {
                                throw (CommandNotFoundException)exception;
                            }

                            RunspaceMethods.ModuleDetails moduleDetails = RunspaceMethods.GetModuleDetails(commandInfo, debugStrings);
                            LogHelper.LogDebug(debugStrings, this);
                            LogHelper.Log(fileVerboseLogTypes, exception.Message, this, NoFileLogging);

                            if (moduleDetails.IsFromRemotingModule)
                            {
                                LogHelper.Log(fileVerboseLogTypes, "The command is from a remotePS connection, cannot load local and remote runspaces together", this, NoFileLogging);
                                throw exception;
                            }

                            RunspaceMethods.LoadISSWithModuleDetails(moduleDetails, runspacePool.InitialSessionState);
                        }
                        runspacePool = RunspaceMethods.ResetRunspacePool(1, MaxThreads, runspacePool.InitialSessionState.Clone(), new DummyCustomPSHost());
                        LogHelper.LogDebug("Resetting Runspace to load the new modules", this);
                        LogHelper.Log(fileVerboseLogTypes, "Retrying first job", this, NoFileLogging);
                        runspacePool.Open();
                    }
                } while (!Force.IsPresent && jobNum == 1 && jobs.FirstOrDefault().Value?.IsFaulted == true);

                if (!Force.IsPresent &&
                    (jobs.Values.Where(f => f.IsFaulted == true).Count() / jobNum * 100) > promptOnPercentFaults)
                {
                    if (!ShouldContinue(string.Format("More than {0}% of the jobs are faulted, Do you want to continue with processing rest of the Inputs?", promptOnPercentFaults), "Most Jobs Faulted"))
                    {
                        throw new Exception("Most jobs faulted");
                    }
                }
            }
            catch (Exception e)
            {
                ErrorRecord error = new ErrorRecord(e, e.GetType().Name, ErrorCategory.InvalidData, this);
                LogHelper.Log(fileVerboseLogTypes, error.Exception.ToString(), this, NoFileLogging.IsPresent);
                CleanupObjs(this, proxyFunction, runspacePool, e is PipelineStoppedException);
                ThrowTerminatingError(error);
            }
        }
Пример #2
0
        ConcurrentDictionary <int, Job> jobs = new ConcurrentDictionary <int, Job>();//(MaxParallel, MaxParallel);

        /// <summary>
        /// Discover the commad passed and prepare the RunspacePool
        /// </summary>
        protected override void BeginProcessing()
        {
            List <string> debugStrings = new List <string>();

            //System.Globalization.CultureInfo
            //Environment.GetFolderPath(Environment.SpecialFolder.)
            //Cmdlet will work properly if the Job input is from pipeline, if Input was specified as a parameter just error out
            if (!MyInvocation.ExpectingInput)
            {
                throw new Exception("Cmdlet will only accept input from Pipeline. Please see examples");
            }
            LogHelper.LogProgress("Starting script", this, quiet: Quiet.IsPresent);

            if (Async.IsPresent)
            {
                LogHelper.Log(fileWarningLogTypes, "Async switch is used, Use Get-InvokeAllJobsStatus to check the job status and Receive-InvokeAllJobs to collect the results", this, NoFileLogging.IsPresent);
                LogHelper.Log(fileWarningLogTypes,
                              "No Error check done when Async switch is used. If additional PS modules are required to run the command, please specify them using -ModulestoLoad switch",
                              this, NoFileLogging.IsPresent);
            }

            if (BatchSize < MaxThreads)
            {
                //TODo: Log warning not error
                LogHelper.Log(fileErrorLogTypes,
                              new ErrorRecord(new Exception(string.Format("The Batchsize {0} is less than the MaxThreads {1}. If you have reduced the Batchsize as a result of throttling, " +
                                                                          "this is ok, else increase the BatchSize for better performance", BatchSize, MaxThreads)),
                                              "BadParameterValues", ErrorCategory.InvalidData, this),
                              this,
                              NoFileLogging.IsPresent);
            }

            try
            {
                LogHelper.Log(fileVerboseLogTypes, "Starting script", this, NoFileLogging.IsPresent);
                LogHelper.Log(fileVerboseLogTypes, MyInvocation.Line, this, NoFileLogging.IsPresent);
                cmdInfo = RunspaceMethods.CmdDiscovery(ScriptToRun, out commandName);

                if (cmdInfo == null)
                {
                    throw new Exception("Unable to find the command specified to Invoke, please make sure required modules or functions are loaded to PS Session");
                }

                if (supportedCommandTypes.Contains(cmdInfo.CommandType))
                {
                    LogHelper.Log(fileVerboseLogTypes, String.Format("The supplied command {0} is of type {1}", commandName, cmdInfo.CommandType.ToString()), this, NoFileLogging.IsPresent);
                }
                else
                {
                    string notSupportedErrorString = "Invoke-All doesn't implement methods to run this command, Supported types are PSScripts, PSCmdlets and PSFunctions";
                    LogHelper.Log(fileVerboseLogTypes, notSupportedErrorString, this, NoFileLogging.IsPresent);
                    throw new Exception(notSupportedErrorString);
                }

                proxyFunction = RunspaceMethods.CreateProxyFunction(cmdInfo, debugStrings);
                LogHelper.LogDebug(debugStrings, this);
                if (proxyFunction == null)
                {
                    throw new Exception("Unable to create proxyfunction, please restart the powershell session and try again");
                }
                else
                {
                    LogHelper.Log(fileVerboseLogTypes, string.Format("Created ProxyFunction {0}", proxyFunction.Name), this, NoFileLogging.IsPresent);
                }

                IList <SessionStateVariableEntry> stateVariableEntries = new List <SessionStateVariableEntry>();
                if (CopyLocalVariables.IsPresent)
                {
                    string psGetUDVariables = @"
                        function Get-UDVariable {
                          get-variable | where-object {(@(
                            'FormatEnumerationLimit',
                            'MaximumAliasCount',
                            'MaximumDriveCount',
                            'MaximumErrorCount',
                            'MaximumFunctionCount',
                            'MaximumVariableCount',
                            'PGHome',
                            'PGSE',
                            'PGUICulture',
                            'PGVersionTable',
                            'PROFILE',
                            'PSSessionOption'
                            ) -notcontains $_.name) -and `
                            (([psobject].Assembly.GetType('System.Management.Automation.SpecialVariables').GetFields('NonPublic,Static') `
                            | Where-Object FieldType -eq ([string]) | ForEach-Object GetValue $null)) -notcontains $_.name
                          }
                        }

                        Get-UDVariable

                        ";

                    IEnumerable <PSVariable> variables = ScriptBlock.Create(psGetUDVariables).Invoke().Select(v => v.BaseObject as PSVariable);
                    variables.ToList().ForEach(v => stateVariableEntries.Add(new SessionStateVariableEntry(v.Name, v.Value, null)));
                    LogHelper.Log(fileVerboseLogTypes,
                                  string.Format("Will copy {0} local variables to the Runspace. Use -debug switch to see the details", stateVariableEntries.Count),
                                  this,
                                  NoFileLogging.IsPresent);
                }

                //Create a runspace pool with the cmdInfo collected
                //Using Dummy PSHost to avoid any messages to the Host. Job will collect all the output on the powershell streams
                runspacePool = RunspaceMethods.CreateRunspacePool(
                    commandInfo: cmdInfo,
                    pSHost: new DummyCustomPSHost(),
                    maxRunspaces: MaxThreads,
                    debugStrings: out debugStrings,
                    loadAllTypedata: LoadAllTypeDatas.IsPresent,
                    useRemotePS: (PSSession)UseRemotePSSession?.BaseObject,
                    modules: ModulestoLoad,
                    snapIns: PSSnapInsToLoad,
                    variableEntries: stateVariableEntries
                    );

                LogHelper.LogDebug(debugStrings, this);
                runspacePool.ThreadOptions = PSThreadOptions.ReuseThread;
                runspacePool.Open();
                LogHelper.LogDebug("Opened RunspacePool", this);

                //for Logging Purposes, create a powershell instance and log the modules that are *actually* loaded on the runspace
                if (runspacePool.ConnectionInfo == null)
                {
                    using (PowerShell tempPS = PowerShell.Create())
                    {
                        tempPS.RunspacePool = runspacePool;
                        var modules     = tempPS.AddCommand("Get-Module").Invoke();
                        var moduleNames = from module in modules select((PSModuleInfo)(module.BaseObject)).Name;

                        LogHelper.LogDebug(new List <string>()
                        {
                            "Modules found on the RunspacePool:"
                        }, this);
                        LogHelper.LogDebug(moduleNames.ToList(), this);
                        tempPS.Commands.Clear();

                        var loadedVariables = tempPS.AddCommand("Get-Variable").Invoke();
                        var varsValues      = from varValue in loadedVariables select((PSVariable)(varValue.BaseObject)).Name;

                        LogHelper.LogDebug(new List <string>()
                        {
                            "Vars found on the RunspacePool:"
                        }, this);
                        LogHelper.LogDebug(varsValues.ToList(), this);
                    }
                }
                else
                {
                    if (ModulestoLoad != null || PSSnapInsToLoad != null)
                    {
                        LogHelper.Log(fileWarningLogTypes,
                                      "No additional modules that were specified to be loaded will be loaded as it is not supported when using remote PSSession",
                                      this,
                                      NoFileLogging.IsPresent);
                    }
                }
            }
            catch (Exception e)
            {
                ErrorRecord error = new ErrorRecord(e, e.GetType().Name, ErrorCategory.InvalidData, this);
                //Log any debug strings
                LogHelper.LogDebug(debugStrings, this);
                LogHelper.Log(fileVerboseLogTypes, error.Exception.ToString(), this, NoFileLogging.IsPresent);
                CleanupObjs(this, proxyFunction, runspacePool, false);

                ThrowTerminatingError(error);
            }
        }