所以这就是我所拥有的(完整的加载项代码):
using Microsoft.SystemCenter.VirtualMachineManager;
using Microsoft.SystemCenter.VirtualMachineManager.UIAddIns;
using Microsoft.SystemCenter.VirtualMachineManager.UIAddIns.ContextTypes;
using Microsoft.VirtualManager.Remoting;
using System;
using System.AddIn;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows.Forms;
namespace Microsoft.VirtualManager.UI.AddIns.BackupAddIn
{
[AddIn("Make Backup")]
public class BackupAddIn : ActionAddInBase
{
protected const string PowershellPath = "%WINDIR%\\System32\\WindowsPowershell\\v1.0\\powershell.exe";
protected const string DefaultDirectory = "C:\\ClusterStorage\\Volume2";
protected const string WildcardVMName = "{{VMName}}";
protected const string WildcardError = "{{Error}}";
protected const string BackupDirectoryBase = "{{Base}}";
protected const string BackupDirectoryName = "{{Name}}";
protected const string BackupDirectoryDate = "{{Date}}";
public enum JobState { Initialized, Success, Fail };
protected const string BackupDirectoryTemplate = BackupDirectoryBase + "\\" + BackupDirectoryName + "\\" + BackupDirectoryDate + "\\";
protected static readonly ReadOnlyCollection<string> AllowedBackupStates = new ReadOnlyCollection<string>(new string[] { "PowerOff", "Paused"/*, "Saved"*/});
public override bool CheckIfEnabledFor(IList<ContextObject> contextObjects)
{
if (contextObjects != null && contextObjects.Count > 0)
{
foreach (var host in contextObjects.OfType<HostContext>())
{
if (host.ComputerState != ComputerState.Responding)
{
return false;
}
}
return true;
}
return false;
}
public override void PerformAction(IList<ContextObject> contextObjects)
{
if (contextObjects != null)
{
// check if we have VMs selected
var VMs = contextObjects.OfType<VMContext>();
if (VMs != null && VMs.Count() > 0)
{
// check if VMs are in a good state
var badVMs = VMs.Where(vm => AllowedBackupStates.Contains(vm.Status.ToString()) == false).ToArray();
if (badVMs != null && badVMs.Length > 0)
{
MessageBox.Show("Backup not possible!\r\nThe following VMs are still running:\r\n\r\n" + string.Join(", ", badVMs.Select(vm => vm.Name)));
}
else
{
// ask for backup directory
string backupDir = Microsoft.VisualBasic.Interaction.InputBox("Enter a path on the host to export the selected virtual machine(s) to.", "Export path", DefaultDirectory);
if (string.IsNullOrEmpty(backupDir) == false)
{
if (backupDir.EndsWith("\\"))
{
backupDir = backupDir.Substring(0, backupDir.Length - 1);
}
// go
/*foreach (var vm in VMs)
{
exportVM(vm, backupDir);
}*/
// testing to export multiple vms in one invoke
exportVMs(VMs, backupDir);
}
}
}
}
}
public string getDate()
{
var date = DateTime.Now;
return date.Year.ToString()
+ (date.Month < 10 ? "0" : "") + date.Month.ToString()
+ (date.Day < 10 ? "0" : "") + date.Day.ToString()
+ "_"
+ (date.Hour < 10 ? "0" : "") + date.Hour.ToString()
+ (date.Minute < 10 ? "0" : "") + date.Minute.ToString();
}
public void ManageJob(string name, JobState state, string message = null)
{
string command;
if (state == JobState.Initialized)
{
command = string.Format("New-SCExternalJob -Name \"{0}\"", name);
}
else if (state == JobState.Success)
{
command = string.Format("Set-SCExternalJob -Job (Get-SCJob -Name \"{0}\")[0] -Complete -InfoMessage \"" + (string.IsNullOrEmpty(message) ? "Backup successfully started." : message.Replace("\"", "'")) + "\"", name);
}
else
{
command = string.Format("Set-SCExternalJob -Job (Get-SCJob -Name \"{0}\")[0] -Failed -InfoMessage \"" + (string.IsNullOrEmpty(message) ? "Backup FAILED." : message.Replace("\"", "'")) + "\"", name);
}
//MessageBox.Show(command);
PowerShellContext.ExecuteScript<Host>(
command,
(profiles, error) =>
{
if (error != null)
{
MessageBox.Show("Cannot modify job state\r\nError: " + error.Problem);
}
}
);
}
public void exportVMs(IEnumerable<VMContext> VMs, string backupDir)
{
string date = getDate();
string VMS = "";
string fullBackupDirS = BackupDirectoryTemplate.Replace(BackupDirectoryBase, backupDir).Replace(BackupDirectoryName, "_VMBackups").Replace(BackupDirectoryDate, date);
VMS = "'" + string.Join("', '", VMs.Select(vm => vm.Name).ToArray()) + "'";
string command = string.Format("Export-VM -Name {0} -Path '{1}'", VMS, fullBackupDirS);
MessageBox.Show(command);
// We need to manager jobs in another thread probably --------------------------------------------------------------!!!
string jobname = "Starting_backup_of_multiple_machines";
mkShortcuts(backupDir, date, VMs.Select(vm => vm.Name).ToArray(), VMs.First());
//! execPSScript(jobname, scvmmPsCommand(command, VMs.First()), VMs.First(), WildcardVMName + ": Backup successful.", WildcardVMName + ": Backup FAILED!\r\nError: " + WildcardError, backupDir, date, VMs.Select(vm => vm.Name).ToArray());
}
public String scvmmPsCommand(string command, VMContext vm, string appPath = PowershellPath)
{
return string.Format("Invoke-SCScriptCommand -Executable {0} -VMHost (Get-SCVMHost -ID \"{1}\") -CommandParameters \"{2}\" -RunAsynchronous -TimeoutSeconds 360000", appPath, vm.VMHostId.ToString(), command);
}
// Make a shortcut from the machines backup directory to the backup in the "_VMBackups"-folder
public void mkShortcuts(string path, string date, string[] names, VMContext vm)
{
string command = "$shell = New-Object -ComObject WScript.Shell;";
foreach (var n in names)
{
command = command + string.Format(" $shortc = $shell.CreateShortcut('{0}\\{1}\\{2}.lnk'); $shortc.TargetPath = '{0}\\_VMBackup\\{2}\\{1}'; $shortc.Save();", path, n, date);
}
string fullCommand = scvmmPsCommand(command, vm);
MessageBox.Show(fullCommand);
execPSScript("Create_ShortcutS", fullCommand, vm, "Shortcut(s) created.", "FAILED to create Shortcut(s)!");
}
public void execPSScript(string jobname, string command, VMContext vm, string successMessage, string errorMessage, string path = "", string date = "", string[] names = null)
{
ManageJob(jobname, JobState.Initialized);
PowerShellContext.ExecuteScript<Host>(
command,
(vms, error) =>
{
if (error != null)
{
ManageJob(jobname, JobState.Fail, errorMessage.Replace(WildcardVMName, vm.Name).Replace(WildcardError, error.Problem));
}
else
{
ManageJob(jobname, JobState.Success, successMessage.Replace(WildcardVMName, vm.Name));
if (string.IsNullOrEmpty(path) == false)
{
//mkShortcuts(path, date, names, vm);
}
}
}
);
}
}
}
当我运行插件时,我得到一个错误框,表示如下:&#34;未知脚本错误。表达未关闭 - &#34;)&#34;丢失。
+ ... andard'; .Save();
+ ~
An expression was expected after '('.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordEx
ception
+ FullyQualifiedErrorId : ExpectedExpression". Weitere Informationen finden Sie im Standardfehlerprotokoll "C:\Windows\TEMP\gce_stderrord07b04547c74493caa6bdba9087df444.log".
但我无法在我的代码中找到错误,因为它在手动输入到主机上的powershell时起作用,而.Save()不带参数。你有什么想法吗?
答案 0 :(得分:0)
好的,而不是
command2 = command2 + string.Format(" ${1} = $shell.CreateShortcut(\"C:\\ClusterStorage\\Volume2\\test.lnk\"); ${1}.TargetPath = \"{0}\\_VMBackup\\{2}\\{1}\"; ${1}.Save();", backupDir, vm.Name, date);
尝试
command2 = command2 + string.Format(" $shortc = $shell.CreateShortcut(\"C:\\ClusterStorage\\Volume2\\test.lnk\"); $shortc.TargetPath = \"{0}\\_VMBackup\\{2}\\{1}\"; $shortc.Save();", backupDir, vm.Name, date);
command2 = "'" + command2 + "'"
这不起作用,也没有做很多其他事情。 基本上,问题是命令字符串传递给PowerShell执行的方式。 PowerShell将检查sting并尝试枚举所有变量。这不是理想的行为。试图将整个字符串用单引号括起来,但这并没有帮助。 最后,解决方案是使用PowerShell转义字符“`”来掩盖所有变量名称。
command2 = command2 + string.Format(" `$shortc = `$shell.CreateShortcut(\"C:\\ClusterStorage\\Volume2\\test.lnk\"); `$shortc.TargetPath = \"{0}\\_VMBackup\\{2}\\{1}\"; `$shortc.Save();", backupDir, vm.Name, date);