// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CloudStack.Plugin.WmiWrappers.ROOT.VIRTUALIZATION;
using log4net;
using System.Globalization;
using System.Management;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using CloudStack.Plugin.WmiWrappers.ROOT.CIMV2;
using System.IO;
namespace HypervResource
{
public class WmiCalls
{
public static void Initialize()
{
// Trigger assembly load into curren appdomain
}
private static ILog logger = LogManager.GetLogger(typeof(WmiCalls));
///
/// Returns ComputerSystem lacking any NICs and VOLUMEs
///
public static ComputerSystem CreateVM(string name, long memory_mb, int vcpus)
{
// Obtain controller for Hyper-V virtualisation subsystem
VirtualSystemManagementService vmMgmtSvc = GetVirtualisationSystemManagementService();
// Create VM with correct name and default resources
ComputerSystem vm = CreateDefaultVm(vmMgmtSvc, name);
// Update the resource settings for the VM.
// Resource settings are referenced through the Msvm_VirtualSystemSettingData object.
VirtualSystemSettingData vmSettings = GetVmSettings(vm);
// For memory settings, there is no Dynamic Memory, so reservation, limit and quantity are identical.
MemorySettingData memSettings = GetMemSettings(vmSettings);
memSettings.LateBoundObject["VirtualQuantity"] = memory_mb;
memSettings.LateBoundObject["Reservation"] = memory_mb;
memSettings.LateBoundObject["Limit"] = memory_mb;
// Update the processor settings for the VM, static assignment of 100% for CPU limit
ProcessorSettingData procSettings = GetProcSettings(vmSettings);
procSettings.LateBoundObject["VirtualQuantity"] = vcpus;
procSettings.LateBoundObject["Reservation"] = vcpus;
procSettings.LateBoundObject["Limit"] = 100000;
ModifyVmResources(vmMgmtSvc, vm, new String[] {
memSettings.LateBoundObject.GetText(TextFormat.CimDtd20),
procSettings.LateBoundObject.GetText(TextFormat.CimDtd20)
});
logger.InfoFormat("VM with display name {0} has GUID {1}", vm.ElementName, vm.Name);
logger.DebugFormat("Resources for vm {0}: {1} MB memory, {2} vcpus", name, memory_mb, vcpus);
return vm;
}
///
/// Create a (synthetic) nic, and attach it to the vm
///
///
///
///
///
public static SyntheticEthernetPortSettingData CreateNICforVm(ComputerSystem vm, string mac, string vlan)
{
logger.DebugFormat("Creating nic for VM {0} (GUID {1})", vm.ElementName, vm.Name);
// Obtain controller for Hyper-V networking subsystem
VirtualSwitchManagementService vmNetMgmtSvc = GetVirtualSwitchManagementService();
// Create NIC resource by cloning the default NIC
var synthNICsSettings = SyntheticEthernetPortSettingData.GetInstances(vmNetMgmtSvc.Scope, "InstanceID LIKE \"%Default\"");
// Assert
if (synthNICsSettings.Count != 1)
{
var errMsg = string.Format("Internal error, coudl not find default SyntheticEthernetPort instance");
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
var defaultSynthNICSettings = synthNICsSettings.OfType().First();
var newSynthNICSettings = new SyntheticEthernetPortSettingData((ManagementBaseObject)defaultSynthNICSettings.LateBoundObject.Clone());
// Get the virtual switch
VirtualSwitch vSwitch = GetExternalVirtSwitch();
// Crate switch port for new VM
ManagementPath newSwitchPath = CreateSwitchPortForVm(vm, vmNetMgmtSvc, vSwitch);
// Add required VLAND support
if (vlan != null)
{
SetPortVlan(vlan, vmNetMgmtSvc, newSwitchPath);
}
logger.DebugFormat("Created switch port {0} on switch {1}", newSwitchPath.Path, vSwitch.Path.Path);
// Assign configuration to new NIC
string normalisedMAC = string.Join("", (mac.Split(new char[] { ':' })));
newSynthNICSettings.LateBoundObject["Connection"] = new string[] { newSwitchPath.Path };
newSynthNICSettings.LateBoundObject["ElementName"] = vm.ElementName;
newSynthNICSettings.LateBoundObject["Address"] = normalisedMAC;
newSynthNICSettings.LateBoundObject["StaticMacAddress"] = "TRUE";
newSynthNICSettings.LateBoundObject["VirtualSystemIdentifiers"] = new string[] { "{" + Guid.NewGuid().ToString() + "}" };
newSynthNICSettings.CommitObject();
// Insert NIC into vm
string[] newResources = new string[] { newSynthNICSettings.LateBoundObject.GetText(System.Management.TextFormat.CimDtd20)};
ManagementPath[] newResourcePaths = AddVirtualResource(newResources, vm );
// assert
if (newResourcePaths.Length != 1)
{
var errMsg = string.Format(
"Failed to properly insert a single NIC on VM {0} (GUID {1}): number of resource created {2}",
vm.ElementName,
vm.Name,
newResourcePaths.Length);
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
return new SyntheticEthernetPortSettingData(newResourcePaths[0]);
}
public const string IDE_HARDDISK_DRIVE = "Microsoft Synthetic Disk Drive";
public const string IDE_ISO_DRIVE = "Microsoft Synthetic DVD Drive";
public const string IDE_ISO_DISK = "Microsoft Virtual CD/DVD Disk"; // For IDE_ISO_DRIVE
public const string IDE_HARDDISK_DISK = "Microsoft Virtual Hard Disk"; // For IDE_HARDDISK_DRIVE
///
/// Create new VM. By default we start it.
///
public static ComputerSystem DeployVirtualMachine(dynamic jsonObj)
{
var vmInfo = jsonObj.vm;
string vmName = vmInfo.name;
var nicInfo = vmInfo.nics;
int vcpus = vmInfo.cpus;
int memSize = vmInfo.maxRam / 1048576;
string errMsg = vmName;
var diskDrives = vmInfo.disks;
// assert
errMsg = vmName + ": missing disk information, array empty or missing, agent expects *at least* one disk for a VM";
if (diskDrives == null)
{
logger.Error(errMsg);
throw new ArgumentException(errMsg);
}
// assert
errMsg = vmName + ": missing NIC information, array empty or missing, agent expects at least an empty array.";
if (nicInfo == null )
{
logger.Error(errMsg);
throw new ArgumentException(errMsg);
}
// For existing VMs, return when we spot one of this name not stopped. In the meantime, remove any existing VMs of same name.
ComputerSystem vmWmiObj = null;
while ((vmWmiObj = GetComputerSystem(vmName)) != null)
{
logger.WarnFormat("Create request for existing vm, name {0}", vmName);
if (vmWmiObj.EnabledState == EnabledState.Disabled)
{
logger.InfoFormat("Deleting existing VM with name {0}, before we go on to create a VM with the same name", vmName);
DestroyVm(vmName);
}
else
{
// TODO: revise exception type
errMsg = string.Format("Create VM failing, because there exists a VM with name {0}, state {1}",
vmName,
EnabledState.ToString(vmWmiObj.EnabledState));
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
}
// Create vm carcase
logger.DebugFormat("Going ahead with create VM {0}, {1} vcpus, {2}MB RAM", vmName, vcpus, memSize);
var newVm = WmiCalls.CreateVM(vmName, memSize, vcpus);
foreach (var diskDrive in diskDrives)
{
string vhdFile = null;
string diskName = null;
VolumeObjectTO volInfo = VolumeObjectTO.ParseJson(diskDrive.data);
if (volInfo != null)
{
// assert
errMsg = vmName + ": volume missing primaryDataStore for disk " + diskDrive.ToString();
if (volInfo.primaryDataStore == null)
{
logger.Error(errMsg);
throw new ArgumentException(errMsg);
}
diskName = volInfo.name;
// assert
errMsg = vmName + ": can't deal with DataStore type for disk " + diskDrive.ToString();
if (volInfo.primaryDataStore == null)
{
logger.Error(errMsg);
throw new ArgumentException(errMsg);
}
errMsg = vmName + ": Malformed PrimaryDataStore for disk " + diskDrive.ToString();
if (String.IsNullOrEmpty(volInfo.primaryDataStore.path))
{
logger.Error(errMsg);
throw new ArgumentException(errMsg);
}
errMsg = vmName + ": Missing folder PrimaryDataStore for disk " + diskDrive.ToString() + ", missing path: " + volInfo.primaryDataStore.path;
if (!Directory.Exists(volInfo.primaryDataStore.path))
{
logger.Error(errMsg);
throw new ArgumentException(errMsg);
}
vhdFile = volInfo.FullFileName;
if (!System.IO.File.Exists(vhdFile))
{
errMsg = vmName + ": non-existent volume, missing " + vhdFile + " for drive " + diskDrive.ToString();
logger.Error(errMsg);
throw new ArgumentException(errMsg);
}
logger.Debug("Going to create " + vmName + " with attached voluem " + diskName + " at " + vhdFile);
}
string driveType = diskDrive.type;
string ideCtrllr = "0";
string driveResourceType = null;
switch (driveType) {
case "ROOT":
ideCtrllr = "0";
driveResourceType = IDE_HARDDISK_DRIVE;
break;
case "ISO":
ideCtrllr = "1";
driveResourceType = IDE_ISO_DRIVE;
break;
default:
// TODO: double check exception type
errMsg = string.Format("Unknown disk type {0} for disk {1}, vm named {2}",
string.IsNullOrEmpty(driveType) ? "NULL" : driveType,
string.IsNullOrEmpty(diskName) ? "NULL" : diskName, vmName);
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
logger.DebugFormat("Create disk type {1} (Named: {0}), on vm {2} {3}", diskName, driveResourceType, vmName,
string.IsNullOrEmpty(vhdFile) ? " no disk to insert" : ", inserting disk" +vhdFile );
AddDiskDriveToVm(newVm, vhdFile, ideCtrllr, driveResourceType);
}
// add nics
foreach (var nic in nicInfo)
{
string mac = nic.mac;
string vlan = null;
string isolationUri = nic.isolationUri;
if (isolationUri != null && isolationUri.StartsWith("vlan://") && !isolationUri.Equals("vlan://untagged"))
{
vlan = isolationUri.Substring("vlan://".Length);
int tmp;
if (!int.TryParse(vlan, out tmp))
{
// TODO: double check exception type
errMsg = string.Format("Invalid VLAN value {0} for on vm {1} for nic uuid {2}", isolationUri, vmName, nic.uuid);
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
}
CreateNICforVm(newVm, mac, vlan);
}
logger.DebugFormat("Starting VM {0}", vmName);
SetState(newVm, RequiredState.Enabled);
logger.InfoFormat("Started VM {0}", vmName);
return newVm;
}
///
/// Create a disk and attach it to the vm
///
///
///
/// IDE_HARDDISK_DRIVE or IDE_ISO_DRIVE
public static ManagementPath AddDiskDriveToVm(ComputerSystem vm, string vhdfile, string cntrllerAddr, string driveResourceType)
{
logger.DebugFormat("Creating DISK for VM {0} (GUID {1}) by attaching {2}",
vm.ElementName,
vm.Name,
vhdfile);
// Determine disk type for drive and assert drive type valid
string diskResourceSubType = null;
switch(driveResourceType) {
case IDE_HARDDISK_DRIVE:
diskResourceSubType = IDE_HARDDISK_DISK;
break;
case IDE_ISO_DRIVE:
diskResourceSubType = IDE_ISO_DISK;
break;
default:
var errMsg = string.Format(
"Unrecognised disk drive type {0} for VM {1} (GUID {2})",
string.IsNullOrEmpty(driveResourceType) ? "NULL": driveResourceType,
vm.ElementName,
vm.Name);
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
ManagementPath newDrivePath = AttachNewDriveToVm(vm, cntrllerAddr, driveResourceType);
// If there's not disk to insert, we are done.
if (String.IsNullOrEmpty(vhdfile))
{
logger.DebugFormat("No disk to be added to drive, disk drive {0} is complete", newDrivePath.Path);
}
else
{
InsertDiskImage(vm, vhdfile, diskResourceSubType, newDrivePath);
}
return newDrivePath;
}
private static ManagementPath AttachNewDriveToVm(ComputerSystem vm, string cntrllerAddr, string driveType)
{
// Disk drives are attached to a 'Parent' IDE controller. We IDE Controller's settings for the 'Path', which our new Disk drive will use to reference it.
VirtualSystemSettingData vmSettings = GetVmSettings(vm);
var ctrller = GetIDEControllerSettings(vmSettings, cntrllerAddr);
// A description of the drive is created by modifying a clone of the default ResourceAllocationSettingData for that drive type
string defaultDriveQuery = String.Format("ResourceSubType LIKE \"{0}\" AND InstanceID LIKE \"%Default\"", driveType);
var newDiskDriveSettings = CloneResourceAllocationSetting(defaultDriveQuery);
// Set IDE controller and address on the controller for the new drive
newDiskDriveSettings.LateBoundObject["Parent"] = ctrller.Path.ToString();
newDiskDriveSettings.LateBoundObject["Address"] = "0";
newDiskDriveSettings.CommitObject();
// Add this new disk drive to the VM
logger.DebugFormat("Creating disk drive type {0}, parent IDE controller is {1} and address on controller is {2}",
newDiskDriveSettings.ResourceSubType,
newDiskDriveSettings.Parent,
newDiskDriveSettings.Address);
string[] newDriveResource = new string[] { newDiskDriveSettings.LateBoundObject.GetText(System.Management.TextFormat.CimDtd20) };
ManagementPath[] newDrivePaths = AddVirtualResource(newDriveResource, vm);
// assert
if (newDrivePaths.Length != 1)
{
var errMsg = string.Format(
"Failed to add disk drive type {3} to VM {0} (GUID {1}): number of resource created {2}",
vm.ElementName,
vm.Name,
newDrivePaths.Length,
driveType);
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
logger.DebugFormat("New disk drive type {0} WMI path is {1}s",
newDiskDriveSettings.ResourceSubType,
newDrivePaths[0].Path);
return newDrivePaths[0];
}
///
/// Attach iso to the vm
///
///
///
private static void AttachIsoToVm(ComputerSystem vm, string isoPath)
{
// Disk drives are attached to a 'Parent' IDE controller. We IDE Controller's settings for the 'Path', which our new Disk drive will use to reference it.
VirtualSystemSettingData vmSettings = GetVmSettings(vm);
var ctrller = GetDvdDriveSettings(vmSettings);
// A description of the drive is created by modifying a clone of the default ResourceAllocationSettingData for that drive type
string defaultDiskQuery = String.Format("ResourceSubType LIKE \"{0}\" AND InstanceID LIKE \"%Default\"", IDE_ISO_DISK);
var newDiskSettings = CloneResourceAllocationSetting(defaultDiskQuery);
// Set IDE controller and address on the controller for the new drive
newDiskSettings.LateBoundObject["Parent"] = ctrller.Path.ToString();
newDiskSettings.LateBoundObject["Connection"] = new string[] { isoPath };
newDiskSettings.CommitObject();
// Add the new vhd object as a virtual hard disk to the vm.
string[] newDiskResource = new string[] { newDiskSettings.LateBoundObject.GetText(System.Management.TextFormat.CimDtd20) };
ManagementPath[] newDiskPaths = AddVirtualResource(newDiskResource, vm);
// assert
if (newDiskPaths.Length != 1)
{
var errMsg = string.Format(
"Failed to add disk image to VM {0} (GUID {1}): number of resource created {2}",
vm.ElementName,
vm.Name,
newDiskPaths.Length);
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
logger.InfoFormat("Created disk {2} for VM {0} (GUID {1}), image {3} ",
vm.ElementName,
vm.Name,
newDiskPaths[0].Path,
isoPath);
}
private static void InsertDiskImage(ComputerSystem vm, string vhdfile, string diskResourceSubType, ManagementPath drivePath)
{
// A description of the disk is created by modifying a clone of the default ResourceAllocationSettingData for that disk type
string defaultDiskQuery = String.Format("ResourceSubType LIKE \"{0}\" AND InstanceID LIKE \"%Default\"", diskResourceSubType);
var newDiskSettings = CloneResourceAllocationSetting(defaultDiskQuery);
// Set disk drive and VHD file on disk for new disk
newDiskSettings.LateBoundObject["Parent"] = drivePath.Path;
newDiskSettings.LateBoundObject["Connection"] = new string[] { vhdfile };
newDiskSettings.CommitObject();
// Add the new vhd object as a virtual hard disk to the vm.
string[] newDiskResource = new string[] { newDiskSettings.LateBoundObject.GetText(System.Management.TextFormat.CimDtd20) };
ManagementPath[] newDiskPaths = AddVirtualResource(newDiskResource, vm);
// assert
if (newDiskPaths.Length != 1)
{
var errMsg = string.Format(
"Failed to add disk image type {3} to VM {0} (GUID {1}): number of resource created {2}",
vm.ElementName,
vm.Name,
newDiskPaths.Length,
diskResourceSubType);
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
logger.InfoFormat("Created disk {2} for VM {0} (GUID {1}), image {3} ",
vm.ElementName,
vm.Name,
newDiskPaths[0].Path,
vhdfile);
}
private static ResourceAllocationSettingData CloneResourceAllocationSetting(string wmiQuery)
{
var defaultDiskDriveSettingsObjs = ResourceAllocationSettingData.GetInstances(wmiQuery);
// assert
if (defaultDiskDriveSettingsObjs.Count != 1)
{
var errMsg = string.Format("Failed to find Msvm_ResourceAllocationSettingData for the query {0}", wmiQuery);
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
ResourceAllocationSettingData defaultDiskDriveSettings = defaultDiskDriveSettingsObjs.OfType().First();
return new ResourceAllocationSettingData((ManagementBaseObject)defaultDiskDriveSettings.LateBoundObject.Clone());
}
public static void AttachIso(string displayName, string iso)
{
logger.DebugFormat("Got request to attach iso {0} to vm {1}", iso, displayName);
ComputerSystem vm = GetComputerSystem(displayName);
if (vm == null)
{
logger.DebugFormat("VM {0} not found", displayName);
return;
}
else
{
AttachIsoToVm(vm, iso);
}
}
public static void DestroyVm(dynamic jsonObj)
{
string vmToDestroy = jsonObj.vmName;
DestroyVm(vmToDestroy);
}
///
/// Remove all VMs and all SwitchPorts with the displayName. VHD gets deleted elsewhere.
///
///
public static void DestroyVm(string displayName)
{
logger.DebugFormat("Got request to destroy vm {0}", displayName);
var vm = GetComputerSystem(displayName);
if ( vm == null )
{
logger.DebugFormat("VM {0} already destroyed (or never existed)", displayName);
return;
}
// Stop VM
logger.DebugFormat("Stop VM {0} (GUID {1})", vm.ElementName, vm.Name);
SetState(vm, RequiredState.Disabled);
// Delete SwitchPort
DeleteSwitchPort(vm.ElementName);
// Delete VM
var virtSysMgmtSvc = GetVirtualisationSystemManagementService();
ManagementPath jobPath;
do
{
logger.DebugFormat("Delete VM {0} (GUID {1})", vm.ElementName, vm.Name);
var ret_val = virtSysMgmtSvc.DestroyVirtualSystem(vm.Path, out jobPath);
if (ret_val == ReturnCode.Started)
{
JobCompleted(jobPath);
}
else if (ret_val != ReturnCode.Completed)
{
var errMsg = string.Format(
"Failed Delete VM {0} (GUID {1}) due to {2}",
vm.ElementName,
vm.Name,
ReturnCode.ToString(ret_val));
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
vm = GetComputerSystem(displayName);
}
while (vm != null);
}
public static void SetState(ComputerSystem vm, ushort requiredState)
{
logger.InfoFormat(
"Changing state of {0} (GUID {1}) to {2}",
vm.ElementName,
vm.Name,
RequiredState.ToString(requiredState));
ManagementPath jobPath;
// TimeSpan is a value type; default ctor is equivalent to 0.
var ret_val = vm.RequestStateChange(requiredState, new TimeSpan(), out jobPath);
// If the Job is done asynchronously
if (ret_val == ReturnCode.Started)
{
JobCompleted(jobPath);
}
else if (ret_val == 32775)
{
logger.InfoFormat("RequestStateChange returned 32775, which means vm in wrong state for requested state change. Treating as if requested state was reached");
}
else if (ret_val != ReturnCode.Completed)
{
var errMsg = string.Format(
"Failed to change state of VM {0} (GUID {1}) to {2} due to {3}",
vm.ElementName,
vm.Name,
RequiredState.ToString(requiredState),
ReturnCode.ToString(ret_val));
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
logger.InfoFormat(
"Successfully changed vm state of {0} (GUID {1} to requested state {2}",
vm.ElementName,
vm.Name,
requiredState);
}
//TODO: Write method to delete SwitchPort based on Name
public static bool DeleteSwitchPort(string elementName)
{
var virtSwitchMgmtSvc = GetVirtualSwitchManagementService();
// Get NIC path
var condition = string.Format("ElementName=\"{0}\"", elementName);
var switchPortCollection = SwitchPort.GetInstances(virtSwitchMgmtSvc.Scope, condition);
if (switchPortCollection.Count == 0)
{
return true;
}
foreach (SwitchPort port in switchPortCollection)
{
// Destroy
var ret_val = virtSwitchMgmtSvc.DeleteSwitchPort(port.Path);
if (ret_val != ReturnCode.Completed)
{
return false;
}
}
return true;
}
// Add new
private static ManagementPath[] AddVirtualResource(string[] resourceSettings, ComputerSystem vm )
{
var virtSysMgmtSvc = GetVirtualisationSystemManagementService();
ManagementPath jobPath;
ManagementPath[] resourcePaths;
var ret_val = virtSysMgmtSvc.AddVirtualSystemResources(
resourceSettings,
vm.Path,
out jobPath,
out resourcePaths);
// If the Job is done asynchronously
if (ret_val == ReturnCode.Started)
{
JobCompleted(jobPath);
}
else if (ret_val != ReturnCode.Completed)
{
var errMsg = string.Format(
"Failed to add resources to VM {0} (GUID {1}) due to {2}",
vm.ElementName,
vm.Name,
ReturnCode.ToString(ret_val));
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
return resourcePaths;
}
private static ManagementPath CreateSwitchPortForVm(ComputerSystem vm, VirtualSwitchManagementService vmNetMgmtSvc, VirtualSwitch vSwitch)
{
ManagementPath newSwitchPath = null;
var ret_val = vmNetMgmtSvc.CreateSwitchPort(
vm.ElementName,
Guid.NewGuid().ToString(),
"",
vSwitch.Path,
out newSwitchPath);
// Job is always done synchronously
if (ret_val != ReturnCode.Completed)
{
var errMsg = string.Format(
"Failed to create switch for NIC on VM {0} (GUID {1}), error code {2}",
vm.ElementName,
vm.Name,
ret_val);
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
return newSwitchPath;
}
// add vlan support by setting AccessVLAN on VLANEndpointSettingData for port
private static void SetPortVlan(string vlan, VirtualSwitchManagementService vmNetMgmtSvc, ManagementPath newSwitchPath)
{
logger.DebugFormat("Setting VLAN to {0}", vlan);
VLANEndpointSettingData vlanEndpointSettings = GetVlanEndpointSettings(vmNetMgmtSvc, newSwitchPath);
vlanEndpointSettings.LateBoundObject["AccessVLAN"] = vlan;
vlanEndpointSettings.CommitObject();
}
public static VLANEndpointSettingData GetVlanEndpointSettings(VirtualSwitchManagementService vmNetMgmtSvc, ManagementPath newSwitchPath)
{
// Get Msvm_VLANEndpoint through associated with new Port
var vlanEndpointQuery = new RelatedObjectQuery(newSwitchPath.Path, VLANEndpoint.CreatedClassName);
var vlanEndpointSearch = new ManagementObjectSearcher(vmNetMgmtSvc.Scope, vlanEndpointQuery);
var vlanEndpointCollection = new VLANEndpoint.VLANEndpointCollection(vlanEndpointSearch.Get());
// assert
if (vlanEndpointCollection.Count != 1)
{
var errMsg = string.Format("No VLANs for vSwitch on Hyper-V server for switch {0}", newSwitchPath.Path);
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
VLANEndpoint vlanEndpoint = vlanEndpointCollection.OfType().First();
// Get Msvm_VLANEndpointSettingData assocaited with Msvm_VLANEndpoint
var vlanEndpointSettingsQuery = new RelatedObjectQuery(vlanEndpoint.Path.Path, VLANEndpointSettingData.CreatedClassName);
var vlanEndpointSettingsSearch = new ManagementObjectSearcher(vmNetMgmtSvc.Scope, vlanEndpointSettingsQuery);
var vlanEndpointSettingsCollection = new VLANEndpointSettingData.VLANEndpointSettingDataCollection(vlanEndpointSettingsSearch.Get());
// assert
if (vlanEndpointSettingsCollection.Count != 1)
{
var errMsg = string.Format("Internal error, VLAN for vSwitch not setup propertly Hyper-V");
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
VLANEndpointSettingData vlanEndpointSettings = vlanEndpointSettingsCollection.OfType().First();
return vlanEndpointSettings;
}
///
/// External VSwitch has an external NIC, and we assume there is only one external NIC
///
///
///
/// Throws if there is no vswitch
public static VirtualSwitch GetExternalVirtSwitch()
{
// Work back from the first *bound* external NIC we find.
var externNICs = ExternalEthernetPort.GetInstances("IsBound = TRUE");
if (externNICs.Count == 0 )
{
var errMsg = "No ExternalEthernetPort available to Hyper-V";
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
ExternalEthernetPort externNIC = externNICs.OfType().First();
// A sequence of ASSOCIATOR objects need to be traversed to get from external NIC the vswitch.
// We use ManagementObjectSearcher objects to execute this sequence of questions
// NB: default scope of ManagementObjectSearcher is '\\.\root\cimv2', which does not contain
// the virtualisation objects.
var endpointQuery = new RelatedObjectQuery(externNIC.Path.Path, SwitchLANEndpoint.CreatedClassName);
var endpointSearch = new ManagementObjectSearcher(externNIC.Scope, endpointQuery);
var endpointCollection = new SwitchLANEndpoint.SwitchLANEndpointCollection(endpointSearch.Get());
// assert
if (endpointCollection.Count < 1 )
{
var errMsg = string.Format("No SwitchLANEndpoint for external NIC {0} on Hyper-V server", externNIC.Name);
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
SwitchLANEndpoint endPoint = endpointCollection.OfType().First();
var switchPortQuery = new RelatedObjectQuery(endPoint.Path.Path, SwitchPort.CreatedClassName);
var switchPortSearch = new ManagementObjectSearcher(externNIC.Scope, switchPortQuery);
var switchPortCollection = new SwitchPort.SwitchPortCollection(switchPortSearch.Get());
// assert
if (switchPortCollection.Count < 1 )
{
var errMsg = string.Format("No SwitchPort for external NIC {0} on Hyper-V server", externNIC.Name);
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
SwitchPort switchPort = switchPortCollection.OfType().First();
var vSwitchQuery = new RelatedObjectQuery(switchPort.Path.Path, VirtualSwitch.CreatedClassName);
var vSwitchSearch = new ManagementObjectSearcher(externNIC.Scope, vSwitchQuery);
var vSwitchCollection = new VirtualSwitch.VirtualSwitchCollection(vSwitchSearch.Get());
// assert
if (vSwitchCollection.Count < 1)
{
var errMsg = string.Format("No virtual switch for external NIC {0} on Hyper-V server", externNIC.Name);
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
VirtualSwitch vSwitch = vSwitchCollection.OfType().First();
return vSwitch;
}
private static void ModifyVmResources(VirtualSystemManagementService vmMgmtSvc, ComputerSystem vm, string[] resourceSettings)
{
// Resource settings are changed through the management service
System.Management.ManagementPath jobPath;
var ret_val = vmMgmtSvc.ModifyVirtualSystemResources(vm.Path,
resourceSettings,
out jobPath);
// If the Job is done asynchronously
if (ret_val == ReturnCode.Started)
{
JobCompleted(jobPath);
}
else if (ret_val != ReturnCode.Completed)
{
var errMsg = string.Format(
"Failed to update VM {0} (GUID {1}) due to {2} (ModifyVirtualSystem call), existing VM not deleted",
vm.ElementName,
vm.Name,
ReturnCode.ToString(ret_val));
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
}
private static ComputerSystem CreateDefaultVm(VirtualSystemManagementService vmMgmtSvc, string name)
{
// Tweak default settings by basing new VM on default global setting object
// with designed display name.
VirtualSystemGlobalSettingData vs_gs_data = VirtualSystemGlobalSettingData.CreateInstance();
vs_gs_data.LateBoundObject["ElementName"] = name;
System.Management.ManagementPath jobPath;
System.Management.ManagementPath defined_sys;
var ret_val = vmMgmtSvc.DefineVirtualSystem(
new string[0],
null,
vs_gs_data.LateBoundObject.GetText(System.Management.TextFormat.CimDtd20),
out defined_sys,
out jobPath);
// If the Job is done asynchronously
if (ret_val == ReturnCode.Started)
{
JobCompleted(jobPath);
}
else if (ret_val != ReturnCode.Completed)
{
var errMsg = string.Format(
"Failed to create VM {0} due to {1} (DefineVirtualSystem call)",
name, ReturnCode.ToString(ret_val));
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
logger.DebugFormat(CultureInfo.InvariantCulture, "Created VM {0}", name);
// Is the defined_system real?
var vm = new ComputerSystem(defined_sys);
// Assertion
if (vm.ElementName.CompareTo(name) != 0)
{
var errMsg = string.Format(
"New VM created with wrong name (is {0}, should be {1}, GUID {2})",
vm.ElementName,
name,
vm.Name);
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
return vm;
}
public static VirtualSwitchManagementService GetVirtualSwitchManagementService()
{
// VirtualSwitchManagementService is a singleton, most anonymous way of lookup is by asking for the set
// of local instances, which should be size 1.
var virtSwtichSvcCollection = VirtualSwitchManagementService.GetInstances();
foreach (VirtualSwitchManagementService item in virtSwtichSvcCollection)
{
return item;
}
var errMsg = string.Format("No Hyper-V subsystem on server");
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
public static void CreateDynamicVirtualHardDisk(ulong MaxInternalSize, string Path)
{
// Resource settings are changed through the management service
System.Management.ManagementPath jobPath;
var imgMgr = GetImageManagementService();
var ret_val = imgMgr.CreateDynamicVirtualHardDisk(MaxInternalSize, Path, out jobPath);
// If the Job is done asynchronously
if (ret_val == ReturnCode.Started)
{
JobCompleted(jobPath);
}
else if (ret_val != ReturnCode.Completed)
{
var errMsg = string.Format(
"Failed to CreateDynamicVirtualHardDisk size {0}, path {1} to {2}",
MaxInternalSize,
Path,
ReturnCode.ToString(ret_val));
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
}
public static ImageManagementService GetImageManagementService()
{
// VirtualSystemManagementService is a singleton, most anonymous way of lookup is by asking for the set
// of local instances, which should be size 1.
var coll = ImageManagementService.GetInstances();
foreach (ImageManagementService item in coll)
{
return item;
}
var errMsg = string.Format("No Hyper-V subsystem on server");
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
public static VirtualSystemManagementService GetVirtualisationSystemManagementService()
{
// VirtualSystemManagementService is a singleton, most anonymous way of lookup is by asking for the set
// of local instances, which should be size 1.
var virtSysMgmtSvcCollection = VirtualSystemManagementService.GetInstances();
foreach (VirtualSystemManagementService item in virtSysMgmtSvcCollection)
{
return item;
}
var errMsg = string.Format("No Hyper-V subsystem on server");
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
///
/// Similar to http://msdn.microsoft.com/en-us/library/hh850031%28v=vs.85%29.aspx
///
///
///
private static void JobCompleted(ManagementPath jobPath)
{
ConcreteJob jobObj = null;
for(;;)
{
jobObj = new ConcreteJob(jobPath);
if (jobObj.JobState != JobState.Starting && jobObj.JobState != JobState.Running)
{
break;
}
logger.InfoFormat("In progress... {0}% completed.", jobObj.PercentComplete);
System.Threading.Thread.Sleep(1000);
}
if (jobObj.JobState != JobState.Completed)
{
var errMsg = string.Format(
"Hyper-V Job failed, Error Code:{0}, Description: {1}",
jobObj.ErrorCode,
jobObj.ErrorDescription);
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
logger.DebugFormat("WMI job succeeded: {0}, Elapsed={1}", jobObj.Description, jobObj.ElapsedTime);
}
public static void GetProcessorResources(out uint cores, out uint mhz)
{
// Processor processors
cores = 0;
mhz = 0;
Processor.ProcessorCollection procCol = Processor.GetInstances();
foreach (Processor procInfo in procCol)
{
cores += procInfo.NumberOfCores;
mhz = procInfo.MaxClockSpeed;
}
}
public static void GetProcessorUsageInfo(out double cpuUtilization)
{
PerfFormattedData_Counters_ProcessorInformation.PerfFormattedData_Counters_ProcessorInformationCollection coll =
PerfFormattedData_Counters_ProcessorInformation.GetInstances("Name=\"_Total\"");
cpuUtilization = 100;
// Use the first one
foreach (PerfFormattedData_Counters_ProcessorInformation procInfo in coll)
{
// Idle during a given internal
// See http://library.wmifun.net/cimv2/win32_perfformatteddata_counters_processorinformation.html
cpuUtilization = 100.0 - (double)procInfo.PercentIdleTime;
}
}
public static void GetMemoryResources(out ulong physicalRamKBs, out ulong freeMemoryKBs)
{
OperatingSystem0 os = new OperatingSystem0();
physicalRamKBs = os.TotalVisibleMemorySize;
freeMemoryKBs = os.FreePhysicalMemory;
}
public static string GetDefaultVirtualDiskFolder()
{
VirtualSystemManagementServiceSettingData.VirtualSystemManagementServiceSettingDataCollection coll = VirtualSystemManagementServiceSettingData.GetInstances();
string defaultVirtualHardDiskPath = null;
foreach (VirtualSystemManagementServiceSettingData settings in coll)
{
return settings.DefaultVirtualHardDiskPath;
}
// assert
if (!System.IO.Directory.Exists(defaultVirtualHardDiskPath) ){
var errMsg = string.Format(
"Hyper-V DefaultVirtualHardDiskPath is invalid!");
logger.Error(errMsg);
return null;
}
return defaultVirtualHardDiskPath;
}
public static ComputerSystem GetComputerSystem(string displayName)
{
var wmiQuery = String.Format("ElementName=\"{0}\"", displayName);
ComputerSystem.ComputerSystemCollection vmCollection = ComputerSystem.GetInstances(wmiQuery);
// Return the first one
foreach (ComputerSystem vm in vmCollection)
{
return vm;
}
return null;
}
public static List GetVmElementNames()
{
List result = new List();
ComputerSystem.ComputerSystemCollection vmCollection = ComputerSystem.GetInstances();
// Return the first one
foreach (ComputerSystem vm in vmCollection)
{
if (vm.Caption.StartsWith("Hosting Computer System") )
{
continue;
}
result.Add(vm.ElementName);
}
return result;
}
public static ProcessorSettingData GetProcSettings(VirtualSystemSettingData vmSettings)
{
// An ASSOCIATOR object provides the cross reference from the VirtualSystemSettingData and the
// ProcessorSettingData, but generated wrappers do not expose a ASSOCIATOR OF query as a method.
// Instead, we use the System.Management to code the equivalant of
// string query = string.Format( "ASSOCIATORS OF {{{0}}} WHERE ResultClass = {1}", vmSettings.path, resultclassName);
//
var wmiObjQuery = new RelatedObjectQuery(vmSettings.Path.Path, ProcessorSettingData.CreatedClassName);
// NB: default scope of ManagementObjectSearcher is '\\.\root\cimv2', which does not contain
// the virtualisation objects.
var wmiObjectSearch = new ManagementObjectSearcher(vmSettings.Scope, wmiObjQuery);
var wmiObjCollection = new ProcessorSettingData.ProcessorSettingDataCollection(wmiObjectSearch.Get());
foreach (ProcessorSettingData wmiObj in wmiObjCollection)
{
return wmiObj;
}
var errMsg = string.Format("No ProcessorSettingData in VirtualSystemSettingData {0}", vmSettings.Path.Path);
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
public static MemorySettingData GetMemSettings(VirtualSystemSettingData vmSettings)
{
// An ASSOCIATOR object provides the cross reference from the VirtualSystemSettingData and the
// MemorySettingData, but generated wrappers do not expose a ASSOCIATOR OF query as a method.
// Instead, we use the System.Management to code the equivalant of
// string query = string.Format( "ASSOCIATORS OF {{{0}}} WHERE ResultClass = {1}", vmSettings.path, resultclassName);
//
var wmiObjQuery = new RelatedObjectQuery(vmSettings.Path.Path, MemorySettingData.CreatedClassName);
// NB: default scope of ManagementObjectSearcher is '\\.\root\cimv2', which does not contain
// the virtualisation objects.
var wmiObjectSearch = new ManagementObjectSearcher(vmSettings.Scope, wmiObjQuery);
var wmiObjCollection = new MemorySettingData.MemorySettingDataCollection(wmiObjectSearch.Get());
foreach (MemorySettingData wmiObj in wmiObjCollection)
{
return wmiObj;
}
var errMsg = string.Format("No MemorySettingData in VirtualSystemSettingData {0}", vmSettings.Path.Path);
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
public static ResourceAllocationSettingData GetDvdDriveSettings(VirtualSystemSettingData vmSettings)
{
var wmiObjCollection = GetResourceAllocationSettings(vmSettings);
foreach (ResourceAllocationSettingData wmiObj in wmiObjCollection)
{
if (wmiObj.ResourceType == 16)
{
return wmiObj;
}
}
var errMsg = string.Format(
"Cannot find the Dvd drive in VirtualSystemSettingData {0}",
vmSettings.Path.Path);
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
public static ResourceAllocationSettingData GetIDEControllerSettings(VirtualSystemSettingData vmSettings, string cntrllerAddr)
{
var wmiObjCollection = GetResourceAllocationSettings(vmSettings);
foreach (ResourceAllocationSettingData wmiObj in wmiObjCollection)
{
if (wmiObj.ResourceSubType == "Microsoft Emulated IDE Controller" && wmiObj.Address == cntrllerAddr)
{
return wmiObj;
}
}
var errMsg = string.Format(
"Cannot find the Microsoft Emulated IDE Controlle at address {0} in VirtualSystemSettingData {1}",
cntrllerAddr,
vmSettings.Path.Path);
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
///
/// VM resources, typically hardware a described by a generic MSVM_ResourceAllocationSettingData object. The hardware type being
/// described is identified in two ways: in general terms using an enum in the ResourceType field, and in terms of the implementation
/// using text in the ResourceSubType field.
/// See http://msdn.microsoft.com/en-us/library/cc136877%28v=vs.85%29.aspx
///
///
///
public static ResourceAllocationSettingData.ResourceAllocationSettingDataCollection GetResourceAllocationSettings(VirtualSystemSettingData vmSettings)
{
// An ASSOCIATOR object provides the cross reference from the VirtualSystemSettingData and the
// ResourceAllocationSettingData, but generated wrappers do not expose a ASSOCIATOR OF query as a method.
// Instead, we use the System.Management to code the equivalant of
// string query = string.Format( "ASSOCIATORS OF {{{0}}} WHERE ResultClass = {1}", vmSettings.path, resultclassName);
//
var wmiObjQuery = new RelatedObjectQuery(vmSettings.Path.Path, ResourceAllocationSettingData.CreatedClassName);
// NB: default scope of ManagementObjectSearcher is '\\.\root\cimv2', which does not contain
// the virtualisation objects.
var wmiObjectSearch = new ManagementObjectSearcher(vmSettings.Scope, wmiObjQuery);
var wmiObjCollection = new ResourceAllocationSettingData.ResourceAllocationSettingDataCollection(wmiObjectSearch.Get());
if (wmiObjCollection != null)
{
return wmiObjCollection;
}
var errMsg = string.Format("No ResourceAllocationSettingData in VirtualSystemSettingData {0}", vmSettings.Path.Path);
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
public static SwitchPort[] GetSwitchPorts(ComputerSystem vm)
{
var virtSwitchMgmtSvc = GetVirtualSwitchManagementService();
// Get NIC path
var condition = string.Format("ElementName=\"{0}\"", vm.ElementName);
var switchPortCollection = SwitchPort.GetInstances(virtSwitchMgmtSvc.Scope, condition);
List result = new List(switchPortCollection.Count);
foreach (SwitchPort item in switchPortCollection)
{
result.Add(item);
}
return result.ToArray();
}
///
/// Deprecated
///
///
///
public static SwitchPort GetSwitchPort(SyntheticEthernetPort nic)
{
// An ASSOCIATOR object provides the cross reference between WMI objects,
// but generated wrappers do not expose a ASSOCIATOR OF query as a method.
// Instead, we use the System.Management to code the equivalant of
// string query = string.Format( "ASSOCIATORS OF {{{0}}} WHERE ResultClass = {1}", wmiObject.path, resultclassName);
//
var wmiObjQuery = new RelatedObjectQuery(nic.Path.Path, VmLANEndpoint.CreatedClassName);
// NB: default scope of ManagementObjectSearcher is '\\.\root\cimv2', which does not contain
// the virtualisation objects.
var wmiObjectSearch = new ManagementObjectSearcher(nic.Scope, wmiObjQuery);
var wmiObjCollection = new VmLANEndpoint.VmLANEndpointCollection(wmiObjectSearch.Get());
// assert
if (wmiObjCollection.Count < 1)
{
var errMsg = string.Format("No VmLANEndpoint for external NIC {0} on Hyper-V server", nic.Name);
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
VmLANEndpoint vmEndPoint = wmiObjCollection.OfType().First();
var switchPortQuery = new RelatedObjectQuery(vmEndPoint.Path.Path, SwitchPort.CreatedClassName);
var switchPortSearch = new ManagementObjectSearcher(nic.Scope, switchPortQuery);
var switchPortCollection = new SwitchPort.SwitchPortCollection(switchPortSearch.Get());
// assert
if (switchPortCollection.Count < 1)
{
var errMsg = string.Format("No SwitchPort for external NIC {0} on Hyper-V server", nic.Name);
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
SwitchPort switchPort = wmiObjCollection.OfType().First();
return switchPort;
}
public static SyntheticEthernetPortSettingData[] GetEthernetPorts(ComputerSystem vm)
{
// An ASSOCIATOR object provides the cross reference from the ComputerSettings and the
// SyntheticEthernetPortSettingData, via the VirtualSystemSettingData.
// However, generated wrappers do not expose a ASSOCIATOR OF query as a method.
// Instead, we use the System.Management to code the equivalant of
//
// string query = string.Format( "ASSOCIATORS OF {{{0}}} WHERE ResultClass = {1}", vm.path, resultclassName);
//
VirtualSystemSettingData vmSettings = GetVmSettings(vm);
var wmiObjQuery = new RelatedObjectQuery(vmSettings.Path.Path, SyntheticEthernetPortSettingData.CreatedClassName);
// NB: default scope of ManagementObjectSearcher is '\\.\root\cimv2', which does not contain
// the virtualisation objects.
var wmiObjectSearch = new ManagementObjectSearcher(vm.Scope, wmiObjQuery);
var wmiObjCollection = new SyntheticEthernetPortSettingData.SyntheticEthernetPortSettingDataCollection(wmiObjectSearch.Get());
List results = new List(wmiObjCollection.Count);
foreach (SyntheticEthernetPortSettingData item in wmiObjCollection)
{
results.Add(item);
}
return results.ToArray();
}
public static VirtualSystemSettingData GetVmSettings(ComputerSystem vm)
{
// An ASSOCIATOR object provides the cross reference from the ComputerSettings and the
// VirtualSystemSettingData, but generated wrappers do not expose a ASSOCIATOR OF query as a method.
// Instead, we use the System.Management to code the equivalant of
// string query = string.Format( "ASSOCIATORS OF {{{0}}} WHERE ResultClass = {1}", vm.path, resultclassName);
//
var wmiObjQuery = new RelatedObjectQuery(vm.Path.Path, VirtualSystemSettingData.CreatedClassName);
// NB: default scope of ManagementObjectSearcher is '\\.\root\cimv2', which does not contain
// the virtualisation objects.
var wmiObjectSearch = new ManagementObjectSearcher(vm.Scope, wmiObjQuery);
var wmiObjCollection = new VirtualSystemSettingData.VirtualSystemSettingDataCollection(wmiObjectSearch.Get());
// When snapshots are taken into account, there can be multiple settings objects
// take the first one that isn't a snapshot
foreach (VirtualSystemSettingData wmiObj in wmiObjCollection)
{
if (wmiObj.SettingType == 3)
{
return wmiObj;
}
}
var errMsg = string.Format("No VirtualSystemSettingData for VM {0}, path {1}", vm.ElementName, vm.Path.Path);
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
}
}