Hyper-V Cluster - Shared Disk replication througth WMI

1

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;
    }
    
asked by SrKatman 16.04.2018 в 10:03
source

0 answers