First of all to thank for this wonderful forum, it has saved me more than 100.
To introduce a little the problem that I have, I will start by explaining the infrastructure in which lies the error.
We have a client that currently has 4 Hyper-V host (s), in Failover Cluster, with Windows Server 2016.
Microsoft says in the release notes that the replication of the shared disk is only available through the WMI% Msvm_ReplicationCollectionService
class, so we have gotten to work:
Class Msvm_CollectionReplicationService
We have implemented the class according to the old model of replication of VMs, in scope root/virtualization/v2
, the new one we use is root/HyperVCluster/v2
, but there is no model to follow.
As a first step, we try to create a relationship using the CreateReplicationRelationship
method. The method call fails saying:
The method call failed.
without giving more details.
Do you have any slight idea of what happens? Is there any way to make more debug WMI?
ERROR TRACES:
Unhandled Exception: System.Management.ManagementException: The method call failed. at Microsoft.Samples.HyperV.Common.WmiUtilities.ValidateOutput (ManagementBaseObject outputParameters, ManagementScope scope, Boolean throwIfFailed, Boolean printErrors) in C: \ Users \ isarria \ Documents \ Replica \ cs \ WmiUtilities.cs: line 136 at Microsoft.Samples.HyperV.Common.WmiUtilities.ValidateOutput (ManagementBaseObject outputParameters, ManagementScope scope) in C: \ Users \ isarria \ Documents \ Replica \ cs \ WmiUtilities.cs: line 59 at Microsoft.Samples.HyperV.Replica.ManageReplication.CreateReplicationRelationship (String name, String name2, String CollectionID, String recoveryServerName) in C: \ Users \ isarria \ Documents \ Replica \ cs \ ManageReplication.cs: line 85
at Microsoft.Samples.HyperV.Replica.Program.Main (String [] args) in C: \ Users \ isarria \ Documents \ Replica \ cs \ Program.cs: line 107
CURRENT CODE:
/// <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);
}
}
@Pikoh: Thank you very much for the response. The "ValidateOutput" method only responds with the information of the call to the WMI. I paste the code below.
/// <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;
}