def _check_socket(self): sock_stat = None try: sock_stat = os.stat(self.monitor_filename) except EnvironmentError, err: if err.errno == errno.ENOENT: raise errors.HypervisorError("No monitor socket found") else: raise errors.HypervisorError("Error checking monitor socket: %s", utils.ErrnoOrStr(err))
def _check_socket(self): sock_stat = None try: sock_stat = os.stat(self.monitor_filename) except EnvironmentError as err: if err.errno == errno.ENOENT: raise errors.HypervisorError("No monitor socket found") else: raise errors.HypervisorError( "Error checking monitor socket: %s", utils.ErrnoOrStr(err)) if not stat.S_ISSOCK(sock_stat.st_mode): raise errors.HypervisorError("Monitor socket is not a socket")
def check_boot_parameters(hvparams): boot_order = hvparams[constants.HV_BOOT_ORDER] if (boot_order == constants.HT_BO_CDROM and not hvparams[constants.HV_CDROM_IMAGE_PATH]): raise errors.HypervisorError("Cannot boot from cdrom without an" " ISO path") kernel_path = hvparams[constants.HV_KERNEL_PATH] if kernel_path: if not hvparams[constants.HV_ROOT_PATH]: raise errors.HypervisorError("Need a root partition for the instance," " if a kernel is defined") return True
def OpenTap(vnet_hdr=True, virtio_net_queues=1, name=""): """Open a new tap device and return its file descriptor. This is intended to be used by a qemu-type hypervisor together with the -net tap,fd=<fd> or -net tap,fds=x:y:...:z command line parameter. @type vnet_hdr: boolean @param vnet_hdr: Enable the VNET Header @type virtio_net_queues: int @param virtio_net_queues: Set number of tap queues but not more than 8, queues only work with virtio-net device; disabled by default (one queue). @type name: string @param name: name for the TAP interface being created; if an empty string is passed, the OS will generate a unique name @return: (ifname, [tapfds]) @rtype: tuple """ tapfds = [] for _ in range(virtio_net_queues): try: tapfd = os.open("/dev/net/tun", os.O_RDWR) except EnvironmentError: raise errors.HypervisorError("Failed to open /dev/net/tun") flags = IFF_TAP | IFF_NO_PI if vnet_hdr and _ProbeTapVnetHdr(tapfd): flags |= IFF_VNET_HDR # Check if it's ok to enable IFF_MULTI_QUEUE if virtio_net_queues > 1 and _ProbeTapMqVirtioNet(tapfd): flags |= IFF_MULTI_QUEUE else: flags |= IFF_ONE_QUEUE # The struct ifreq ioctl request (see netdevice(7)) ifr = struct.pack("16sh", name, flags) try: res = fcntl.ioctl(tapfd, TUNSETIFF, ifr) except EnvironmentError, err: raise errors.HypervisorError("Failed to allocate a new TAP device: %s" % err) tapfds.append(tapfd)
def _CheckToolstack(self, xen_cmd): """Check whether the given toolstack is available on the node. @type xen_cmd: string @param xen_cmd: xen command (e.g. 'xm' or 'xl') """ binary_found = self._CheckToolstackBinary(xen_cmd) if not binary_found: raise errors.HypervisorError("No '%s' binary found on node." % xen_cmd) elif xen_cmd == constants.XEN_CMD_XL: if not self._CheckToolstackXlConfigured(): raise errors.HypervisorError("Toolstack '%s' is not enabled on this" "node." % xen_cmd)
def check_security_model(hvparams): security_model = hvparams[constants.HV_SECURITY_MODEL] if security_model == constants.HT_SM_USER: if not hvparams[constants.HV_SECURITY_DOMAIN]: raise errors.HypervisorError( "A security domain (user to run kvm as)" " must be specified") elif (security_model == constants.HT_SM_NONE or security_model == constants.HT_SM_POOL): if hvparams[constants.HV_SECURITY_DOMAIN]: raise errors.HypervisorError( "Cannot have a security domain when the" " security model is 'none' or 'pool'") return True
def _Recv(self): """Receives a message from QMP and decodes the received JSON object. @rtype: QmpMessage @return: the received message @raise errors.HypervisorError: when there are communication errors @raise errors.ProgrammerError: when there are data serialization errors """ self._check_connection() # Check if there is already a message in the buffer (message, self._buf) = self._ParseMessage(self._buf) if message: return message recv_buffer = StringIO.StringIO(self._buf) recv_buffer.seek(len(self._buf)) try: while True: data = self.sock.recv(4096) if not data: break recv_buffer.write(data) (message, self._buf) = self._ParseMessage(recv_buffer.getvalue()) if message: return message except socket.timeout, err: raise errors.HypervisorError( "Timeout while receiving a QMP message: " "%s" % (err))
def GetInstanceInfo(self, instance_name, hvparams=None): """Get instance properties. @type instance_name: string @param instance_name: the instance name @type hvparams: dict of strings @param hvparams: hvparams to be used with this instance @rtype: tuple of strings @return: (name, id, memory, vcpus, stat, times) """ # TODO: read container info from the cgroup mountpoint result = utils.RunCmd(["lxc-info", "-s", "-n", instance_name]) if result.failed: raise errors.HypervisorError("Running lxc-info failed: %s" % result.output) # lxc-info output examples: # 'state: STOPPED # 'state: RUNNING _, state = result.stdout.rsplit(None, 1) if state != "RUNNING": return None cpu_list = self._GetCgroupCpuList(instance_name) memory = self._GetCgroupMemoryLimit(instance_name) / (1024**2) return (instance_name, 0, memory, len(cpu_list), hv_base.HvInstanceState.RUNNING, 0)
def _GetResponse(self, command): """Parse the QMP response If error key found in the response message raise HypervisorError. Ignore any async event and thus return the response message related to command. """ # According the the QMP specification, there are only two reply types to a # command: either error (containing the "error" key) or success (containing # the "return" key). There is also a third possibility, that of an # (unrelated to the command) asynchronous event notification, identified by # the "event" key. while True: response = self._Recv() err = response[self._ERROR_KEY] if err: raise errors.HypervisorError( "kvm: error executing the %s" " command: %s (%s):" % (command, err[self._ERROR_DESC_KEY], err[self._ERROR_CLASS_KEY])) elif response[self._EVENT_KEY]: # Filter-out any asynchronous events continue return response[self._RETURN_KEY]
def GetLinuxNodeInfo(meminfo="/proc/meminfo", cpuinfo="/proc/cpuinfo"): """For linux systems, return actual OS information. This is an abstraction for all non-hypervisor-based classes, where the node actually sees all the memory and CPUs via the /proc interface and standard commands. The other case if for example xen, where you only see the hardware resources via xen-specific tools. @param meminfo: name of the file containing meminfo @type meminfo: string @param cpuinfo: name of the file containing cpuinfo @type cpuinfo: string @return: a dict with the following keys (values in MiB): - memory_total: the total memory size on the node - memory_free: the available memory on the node for instances - memory_dom0: the memory used by the node itself, if available - cpu_total: total number of CPUs - cpu_dom0: number of CPUs used by the node OS - cpu_nodes: number of NUMA domains - cpu_sockets: number of physical CPU sockets """ try: data = utils.ReadFile(meminfo).splitlines() except EnvironmentError, err: raise errors.HypervisorError("Failed to list node info: %s" % (err,))
class MonitorSocket(object): _SOCKET_TIMEOUT = 5 def __init__(self, monitor_filename): """Instantiates the MonitorSocket object. @type monitor_filename: string @param monitor_filename: the filename of the UNIX raw socket on which the monitor (QMP or simple one) is listening """ self.monitor_filename = monitor_filename self._connected = False def _check_socket(self): sock_stat = None try: sock_stat = os.stat(self.monitor_filename) except EnvironmentError, err: if err.errno == errno.ENOENT: raise errors.HypervisorError("No monitor socket found") else: raise errors.HypervisorError( "Error checking monitor socket: %s", utils.ErrnoOrStr(err)) if not stat.S_ISSOCK(sock_stat.st_mode): raise errors.HypervisorError("Monitor socket is not a socket")
def GetInstanceInfo(self, instance_name, hvparams=None): """Get instance properties. @type instance_name: string @param instance_name: the instance name @type hvparams: dict of strings @param hvparams: hvparams to be used with this instance @return: tuple of (name, id, memory, vcpus, stat, times) """ file_name = self._InstanceFile(instance_name) if not os.path.exists(file_name): return None try: fh = open(file_name, "r") try: inst_id = fh.readline().strip() memory = utils.TryConvert(int, fh.readline().strip()) vcpus = utils.TryConvert(int, fh.readline().strip()) stat = hv_base.HvInstanceState.RUNNING times = 0 return (instance_name, inst_id, memory, vcpus, stat, times) finally: fh.close() except IOError, err: raise errors.HypervisorError("Failed to list instance %s: %s" % (instance_name, err))
def check_vnc_parameters(hvparams): if (hvparams[constants.HV_VNC_X509_VERIFY] and not hvparams[constants.HV_VNC_X509]): raise errors.HypervisorError("%s must be defined, if %s is" % (constants.HV_VNC_X509, constants.HV_VNC_X509_VERIFY)) return True
def StartInstance(self, instance, block_devices, startup_paused): """Start an instance. """ startup_memory = self._InstanceStartupMemory(instance) self._MakeConfigFile(instance, startup_memory, block_devices) cmd = ["create"] if startup_paused: cmd.append("-p") cmd.append(self._ConfigFileName(instance.name)) result = self._RunXen(cmd, instance.hvparams) if result.failed: # Move the Xen configuration file to the log directory to avoid # leaving a stale config file behind. stashed_config = self._StashConfigFile(instance.name) raise errors.HypervisorError("Failed to start instance %s: %s (%s). Moved" " config file to %s" % (instance.name, result.fail_reason, result.output, stashed_config)) for nic_seq, nic in enumerate(instance.nics): if nic.name and nic.name.startswith("gnt.com."): _ConfigureNIC(instance, nic_seq, nic, nic.name)
class MonitorSocket(object): _SOCKET_TIMEOUT = 5 def __init__(self, monitor_filename): """Instantiates the MonitorSocket object. @type monitor_filename: string @param monitor_filename: the filename of the UNIX raw socket on which the monitor (QMP or simple one) is listening """ self.monitor_filename = monitor_filename self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) # We want to fail if the server doesn't send a complete message # in a reasonable amount of time self.sock.settimeout(self._SOCKET_TIMEOUT) self._connected = False def _check_socket(self): sock_stat = None try: sock_stat = os.stat(self.monitor_filename) except EnvironmentError, err: if err.errno == errno.ENOENT: raise errors.HypervisorError("No monitor socket found") else: raise errors.HypervisorError( "Error checking monitor socket: %s", utils.ErrnoOrStr(err)) if not stat.S_ISSOCK(sock_stat.st_mode): raise errors.HypervisorError("Monitor socket is not a socket")
def StartInstance(self, instance, block_devices, startup_paused): """Start an instance. For the fake hypervisor, it just creates a file in the base dir, creating an exception if it already exists. We don't actually handle race conditions properly, since these are *FAKE* instances. """ if self._IsAlive(instance.name): raise errors.HypervisorError("Failed to start instance %s: %s" % (instance.name, "already running")) try: self._MarkUp(instance, self._InstanceStartupMemory(instance)) except IOError, err: raise errors.HypervisorError("Failed to start instance %s: %s" % (instance.name, err))
def _WriteNICInfoFile(cls, instance, idx, nic): """Write the Xen config file for the instance. This version of the function just writes the config file from static data. """ instance_name = instance.name dirs = [(dname, constants.RUN_DIRS_MODE) for dname in cls._DIRS + [cls._InstanceNICDir(instance_name)]] utils.EnsureDirs(dirs) cfg_file = cls._InstanceNICFile(instance_name, idx) data = StringIO() data.write("TAGS=%s\n" % r"\ ".join(instance.GetTags())) if nic.netinfo: netinfo = objects.Network.FromDict(nic.netinfo) for k, v in netinfo.HooksDict().iteritems(): data.write("%s=%s\n" % (k, v)) data.write("MAC=%s\n" % nic.mac) if nic.ip: data.write("IP=%s\n" % nic.ip) data.write("INTERFACE_INDEX=%s\n" % str(idx)) if nic.name: data.write("INTERFACE_NAME=%s\n" % nic.name) data.write("INTERFACE_UUID=%s\n" % nic.uuid) data.write("MODE=%s\n" % nic.nicparams[constants.NIC_MODE]) data.write("LINK=%s\n" % nic.nicparams[constants.NIC_LINK]) try: utils.WriteFile(cfg_file, data=data.getvalue()) except EnvironmentError, err: raise errors.HypervisorError("Cannot write Xen instance configuration" " file %s: %s" % (cfg_file, err))
def GetAllInstancesInfo(self, hvparams=None): """Get properties of all instances. @type hvparams: dict of strings @param hvparams: hypervisor parameter @return: list of tuples (name, id, memory, vcpus, stat, times) """ data = [] for file_name in os.listdir(self._ROOT_DIR): try: fh = open(utils.PathJoin(self._ROOT_DIR, file_name), "r") inst_id = "-1" memory = 0 vcpus = 1 stat = hv_base.HvInstanceState.SHUTDOWN times = -1 try: inst_id = fh.readline().strip() memory = utils.TryConvert(int, fh.readline().strip()) vcpus = utils.TryConvert(int, fh.readline().strip()) stat = hv_base.HvInstanceState.RUNNING times = 0 finally: fh.close() data.append((file_name, inst_id, memory, vcpus, stat, times)) except IOError, err: raise errors.HypervisorError("Failed to list instances: %s" % err)
def _DestroyInstanceIfAlive(self, name, hvparams): instance_info = self.GetInstanceInfo(name, hvparams=hvparams) if instance_info is None: raise errors.HypervisorError("Failed to destroy instance %s, already" " destroyed" % name) else: self._DestroyInstance(name, hvparams)
def validate_machine_version(hvparams, kvm_machine_output): machine_version = hvparams[constants.HV_KVM_MACHINE_VERSION] if machine_version: for test in _CHECK_MACHINE_VERSION_RE: if not test(machine_version).search(kvm_machine_output): raise errors.HypervisorError("Unsupported machine version: %s" % machine_version) return True
def _CheckToolstackBinary(self, xen_cmd): """Checks whether the xen command's binary is found on the machine. """ if xen_cmd not in constants.KNOWN_XEN_COMMANDS: raise errors.HypervisorError("Unknown xen command '%s'." % xen_cmd) result = self._run_cmd_fn(["which", xen_cmd]) return not result.failed
def check_console_parameters(hvparams): if hvparams[constants.HV_SERIAL_CONSOLE]: serial_speed = hvparams[constants.HV_SERIAL_SPEED] valid_speeds = constants.VALID_SERIAL_SPEEDS if not serial_speed or serial_speed not in valid_speeds: raise errors.HypervisorError("Invalid serial console speed, must be" " one of: %s" % utils.CommaJoin(valid_speeds)) return True
def _Send(self, message): """Encodes and sends a message to KVM using QMP. @type message: QmpMessage @param message: message to send to KVM @raise errors.HypervisorError: when there are communication errors @raise errors.ProgrammerError: when there are data serialization errors """ self._check_connection() try: self.sock.sendall(message.to_bytes()) except socket.timeout as err: raise errors.HypervisorError("Timeout while sending a QMP message: " "%s" % err) except socket.error as err: raise errors.HypervisorError("Unable to send data from KVM using the" " QMP protocol: %s" % err)
def testVerifyToolstackNotOk(self): hvparams = {constants.HV_XEN_CMD: constants.XEN_CMD_XL} mock_run_cmd = mock.Mock(return_value=self._result_ok) hv = hv_xen.XenHypervisor(_cfgdir=NotImplemented, _run_cmd_fn=mock_run_cmd) hv._CheckToolstack = mock.Mock() hv._CheckToolstack.side_effect = errors.HypervisorError("foo") result = hv.Verify(hvparams) self.assertTrue(result is not None)
def check_disk_cache_parameters(hvparams): disk_aio = hvparams[constants.HV_KVM_DISK_AIO] disk_cache = hvparams[constants.HV_DISK_CACHE] if disk_aio == constants.HT_KVM_AIO_NATIVE and \ disk_cache != constants.HT_CACHE_NONE: raise errors.HypervisorError("When 'disk_aio' is set to 'native', the " "only supported value for 'disk_cache' is " "'none'.") return True
def _XenToHypervisorInstanceState(instance_info): if _IsInstanceRunning(instance_info): return hv_base.HvInstanceState.RUNNING elif _IsInstanceShutdown(instance_info): return hv_base.HvInstanceState.SHUTDOWN else: raise errors.HypervisorError("hv_xen._XenToHypervisorInstanceState:" " unhandled Xen instance state '%s'" % instance_info)
def validate_security_model(hvparams): security_model = hvparams[constants.HV_SECURITY_MODEL] if security_model == constants.HT_SM_USER: username = hvparams[constants.HV_SECURITY_DOMAIN] try: pwd.getpwnam(username) except KeyError: raise errors.HypervisorError("Unknown security domain user %s" % username) return True
def BalloonInstanceMemory(self, instance, mem): """Balloon an instance memory to a certain value. @type instance: L{objects.Instance} @param instance: instance to be accepted @type mem: int @param mem: actual memory size to use for instance runtime """ if not self._IsAlive(instance.name): raise errors.HypervisorError( "Failed to balloon memory for %s: %s" % (instance.name, "not running")) try: self._MarkUp(instance, mem) except EnvironmentError, err: raise errors.HypervisorError( "Failed to balloon memory for %s: %s" % (instance.name, utils.ErrnoOrStr(err)))
def _GetCgroupCpuList(cls, instance_name): """Return the list of CPU ids for an instance. """ try: cpumask = cls._GetCgroupInstanceValue(instance_name, "cpuset.cpus") except EnvironmentError, err: raise errors.HypervisorError("Getting CPU list for instance" " %s failed: %s" % (instance_name, err))
def _ReadConfigFile(self, instance_name): """Returns the contents of the instance config file. """ filename = self._ConfigFileName(instance_name) try: file_content = utils.ReadFile(filename) except EnvironmentError, err: raise errors.HypervisorError("Failed to load Xen config file: %s" % err)