debuggers.hg

view tools/python/xen/xend/XendNode.py @ 16754:b64be2bc7a91

xend: Remove hardcoded (and apparently unused) xend version.
Signed-off-by: Jim Fehlig <jfehlig@novell.com>
author Keir Fraser <keir.fraser@citrix.com>
date Tue Jan 15 14:24:05 2008 +0000 (2008-01-15)
parents 94b3979606cd
children 6ef22eefc360
line source
1 #============================================================================
2 # This library is free software; you can redistribute it and/or
3 # modify it under the terms of version 2.1 of the GNU Lesser General Public
4 # License as published by the Free Software Foundation.
5 #
6 # This library is distributed in the hope that it will be useful,
7 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 # Lesser General Public License for more details.
10 #
11 # You should have received a copy of the GNU Lesser General Public
12 # License along with this library; if not, write to the Free Software
13 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14 #============================================================================
15 # Copyright (C) 2004, 2005 Mike Wray <mike.wray@hp.com>
16 # Copyright (c) 2006, 2007 Xensource Inc.
17 #============================================================================
19 import os
20 import socket
21 import xen.lowlevel.xc
23 from xen.util import Brctl
24 from xen.xend import XendAPIStore
26 import uuid, arch
27 from XendPBD import XendPBD
28 from XendError import *
29 from XendOptions import instance as xendoptions
30 from XendQCoWStorageRepo import XendQCoWStorageRepo
31 from XendLocalStorageRepo import XendLocalStorageRepo
32 from XendLogging import log
33 from XendPIF import *
34 from XendPIFMetrics import XendPIFMetrics
35 from XendNetwork import *
36 from XendStateStore import XendStateStore
37 from XendMonitor import XendMonitor
39 class XendNode:
40 """XendNode - Represents a Domain 0 Host."""
42 def __init__(self):
43 """Initalises the state of all host specific objects such as
45 * host
46 * host_CPU
47 * host_metrics
48 * PIF
49 * PIF_metrics
50 * network
51 * Storage Repository
52 """
54 self.xc = xen.lowlevel.xc.xc()
55 self.state_store = XendStateStore(xendoptions().get_xend_state_path())
56 self.monitor = XendMonitor()
57 self.monitor.start()
59 # load host state from XML file
60 saved_host = self.state_store.load_state('host')
61 if saved_host and len(saved_host.keys()) == 1:
62 self.uuid = saved_host.keys()[0]
63 host = saved_host[self.uuid]
64 self.name = host.get('name_label', socket.gethostname())
65 self.desc = host.get('name_description', '')
66 self.host_metrics_uuid = host.get('metrics_uuid',
67 uuid.createString())
68 try:
69 self.other_config = eval(host['other_config'])
70 except:
71 self.other_config = {}
72 self.cpus = {}
73 else:
74 self.uuid = uuid.createString()
75 self.name = socket.gethostname()
76 self.desc = ''
77 self.other_config = {}
78 self.cpus = {}
79 self.host_metrics_uuid = uuid.createString()
81 # put some arbitrary params in other_config as this
82 # is directly exposed via XenAPI
83 self.other_config["xen_pagesize"] = self.xeninfo_dict()["xen_pagesize"]
84 self.other_config["platform_params"] = self.xeninfo_dict()["platform_params"]
86 # load CPU UUIDs
87 saved_cpus = self.state_store.load_state('cpu')
88 for cpu_uuid, cpu in saved_cpus.items():
89 self.cpus[cpu_uuid] = cpu
91 cpuinfo = parse_proc_cpuinfo()
92 physinfo = self.physinfo_dict()
93 cpu_count = physinfo['nr_cpus']
94 cpu_features = physinfo['hw_caps']
96 # If the number of CPUs don't match, we should just reinitialise
97 # the CPU UUIDs.
98 if cpu_count != len(self.cpus):
99 self.cpus = {}
100 for i in range(cpu_count):
101 u = uuid.createString()
102 self.cpus[u] = {'uuid': u, 'number': i }
104 for u in self.cpus.keys():
105 number = self.cpus[u]['number']
106 # We can run off the end of the cpuinfo list if domain0 does not
107 # have #vcpus == #pcpus. In that case we just replicate one that's
108 # in the hash table.
109 if not cpuinfo.has_key(number):
110 number = cpuinfo.keys()[0]
111 if arch.type == "x86":
112 self.cpus[u].update(
113 { 'host' : self.uuid,
114 'features' : cpu_features,
115 'speed' : int(float(cpuinfo[number]['cpu MHz'])),
116 'vendor' : cpuinfo[number]['vendor_id'],
117 'modelname': cpuinfo[number]['model name'],
118 'stepping' : cpuinfo[number]['stepping'],
119 'flags' : cpuinfo[number]['flags'],
120 })
121 elif arch.type == "ia64":
122 self.cpus[u].update(
123 { 'host' : self.uuid,
124 'features' : cpu_features,
125 'speed' : int(float(cpuinfo[number]['cpu MHz'])),
126 'vendor' : cpuinfo[number]['vendor'],
127 'modelname': cpuinfo[number]['family'],
128 'stepping' : cpuinfo[number]['model'],
129 'flags' : cpuinfo[number]['features'],
130 })
131 else:
132 self.cpus[u].update(
133 { 'host' : self.uuid,
134 'features' : cpu_features,
135 })
137 self.srs = {}
139 # Initialise networks
140 # First configure ones off disk
141 saved_networks = self.state_store.load_state('network')
142 if saved_networks:
143 for net_uuid, network in saved_networks.items():
144 try:
145 XendNetwork.recreate(network, net_uuid)
146 except CreateUnspecifiedAttributeError:
147 log.warn("Error recreating network %s", net_uuid)
149 # Next discover any existing bridges and check
150 # they are not already configured
151 bridges = Brctl.get_state().keys()
152 configured_bridges = [XendAPIStore.get(
153 network_uuid, "network")
154 .get_name_label()
155 for network_uuid in XendNetwork.get_all()]
156 unconfigured_bridges = [bridge
157 for bridge in bridges
158 if bridge not in configured_bridges]
159 for unconfigured_bridge in unconfigured_bridges:
160 XendNetwork.create_phy(unconfigured_bridge)
162 # Initialise PIFs
163 # First configure ones off disk
164 saved_pifs = self.state_store.load_state('pif')
165 if saved_pifs:
166 for pif_uuid, pif in saved_pifs.items():
167 try:
168 XendPIF.recreate(pif, pif_uuid)
169 except CreateUnspecifiedAttributeError:
170 log.warn("Error recreating PIF %s", pif_uuid)
172 # Next discover any existing PIFs and check
173 # they are not already configured
174 configured_pifs = [XendAPIStore.get(
175 pif_uuid, "PIF")
176 .get_interface_name()
177 for pif_uuid in XendPIF.get_all()]
178 unconfigured_pifs = [(name, mtu, mac)
179 for name, mtu, mac in linux_get_phy_ifaces()
180 if name not in configured_pifs]
182 # Get a mapping from interface to bridge
183 if_to_br = dict([(i,b)
184 for (b,ifs) in Brctl.get_state().items()
185 for i in ifs])
187 for name, mtu, mac in unconfigured_pifs:
188 # Check PIF is on bridge
189 # if not, ignore
190 bridge_name = if_to_br.get(name, None)
191 if bridge_name is not None:
192 # Translate bridge name to network uuid
193 for network_uuid in XendNetwork.get_all():
194 network = XendAPIStore.get(
195 network_uuid, 'network')
196 if network.get_name_label() == bridge_name:
197 XendPIF.create_phy(network_uuid, name,
198 mac, mtu)
199 break
200 else:
201 log.debug("Cannot find network for bridge %s "
202 "when configuring PIF %s",
203 (bridge_name, name))
205 # initialise storage
206 saved_srs = self.state_store.load_state('sr')
207 if saved_srs:
208 for sr_uuid, sr_cfg in saved_srs.items():
209 if sr_cfg['type'] == 'qcow_file':
210 self.srs[sr_uuid] = XendQCoWStorageRepo(sr_uuid)
211 elif sr_cfg['type'] == 'local':
212 self.srs[sr_uuid] = XendLocalStorageRepo(sr_uuid)
214 # Create missing SRs if they don't exist
215 if not self.get_sr_by_type('local'):
216 image_sr_uuid = uuid.createString()
217 self.srs[image_sr_uuid] = XendLocalStorageRepo(image_sr_uuid)
219 if not self.get_sr_by_type('qcow_file'):
220 qcow_sr_uuid = uuid.createString()
221 self.srs[qcow_sr_uuid] = XendQCoWStorageRepo(qcow_sr_uuid)
223 saved_pbds = self.state_store.load_state('pbd')
224 if saved_pbds:
225 for pbd_uuid, pbd_cfg in saved_pbds.items():
226 try:
227 XendPBD.recreate(pbd_uuid, pbd_cfg)
228 except CreateUnspecifiedAttributeError:
229 log.warn("Error recreating PBD %s", pbd_uuid)
231 ## def network_destroy(self, net_uuid):
232 ## del self.networks[net_uuid]
233 ## self.save_networks()
236 def get_PIF_refs(self):
237 log.debug(XendPIF.get_all())
238 return XendPIF.get_all()
240 ## def _PIF_create(self, name, mtu, vlan, mac, network, persist = True,
241 ## pif_uuid = None, metrics_uuid = None):
242 ## for pif in self.pifs.values():
243 ## if pif.network == network:
244 ## raise NetworkAlreadyConnected(pif.uuid)
246 ## if pif_uuid is None:
247 ## pif_uuid = uuid.createString()
248 ## if metrics_uuid is None:
249 ## metrics_uuid = uuid.createString()
251 ## metrics = XendPIFMetrics(metrics_uuid)
252 ## pif = XendPIF(pif_uuid, metrics, name, mtu, vlan, mac, network, self)
253 ## metrics.set_PIF(pif)
255 ## self.pif_metrics[metrics_uuid] = metrics
256 ## self.pifs[pif_uuid] = pif
258 ## if persist:
259 ## self.save_PIFs()
260 ## self.refreshBridges()
261 ## return pif_uuid
263 ## def PIF_destroy(self, pif_uuid):
264 ## pif = self.pifs[pif_uuid]
266 ## if pif.vlan == -1:
267 ## raise PIFIsPhysical()
269 ## del self.pifs[pif_uuid]
270 ## self.save_PIFs()
273 def save(self):
274 # save state
275 host_record = {self.uuid: {'name_label':self.name,
276 'name_description':self.desc,
277 'metrics_uuid': self.host_metrics_uuid,
278 'other_config': self.other_config}}
279 self.state_store.save_state('host',host_record)
280 self.state_store.save_state('cpu', self.cpus)
281 self.save_PIFs()
282 self.save_networks()
283 self.save_PBDs()
284 self.save_SRs()
286 def save_PIFs(self):
287 pif_records = dict([(pif_uuid, XendAPIStore.get(
288 pif_uuid, "PIF").get_record())
289 for pif_uuid in XendPIF.get_all()])
290 self.state_store.save_state('pif', pif_records)
292 def save_networks(self):
293 net_records = dict([(network_uuid, XendAPIStore.get(
294 network_uuid, "network").get_record())
295 for network_uuid in XendNetwork.get_all()])
296 self.state_store.save_state('network', net_records)
298 def save_PBDs(self):
299 pbd_records = dict([(pbd_uuid, XendAPIStore.get(
300 pbd_uuid, "PBD").get_record())
301 for pbd_uuid in XendPBD.get_all()])
302 self.state_store.save_state('pbd', pbd_records)
304 def save_SRs(self):
305 sr_records = dict([(k, v.get_record(transient = False))
306 for k, v in self.srs.items()])
307 self.state_store.save_state('sr', sr_records)
309 def shutdown(self):
310 return 0
312 def reboot(self):
313 return 0
315 def notify(self, _):
316 return 0
318 #
319 # Ref validation
320 #
322 def is_valid_host(self, host_ref):
323 return (host_ref == self.uuid)
325 def is_valid_cpu(self, cpu_ref):
326 return (cpu_ref in self.cpus)
328 def is_valid_sr(self, sr_ref):
329 return (sr_ref in self.srs)
331 def is_valid_vdi(self, vdi_ref):
332 for sr in self.srs.values():
333 if sr.is_valid_vdi(vdi_ref):
334 return True
335 return False
337 #
338 # Storage Repositories
339 #
341 def get_sr(self, sr_uuid):
342 return self.srs.get(sr_uuid)
344 def get_sr_by_type(self, sr_type):
345 return [sr.uuid for sr in self.srs.values() if sr.type == sr_type]
347 def get_sr_by_name(self, name):
348 return [sr.uuid for sr in self.srs.values() if sr.name_label == name]
350 def get_all_sr_uuid(self):
351 return self.srs.keys()
353 def get_vdi_by_uuid(self, vdi_uuid):
354 for sr in self.srs.values():
355 if sr.is_valid_vdi(vdi_uuid):
356 return sr.get_vdi_by_uuid(vdi_uuid)
357 return None
359 def get_vdi_by_name_label(self, name):
360 for sr in self.srs.values():
361 vdi = sr.get_vdi_by_name_label(name)
362 if vdi:
363 return vdi
364 return None
366 def get_sr_containing_vdi(self, vdi_uuid):
367 for sr in self.srs.values():
368 if sr.is_valid_vdi(vdi_uuid):
369 return sr
370 return None
373 #
374 # Host Functions
375 #
377 def xen_version(self):
378 info = self.xc.xeninfo()
380 info = {'Xen': '%(xen_major)d.%(xen_minor)d' % info}
382 # Add xend_config_format
383 info.update(self.xendinfo_dict())
385 # Add version info about machine
386 info.update(self.nodeinfo_dict())
388 # Add specific xen version info
389 xeninfo_dict = self.xeninfo_dict()
391 info.update({
392 "xen_major": xeninfo_dict["xen_major"],
393 "xen_minor": xeninfo_dict["xen_minor"],
394 "xen_extra": xeninfo_dict["xen_extra"],
395 "cc_compiler": xeninfo_dict["cc_compiler"],
396 "cc_compile_by": xeninfo_dict["cc_compile_by"],
397 "cc_compile_domain": xeninfo_dict["cc_compile_domain"],
398 "cc_compile_date": xeninfo_dict["cc_compile_date"],
399 "xen_changeset": xeninfo_dict["xen_changeset"]
400 })
402 return info
404 def get_name(self):
405 return self.name
407 def set_name(self, new_name):
408 self.name = new_name
410 def get_description(self):
411 return self.desc
413 def set_description(self, new_desc):
414 self.desc = new_desc
416 def get_uuid(self):
417 return self.uuid
419 def get_capabilities(self):
420 return self.xc.xeninfo()['xen_caps'].split(" ")
422 #
423 # Host CPU Functions
424 #
426 def get_host_cpu_by_uuid(self, host_cpu_uuid):
427 if host_cpu_uuid in self.cpus:
428 return host_cpu_uuid
429 raise XendError('Invalid CPU UUID')
431 def get_host_cpu_refs(self):
432 return self.cpus.keys()
434 def get_host_cpu_uuid(self, host_cpu_ref):
435 if host_cpu_ref in self.cpus:
436 return host_cpu_ref
437 else:
438 raise XendError('Invalid CPU Reference')
440 def get_host_cpu_field(self, ref, field):
441 try:
442 return self.cpus[ref][field]
443 except KeyError:
444 raise XendError('Invalid CPU Reference')
446 def get_host_cpu_load(self, host_cpu_ref):
447 host_cpu = self.cpus.get(host_cpu_ref)
448 if not host_cpu:
449 return 0.0
451 vcpu = int(host_cpu['number'])
452 cpu_loads = self.monitor.get_domain_vcpus_util()
453 if 0 in cpu_loads and vcpu in cpu_loads[0]:
454 return cpu_loads[0][vcpu]
456 return 0.0
458 def get_vcpus_policy(self):
459 sched_id = self.xc.sched_id_get()
460 if sched_id == xen.lowlevel.xc.XEN_SCHEDULER_SEDF:
461 return 'sedf'
462 elif sched_id == xen.lowlevel.xc.XEN_SCHEDULER_CREDIT:
463 return 'credit'
464 else:
465 return 'unknown'
467 def get_cpu_configuration(self):
468 phys_info = self.physinfo_dict()
470 cpu_info = {
471 "nr_nodes": phys_info["nr_nodes"],
472 "nr_cpus": phys_info["nr_cpus"],
473 "cores_per_socket": phys_info["cores_per_socket"],
474 "threads_per_core": phys_info["threads_per_core"]
475 }
477 return cpu_info
479 #
480 # Network Functions
481 #
483 def bridge_to_network(self, bridge):
484 """
485 Determine which network a particular bridge is attached to.
487 @param bridge The name of the bridge. If empty, the default bridge
488 will be used instead (the first one in the list returned by brctl
489 show); this is the behaviour of the vif-bridge script.
490 @return The XendNetwork instance to which this bridge is attached.
491 @raise Exception if the interface is not connected to a network.
492 """
493 if not bridge:
494 rc, bridge = commands.getstatusoutput(
495 'brctl show | cut -d "\n" -f 2 | cut -f 1')
496 if rc != 0 or not bridge:
497 raise Exception(
498 'Could not find default bridge, and none was specified')
500 for network_uuid in XendNetwork.get_all():
501 network = XendAPIStore.get(network_uuid, "network")
502 if network.get_name_label() == bridge:
503 return network
504 else:
505 raise Exception('Cannot find network for bridge %s' % bridge)
507 #
508 # Debug keys.
509 #
511 def send_debug_keys(self, keys):
512 return self.xc.send_debug_keys(keys)
514 #
515 # Getting host information.
516 #
518 def info(self):
519 return (self.nodeinfo() + self.physinfo() + self.xeninfo() +
520 self.xendinfo())
522 def nodeinfo(self):
523 (sys, host, rel, ver, mch) = os.uname()
524 return [['system', sys],
525 ['host', host],
526 ['release', rel],
527 ['version', ver],
528 ['machine', mch]]
530 def list_to_rangepairs(self,cmap):
531 cmap.sort()
532 pairs = []
533 x = y = 0
534 for i in range(0,len(cmap)):
535 try:
536 if ((cmap[y+1] - cmap[i]) > 1):
537 pairs.append((cmap[x],cmap[y]))
538 x = y = i+1
539 else:
540 y = y + 1
541 # if we go off the end, then just add x to y
542 except IndexError:
543 pairs.append((cmap[x],cmap[y]))
545 return pairs
547 def format_pairs(self,pairs):
548 if not pairs:
549 return "no cpus"
550 out = ""
551 for f,s in pairs:
552 if (f==s):
553 out += '%d'%f
554 else:
555 out += '%d-%d'%(f,s)
556 out += ','
557 # trim trailing ','
558 return out[:-1]
560 def list_to_strrange(self,list):
561 return self.format_pairs(self.list_to_rangepairs(list))
563 def format_node_to_cpu(self, pinfo):
564 str=''
565 whitespace=''
566 try:
567 node_to_cpu=pinfo['node_to_cpu']
568 for i in range(0, pinfo['nr_nodes']):
569 str+='%snode%d:%s\n' % (whitespace,
570 i,
571 self.list_to_strrange(node_to_cpu[i]))
572 whitespace='%25s' % ''
573 except:
574 str='none\n'
575 return str[:-1];
577 def physinfo(self):
578 info = self.xc.physinfo()
580 info['cpu_mhz'] = info['cpu_khz'] / 1000
582 # physinfo is in KiB, need it in MiB
583 info['total_memory'] = info['total_memory'] / 1024
584 info['free_memory'] = info['free_memory'] / 1024
585 info['node_to_cpu'] = self.format_node_to_cpu(info)
587 ITEM_ORDER = ['nr_cpus',
588 'nr_nodes',
589 'cores_per_socket',
590 'threads_per_core',
591 'cpu_mhz',
592 'hw_caps',
593 'total_memory',
594 'free_memory',
595 'node_to_cpu'
596 ]
598 return [[k, info[k]] for k in ITEM_ORDER]
600 def xenschedinfo(self):
601 sched_id = self.xc.sched_id_get()
602 if sched_id == xen.lowlevel.xc.XEN_SCHEDULER_SEDF:
603 return 'sedf'
604 elif sched_id == xen.lowlevel.xc.XEN_SCHEDULER_CREDIT:
605 return 'credit'
606 else:
607 return 'unknown'
609 def xeninfo(self):
610 info = self.xc.xeninfo()
611 info['xen_scheduler'] = self.xenschedinfo()
613 ITEM_ORDER = ['xen_major',
614 'xen_minor',
615 'xen_extra',
616 'xen_caps',
617 'xen_scheduler',
618 'xen_pagesize',
619 'platform_params',
620 'xen_changeset',
621 'cc_compiler',
622 'cc_compile_by',
623 'cc_compile_domain',
624 'cc_compile_date',
625 ]
627 return [[k, info[k]] for k in ITEM_ORDER]
629 def xendinfo(self):
630 return [['xend_config_format', 4]]
632 #
633 # utilisation tracking
634 #
636 def get_vcpu_util(self, domid, vcpuid):
637 cpu_loads = self.monitor.get_domain_vcpus_util()
638 if domid in cpu_loads:
639 return cpu_loads[domid].get(vcpuid, 0.0)
640 return 0.0
642 def get_vif_util(self, domid, vifid):
643 vif_loads = self.monitor.get_domain_vifs_util()
644 if domid in vif_loads:
645 return vif_loads[domid].get(vifid, (0.0, 0.0))
646 return (0.0, 0.0)
648 def get_vif_stat(self, domid, vifid):
649 vif_loads = self.monitor.get_domain_vifs_stat()
650 if domid in vif_loads:
651 return vif_loads[domid].get(vifid, (0.0, 0.0))
652 return (0.0, 0.0)
654 def get_vbd_util(self, domid, vbdid):
655 vbd_loads = self.monitor.get_domain_vbds_util()
656 if domid in vbd_loads:
657 return vbd_loads[domid].get(vbdid, (0.0, 0.0))
658 return (0.0, 0.0)
660 # dictionary version of *info() functions to get rid of
661 # SXPisms.
662 def nodeinfo_dict(self):
663 return dict(self.nodeinfo())
664 def xendinfo_dict(self):
665 return dict(self.xendinfo())
666 def xeninfo_dict(self):
667 return dict(self.xeninfo())
668 def physinfo_dict(self):
669 return dict(self.physinfo())
670 def info_dict(self):
671 return dict(self.info())
673 def parse_proc_cpuinfo():
674 cpuinfo = {}
675 f = file('/proc/cpuinfo', 'r')
676 try:
677 p = -1
678 d = {}
679 for line in f:
680 keyvalue = line.split(':')
681 if len(keyvalue) != 2:
682 continue
683 key = keyvalue[0].strip()
684 val = keyvalue[1].strip()
685 if key == 'processor':
686 if p != -1:
687 cpuinfo[p] = d
688 p = int(val)
689 d = {}
690 else:
691 d[key] = val
692 cpuinfo[p] = d
693 return cpuinfo
694 finally:
695 f.close()
698 def instance():
699 global inst
700 try:
701 inst
702 except:
703 inst = XendNode()
704 inst.save()
705 return inst