首先,我要感谢这个令人敬畏的网站,让我的生命节省了一百多次。
为了介绍一下我们遇到的问题,我将开始解释我们的基础设施。
实际上,我们在Windows 2016上有一个使用故障转移群集功能的4个Hyper-V主机的客户端。
微软在发行版中指出,共享磁盘复制只能通过WMI类Msvm_ReplicationCollectionService
获得,所以我们开始研究它。
- Msvm_CollectionReplicationService Class
https://msdn.microsoft.com/en-us/library/mt167787(v=vs.85).aspx
我们已经按照同一类的旧模型实现了这个类。
旧命名空间:" root / virtualization / v2" (这很好用,但没有共享磁盘复制)
新命名空间:" root / HyperVCluster / v2" (失败)
作为第一步,我们尝试使用先前创建的集合(ReplicaTestGroup)在共享磁盘的两个虚拟机之间创建关系,然后我们尝试调用" CreateReplicationRelationship"班级的方法。
发出命令:PS C:\ REPLICA \ 1 \ 1> 。\ ReplicaSamples.exe CreateReplicationRelationship REPLICA1 REPLICA2 7ccff3fe-da17-436a-8f38-8a34833b31e6 hypervdrhost.mydom.local
Method fails with: "The method call failed.", without more details.
有没有人知道发生了什么?有什么方法可以调试WMI,这样我就可以看出它出了什么问题?
错误:
未处理的异常:System.Management.ManagementException:方法 呼叫失败。在 Microsoft.Samples.HyperV.Common.WmiUtilities.ValidateOutput(ManagementBaseObject outputParameters,ManagementScope scope,Boolean throwIfFailed, 布尔打印错误) C:\ Users \ isarria \ Documents \ Replica \ cs \ WmiUtilities.cs:第136行 Microsoft.Samples.HyperV.Common.WmiUtilities.ValidateOutput(ManagementBaseObject outputParameters,ManagementScope scope)in C:\ Users \ isarria \ Documents \ Replica \ cs \ WmiUtilities.cs:第59行 Microsoft.Samples.HyperV.Replica.ManageReplication.CreateReplicationRelationship(字符串 name,String name2,String CollectionID,String recoveryServerName)in C:\ Users \ isarria \ Documents \ Replica \ cs \ ManageReplication.cs:第85行 Microsoft.Samples.HyperV.Replica.Program.Main(String [] args)in C:\ Users \ isarria \ Documents \ Replica \ cs \ Program.cs:第107行
已使用的代码:
/// <summary>
/// Enables replication for a collection of virtual machines to a specified DRserver using
/// integrated authentication.
/// </summary>
/// <param name="name">Name of VM 1</param>
/// <param name="name2">Name of VM 2</param>
/// <param name="CollectionID">CollectionID to enable replication.</param>
/// <param name="recoveryServerName">The name of the recovery server.</param>
internal static void
CreateReplicationRelationship(
string name,
string name2,
string CollectionID,
string recoveryServerName)
{
ManagementScope scope = new ManagementScope(@"root\HyperVCluster\v2");
ManagementScope oldScope = new ManagementScope(@"root\virtualization\v2");
//
// Retrieve the Msvm_VirtualSystemCollection.
//
using (ManagementObject vm = WmiUtilities.GetVirtualMachine(name, oldScope))
{
using (ManagementObject vm2 = WmiUtilities.GetVirtualMachine(name2, oldScope))
{
string vmPath = vm.Path.Path;
string vmPath2 = vm2.Path.Path;
using (ManagementObject collection = WmiUtilities.GetVMGroup(CollectionID, scope))
{
using (ManagementObject replicationSettingData =
ReplicaUtilities.GetReplicationSettings(vm))
{
replicationSettingData["RecoveryConnectionPoint"] = recoveryServerName;
replicationSettingData["AuthenticationType"] = 1;
replicationSettingData["RecoveryServerPortNumber"] = 80;
replicationSettingData["CompressionEnabled"] = 1;
// Keep 24 recovery points.
replicationSettingData["RecoveryHistory"] = 24;
// Replicate changes after every 300 seconds.
replicationSettingData["ReplicationInterval"] = 300;
// Take VSS snapshot every one hour.
replicationSettingData["ApplicationConsistentSnapshotInterval"] = 1;
// Include all disks for replication.
replicationSettingData["IncludedDisks"] = WmiUtilities.GetVhdSettings(vm);
//replicationSettingData["IncludedDisks"] = WmiUtilities.GetVhdSettings(vm2);
Console.WriteLine(replicationSettingData["IncludedDisks"].ToString());
string settingDataEmbedded =
replicationSettingData.GetText(TextFormat.WmiDtd20);
using (ManagementObject replicationService =
ReplicaUtilities.GetVirtualMachineReplicationService(scope))
{
using (ManagementBaseObject inParams =
replicationService.GetMethodParameters("CreateReplicationRelationship"))
{
inParams["Collection"] = collection;
inParams["CollectionReplicationSettingData"] = settingDataEmbedded;
using (ManagementBaseObject outParams =
replicationService.InvokeMethod("CreateReplicationRelationship",
inParams,
null))
{
WmiUtilities.ValidateOutput(outParams, scope);
}
}
}
Console.WriteLine(string.Format(CultureInfo.CurrentCulture,
"Replication is successfully enabled for vmGroup: \"{0}\"", CollectionID));
}
}
}
}
}
/// <summary>
/// Gets the Msvm_VirtualSystemCollection instance that matches the requested VMGroup name.
/// </summary>
/// <param name="CollectionID">The CollectionID to retrieve the path for.</param>
/// <param name="scope">The ManagementScope to use to connect to WMI.</param>
/// <returns>The Msvm_VirtualSystemCollection instance.</returns>
public static ManagementObject
GetVMGroup(
string CollectionID,
ManagementScope scope)
{
return GetCollectionObject(CollectionID, "Msvm_VirtualSystemCollection", scope);
}
/// <summary>
/// Gets the Msvm_ComputerSystem instance that matches the requested virtual machine name.
/// </summary>
/// <param name="name">The name of the virtual machine to retrieve the path for.</param>
/// <param name="scope">The ManagementScope to use to connect to WMI.</param>
/// <returns>The Msvm_ComputerSystem instance.</returns>
public static ManagementObject
GetVirtualMachine(
string name,
ManagementScope scope)
{
return GetVmObject(name, "Msvm_ComputerSystem", scope);
}
/// <summary>
/// Gets the first virtual machine object of the given class with the given name.
/// </summary>
/// <param name="name">The name of the virtual machine to retrieve the path for.</param>
/// <param name="className">The class of virtual machine to search for.</param>
/// <param name="scope">The ManagementScope to use to connect to WMI.</param>
/// <returns>The instance representing the virtual machine.</returns>
private static ManagementObject
GetVmObject(
string name,
string className,
ManagementScope scope)
{
string vmQueryWql = string.Format(CultureInfo.InvariantCulture,
"SELECT * FROM {0} WHERE ElementName=\"{1}\"", className, name);
SelectQuery vmQuery = new SelectQuery(vmQueryWql);
using (ManagementObjectSearcher vmSearcher = new ManagementObjectSearcher(scope, vmQuery))
using (ManagementObjectCollection vmCollection = vmSearcher.Get())
{
if (vmCollection.Count == 0)
{
throw new ManagementException(string.Format(CultureInfo.CurrentCulture,
"No {0} could be found with name \"{1}\"",
className,
name));
}
//
// If multiple virtual machines exist with the requested name, return the first
// one.
//
ManagementObject vm = GetFirstObjectFromCollection(vmCollection);
return vm;
}
}
/// <summary>
/// Gets the replication settings object for a collection.
/// </summary>
/// <param name="systemCollection">The Msvm_VirtualSystemCollection mache object.</param>
/// <returns>The replication settings object.</returns>
internal static ManagementObject
GetReplicationSettings(
ManagementObject systemCollection)
{
using (ManagementObjectCollection settingsCollection =
systemCollection.GetRelated("Msvm_ReplicationSettingData"))
{
ManagementObject replicationSettings =
WmiUtilities.GetFirstObjectFromCollection(settingsCollection);
return replicationSettings;
}
}
/// <summary>
/// Gets the first virtual machine object of the given class with the given name.
/// </summary>
/// <param CollectionID="CollectionID">The name of the virtual machine to retrieve the path for.</param>
/// <param name="className">The class of virtual machine to search for.</param>
/// <param name="scope">The ManagementScope to use to connect to WMI.</param>
/// <returns>The instance representing the virtual machine.</returns>
private static ManagementObject
GetCollectionObject(
string CollectionID,
string className,
ManagementScope scope)
{
string vmQueryWql = string.Format(CultureInfo.InvariantCulture,
"SELECT * FROM {0} WHERE CollectionID=\"{1}\"", className, CollectionID);
SelectQuery vmQuery = new SelectQuery(vmQueryWql);
using (ManagementObjectSearcher vmSearcher = new ManagementObjectSearcher(scope, vmQuery))
using (ManagementObjectCollection vmCollection = vmSearcher.Get())
{
ManagementObject CollectionObject = null;
if (vmCollection.Count == 1)
{
foreach (ManagementObject managementObject in vmCollection)
{
CollectionObject = managementObject;
}
}
else
{
throw new ManagementException(string.Format(CultureInfo.CurrentCulture,
"No {0} could be found with ID \"{1}\"",
className,
CollectionID));
}
return CollectionObject;
}
}
/// <summary>
/// Gets the virtual system replication service.
/// </summary>
/// <param name="scope">The scope to use when connecting to WMI.</param>
/// <returns>The collection virtual machine replication service.</returns>
public static ManagementObject
GetVirtualMachineReplicationService(
ManagementScope scope)
{
ManagementScope scope2 = new ManagementScope(@"root\HyperVCluster\v2");
SelectQuery query = new SelectQuery("select * from Msvm_CollectionReplicationService");
using (ManagementObjectSearcher queryExecute = new ManagementObjectSearcher(scope2, query))
using (ManagementObjectCollection serviceCollection = queryExecute.Get())
{
if (serviceCollection.Count == 0)
{
throw new ManagementException("Cannot find the collection replication service object. " +
"Please check that the Hyper-V Virtual Machine Management service is running.");
}
return WmiUtilities.GetFirstObjectFromCollection(serviceCollection);
}
}
此外,这是ValidateOutput方法。
/// <summary>
/// Validates the output parameters of a method call and prints errors, if any.
/// </summary>
/// <param name="outputParameters">The output parameters of a WMI method call.</param>
/// <param name="scope">The ManagementScope to use to connect to WMI.</param>
/// <returns><c>true</c> if successful and not firing an alert; otherwise, <c>false</c>.</returns>
public static bool
ValidateOutput(
ManagementBaseObject outputParameters,
ManagementScope scope)
{
return ValidateOutput(outputParameters, scope, true, false);
}
/// <summary>
/// Validates the output parameters of a method call and prints errors, if any.
/// </summary>
/// <param name="outputParameters">The output parameters of a WMI method call.</param>
/// <param name="scope">The ManagementScope to use to connect to WMI.</param>
/// <param name="throwIfFailed"> If true, the method throws on failure.</param>
/// <param name="printErrors">If true, Msvm_Error messages are displayed.</param>
/// <returns><c>true</c> if successful and not firing an alert; otherwise, <c>false</c>.</returns>
public static bool
ValidateOutput(
ManagementBaseObject outputParameters,
ManagementScope scope,
bool throwIfFailed,
bool printErrors)
{
bool succeeded = true;
string errorMessage = "The method call failed.";
if ((uint)outputParameters["ReturnValue"] == 4096)
{
//
// The method invoked an asynchronous operation. Get the Job object
// and wait for it to complete. Then we can check its result.
//
using (ManagementObject job = new ManagementObject((string)outputParameters["Job"]))
{
job.Scope = scope;
while (!IsJobComplete(job["JobState"]))
{
Thread.Sleep(TimeSpan.FromSeconds(1));
//
// ManagementObjects are offline objects. Call Get() on the object to have its
// current property state.
//
job.Get();
}
if (!IsJobSuccessful(job["JobState"]))
{
succeeded = false;
//
// In some cases the Job object can contain helpful information about
// why the method call failed. If it did contain such information,
// use it instead of a generic message.
//
if (!string.IsNullOrEmpty((string)job["ErrorDescription"]))
{
errorMessage = (string)job["ErrorDescription"];
}
if (printErrors)
{
PrintMsvmErrors(job);
}
if (throwIfFailed)
{
throw new ManagementException(errorMessage);
}
}
}
}
else if ((uint)outputParameters["ReturnValue"] != 0)
{
succeeded = false;
if (throwIfFailed)
{
throw new ManagementException(errorMessage);
}
}
return succeeded;
}