Esempio n. 1
0
File: vm.py Progetto: OpenVZ/vcmmd
    def __init__(self, name):
        try:
            self._libvirt_domain = virDomainProxy(name)
        except libvirtError as err:
            raise Error('Failed to lookup libvirt domain: %s' % err)

        self.set_memstats_period(0)
        # libvirt places every virtual machine in its own cgroup
        try:
            cgroup = lookup_qemu_machine_cgroup(self._libvirt_domain.name())
        except EnvironmentError as err:
            raise Error('Failed to lookup machine cgroup: %s' % err)

        self._memcg = MemoryCgroup(cgroup[MemoryCgroup.CONTROLLER])
        if not self._memcg.exists():
            raise Error("Memory cgroup not found: '%s'" % self._memcg.abs_path)
        self._cpucg = CpuCgroup(cgroup[CpuCgroup.CONTROLLER])
        if not self._cpucg.exists():
            raise Error("Cpu cgroup not found: '%s'" % self._cpucg.abs_path)

        self._emulatorcg = CpuSetCgroup(cgroup[CpuSetCgroup.CONTROLLER])
        if not self._emulatorcg.exists():
            raise Error("Cpuset cgroup not found: '%s'" % self._cpucg.abs_path)

        max_vcpus = self._libvirt_domain.maxVcpus()
        self._vcpucg = {}
        vcpu_path = self._emulatorcg.path
        assert vcpu_path.endswith('emulator')
        vcpu_path = '%s/vcpu%%d' % vcpu_path[:-len('emulator')]

        for vcpu in range(max_vcpus):
            self._vcpucg[vcpu] = CpuSetCgroup(vcpu_path % vcpu)
Esempio n. 2
0
File: vm.py Progetto: OpenVZ/vcmmd
class VMImpl(VEImpl):

    VE_TYPE = VE_TYPE_VM
    __cached_stats = {}

    def __init__(self, name):
        try:
            self._libvirt_domain = virDomainProxy(name)
        except libvirtError as err:
            raise Error('Failed to lookup libvirt domain: %s' % err)

        self.set_memstats_period(0)
        # libvirt places every virtual machine in its own cgroup
        try:
            cgroup = lookup_qemu_machine_cgroup(self._libvirt_domain.name())
        except EnvironmentError as err:
            raise Error('Failed to lookup machine cgroup: %s' % err)

        self._memcg = MemoryCgroup(cgroup[MemoryCgroup.CONTROLLER])
        if not self._memcg.exists():
            raise Error("Memory cgroup not found: '%s'" % self._memcg.abs_path)
        self._cpucg = CpuCgroup(cgroup[CpuCgroup.CONTROLLER])
        if not self._cpucg.exists():
            raise Error("Cpu cgroup not found: '%s'" % self._cpucg.abs_path)

        self._emulatorcg = CpuSetCgroup(cgroup[CpuSetCgroup.CONTROLLER])
        if not self._emulatorcg.exists():
            raise Error("Cpuset cgroup not found: '%s'" % self._cpucg.abs_path)

        max_vcpus = self._libvirt_domain.maxVcpus()
        self._vcpucg = {}
        vcpu_path = self._emulatorcg.path
        assert vcpu_path.endswith('emulator')
        vcpu_path = '%s/vcpu%%d' % vcpu_path[:-len('emulator')]

        for vcpu in range(max_vcpus):
            self._vcpucg[vcpu] = CpuSetCgroup(vcpu_path % vcpu)

    def set_memstats_period(self, period):
        try:
            self._libvirt_domain.setMemoryStatsPeriod(period)
            self.__memstats_update_period = period
        except libvirtError as err:
            raise Error('Failed to enable libvirt domain memory stats: %s' % err)

    @staticmethod
    def mem_overhead(config_limit):
        # we assume, that for one guest page need at least 8b overhead
        # in qemu process
        guest_mem_overhead = 8 * config_limit / PAGE_SIZE
        config_overhead =  VCMMDConfig().get_num('VE.VM.MemOverhead', default=(64 << 20),
                                                 integer=True, minimum=0)
        return config_overhead + guest_mem_overhead

    def get_stats(self):
        self.set_memstats_period(2)
        try:
            name = self._libvirt_domain.name()
            if name not in VMImpl.__cached_stats:
                conn = virConnectionProxy()
                VMImpl.__cached_stats = {dom.name(): stats for dom, stats in \
                                         conn.getAllDomainStats(STATS_BLOCK | STATS_BALLOON,
                                         GET_ALL_RUNNING)}
            stats = VMImpl.__cached_stats.pop(name, {})
        except libvirtError as err:
            raise Error('Failed to retrieve libvirt domain stats: %s' % err)

        memstats = {k.split('.')[1]: v for k,v in stats.iteritems() if k.startswith('balloon')}
        try:
            memcg_stat = self._memcg.read_mem_stat()
        except IOError as err:
            raise Error('Cgroup read failed: %s' % err)

        try:
            # Unmapped file pages are of no interest in case of VMs
            host_mem = memcg_stat['rss'] + memcg_stat['mapped_file']
        except KeyError:
            host_mem = -1

        host_swap = memcg_stat.get('swap', -1)

        blk_stat = {'rd.reqs': 0, 'rd.bytes': 0, 'wr.reqs': 0, 'wr.bytes': 0}
        for s in blk_stat:
            for c in range(0, stats.get('block.count', 0)):
                blk_stat[s] += stats['block.%d.%s' % (c, s)]

        # libvirt reports memory values in kB, so we need to convert them to
        # bytes
        return {'actual': memstats.get('actual', -1) << 10,
                'rss': memstats.get('rss', -1) << 10,
                'host_mem': host_mem,
                'host_swap': host_swap,
                'memfree': memstats.get('unused', -1) << 10,
                'memavail': memstats.get('usable', -1) << 10,
                'swapin': memstats.get('swap_in', -1) << 10,
                'swapout': memstats.get('swap_out', -1) << 10,
                'minflt': memstats.get('minor_fault', -1),
                'majflt': memstats.get('major_fault', -1),
                'rd_req': blk_stat['rd.reqs'],
                'rd_bytes': blk_stat['rd.bytes'],
                'wr_req': blk_stat['wr.reqs'],
                'wr_bytes': blk_stat['wr.bytes'],
                'last_update': memstats.get('last-update', -1)}

    def set_mem_protection(self, value):
        # Use memcg/memory.low to protect the VM from host pressure.
        try:
            self._memcg.write_mem_low(value)
        except IOError as err:
            raise Error('Cgroup write failed: %s' % err)

    def set_mem_target(self, value):
        # Update current allocation size by inflating/deflating balloon.
        try:
            # libvirt wants kB
            self._libvirt_domain.setMemory(value >> 10)
        except libvirtError as err:
            raise Error('Failed to set libvirt domain memory size: %s' % err)

    def _hotplug_memory(self, value):
        grain = VCMMDConfig().get_num('VE.VM.MemHotplugGrain',
                                      default=134217728, integer=True,
                                      minimum=1048576)
        value = roundup(value, grain)
        value >>= 10  # libvirt wants kB
        xml = ("<memory model='dimm'>"
               "  <target>"
               "    <size unit='KiB'>{memsize}</size>"
               "    <node>0</node>"
               "  </target>"
               "</memory>").format(memsize=value)
        self._libvirt_domain.attachDevice(xml)

    def set_config(self, config):
        # Set protection against OOM killer according to configured guarantee
        try:
            self._memcg.write_oom_guarantee(config.guarantee)
        except IOError as err:
            raise Error('Cgroup write failed: %s' % err)

        # Update memory limit
        value = config.limit
        try:
            # If value is greater than MaxMemory, we have to initiate memory
            # hotplug to increase the limit.
            #
            # We ignore limit decrease here, because memory hotunplug is not
            # expected to work. Memory allocation is supposed to be decreased
            # by the policy in this case.
            max_mem = self._libvirt_domain.maxMemory()
            max_mem <<= 10  # libvirt reports in kB
            if value > max_mem:
                self._hotplug_memory(value - max_mem)
        except libvirtError as err:
            raise Error('Failed to hotplug libvirt domain memory: %s' % err)

    def get_node_list(self):
        '''Get list of nodes where VM is running
        NOTE: only for memory
        '''
        try:
            ret = self._libvirt_domain.numaParameters()
        except libvirtError as err:
            raise Error(str(err))

        return parse_range_list(ret['numa_nodeset'])

    def pin_node_mem(self, nodes, libvirt = False):
        '''Change list of memory nodes for VM

        This function changes VM affinity for memory and migrates VM's memory
        accordingly
        '''
        if libvirt:
            return self.pin_node_mem_libvirt(nodes)

        node_mask = ','.join([str(node) for node in nodes])
        try:
            for vcpu in range(self.nr_cpus):
                self._vcpucg[vcpu].set_node_list(nodes)

            self._emulatorcg.set_node_list(nodes)
        except (IOError, libvirtError) as err:
            raise Error('Cgroup write failed: %s' % err)

    def pin_node_mem_libvirt(self, nodes):
        params = {'numa_nodeset': ','.join([str(node) for node in nodes]),
                  'numa_mode': NUMATUNE_MEM_STRICT}

        try:
            self._libvirt_domain.setNumaParameters(params, AFFECT_LIVE)
        except libvirtError as err:
            raise Error(str(err))

    def pin_cpu_list(self, cpus, libvirt = False):
        '''Change list of CPUs for VM

        This function changes VM affinity for CPUs
        '''
        if libvirt:
            return self.pin_cpu_list_libvirt(cpus)

        cpu_map = ','.join([str(cpu) for cpu in cpus])
        try:
            for vcpu in range(self.nr_cpus):
                self._vcpucg[vcpu].set_cpu_list(cpus)

            self._emulatorcg.set_cpu_list(cpus)
        except (IOError, libvirtError) as err:
            raise Error('Cgroup write failed: %s' % err)

    def pin_cpu_list_libvirt(self, cpus):
        cpu_map = [0] * (max(cpus) + 1)
        for i in cpus:
            cpu_map[i] = 1
        cpu_map = tuple(cpu_map)

        try:
            for vcpu in range(self.nr_cpus):
                self._libvirt_domain.pinVcpu(vcpu, cpu_map)
            self._libvirt_domain.pinEmulator(cpu_map, AFFECT_LIVE)
        except libvirtError as err:
            raise Error(str(err))

    @property
    def nr_cpus(self):
        try:
            self._nr_cpus = self._libvirt_domain.vcpusFlags(AFFECT_LIVE)
            return self._nr_cpus
        except libvirtError:
            return getattr(self, "_nr_cpus", -1)