none
Hyper-V Cluster - Shared Disk replication through WMI RRS feed

  • Question

  • First of all, I wanna say thanks to this awesome site, saved my life more than a hundred times. 

    To introduce a little the problem we're having, I'll begin explaining our infrastructure. 

    Actually, we've got a client that has 4 Hyper-V host(s) using Failover Cluster feature, with Windows 2016.

    Microsoft says in releases notes that shared disk replication is only available through the WMI class `Msvm_CollectionReplicationService`, so said, we started working on it.

    -- `Msvm_CollectionReplicationService Class`

    https://msdn.microsoft.com/en-us/library/mt167787(v=vs.85).aspx

    We've implemented the class following the old model of this same class.

    **Old namespace**: "root/virtualization/v2" 
    (this works well, but no shared disk replication)

    **New namespace**: "root/HyperVCluster/v2" 
    (fails)

    As first step we try to create a relationship between the two virtual machines sharing the disk, using previous created collection (ReplicaTestGroup), then we try to call "CreateReplicationRelationship" method of the class.

    Issued command: 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." Also, if I check the "returnValue" of "InvokeMethod" I see that I am getting error number "32776" which according to Microsoft is "Incorrect data type".

    Why am I getting that error? is it because I pass an instance of "ReplicationSettingData" instead of "CollectionReplicationSettingData"?

    There are 0 instances of "CollectionReplicationSettingData", do I need to create one by calling "ModifyReplicationSettings"?

    Does anyone has any idea of what's going on? Is there any way of debugging WMI so I can see what's wrong?

    **ERROR:**

    > 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

    **USED CODE**:

    <!-- language: c# -->

            /// <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);
                }
            }

    Also, here is ValidateOutput method.
    <!-- language: c# -->

            /// <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;
            }

                                            
    Thursday, April 19, 2018 6:27 AM

All replies

  • Hi SrKatman,

    Thank you for posting here.

    For your question is more related to the samples in GitHub, please post a new thread in GitHub.

    https://github.com/Microsoft/Windows-classic-samples/issues

    The Visual C# forum discuss and ask questions about the C# programming language, IDE, libraries, samples, and tools.

    Best Regards,

    Wendy


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Sunday, April 22, 2018 10:09 PM
    Moderator
  • Thank you Wendy.

    For those who want more info, here is the created issue @ GitHub.

    https://github.com/Microsoft/Windows-classic-samples/issues/39

    Best regards,

    Iván S. Rodríguez.

    Tuesday, April 24, 2018 8:37 PM