def connect(self, virtual_switch: 'VirtualSwitch'): """ Connect adapter to given virtual switch. :param virtual_switch: virtual switch to connect """ management_service = VirtualSystemManagementService( self.Scope.query_one( 'SELECT * FROM Msvm_VirtualSystemManagementService')) Msvm_VirtualSystemSettingData = self.traverse((RelatedNode( ("Msvm_VirtualSystemSettingData", )), ))[-1][-1] Msvm_ResourcePool = self.Scope.query_one( "SELECT * FROM Msvm_ResourcePool WHERE ResourceSubType = 'Microsoft:Hyper-V:Ethernet Connection' AND Primordial = True" ) Msvm_EthernetPortAllocationSettingData_Path = ( RelatedNode( ("Msvm_AllocationCapabilities", "Msvm_ElementCapabilities", None, None, None, None, False, None)), RelationshipNode(("Msvm_SettingsDefineCapabilities", ), selector=PropertiesSelector(ValueRole=0)), PropertyNode("PartComponent", transformer=ReferenceTransformer())) Msvm_EthernetPortAllocationSettingData = \ Msvm_ResourcePool.traverse(Msvm_EthernetPortAllocationSettingData_Path)[-1][-1] Msvm_EthernetPortAllocationSettingData.properties.Parent = self Msvm_EthernetPortAllocationSettingData.properties.HostResource = [ virtual_switch ] management_service.AddResourceSettings( Msvm_VirtualSystemSettingData, Msvm_EthernetPortAllocationSettingData)
def add_vhd_disk(self, vhd_disk: VHDDisk): """ Adds given ``VHDDisk`` to virtual machine. :param vhd_disk: ``VHDDisk`` to add to machine """ # TODO ability to select controller, disk port, error checking. Make disk bootable by default, etc management_service = VirtualSystemManagementService( self.Scope.query_one( 'SELECT * FROM Msvm_VirtualSystemManagementService')) Msvm_VirtualSystemSettingData = self.get_child( (VirtualSystemSettingDataNode, )) Msvm_ResourcePool_SyntheticDiskDrive = self.Scope.query_one( "SELECT * FROM Msvm_ResourcePool WHERE ResourceSubType = 'Microsoft:Hyper-V:Synthetic Disk Drive' AND Primordial = True" ) Msvm_StorageAllocationSettingData_Path = ( RelatedNode( ("Msvm_AllocationCapabilities", "Msvm_ElementCapabilities", None, None, None, None, False, None)), RelationshipNode(("Msvm_SettingsDefineCapabilities", ), selector=PropertiesSelector(ValueRole=0)), PropertyNode("PartComponent", transformer=ReferenceTransformer())) IdeController_Path = (RelatedNode( ("Msvm_ResourceAllocationSettingData", ), selector=PropertiesSelector( ResourceType=5, ResourceSubType='Microsoft:Hyper-V:Emulated IDE Controller', Address=0)), ) IdeController = Msvm_VirtualSystemSettingData.get_child( IdeController_Path) Msvm_StorageAllocationSettingData = Msvm_ResourcePool_SyntheticDiskDrive.get_child( Msvm_StorageAllocationSettingData_Path).clone() Msvm_StorageAllocationSettingData.properties.Parent = IdeController Msvm_StorageAllocationSettingData.properties.AddressOnParent = 0 synthetic_disk_drive = \ management_service.AddResourceSettings(Msvm_VirtualSystemSettingData, Msvm_StorageAllocationSettingData)[ 'ResultingResourceSettings'][-1] Msvm_ResourcePool_VirtualHardDisk = self.Scope.query_one( "SELECT * FROM Msvm_ResourcePool WHERE ResourceSubType = 'Microsoft:Hyper-V:Virtual Hard Disk' AND Primordial = True" ) virtual_hard_disk_path = (RelatedNode( ("Msvm_AllocationCapabilities", "Msvm_ElementCapabilities", None, None, None, None, False, None)), RelationshipNode( ("Msvm_SettingsDefineCapabilities", ), selector=PropertiesSelector( ValueRole=0)), PropertyNode( "PartComponent", transformer=ReferenceTransformer())) virtual_hard_disk_data = Msvm_ResourcePool_VirtualHardDisk.get_child( virtual_hard_disk_path).clone() virtual_hard_disk_data.properties.Parent = synthetic_disk_drive virtual_hard_disk_data.properties.HostResource = [vhd_disk.Path] management_service.AddResourceSettings(Msvm_VirtualSystemSettingData, virtual_hard_disk_data)
def com_ports(self) -> List[VirtualComPort]: """ Returns list of machine com-ports. :return: machine com-ports """ result = [] com_ports_path = ( VirtualSystemSettingDataNode, RelatedNode( ("Msvm_ResourceAllocationSettingData", ), selector=PropertiesSelector( ResourceSubtype="Microsoft:Hyper-V:Serial Controller")), RelatedNode(("Msvm_SerialPortSettingData", ))) for _, _, Msvm_SerialPortSettingData in self.traverse(com_ports_path): result.append(VirtualComPort(Msvm_SerialPortSettingData, self)) return result
def guest_settings(self) -> AdapterGuestSettings: """ Returns instance of AdapterGuestSettings. :return: associated AdapterGuestSettings with given adapter """ settings_path = (RelatedNode( ("Msvm_GuestNetworkAdapterConfiguration", )), ) return AdapterGuestSettings(self.get_child(settings_path), self)
def _get_shutdown_component(self): shutdown_component_traverse_result = self.traverse((RelatedNode( ("Msvm_ShutdownComponent", )), )) if shutdown_component_traverse_result: Msvm_ShutdownComponent = shutdown_component_traverse_result[-1][-1] operational_status = ShutdownComponent_OperationalStatus.from_code( Msvm_ShutdownComponent.properties['OperationalStatus'][0]) if operational_status in ( ShutdownComponent_OperationalStatus.OK, ShutdownComponent_OperationalStatus.Degraded): return ShutdownComponent(Msvm_ShutdownComponent) return None
def network_adapters(self) -> List[VirtualNetworkAdapter]: """ Returns list of machines network adapters. :return: list of machines network adapters """ result = [] port_to_switch_path = (VirtualSystemSettingDataNode, RelatedNode(( "Msvm_SyntheticEthernetPortSettingData", ))) for _, Msvm_SyntheticEthernetPortSettingData in self.traverse( port_to_switch_path): result.append( VirtualNetworkAdapter(Msvm_SyntheticEthernetPortSettingData, self)) return result
def switch(self) -> 'VirtualSwitch': result = [] port_to_switch_path = (RelatedNode( ("Msvm_EthernetPortAllocationSettingData", )), PropertyNode( "HostResource", transformer=ReferenceTransformer())) for _, virtual_switch in self.traverse(port_to_switch_path): result.append(VirtualSwitch(virtual_switch)) if len(result) > 1: raise Exception( "Something horrible happened, virtual network adapter connected to more that one virtual switch" ) if result: return result[0] return None
def add_adapter(self, static_mac=False, mac=None, adapter_name="Network Adapter") -> 'VirtualNetworkAdapter': """ Add adapter to virtual machine. :param static_mac: make adapter with static mac :param mac: mac address t assign :param adapter_name: adapter name :return: created adapter """ management_service = VirtualSystemManagementService( self.Scope.query_one( 'SELECT * FROM Msvm_VirtualSystemManagementService')) Msvm_ResourcePool = self.Scope.query_one( "SELECT * FROM Msvm_ResourcePool WHERE ResourceSubType = 'Microsoft:Hyper-V:Synthetic Ethernet Port' " "AND Primordial = True") Msvm_SyntheticEthernetPortSettingData_Path = ( RelatedNode( ("Msvm_AllocationCapabilities", "Msvm_ElementCapabilities", None, None, None, None, False, None)), RelationshipNode(("Msvm_SettingsDefineCapabilities", ), selector=PropertiesSelector(ValueRole=0)), PropertyNode("PartComponent", transformer=ReferenceTransformer())) Msvm_VirtualSystemSettingData = self.traverse( (VirtualSystemSettingDataNode, ))[-1][-1] Msvm_SyntheticEthernetPortSettingData = Msvm_ResourcePool.traverse( Msvm_SyntheticEthernetPortSettingData_Path)[-1][-1] Msvm_SyntheticEthernetPortSettingData.properties.VirtualSystemIdentifiers = clr_Array[ clr_String]([generate_guid()]) Msvm_SyntheticEthernetPortSettingData.properties.ElementName = adapter_name Msvm_SyntheticEthernetPortSettingData.properties.StaticMacAddress = static_mac if mac: Msvm_SyntheticEthernetPortSettingData.properties.Address = mac result = management_service.AddResourceSettings( Msvm_VirtualSystemSettingData, Msvm_SyntheticEthernetPortSettingData) return VirtualNetworkAdapter(result['ResultingResourceSettings'][-1], self)
class VirtualMachine(MOWrapper): """ Represents virtual machine. Gives access to machine name and id, network adapters, gives ability to start, stop, pause, save, reset machine. """ LOG = logging.getLogger('%s.%s' % (__module__, __qualname__)) MO_CLS = 'Msvm_ComputerSystem' _CLS_MAP_PRIORITY = {"Msvm_VirtualSystemSettingData": 0} PATH_MAP = { "Msvm_ProcessorSettingData": (VirtualSystemSettingDataNode, RelatedNode( ("Msvm_ProcessorSettingData", ))), "Msvm_MemorySettingData": ( VirtualSystemSettingDataNode, RelatedNode(("Msvm_MemorySettingData", )), ), "Msvm_VirtualSystemSettingData": (VirtualSystemSettingDataNode, ) } RESOURCE_CLASSES = ("Msvm_ProcessorSettingData", "Msvm_MemorySettingData") SYSTEM_CLASSES = ("Msvm_VirtualSystemSettingData", ) def apply_properties(self, class_name: str, properties: Dict[str, Any]): """ Apply ``properties`` for ``class_name`` that associated with virtual machine. :param class_name: class name that will be used for modification :param properties: properties to apply """ management_service = VirtualSystemManagementService( self.Scope.query_one( 'SELECT * FROM Msvm_VirtualSystemManagementService')) class_instance = self.traverse(self.PATH_MAP[class_name])[0][-1] for property_name, property_value in properties.items(): setattr(class_instance.properties, property_name, property_value) if class_name in self.RESOURCE_CLASSES: management_service.ModifyResourceSettings(class_instance) if class_name in self.SYSTEM_CLASSES: management_service.ModifySystemSettings( SystemSettings=class_instance) def apply_properties_group(self, properties_group: Dict[str, Dict[str, Any]]): """ Applies given properties to virtual machine. :param properties_group: dict of classes and their properties """ if properties_group: for cls, properties in sorted( properties_group.items(), key=lambda itm: self._CLS_MAP_PRIORITY.get(itm[0], 100)): self.apply_properties(cls, properties) @property def name(self) -> str: """ Virtual machine name that displayed everywhere in windows UI and other places. :return: virtual machine name """ return self.properties['ElementName'] @property def id(self) -> str: """ Unique virtual machine identifier. :return: virtual machine identifier """ return self.properties['Name'] @property def state(self) -> VirtualMachineState: """ Current virtual machine state. It will try to get actual real state(like running, stopped, etc) for ``DEFAULT_WAIT_OP_TIMEOUT`` seconds before returning ``VirtualMachineState.UNDEFINED``. We need this ``DEFAULT_WAIT_OP_TIMEOUT`` because hyper-v likes some middle states, like starting, stopping, etc. Usually this middle states long not more that 10 seconds and soon will changed to something that we expecting. :return: virtual machine state """ _start = time.time() state = self._enabled_state.to_virtual_machine_state() while state == VirtualMachineState.UNDEFINED and time.time( ) - _start < DEFAULT_WAIT_OP_TIMEOUT: state = self._enabled_state.to_virtual_machine_state() time.sleep(.1) return state def start(self): """ Try to start virtual machine and wait for started state for ``timeout`` seconds. """ if self.state != VirtualMachineState.RUNNING: self.LOG.debug("Starting machine '%s'", self.id) desired_state = ComputerSystem_RequestStateChange_RequestedState.Running target_enabled_state = desired_state.to_ComputerSystem_EnabledState( ) self.RequestStateChange(desired_state) if not self._wait_for_enabled_state( target_enabled_state, timeout=DEFAULT_WAIT_OP_TIMEOUT): raise Exception( "Failed to put machine to '%s' in %s seconds" % (target_enabled_state, DEFAULT_WAIT_OP_TIMEOUT)) self.LOG.debug("Started machine '%s'", self.id) else: self.LOG.debug("Machine '%s' is already started", self.id) def stop(self, force=False, hard=False): """ Try to stop virtual machine and wait for stopped state for ``timeout`` seconds. :param force: indicates if we need to wait for user programs completion, ignored if *force* is *True* :param hard: indicates if we need to perform turn off(power off) """ self.LOG.debug("Stopping machine '%s'", self.id) desired_state = ComputerSystem_RequestStateChange_RequestedState.Off target_enabled_state = desired_state.to_ComputerSystem_EnabledState() if not hard: shutdown_component = self._get_shutdown_component() if shutdown_component: shutdown_component.InitiateShutdown(force, "hvapi shutdown") if not self._wait_for_enabled_state( target_enabled_state, timeout=DEFAULT_WAIT_OP_TIMEOUT): self.LOG.debug( "Failed to stop machine '%s' gracefully, killing...", self.id) self.kill() else: self.LOG.debug( "Graceful stop for machine '%s' not available, killing...", self.id) self.kill() else: self.kill() self.LOG.debug("Stopped machine '%s'", self.id) def kill(self): """ Hard-kill vm. """ desired_state = ComputerSystem_RequestStateChange_RequestedState.Off target_enabled_state = desired_state.to_ComputerSystem_EnabledState() self.RequestStateChange(desired_state) if not self._wait_for_enabled_state(target_enabled_state, timeout=DEFAULT_WAIT_OP_TIMEOUT): raise Exception("Failed to put machine to '%s' in %s seconds" % (target_enabled_state, DEFAULT_WAIT_OP_TIMEOUT)) def save(self): """ Try to save virtual machine state and wait for saved state for ``timeout`` seconds. """ if self.state != VirtualMachineState.SAVED: self.LOG.debug("Saving machine '%s'", self.id) desired_state = ComputerSystem_RequestStateChange_RequestedState.Saved target_enabled_state = desired_state.to_ComputerSystem_EnabledState( ) self.RequestStateChange(desired_state) if not self._wait_for_enabled_state( target_enabled_state, timeout=DEFAULT_WAIT_OP_TIMEOUT): raise Exception( "Failed to put machine to '%s' in %s seconds" % (target_enabled_state, DEFAULT_WAIT_OP_TIMEOUT)) self.LOG.debug("Saved machine '%s'", self.id) else: self.LOG.debug("Machine '%s' is already saved", self.id) def pause(self): if self.state != VirtualMachineState.PAUSED: self.LOG.debug("Pausing machine '%s'", self.id) desired_state = ComputerSystem_RequestStateChange_RequestedState.Paused target_enabled_state = desired_state.to_ComputerSystem_EnabledState( ) self.RequestStateChange(desired_state) if not self._wait_for_enabled_state( target_enabled_state, timeout=DEFAULT_WAIT_OP_TIMEOUT): raise Exception( "Failed to put machine to '%s' in %s seconds" % (target_enabled_state, DEFAULT_WAIT_OP_TIMEOUT)) self.LOG.debug("Paused machine '%s'", self.id) else: self.LOG.debug("Machine '%s' is already paused", self.id) def add_adapter(self, static_mac=False, mac=None, adapter_name="Network Adapter") -> 'VirtualNetworkAdapter': """ Add adapter to virtual machine. :param static_mac: make adapter with static mac :param mac: mac address t assign :param adapter_name: adapter name :return: created adapter """ management_service = VirtualSystemManagementService( self.Scope.query_one( 'SELECT * FROM Msvm_VirtualSystemManagementService')) Msvm_ResourcePool = self.Scope.query_one( "SELECT * FROM Msvm_ResourcePool WHERE ResourceSubType = 'Microsoft:Hyper-V:Synthetic Ethernet Port' " "AND Primordial = True") Msvm_SyntheticEthernetPortSettingData_Path = ( RelatedNode( ("Msvm_AllocationCapabilities", "Msvm_ElementCapabilities", None, None, None, None, False, None)), RelationshipNode(("Msvm_SettingsDefineCapabilities", ), selector=PropertiesSelector(ValueRole=0)), PropertyNode("PartComponent", transformer=ReferenceTransformer())) Msvm_VirtualSystemSettingData = self.traverse( (VirtualSystemSettingDataNode, ))[-1][-1] Msvm_SyntheticEthernetPortSettingData = Msvm_ResourcePool.traverse( Msvm_SyntheticEthernetPortSettingData_Path)[-1][-1] Msvm_SyntheticEthernetPortSettingData.properties.VirtualSystemIdentifiers = clr_Array[ clr_String]([generate_guid()]) Msvm_SyntheticEthernetPortSettingData.properties.ElementName = adapter_name Msvm_SyntheticEthernetPortSettingData.properties.StaticMacAddress = static_mac if mac: Msvm_SyntheticEthernetPortSettingData.properties.Address = mac result = management_service.AddResourceSettings( Msvm_VirtualSystemSettingData, Msvm_SyntheticEthernetPortSettingData) return VirtualNetworkAdapter(result['ResultingResourceSettings'][-1], self) def is_connected_to_switch(self, virtual_switch: 'VirtualSwitch'): """ Returns ``True`` if machine is connected to given ``VirtualSwitch``. :param virtual_switch: virtual switch to check connection :return: ``True`` if connected, otherwise ``False`` """ for adapter in self.network_adapters: if virtual_switch == adapter.switch: return True def add_vhd_disk(self, vhd_disk: VHDDisk): """ Adds given ``VHDDisk`` to virtual machine. :param vhd_disk: ``VHDDisk`` to add to machine """ # TODO ability to select controller, disk port, error checking. Make disk bootable by default, etc management_service = VirtualSystemManagementService( self.Scope.query_one( 'SELECT * FROM Msvm_VirtualSystemManagementService')) Msvm_VirtualSystemSettingData = self.get_child( (VirtualSystemSettingDataNode, )) Msvm_ResourcePool_SyntheticDiskDrive = self.Scope.query_one( "SELECT * FROM Msvm_ResourcePool WHERE ResourceSubType = 'Microsoft:Hyper-V:Synthetic Disk Drive' AND Primordial = True" ) Msvm_StorageAllocationSettingData_Path = ( RelatedNode( ("Msvm_AllocationCapabilities", "Msvm_ElementCapabilities", None, None, None, None, False, None)), RelationshipNode(("Msvm_SettingsDefineCapabilities", ), selector=PropertiesSelector(ValueRole=0)), PropertyNode("PartComponent", transformer=ReferenceTransformer())) IdeController_Path = (RelatedNode( ("Msvm_ResourceAllocationSettingData", ), selector=PropertiesSelector( ResourceType=5, ResourceSubType='Microsoft:Hyper-V:Emulated IDE Controller', Address=0)), ) IdeController = Msvm_VirtualSystemSettingData.get_child( IdeController_Path) Msvm_StorageAllocationSettingData = Msvm_ResourcePool_SyntheticDiskDrive.get_child( Msvm_StorageAllocationSettingData_Path).clone() Msvm_StorageAllocationSettingData.properties.Parent = IdeController Msvm_StorageAllocationSettingData.properties.AddressOnParent = 0 synthetic_disk_drive = \ management_service.AddResourceSettings(Msvm_VirtualSystemSettingData, Msvm_StorageAllocationSettingData)[ 'ResultingResourceSettings'][-1] Msvm_ResourcePool_VirtualHardDisk = self.Scope.query_one( "SELECT * FROM Msvm_ResourcePool WHERE ResourceSubType = 'Microsoft:Hyper-V:Virtual Hard Disk' AND Primordial = True" ) virtual_hard_disk_path = (RelatedNode( ("Msvm_AllocationCapabilities", "Msvm_ElementCapabilities", None, None, None, None, False, None)), RelationshipNode( ("Msvm_SettingsDefineCapabilities", ), selector=PropertiesSelector( ValueRole=0)), PropertyNode( "PartComponent", transformer=ReferenceTransformer())) virtual_hard_disk_data = Msvm_ResourcePool_VirtualHardDisk.get_child( virtual_hard_disk_path).clone() virtual_hard_disk_data.properties.Parent = synthetic_disk_drive virtual_hard_disk_data.properties.HostResource = [vhd_disk.Path] management_service.AddResourceSettings(Msvm_VirtualSystemSettingData, virtual_hard_disk_data) @property def network_adapters(self) -> List[VirtualNetworkAdapter]: """ Returns list of machines network adapters. :return: list of machines network adapters """ result = [] port_to_switch_path = (VirtualSystemSettingDataNode, RelatedNode(( "Msvm_SyntheticEthernetPortSettingData", ))) for _, Msvm_SyntheticEthernetPortSettingData in self.traverse( port_to_switch_path): result.append( VirtualNetworkAdapter(Msvm_SyntheticEthernetPortSettingData, self)) return result @property def com_ports(self) -> List[VirtualComPort]: """ Returns list of machine com-ports. :return: machine com-ports """ result = [] com_ports_path = ( VirtualSystemSettingDataNode, RelatedNode( ("Msvm_ResourceAllocationSettingData", ), selector=PropertiesSelector( ResourceSubtype="Microsoft:Hyper-V:Serial Controller")), RelatedNode(("Msvm_SerialPortSettingData", ))) for _, _, Msvm_SerialPortSettingData in self.traverse(com_ports_path): result.append(VirtualComPort(Msvm_SerialPortSettingData, self)) return result def get_com_port(self, port: ComPort): """ Get concrete com-port :param port: port to get :return: concrete com-port """ # TODO implement this via wmi return self.com_ports[port.value] # internal methods def _wait_for_enabled_state(self, awaitable_state, timeout=DEFAULT_WAIT_OP_TIMEOUT): _start = time.time() while self._enabled_state != awaitable_state and time.time( ) - _start < timeout: time.sleep(1) return self._enabled_state == awaitable_state def _get_shutdown_component(self): shutdown_component_traverse_result = self.traverse((RelatedNode( ("Msvm_ShutdownComponent", )), )) if shutdown_component_traverse_result: Msvm_ShutdownComponent = shutdown_component_traverse_result[-1][-1] operational_status = ShutdownComponent_OperationalStatus.from_code( Msvm_ShutdownComponent.properties['OperationalStatus'][0]) if operational_status in ( ShutdownComponent_OperationalStatus.OK, ShutdownComponent_OperationalStatus.Degraded): return ShutdownComponent(Msvm_ShutdownComponent) return None @property def _enabled_state(self) -> ComputerSystem_EnabledState: self.reload() return ComputerSystem_EnabledState.from_code( self.properties['EnabledState']) # WMI object methods def RequestStateChange( self, RequestedState: ComputerSystem_RequestStateChange_RequestedState, TimeoutPeriod=None): out_objects = self.invoke("RequestStateChange", RequestedState=RequestedState.value, TimeoutPeriod=TimeoutPeriod) return evaluate_invocation_result( out_objects, ComputerSystem_RequestStateChange_ReturnCodes, ComputerSystem_RequestStateChange_ReturnCodes. Completed_with_No_Error, ComputerSystem_RequestStateChange_ReturnCodes. Method_Parameters_Checked_Transition_Started)