public VirtualEthernetSwitchManagementService 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 = VirtualEthernetSwitchManagementService.GetInstances();
foreach (VirtualEthernetSwitchManagementService 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;
}
/// <summary>
/// Always produces a VHDX.
/// </summary>
/// <param name="MaxInternalSize"></param>
/// <param name="Path"></param>
public void CreateDynamicVirtualHardDisk(ulong MaxInternalSize, string Path)
{
// Produce description of the virtual disk in the format of a Msvm_VirtualHardDiskSettings object
// Example at http://www.getcodesamples.com/src/FC025DDC/76689747, but
// Is there a template we can use to fill in the settings?
var newVirtHDSettings = VirtualHardDiskSettingData.CreateInstance();
newVirtHDSettings.LateBoundObject["Type"] = 3; // Dynamic
newVirtHDSettings.LateBoundObject["Format"] = 3; // VHDX
newVirtHDSettings.LateBoundObject["Path"] = Path;
newVirtHDSettings.LateBoundObject["MaxInternalSize"] = MaxInternalSize;
newVirtHDSettings.LateBoundObject["BlockSize"] = 0; // Use defaults
newVirtHDSettings.LateBoundObject["LogicalSectorSize"] = 0; // Use defaults
newVirtHDSettings.LateBoundObject["PhysicalSectorSize"] = 0; // Use defaults
// Optional: newVirtHDSettings.CommitObject();
// Add the new vhd object as a virtual hard disk to the vm.
string newVirtHDSettingsString = newVirtHDSettings.LateBoundObject.GetText(System.Management.TextFormat.CimDtd20);
// Resource settings are changed through the management service
System.Management.ManagementPath jobPath;
var imgMgr = GetImageManagementService();
var ret_val = imgMgr.CreateVirtualHardDisk(newVirtHDSettingsString, out jobPath);
// If the Job is done asynchronously
if (ret_val == ReturnCode.Started)
{
StorageJobCompleted(jobPath);
}
else if (ret_val != ReturnCode.Completed)
{
var errMsg = string.Format(
"Failed to CreateVirtualHardDisk size {0}, path {1} due to {2}",
MaxInternalSize,
Path,
ReturnCode.ToString(ret_val));
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
}
private void SetBandWidthLimit(ulong limit, EthernetPortAllocationSettingData portPath)
{
logger.DebugFormat("Setting network rate limit to {0}", limit);
var vmVirtMgmtSvc = GetVirtualisationSystemManagementService();
var bandwidthSettings = EthernetSwitchPortBandwidthSettingData.GetInstances(vmVirtMgmtSvc.Scope, "InstanceID LIKE \"%Default\"");
// Assert
if (bandwidthSettings.Count != 1)
{
var errMsg = string.Format("Internal error, could not find default EthernetSwitchPortBandwidthSettingData instance");
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
var defaultBandwidthSettings = bandwidthSettings.OfType<EthernetSwitchPortBandwidthSettingData>().First();
var newBandwidthSettings = new EthernetSwitchPortBandwidthSettingData((ManagementBaseObject)defaultBandwidthSettings.LateBoundObject.Clone());
newBandwidthSettings.Limit = limit * 1000000;
// Insert bandwidth settings to nic
string[] newResources = new string[] { newBandwidthSettings.LateBoundObject.GetText(System.Management.TextFormat.CimDtd20) };
ManagementPath[] newResourcePaths = AddFeatureSettings(newResources, portPath.Path);
// assert
if (newResourcePaths.Length != 1)
{
var errMsg = string.Format(
"Failed to properly apply network rate limit {0} for NIC on port {1}",
limit,
portPath.Path);
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
}
/// <summary>
/// External VSwitch has an external NIC, and we assume there is only one external NIC and one external vswitch.
/// </summary>
/// <param name="vmSettings"></param>
/// <returns></returns>
/// <throw>Throws if there is no vswitch</throw>
/// <remarks>
/// With V1 API, external ethernet port was attached to the land endpoint, which was attached to the switch.
/// e.g. Msvm_ExternalEthernetPort -> SwitchLANEndpoint -> SwitchPort -> VirtualSwitch
///
/// With V2 API, there are two kinds of lan endpoint: one on the computer system and one on the switch
/// e.g. Msvm_ExternalEthernetPort -> LANEndpoint -> LANEdnpoint -> EthernetSwitchPort -> VirtualEthernetSwitch
/// </remarks>
public static VirtualEthernetSwitch GetExternalVirtSwitch(String vSwitchName)
{
// Work back from the first *bound* external NIC we find.
var externNICs = ExternalEthernetPort.GetInstances("IsBound = TRUE");
VirtualEthernetSwitch vSwitch = null;
// Assert
if (externNICs.Count == 0 )
{
var errMsg = "No ExternalEthernetPort available to Hyper-V";
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
foreach(ExternalEthernetPort externNIC in externNICs.OfType<ExternalEthernetPort>()) {
// 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, LANEndpoint.CreatedClassName);
var endpointSearch = new ManagementObjectSearcher(externNIC.Scope, endpointQuery);
var endpointCollection = new LANEndpoint.LANEndpointCollection(endpointSearch.Get());
// assert
if (endpointCollection.Count < 1 )
{
var errMsg = string.Format("No adapter-based LANEndpoint for external NIC {0} on Hyper-V server", externNIC.Name);
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
LANEndpoint adapterEndPoint = endpointCollection.OfType<LANEndpoint>().First();
var switchEndpointQuery = new RelatedObjectQuery(adapterEndPoint.Path.Path, LANEndpoint.CreatedClassName);
var switchEndpointSearch = new ManagementObjectSearcher(externNIC.Scope, switchEndpointQuery);
var switchEndpointCollection = new LANEndpoint.LANEndpointCollection(switchEndpointSearch.Get());
// assert
if (endpointCollection.Count < 1)
{
var errMsg = string.Format("No Switch-based LANEndpoint for external NIC {0} on Hyper-V server", externNIC.Name);
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
LANEndpoint switchEndPoint = switchEndpointCollection.OfType<LANEndpoint>().First();
var switchPortQuery = new RelatedObjectQuery(switchEndPoint.Path.Path, EthernetSwitchPort.CreatedClassName);
var switchPortSearch = new ManagementObjectSearcher(switchEndPoint.Scope, switchPortQuery);
var switchPortCollection = new EthernetSwitchPort.EthernetSwitchPortCollection(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;
}
EthernetSwitchPort switchPort = switchPortCollection.OfType<EthernetSwitchPort>().First();
var vSwitchQuery = new RelatedObjectQuery(switchPort.Path.Path, VirtualEthernetSwitch.CreatedClassName);
var vSwitchSearch = new ManagementObjectSearcher(externNIC.Scope, vSwitchQuery);
var vSwitchCollection = new VirtualEthernetSwitch.VirtualEthernetSwitchCollection(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;
}
vSwitch = vSwitchCollection.OfType<VirtualEthernetSwitch>().First();
if (vSwitch.ElementName.Equals(vSwitchName) == true)
{
return vSwitch;
}
}
return vSwitch;
}
private void InsertDiskImage(ComputerSystem vm, string diskImagePath, 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 = CloneStorageAllocationSetting(defaultDiskQuery);
// Set file containing the disk image
newDiskSettings.LateBoundObject["Parent"] = drivePath.Path;
// V2 API uses HostResource to specify image, see http://msdn.microsoft.com/en-us/library/hh859775(v=vs.85).aspx
newDiskSettings.LateBoundObject["HostResource"] = new string[] { diskImagePath };
newDiskSettings.CommitObject();
// Add the new Msvm_StorageAllocationSettingData object as a virtual hard disk to the vm.
string[] newDiskResource = new string[] { newDiskSettings.LateBoundObject.GetText(System.Management.TextFormat.CimDtd20) };
ManagementPath[] newDiskPaths = AddStorageResource(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,
diskImagePath);
}
/// <summary>
/// Removes a disk image from a drive, but does not remove the drive itself.
/// </summary>
/// <param name="vm"></param>
/// <param name="diskFileName"></param>
private void RemoveStorageImage(ComputerSystem vm, string diskFileName)
{
// Obtain StorageAllocationSettingData for disk
StorageAllocationSettingData.StorageAllocationSettingDataCollection storageSettingsObjs = StorageAllocationSettingData.GetInstances();
StorageAllocationSettingData imageToRemove = null;
foreach (StorageAllocationSettingData item in storageSettingsObjs)
{
if (item.HostResource == null || item.HostResource.Length != 1)
{
continue;
}
string hostResource = item.HostResource[0];
if (Path.Equals(hostResource, diskFileName))
{
imageToRemove = item;
break;
}
}
// assert
if (imageToRemove == null)
{
var errMsg = string.Format(
"Failed to remove disk image {0} from VM {1} (GUID {2}): the disk image is not attached.",
diskFileName,
vm.ElementName,
vm.Name);
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
RemoveStorageResource(imageToRemove.Path, vm);
logger.InfoFormat("Removed disk image {0} from VM {1} (GUID {2}): the disk image is not attached.",
diskFileName,
vm.ElementName,
vm.Name);
}
public 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;
// DateTime is unused
var ret_val = vm.RequestStateChange(requiredState, new DateTime(), out jobPath);
// If the Job is done asynchronously
if (ret_val == ReturnCode.Started)
{
JobCompleted(jobPath);
}
else if (ret_val == 32775)
{ // TODO: check
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);
}
/// <summary>
/// Create new storage media resources, e.g. hard disk images and ISO disk images
/// see http://msdn.microsoft.com/en-us/library/hh859775(v=vs.85).aspx
/// </summary>
/// <param name="wmiQuery"></param>
/// <returns></returns>
private static StorageAllocationSettingData CloneStorageAllocationSetting(string wmiQuery)
{
var defaultDiskImageSettingsObjs = StorageAllocationSettingData.GetInstances(wmiQuery);
// assert
if (defaultDiskImageSettingsObjs.Count != 1)
{
var errMsg = string.Format("Failed to find Msvm_StorageAllocationSettingData for the query {0}", wmiQuery);
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
StorageAllocationSettingData defaultDiskDriveSettings = defaultDiskImageSettingsObjs.OfType<StorageAllocationSettingData>().First();
return new StorageAllocationSettingData((ManagementBaseObject)defaultDiskDriveSettings.LateBoundObject.Clone());
}
public 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.VirtualSystemType == "Microsoft:Hyper-V:System:Realized" ||
wmiObj.VirtualSystemType == "Microsoft:Hyper-V:System:Planned")
{
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;
}
public EthernetSwitchPortVlanSettingData GetVlanSettings(EthernetPortAllocationSettingData ethernetConnection)
{
// An ASSOCIATOR object provides the cross reference from the VirtualSystemSettingData and the
// EthernetPortAllocationSettingData, 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(ethernetConnection.Path.Path, EthernetSwitchPortVlanSettingData.CreatedClassName);
// NB: default scope of ManagementObjectSearcher is '\\.\root\cimv2', which does not contain
// the virtualisation objects.
var wmiObjectSearch = new ManagementObjectSearcher(ethernetConnection.Scope, wmiObjQuery);
var wmiObjCollection = new EthernetSwitchPortVlanSettingData.EthernetSwitchPortVlanSettingDataCollection(wmiObjectSearch.Get());
if (wmiObjCollection.Count == 0)
{
return null;
}
// Assert
if (wmiObjCollection.Count > 1)
{
var errMsg = string.Format("Internal error, morn one VLAN settings for a single ethernetConnection");
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
return wmiObjCollection.OfType<EthernetSwitchPortVlanSettingData>().First();
}
private ManagementPath AttachNewDrive(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["AddressOnParent"] = "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.AddressOnParent);
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];
}
private static ComputerSystem CreateDefaultVm(VirtualSystemManagementService vmMgmtSvc, string name)
{
// Tweak default settings by basing new VM on default global setting object
// with designed display name.
UInt16 startupAction = 2; // Do nothing.
UInt16 stopAction = 4; // Shutdown.
VirtualSystemSettingData vs_gs_data = VirtualSystemSettingData.CreateInstance();
vs_gs_data.LateBoundObject["ElementName"] = name;
vs_gs_data.LateBoundObject["AutomaticStartupAction"] = startupAction.ToString();
vs_gs_data.LateBoundObject["AutomaticShutdownAction"] = stopAction.ToString();
vs_gs_data.LateBoundObject["Notes"] = new string[] { "CloudStack creating VM, do not edit. \n" };
System.Management.ManagementPath jobPath;
System.Management.ManagementPath defined_sys;
var ret_val = vmMgmtSvc.DefineSystem(
null,
new string[0],
vs_gs_data.LateBoundObject.GetText(System.Management.TextFormat.CimDtd20),
out jobPath,
out defined_sys);
// 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;
}
private EthernetPortAllocationSettingData AttachNicToPort(ComputerSystem newVm, SyntheticEthernetPortSettingData newAdapter, String vSwitchName)
{
// Get the virtual switch
VirtualEthernetSwitch vSwitch = GetExternalVirtSwitch(vSwitchName);
//check the the recevied vSwitch is the same as vSwitchName.
if (!vSwitchName.Equals("") && !vSwitch.ElementName.Equals(vSwitchName))
{
var errMsg = string.Format("Internal error, coudl not find Virtual Switch with the name : " +vSwitchName);
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
// Create port for adapter
var defaultEthernetPortSettings = EthernetPortAllocationSettingData.GetInstances(vSwitch.Scope, "InstanceID LIKE \"%Default\"");
// assert
if (defaultEthernetPortSettings.Count != 1)
{
var errMsg = string.Format("Internal error, coudl not find default EthernetPortAllocationSettingData instance");
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
var defaultEthernetPortSettingsObj = defaultEthernetPortSettings.OfType<EthernetPortAllocationSettingData>().First();
var newEthernetPortSettings = new EthernetPortAllocationSettingData((ManagementBaseObject)defaultEthernetPortSettingsObj.LateBoundObject.Clone());
newEthernetPortSettings.LateBoundObject["Parent"] = newAdapter.Path.Path;
newEthernetPortSettings.LateBoundObject["HostResource"] = new string[] { vSwitch.Path.Path };
// Insert NIC into vm
string[] newResources = new string[] { newEthernetPortSettings.LateBoundObject.GetText(System.Management.TextFormat.CimDtd20) };
ManagementPath[] newResourcePaths = AddVirtualResource(newResources, newVm);
// 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}",
newVm.ElementName,
newVm.Name,
newResourcePaths.Length);
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
return new EthernetPortAllocationSettingData(newResourcePaths[0]);
}
private static void ModifySystemSetting(VirtualSystemManagementService vmMgmtSvc, string systemSettings)
{
// Resource settings are changed through the management service
System.Management.ManagementPath jobPath;
var ret_val = vmMgmtSvc.ModifySystemSettings(
systemSettings,
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 system setting {0}",
ReturnCode.ToString(ret_val));
var ex = new WmiException(errMsg);
logger.Error(errMsg, ex);
throw ex;
}
}
请发表评论