Newer
Older
#TODO: vm informations gathering !!!
import libvirt
import sys
# we use psutils to get host informations we can't get with
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# libvirt
import psutil
from interface import *
from exceptions import *
from time import sleep
###
# Defined constants
###
##
# Libvirt
##
#
KVM_LIBVIRT_SESSION = 'qemu:///system'
XEN_LIBVIRT_SESSION = 'xen:///'
#########
## States
#########
# Virtual Machines
VM_STATUS = (
'No state',
'Running',
'Blocked on resource',
'Paused',
'Shutting down ...',
'Shutdown',
'Crashed'
)
VM_START_STATES = {
'running': 1,
'paused' : 0
}
SOFT_VM_POWEROFF = True
FORCE_VM_POWEROFF = False
# Storage Pools
POOL_STATE = (
'Not Running',
'Initializing pool, not available',
'Running normally',
'Running degraded',
'Running, but not accessible'
)
#########################
## Pool informations tags
#########################
POOL_NAME = 'pool'
POOL_STATUS = 'pstatus'
POOL_TOTAL_SIZE = 'psize'
POOL_FREE_SPACE = 'pfree_space'
POOL_USED_SPACE = 'pused_space'
POOL_NUM_VOLUMES = 'num_vols'
POOL_BACKEND = 'backend'
DEFAULT_VM_START = VM_START_STATES['running']
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# Pretty size outputs
##
MEGABYTE_DIV = 1024 * 1024
GIGABYTE_DIV = 1024 * 1024 * 1024
KILOBYTE_DIV = 1024
class LibvirtVm(VM):
'''
Libvirt api access to Virtual Machine actions
All libvirt.virDomain calls must stay in this class
:param domain: a libvirt domain instance
:type domain: :class:`libvirt.virDomain`
:param hypervisor: a hypervisor reference object
:type hypervisor: :class:`LibvirtHypervisor`
'''
_print_header = True
def __init__(self, domain, hypervisor):
'''
'''
super(LibvirtVm, self).__init__()
if isinstance(domain, libvirt.virDomain):
self._domain = domain
self.vm_info = {}
self._find_pid()
else:
raise TypeError('Need virDomain object given %s' % type(domain))
self.set_hypervisor(hypervisor)
def __cmp__(self, other):
'''
We overload the compare method so that two virtual machines are
identical if they are bound to the same domain object
'''
return cmp(self._domain, other._domain)
def _find_pid(self):
'''
Finds the PID of the current vm
'''
result = find_process_id(self._domain.UUIDString())
if result:
self._pid = int(result.pop())
else:
self._pid = None
def get_pid(self):
return self._pid
def set_hypervisor(self, hypervisor):
if isinstance(hypervisor, LibvirtHypervisor):
self._hypervisor = hypervisor
else:
raise TypeError('argument type error')
def get_name(self):
return self._domain.name()
def get_used_mem(self):
return self._domain.info()[2] / KILOBYTE_DIV
def get_total_mem(self):
return self._domain.info()[1] / KILOBYTE_DIV
def get_free_mem(self):
return (self.get_total_mem() - self.get_used_mem()) / KILOBYTE_DIV
def get_vcpu(self):
return self._domain.info()[3]
def get_cpu_percent(self):
self._find_pid()
if self._pid:
p = psutil.Process(self._pid)
sleep(0.7)
res = int(p.get_cpu_percent())
res = 100 if res > 100 else res
return res
else:
return 0
def get_status(self):
return VM_STATUS[self._domain.info()[0]]
def shutdown(self):
try:
self._domain.shutdown()
except libvirt.libvirtError:
raise VMError('%s is not running !' % self.get_name())
def force_poweroff(self):
try:
self._domain.destroy()
except libvirt.libvirtError:
raise VMError('%s is not running !' % self.get_name())
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
def start(self):
try:
self._domain.create()
except libvirt.libvirtError:
raise VMError('%s is already running !' % self.get_name())
def suspend(self):
self._domain.suspend()
def resume(self):
self._domain.resume()
def get_uuid(self):
'''
Returns the uuid string of the vm
'''
return self._domain.UUIDString()
def __str__(self):
'''
LibvirtVm object string representation
'''
header = '%-10s | %-10s | %-10s | %-15s | %-10s\n\n' %\
('Name', 'UsedMem', 'VCpu', 'CpuUsage', 'Status')
self._fetch_memory_stats()
self.get_cpu_stats()
stats = '%-10s | %-10s | %-10s | %-15s | %-10s' %\
(self.get_name(), self.used_memory, self.vcpu, self.cpu_use,
self.get_status())
return header + stats
class LibvirtHypervisor(Hypervisor):
'''
Libvirt api access to Hypervisor actions.
All libvirt calls to the hypervisor stay in this class
:param hv_type: This is the hypervisor type, this parameter is
important as it specifies the hypervisor type used when
connecting to libvirt.
:type hv_type: :class:`str`
.. note::
hv_type can be any of kvm, xen, ... , other hypervisors, however only
one connection at a time is allowed
'''
def __init__(self, hv_type):
#FIXME: don't use hard coded hypervisor type in URI
try:
if hv_type == 'kvm':
self._con_handle = libvirt.open(KVM_LIBVIRT_SESSION)
else:
raise NotImplemented('or unknown hypervisor type')
except libvirt.libvirtError as error:
raise HypervisorError(error, 'libvirt cannot connect to hypervisor')
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
#build vm objects
self._build_vm_list()
self.hv_info = {}
self.hv_type = hv_type
def get_name(self):
'''
Returns hypervisor's name
'''
return self._con_handle.getHostname()
def get_hv_type(self):
return self.hv_type
def get_hv_version(self):
return self._con_handle.getVersion()
def _build_vm_list(self):
'''
Builds the lists of all defined vms (running and offline) in the
current hypervisor
'''
self._runing_vm_list = []
self._offline_vm_list = []
self._dom_ids = self._con_handle.listDomainsID()
self._defined_doms = self._con_handle.listDefinedDomains()
for doms in self._dom_ids:
vm = self._con_handle.lookupByID(doms)
self._runing_vm_list.append(LibvirtVm(vm, self))
for defined_dom in self._defined_doms:
vm = self._con_handle.lookupByName(defined_dom)
self._offline_vm_list.append(LibvirtVm(vm, self))
self._vm_list = []
self._vm_list = self._runing_vm_list
self._vm_list.extend(self._offline_vm_list)
def get_vms(self):
'''
Returns a list of :class:`LibvirtVm` objects defining all
current defined vms in the hypervisor
'''
return self._vm_list
def get_arch_type(self):
'''
Get archetcture type of hypervisor
'''
return self._con_handle.getInfo()[0]
def get_nb_cpu(self):
'''
Returns number of active cpus in hypervisor
'''
return self._con_handle.getInfo()[2]
def get_cpu_frequency(self):
'''
Returns the frequency in MHZ of cpus in the hypervisor
'''
return self._con_handle.getInfo()[3]
def get_nb_threads(self):
'''
Number of threads per core
'''
return self._con_handle.getInfo()[7]
def get_free_mem(self):
return psutil.avail_phymem() / MEGABYTE_DIV
def get_used_mem(self):
return psutil.used_phymem() / MEGABYTE_DIV
def get_total_mem(self):
return ((psutil.avail_phymem() + psutil.used_phymem()) /
MEGABYTE_DIV)
def get_cpu_percent(self):
return '%2.0f' % psutil.cpu_percent()
def get_status(self):
raise NotImplementedError()
#TODO: finish storage class, make tests,
class LibvirtHVStorage(HVStorage):
'''
Base storage class using libvirt api for storage managment
Storage pools here are libvirt.virStoragePool instances
'''
def __init__(self, hypervisor):
if isinstance(hypervisor, LibvirtHypervisor):
self.hv_handle = hypervisor
self._fetch_st_pools()
else:
raise TypeError('Excpected %s given %s' % (LibvirtHypervisor,
hypervisor))
def _fetch_st_pools(self):
'''
Sets an attribute self._pool_objs containging a list of libvirt
pool objects bound to the hypervisor
'''
pools = []
pools.extend(self.hv_handle._con_handle.listDefinedStoragePools())
pools.extend(self.hv_handle._con_handle.listStoragePools())
self._pools = []
self._pools.extend(pool_obj(self.hv_handle._con_handle, pool) for pool in pools)
def get_storage_pools(self):
return [pool.name() for pool in self._pools]
def get_pool_info(self, pool):
pool_info = {}
pool_info[POOL_NAME] = pool.name()
pool_info[POOL_STATUS] = self.get_pool_state(pool)
pool_info[POOL_TOTAL_SIZE] = self.get_pool_size(pool) / GIGABYTE_DIV
return pool_info
def get_pool_size(self, pool):
'''
Returns total logical size of the pool
:param pool: the storage pool
:type pool: :class:`libvirt.virStoragePool`
'''
return pool.info()[1]
def get_pool_state(self, pool):
'''
Returns the running state of the pool
:param pool: the storage pool name
:type pool: libvirt.`virStoragePool`
'''
return POOL_STATE[pool.info()[0]]
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
def add_volume(self, pool, name, space):
'''
Adds a volume to the specified pool
:param pool: the pool in which to create the volume
:type pool: :class:`str`
:param name: name of the new volume
:type name: :class:`str`
:param space: size of the new volume in gigabytes
:type space: :class:`int`
'''
xml_desc = """
<volume>
<name>%(vol_name)s</name>
<capacity>%(vol_size)u</capacity>
</volume>
""" % {
"vol_name" : name,
"vol_size" : space * GIGABYTE_DIV,
}
try:
pool.createXML(xml_desc, 0);
except libvirt.libvirtError as e:
raise StorageError("Failed to create the volume (%s)" % e)
def del_volume(self, pool, name):
'''
Deletes a volume in the specified pool
:param pool: the pool in which delete the volume
:type pool: :class:`str`
:param name: the name of the volume
:type name: :class:`str`
'''
try:
vol = pool.storageVolLookupByName(name)
except libvirt.libvirtError as e:
raise StorageError("Volume not found (%s)" % e)
try:
vol.delete(0)
except libvirt.libvirtError as e:
raise StorageError("Failed to delete the volume (%s)" % e)
#### Helper functions
def map_process(process):
'''
map each process object with it's pid and command line options
'''
return (process.pid, process.cmdline)
def find_process_id(uuid):
'''
Find the process id of a kvm process gevin an UUID of a virDomain object
Returns a list of one process id
'''
return [p.pid for p in psutil.get_process_list() if
p.cmdline.__contains__(uuid)]
def pool_obj(con_handle, pool_name):
'''
Creates a libvirt pool object given the name of the pool
:param con_handle: libvirt connection handle
:type con_handle: :class:`libvirt.virConnect`
'''
return con_handle.storagePoolLookupByName(pool_name)