def parse_server_string(server_str): """Parses the given server_string and returns a tuple of host and port. If it's not a combination of host part and port, the port element is an empty string. If the input is invalid expression, return a tuple of two empty strings. """ try: # First of all, exclude pure IPv6 address (w/o port). if netaddr.valid_ipv6(server_str): return (server_str, '') # Next, check if this is IPv6 address with a port number combination. if server_str.find("]:") != -1: (address, port) = server_str.replace('[', '', 1).split(']:') return (address, port) # Third, check if this is a combination of an address and a port if server_str.find(':') == -1: return (server_str, '') # This must be a combination of an address and a port (address, port) = server_str.split(':') return (address, port) except (ValueError, netaddr.AddrFormatError): LOG.error(_LE('Invalid server_string: %s'), server_str) return ('', '')
def local_free(self, handle): try: self._run_and_check_output(kernel32.LocalFree, handle) except exceptions.Win32Exception: LOG.exception( _LE("Could not deallocate memory. " "There could be a memory leak."))
def get_network_iface_ip(self, network_name): networks = [n for n in self._get_network_ifaces_by_name(network_name) if n.DriverDescription == self._HYPERV_VIRT_ADAPTER] if not networks: LOG.error(_LE('No vswitch was found with name: %s'), network_name) return None, None ip_addr = self._scimv2.MSFT_NetIPAddress( InterfaceIndex=networks[0].InterfaceIndex, AddressFamily=self._IPV4_ADDRESS_FAMILY) if not ip_addr: LOG.error(_LE('No IP Address could be found for network: %s'), network_name) return None, None return ip_addr[0].IPAddress, ip_addr[0].PrefixLength
def run(self): try: self._copy() except IOError as err: self._stopped.set() # Invalid argument error means that the vm console pipe was closed, # probably the vm was stopped. The worker can stop it's execution. if err.errno != errno.EINVAL: LOG.error(_LE("Error writing vm console log file from " "serial console pipe. Error: %s") % err)
def _migrate_vm(self, vm_name, new_host, migration_type): vm_group = self._lookup_vm_group_check(vm_name) try: vm_group.MoveToNewNodeParams(self._IGNORE_LOCKED, new_host, [migration_type]) except Exception as e: LOG.error(_LE('Exception during cluster live migration of ' '%(vm_name)s to %(host)s: %(exception)s'), {'vm_name': vm_name, 'host': new_host, 'exception': e})
def run(self): try: self._copy() except IOError as err: self._stopped.set() # Invalid argument error means that the vm console pipe was closed, # probably the vm was stopped. The worker can stop it's execution. if err.errno != errno.EINVAL: LOG.error( _LE("Error writing vm console log file from " "serial console pipe. Error: %s") % err)
def _handle_events(callback): if patcher.is_monkey_patched('thread'): # Retrieve one by one all the events that occurred in # the checked interval. # # We use eventlet.tpool for retrieving the events in # order to avoid issues caused by greenthread/thread # communication. Note that PyMI must use the unpatched # threading module. listen = functools.partial(tpool.execute, listener, event_timeout) else: listen = functools.partial(listener, event_timeout) while True: try: event = listen() vm_name = event.ElementName vm_state = event.EnabledState vm_power_state = self.get_vm_power_state(vm_state) try: callback(vm_name, vm_power_state) except Exception: err_msg = _LE("Executing VM power state change event " "callback failed. " "VM name: %(vm_name)s, " "VM power state: %(vm_power_state)s.") LOG.exception( err_msg, dict(vm_name=vm_name, vm_power_state=vm_power_state)) except exceptions.x_wmi_timed_out: pass except Exception: LOG.exception( _LE("The VM power state change event listener " "encountered an unexpected exception.")) time.sleep(event_timeout / 1000)
def _handle_events(callback): if patcher.is_monkey_patched('thread'): # Retrieve one by one all the events that occurred in # the checked interval. # # We use eventlet.tpool for retrieving the events in # order to avoid issues caused by greenthread/thread # communication. Note that PyMI must use the unpatched # threading module. listen = functools.partial(tpool.execute, listener, event_timeout) else: listen = functools.partial(listener, event_timeout) while True: try: event = listen() vm_name = event.ElementName vm_state = event.EnabledState vm_power_state = self.get_vm_power_state(vm_state) try: callback(vm_name, vm_power_state) except Exception: err_msg = _LE("Executing VM power state change event " "callback failed. " "VM name: %(vm_name)s, " "VM power state: %(vm_power_state)s.") LOG.exception(err_msg, dict(vm_name=vm_name, vm_power_state=vm_power_state)) except wmi.x_wmi_timed_out: pass except Exception: LOG.exception( _LE("The VM power state change event listener " "encountered an unexpected exception.")) time.sleep(event_timeout / 1000)
def _migrate_vm(self, vm_name, new_host, migration_type): vm_group = self._lookup_vm_group_check(vm_name) try: vm_group.MoveToNewNodeParams(self._IGNORE_LOCKED, new_host, [migration_type]) except Exception as e: LOG.error( _LE('Exception during cluster live migration of ' '%(vm_name)s to %(host)s: %(exception)s'), { 'vm_name': vm_name, 'host': new_host, 'exception': e })
def _get_conn_v2(self, host='localhost'): try: return wmi.WMI(moniker='//%s/root/virtualization/v2' % host) except wmi.x_wmi as ex: LOG.exception(_LE('Get version 2 connection error')) if ex.com_error.hresult == -2147217394: msg = (_('Live migration is not supported on target host "%s"') % host) elif ex.com_error.hresult == -2147023174: msg = (_('Target live migration host "%s" is unreachable') % host) else: msg = _('Live migration failed: %s') % ex.message raise exceptions.HyperVException(msg)
def _get_conn_v2(self, host='localhost'): try: return self._get_wmi_obj(self._wmi_namespace % host) except exceptions.x_wmi as ex: LOG.exception(_LE('Get version 2 connection error')) if ex.com_error.hresult == -2147217394: msg = (_('Live migration is not supported on target host "%s"') % host) elif ex.com_error.hresult == -2147023174: msg = (_('Target live migration host "%s" is unreachable') % host) else: msg = _('Live migration failed: %s') % ex.message raise exceptions.HyperVException(msg)
def _get_conn_v2(self, host='localhost'): try: return self._get_wmi_obj(self._wmi_namespace % host) except wmi.x_wmi as ex: LOG.exception(_LE('Get version 2 connection error')) if ex.com_error.hresult == -2147217394: msg = (_('Live migration is not supported on target host "%s"') % host) elif ex.com_error.hresult == -2147023174: msg = (_('Target live migration host "%s" is unreachable') % host) else: msg = _('Live migration failed: %s') % ex.message raise exceptions.HyperVException(msg)
def listener(callback): while True: # We avoid setting an infinite timeout in order to let # the process gracefully stop. Note that the os-win WMI # event listeners are meant to be used as long running # daemons, so no stop API is provided ATM. try: self.monitor_vm_failover( callback, constants.DEFAULT_WMI_EVENT_TIMEOUT_MS) except Exception: LOG.exception(_LE("The VM cluster group owner change " "event listener encountered an " "unexpected exception.")) time.sleep(constants.DEFAULT_WMI_EVENT_TIMEOUT_MS / 1000)
def monitor_vm_failover(self, callback, event_timeout_ms=_WMI_EVENT_TIMEOUT_MS): """Creates a monitor to check for new WMI MSCluster_Resource events. This method will poll the last _WMI_EVENT_CHECK_INTERVAL + 1 seconds for new events and listens for _WMI_EVENT_TIMEOUT_MS miliseconds, since listening is a thread blocking action. Any event object caught will then be processed. """ # TODO(lpetrut): mark this method as private once compute-hyperv # stops using it. We should also remove the instance '_watcher' # attribute since we end up spawning unused event listeners. vm_name = None new_host = None try: # wait for new event for _WMI_EVENT_TIMEOUT_MS miliseconds. if patcher.is_monkey_patched('thread'): wmi_object = tpool.execute(self._watcher, event_timeout_ms) else: wmi_object = self._watcher(event_timeout_ms) old_host = wmi_object.previous.OwnerNode new_host = wmi_object.OwnerNode # wmi_object.Name field is of the form: # 'Virtual Machine nova-instance-template' # wmi_object.Name filed is a key and as such is not affected # by locale, so it will always be 'Virtual Machine' match = self._instance_name_regex.search(wmi_object.Name) if match: vm_name = match.group(1) if vm_name: try: callback(vm_name, old_host, new_host) except Exception: LOG.exception( _LE("Exception during failover callback.")) except exceptions.x_wmi_timed_out: pass
def monitor_vm_failover(self, callback): """Creates a monitor to check for new WMI MSCluster_Resource events. This method will poll the last _WMI_EVENT_CHECK_INTERVAL + 1 seconds for new events and listens for _WMI_EVENT_TIMEOUT_MS miliseconds, since listening is a thread blocking action. Any event object caught will then be processed. """ vm_name = None new_host = None try: # wait for new event for _WMI_EVENT_TIMEOUT_MS miliseconds. if patcher.is_monkey_patched('thread'): wmi_object = tpool.execute(self._watcher, self._WMI_EVENT_TIMEOUT_MS) else: wmi_object = self._watcher(self._WMI_EVENT_TIMEOUT_MS) old_host = wmi_object.previous.OwnerNode new_host = wmi_object.OwnerNode # wmi_object.Name field is of the form: # 'Virtual Machine nova-instance-template' # wmi_object.Name filed is a key and as such is not affected # by locale, so it will always be 'Virtual Machine' match = self._instance_name_regex.search(wmi_object.Name) if match: vm_name = match.group(1) if vm_name: try: callback(vm_name, old_host, new_host) except Exception: LOG.exception( _LE("Exception during failover callback.")) except exceptions.x_wmi_timed_out: pass
def ensure_lun_available(self, target_iqn, target_lun, rescan_attempts=_DEFAULT_RESCAN_ATTEMPTS, retry_interval=0, rescan_disks=True): # This method should be called only after the iSCSI # target has already been logged in. for attempt in range(rescan_attempts + 1): sessions = self._get_iscsi_target_sessions(target_iqn) for session in sessions: try: sid = session.SessionId device = self._get_iscsi_device_from_session( sid, target_lun) if not device: continue device_number = device.StorageDeviceNumber.DeviceNumber device_path = device.LegacyName if device_path and device_number not in (None, -1): return device_number, device_path except exceptions.ISCSIInitiatorAPIException: err_msg = _LE("Could not find lun %(target_lun)s " "for iSCSI target %(target_iqn)s.") LOG.exception( err_msg, dict(target_lun=target_lun, target_iqn=target_iqn)) continue if attempt <= rescan_attempts: if retry_interval: time.sleep(retry_interval) if rescan_disks: self._diskutils.rescan_disks() raise exceptions.ISCSILunNotAvailable(target_lun=target_lun, target_iqn=target_iqn)
def get_disk_capacity(self, path, ignore_errors=False): norm_path = os.path.abspath(path) total_bytes = ctypes.c_ulonglong(0) free_bytes = ctypes.c_ulonglong(0) try: self._win32_utils.run_and_check_output( kernel32.GetDiskFreeSpaceExW, ctypes.c_wchar_p(norm_path), None, ctypes.pointer(total_bytes), ctypes.pointer(free_bytes), kernel32_lib_func=True) return total_bytes.value, free_bytes.value except exceptions.Win32Exception as exc: LOG.error( _LE("Could not get disk %(path)s capacity info. " "Exception: %(exc)s"), dict(path=path, exc=exc)) if ignore_errors: return 0, 0 else: raise exc
def monitor_vm_failover(self, callback): """Creates a monitor to check for new WMI MSCluster_Resource events. This method will poll the last _WMI_EVENT_CHECK_INTERVAL + 1 seconds for new events and listens for _WMI_EVENT_TIMEOUT_MS miliseconds, since listening is a thread blocking action. Any event object caught will then be processed. """ vm_name = None new_host = None try: # wait for new event for _WMI_EVENT_TIMEOUT_MS miliseconds. if patcher.is_monkey_patched('thread'): wmi_object = tpool.execute(self._watcher, self._WMI_EVENT_TIMEOUT_MS) else: wmi_object = self._watcher(self._WMI_EVENT_TIMEOUT_MS) old_host = wmi_object.previous.OwnerNode new_host = wmi_object.OwnerNode # wmi_object.Name field is of the form: # 'Virtual Machine nova-instance-template' # wmi_object.Name filed is a key and as such is not affected # by locale, so it will always be 'Virtual Machine' match = self._instance_name_regex.search(wmi_object.Name) if match: vm_name = match.group(1) if vm_name: try: callback(vm_name, old_host, new_host) except Exception: LOG.exception(_LE("Exception during failover callback.")) except wmi.x_wmi_timed_out: pass
def get_share_capacity_info(self, share_path, ignore_errors=False): norm_path = os.path.abspath(share_path) total_bytes = ctypes.c_ulonglong(0) free_bytes = ctypes.c_ulonglong(0) try: self._win32_utils.run_and_check_output( kernel32.GetDiskFreeSpaceExW, ctypes.c_wchar_p(norm_path), None, ctypes.pointer(total_bytes), ctypes.pointer(free_bytes), kernel32_lib_func=True) return total_bytes.value, free_bytes.value except exceptions.Win32Exception as exc: LOG.error(_LE("Could not get share %(share_path)s capacity info. " "Exception: %(exc)s"), dict(share_path=share_path, exc=exc)) if ignore_errors: return 0, 0 else: raise exc
def ensure_lun_available(self, target_iqn, target_lun, retry_attempts=_DEFAULT_RESCAN_ATTEMPTS, retry_interval=0, rescan_disks=True): # This method should be called only after the iSCSI # target has already been logged in. for attempt in range(retry_attempts + 1): sessions = self._get_iscsi_target_sessions(target_iqn) for session in sessions: try: sid = session.SessionId device = self._get_iscsi_device_from_session(sid, target_lun) if not device: continue device_number = device.StorageDeviceNumber.DeviceNumber device_path = device.LegacyName if device_path and device_number not in (None, -1): return device_number, device_path except exceptions.ISCSIInitiatorAPIException: err_msg = _LE("Could not find lun %(target_lun)s " "for iSCSI target %(target_iqn)s.") LOG.exception(err_msg, dict(target_lun=target_lun, target_iqn=target_iqn)) continue if attempt <= retry_attempts: if retry_interval: time.sleep(retry_interval) if rescan_disks: self._diskutils.rescan_disks() raise exceptions.ISCSILunNotAvailable(target_lun=target_lun, target_iqn=target_iqn)
def set_port_qos_rule(self, port_id, qos_rule): """Sets the QoS rule for the given port. :param port_id: the port's ID to which the QoS rule will be applied to. :param qos_rule: a dictionary containing the following keys: min_kbps, max_kbps, max_burst_kbps, max_burst_size_kb. :raises exceptions.HyperVInvalidException: if - min_kbps is smaller than 10MB. - max_kbps is smaller than min_kbps. - max_burst_kbps is smaller than max_kbps. :raises exceptions.HyperVException: if the QoS rule cannot be set. """ # Hyper-V stores bandwidth limits in bytes. min_bps = qos_rule.get("min_kbps", 0) * units.Ki max_bps = qos_rule.get("max_kbps", 0) * units.Ki max_burst_bps = qos_rule.get("max_burst_kbps", 0) * units.Ki max_burst_sz = qos_rule.get("max_burst_size_kb", 0) * units.Ki if not (min_bps or max_bps or max_burst_bps or max_burst_sz): # no limits need to be set return if min_bps and min_bps < 10 * units.Mi: raise exceptions.InvalidParameterValue(param_name="min_kbps", param_value=min_bps) if max_bps and max_bps < min_bps: raise exceptions.InvalidParameterValue(param_name="max_kbps", param_value=max_bps) if max_burst_bps and max_burst_bps < max_bps: raise exceptions.InvalidParameterValue(param_name="max_burst_kbps", param_value=max_burst_bps) port_alloc = self._get_switch_port_allocation(port_id)[0] bandwidth = self._get_bandwidth_setting_data_from_port_alloc( port_alloc) if bandwidth: # Removing the feature because it cannot be modified # due to a wmi exception. self._jobutils.remove_virt_feature(bandwidth) # remove from cache. self._bandwidth_sds.pop(port_alloc.InstanceID, None) bandwidth = self._get_default_setting_data( self._PORT_BANDWIDTH_SET_DATA) bandwidth.Reservation = min_bps bandwidth.Limit = max_bps bandwidth.BurstLimit = max_burst_bps bandwidth.BurstSize = max_burst_sz try: self._jobutils.add_virt_feature(bandwidth, port_alloc) except Exception as ex: if '0x80070057' in ex.message: raise exceptions.InvalidParameterValue(param_name="qos_rule", param_value=qos_rule) raise exceptions.HyperVException( _LE('Unable to set qos rule %(qos_rule)s for port %(port)s. ' 'Error: %(error)s') % dict(qos_rule=qos_rule, port=port_alloc, error=ex))