ia64/xen-unstable

view tools/python/xen/xend/XendNode.py @ 14846:30898de09289

Simplify code to map interfaces to bridges

signed-off-by: Tom Wilkie <tom.wilkie@gmail.com>
author Tom Wilkie <tom.wilkie@gmail.com>
date Fri Apr 13 11:43:50 2007 +0100 (2007-04-13)
parents 887fa548f650
children 0bbc44c0b6e3
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
25 import uuid, arch
26 import XendPBD
27 from XendError import *
28 from XendOptions import instance as xendoptions
29 from XendQCoWStorageRepo import XendQCoWStorageRepo
30 from XendLocalStorageRepo import XendLocalStorageRepo
31 from XendLogging import log
32 from XendPIF import *
33 from XendPIFMetrics import XendPIFMetrics
34 from XendNetwork import *
35 from XendStateStore import XendStateStore
36 from XendMonitor import XendMonitor
38 class XendNode:
39 """XendNode - Represents a Domain 0 Host."""
41 def __init__(self):
42 """Initalises the state of all host specific objects such as
44 * host
45 * host_CPU
46 * host_metrics
47 * PIF
48 * PIF_metrics
49 * network
50 * Storage Repository
51 """
53 self.xc = xen.lowlevel.xc.xc()
54 self.state_store = XendStateStore(xendoptions().get_xend_state_path())
55 self.monitor = XendMonitor()
56 self.monitor.start()
58 # load host state from XML file
59 saved_host = self.state_store.load_state('host')
60 if saved_host and len(saved_host.keys()) == 1:
61 self.uuid = saved_host.keys()[0]
62 host = saved_host[self.uuid]
63 self.name = host.get('name_label', socket.gethostname())
64 self.desc = host.get('name_description', '')
65 self.host_metrics_uuid = host.get('metrics_uuid',
66 uuid.createString())
67 try:
68 self.other_config = eval(host['other_config'])
69 except:
70 self.other_config = {}
71 self.cpus = {}
72 else:
73 self.uuid = uuid.createString()
74 self.name = socket.gethostname()
75 self.desc = ''
76 self.other_config = {}
77 self.cpus = {}
78 self.host_metrics_uuid = uuid.createString()
80 # put some arbitrary params in other_config as this
81 # is directly exposed via XenAPI
82 self.other_config["xen_pagesize"] = self.xeninfo_dict()["xen_pagesize"]
83 self.other_config["platform_params"] = self.xeninfo_dict()["platform_params"]
85 # load CPU UUIDs
86 saved_cpus = self.state_store.load_state('cpu')
87 for cpu_uuid, cpu in saved_cpus.items():
88 self.cpus[cpu_uuid] = cpu
90 cpuinfo = parse_proc_cpuinfo()
91 physinfo = self.physinfo_dict()
92 cpu_count = physinfo['nr_cpus']
93 cpu_features = physinfo['hw_caps']
95 # If the number of CPUs don't match, we should just reinitialise
96 # the CPU UUIDs.
97 if cpu_count != len(self.cpus):
98 self.cpus = {}
99 for i in range(cpu_count):
100 u = uuid.createString()
101 self.cpus[u] = {'uuid': u, 'number': i }
103 for u in self.cpus.keys():
104 number = self.cpus[u]['number']
105 # We can run off the end of the cpuinfo list if domain0 does not
106 # have #vcpus == #pcpus. In that case we just replicate one that's
107 # in the hash table.
108 if not cpuinfo.has_key(number):
109 number = cpuinfo.keys()[0]
110 if arch.type == "x86":
111 self.cpus[u].update(
112 { 'host' : self.uuid,
113 'features' : cpu_features,
114 'speed' : int(float(cpuinfo[number]['cpu MHz'])),
115 'vendor' : cpuinfo[number]['vendor_id'],
116 'modelname': cpuinfo[number]['model name'],
117 'stepping' : cpuinfo[number]['stepping'],
118 'flags' : cpuinfo[number]['flags'],
119 })
120 elif arch.type == "ia64":
121 self.cpus[u].update(
122 { 'host' : self.uuid,
123 'features' : cpu_features,
124 'speed' : int(float(cpuinfo[number]['cpu MHz'])),
125 'vendor' : cpuinfo[number]['vendor'],
126 'modelname': cpuinfo[number]['family'],
127 'stepping' : cpuinfo[number]['model'],
128 'flags' : cpuinfo[number]['features'],
129 })
130 else:
131 self.cpus[u].update(
132 { 'host' : self.uuid,
133 'features' : cpu_features,
134 })
136 self.pifs = {}
137 self.pif_metrics = {}
138 self.networks = {}
139 self.srs = {}
141 # initialise networks
142 saved_networks = self.state_store.load_state('network')
143 if saved_networks:
144 for net_uuid, network in saved_networks.items():
145 self.network_create(network, False, net_uuid)
146 else:
147 bridges = Brctl.get_state().keys()
148 for bridge in bridges:
149 self.network_create({'name_label' : bridge }, False)
151 # Get a mapping from interface to bridge
153 if_to_br = dict([(i,b)
154 for (b,ifs) in Brctl.get_state().items()
155 for i in ifs])
157 # initialise PIFs
158 saved_pifs = self.state_store.load_state('pif')
159 if saved_pifs:
160 for pif_uuid, pif in saved_pifs.items():
161 if pif.get('network') in self.networks:
162 network = self.networks[pif['network']]
163 try:
164 if 'device' not in pif and 'name' in pif:
165 # Compatibility hack, can go pretty soon.
166 pif['device'] = pif['name']
167 if 'metrics' not in pif:
168 # Compatibility hack, can go pretty soon.
169 pif['metrics'] = uuid.createString()
171 try:
172 pif['VLAN'] = int(pif.get('VLAN', -1))
173 except (ValueError, TypeError):
174 pif['VLAN'] = -1
176 self._PIF_create(pif['device'], pif['MTU'],
177 pif['VLAN'],
178 pif['MAC'], network, False, pif_uuid,
179 pif['metrics'])
180 except NetworkAlreadyConnected, exn:
181 log.error('Cannot load saved PIF %s, as network %s ' +
182 'is already connected to PIF %s',
183 pif_uuid, pif['network'], exn.pif_uuid)
184 else:
185 for name, mtu, mac in linux_get_phy_ifaces():
186 bridge_name = if_to_br.get(name, None)
187 if bridge_name is not None:
188 networks = [network for
189 network in self.networks.values()
190 if network.get_name_label() == bridge_name]
191 if len(networks) > 0:
192 network = networks[0]
193 self._PIF_create(name, mtu, -1, mac, network, False)
195 # initialise storage
196 saved_srs = self.state_store.load_state('sr')
197 if saved_srs:
198 for sr_uuid, sr_cfg in saved_srs.items():
199 log.error("SAved SRS %s %s", sr_uuid, sr_cfg['type'])
200 if sr_cfg['type'] == 'qcow_file':
201 self.srs[sr_uuid] = XendQCoWStorageRepo(sr_uuid)
202 elif sr_cfg['type'] == 'local':
203 self.srs[sr_uuid] = XendLocalStorageRepo(sr_uuid)
205 # Create missing SRs if they don't exist
206 if not self.get_sr_by_type('local'):
207 image_sr_uuid = uuid.createString()
208 self.srs[image_sr_uuid] = XendLocalStorageRepo(image_sr_uuid)
210 if not self.get_sr_by_type('qcow_file'):
211 qcow_sr_uuid = uuid.createString()
212 self.srs[qcow_sr_uuid] = XendQCoWStorageRepo(qcow_sr_uuid)
214 saved_pbds = self.state_store.load_state('pbd')
215 if saved_pbds:
216 for pbd_uuid, pbd_cfg in saved_pbds.items():
217 pbd_cfg['uuid'] = pbd_uuid
218 XendPBD.XendPBD(pbd_cfg)
221 def network_create(self, record, persist = True, net_uuid = None):
222 if net_uuid is None:
223 net_uuid = uuid.createString()
224 self.networks[net_uuid] = XendNetwork(net_uuid, record)
225 if persist:
226 self.save_networks()
227 return net_uuid
230 def network_destroy(self, net_uuid):
231 del self.networks[net_uuid]
232 self.save_networks()
235 def get_PIF_refs(self):
236 return self.pifs.keys()
239 def _PIF_create(self, name, mtu, vlan, mac, network, persist = True,
240 pif_uuid = None, metrics_uuid = None):
241 for pif in self.pifs.values():
242 if pif.network == network:
243 raise NetworkAlreadyConnected(pif.uuid)
245 if pif_uuid is None:
246 pif_uuid = uuid.createString()
247 if metrics_uuid is None:
248 metrics_uuid = uuid.createString()
250 metrics = XendPIFMetrics(metrics_uuid)
251 pif = XendPIF(pif_uuid, metrics, name, mtu, vlan, mac, network, self)
252 metrics.set_PIF(pif)
254 self.pif_metrics[metrics_uuid] = metrics
255 self.pifs[pif_uuid] = pif
257 if persist:
258 self.save_PIFs()
259 self.refreshBridges()
260 return pif_uuid
263 def PIF_create_VLAN(self, pif_uuid, network_uuid, vlan):
264 if vlan < 0 or vlan >= 4096:
265 raise VLANTagInvalid()
267 pif = self.pifs[pif_uuid]
268 network = self.networks[network_uuid]
269 return self._PIF_create(pif.device, pif.mtu, vlan, pif.mac, network)
272 def PIF_destroy(self, pif_uuid):
273 pif = self.pifs[pif_uuid]
275 if pif.vlan == -1:
276 raise PIFIsPhysical()
278 del self.pifs[pif_uuid]
279 self.save_PIFs()
282 def save(self):
283 # save state
284 host_record = {self.uuid: {'name_label':self.name,
285 'name_description':self.desc,
286 'metrics_uuid': self.host_metrics_uuid,
287 'other_config': repr(self.other_config)}}
288 self.state_store.save_state('host',host_record)
289 self.state_store.save_state('cpu', self.cpus)
290 self.save_PIFs()
291 self.save_networks()
292 self.save_PBDs()
293 self.save_SRs()
295 def save_PIFs(self):
296 pif_records = dict([(k, v.get_record())
297 for k, v in self.pifs.items()])
298 self.state_store.save_state('pif', pif_records)
300 def save_networks(self):
301 net_records = dict([(k, v.get_record_internal(False))
302 for k, v in self.networks.items()])
303 self.state_store.save_state('network', net_records)
305 def save_PBDs(self):
306 pbd_records = dict([(v.get_uuid(), v.get_record())
307 for v in XendPBD.get_all()])
308 self.state_store.save_state('pbd', pbd_records)
310 def save_SRs(self):
311 sr_records = dict([(k, v.get_record(transient = False))
312 for k, v in self.srs.items()])
313 self.state_store.save_state('sr', sr_records)
315 def shutdown(self):
316 return 0
318 def reboot(self):
319 return 0
321 def notify(self, _):
322 return 0
324 #
325 # Ref validation
326 #
328 def is_valid_host(self, host_ref):
329 return (host_ref == self.uuid)
331 def is_valid_cpu(self, cpu_ref):
332 return (cpu_ref in self.cpus)
334 def is_valid_network(self, network_ref):
335 return (network_ref in self.networks)
337 def is_valid_sr(self, sr_ref):
338 return (sr_ref in self.srs)
340 def is_valid_vdi(self, vdi_ref):
341 for sr in self.srs.values():
342 if sr.is_valid_vdi(vdi_ref):
343 return True
344 return False
346 #
347 # Storage Repositories
348 #
350 def get_sr(self, sr_uuid):
351 return self.srs.get(sr_uuid)
353 def get_sr_by_type(self, sr_type):
354 return [sr.uuid for sr in self.srs.values() if sr.type == sr_type]
356 def get_sr_by_name(self, name):
357 return [sr.uuid for sr in self.srs.values() if sr.name_label == name]
359 def get_all_sr_uuid(self):
360 return self.srs.keys()
362 def get_vdi_by_uuid(self, vdi_uuid):
363 for sr in self.srs.values():
364 if sr.is_valid_vdi(vdi_uuid):
365 return sr.get_vdi_by_uuid(vdi_uuid)
366 return None
368 def get_vdi_by_name_label(self, name):
369 for sr in self.srs.values():
370 vdi = sr.get_vdi_by_name_label(name)
371 if vdi:
372 return vdi
373 return None
375 def get_sr_containing_vdi(self, vdi_uuid):
376 for sr in self.srs.values():
377 if sr.is_valid_vdi(vdi_uuid):
378 return sr
379 return None
382 #
383 # Host Functions
384 #
386 def xen_version(self):
387 info = self.xc.xeninfo()
389 try:
390 from xen import VERSION
391 info = {'Xen': '%(xen_major)d.%(xen_minor)d' % info,
392 'Xend': VERSION}
393 except (ImportError, AttributeError):
394 info = {'Xen': '%(xen_major)d.%(xen_minor)d' % info,
395 'Xend': '3.0.3'}
397 # Add xend_config_format
398 info.update(self.xendinfo_dict())
400 # Add version info about machine
401 info.update(self.nodeinfo_dict())
403 # Add specific xen version info
404 xeninfo_dict = self.xeninfo_dict()
406 info.update({
407 "xen_major": xeninfo_dict["xen_major"],
408 "xen_minor": xeninfo_dict["xen_minor"],
409 "xen_extra": xeninfo_dict["xen_extra"],
410 "cc_compiler": xeninfo_dict["cc_compiler"],
411 "cc_compile_by": xeninfo_dict["cc_compile_by"],
412 "cc_compile_domain": xeninfo_dict["cc_compile_domain"],
413 "cc_compile_date": xeninfo_dict["cc_compile_date"],
414 "xen_changeset": xeninfo_dict["xen_changeset"]
415 })
417 return info
419 def get_name(self):
420 return self.name
422 def set_name(self, new_name):
423 self.name = new_name
425 def get_description(self):
426 return self.desc
428 def set_description(self, new_desc):
429 self.desc = new_desc
431 def get_uuid(self):
432 return self.uuid
434 def get_capabilities(self):
435 return self.xc.xeninfo()['xen_caps'].split(" ")
437 #
438 # Host CPU Functions
439 #
441 def get_host_cpu_by_uuid(self, host_cpu_uuid):
442 if host_cpu_uuid in self.cpus:
443 return host_cpu_uuid
444 raise XendError('Invalid CPU UUID')
446 def get_host_cpu_refs(self):
447 return self.cpus.keys()
449 def get_host_cpu_uuid(self, host_cpu_ref):
450 if host_cpu_ref in self.cpus:
451 return host_cpu_ref
452 else:
453 raise XendError('Invalid CPU Reference')
455 def get_host_cpu_field(self, ref, field):
456 try:
457 return self.cpus[ref][field]
458 except KeyError:
459 raise XendError('Invalid CPU Reference')
461 def get_host_cpu_load(self, host_cpu_ref):
462 host_cpu = self.cpus.get(host_cpu_ref)
463 if not host_cpu:
464 return 0.0
466 vcpu = int(host_cpu['number'])
467 cpu_loads = self.monitor.get_domain_vcpus_util()
468 if 0 in cpu_loads and vcpu in cpu_loads[0]:
469 return cpu_loads[0][vcpu]
471 return 0.0
473 def get_vcpus_policy(self):
474 sched_id = self.xc.sched_id_get()
475 if sched_id == xen.lowlevel.xc.XEN_SCHEDULER_SEDF:
476 return 'sedf'
477 elif sched_id == xen.lowlevel.xc.XEN_SCHEDULER_CREDIT:
478 return 'credit'
479 else:
480 return 'unknown'
482 def get_cpu_configuration(self):
483 phys_info = self.physinfo_dict()
485 cpu_info = {
486 "nr_nodes": phys_info["nr_nodes"],
487 "sockets_per_node": phys_info["sockets_per_node"],
488 "cores_per_socket": phys_info["cores_per_socket"],
489 "threads_per_core": phys_info["threads_per_core"]
490 }
492 return cpu_info
494 #
495 # Network Functions
496 #
498 def get_network_refs(self):
499 return self.networks.keys()
501 def get_network(self, network_ref):
502 return self.networks[network_ref]
504 def bridge_to_network(self, bridge):
505 """
506 Determine which network a particular bridge is attached to.
508 @param bridge The name of the bridge. If empty, the default bridge
509 will be used instead (the first one in the list returned by brctl
510 show); this is the behaviour of the vif-bridge script.
511 @return The XendNetwork instance to which this bridge is attached.
512 @raise Exception if the interface is not connected to a network.
513 """
514 if not bridge:
515 rc, bridge = commands.getstatusoutput(
516 'brctl show | cut -d "\n" -f 2 | cut -f 1')
517 if rc != 0 or not bridge:
518 raise Exception(
519 'Could not find default bridge, and none was specified')
521 bridges = Brctl.get_state()
522 if bridge not in bridges:
523 raise Exception('Bridge %s is not up' % bridge)
524 for pif in self.pifs.values():
525 if pif.interface_name() in bridges[bridge]:
526 return pif.network
527 raise Exception('Bridge %s is not connected to a network' % bridge)
529 #
530 # Debug keys.
531 #
533 def send_debug_keys(self, keys):
534 return self.xc.send_debug_keys(keys)
536 #
537 # Getting host information.
538 #
540 def info(self):
541 return (self.nodeinfo() + self.physinfo() + self.xeninfo() +
542 self.xendinfo())
544 def nodeinfo(self):
545 (sys, host, rel, ver, mch) = os.uname()
546 return [['system', sys],
547 ['host', host],
548 ['release', rel],
549 ['version', ver],
550 ['machine', mch]]
552 def physinfo(self):
553 info = self.xc.physinfo()
555 info['nr_cpus'] = (info['nr_nodes'] *
556 info['sockets_per_node'] *
557 info['cores_per_socket'] *
558 info['threads_per_core'])
559 info['cpu_mhz'] = info['cpu_khz'] / 1000
561 # physinfo is in KiB, need it in MiB
562 info['total_memory'] = info['total_memory'] / 1024
563 info['free_memory'] = info['free_memory'] / 1024
565 ITEM_ORDER = ['nr_cpus',
566 'nr_nodes',
567 'sockets_per_node',
568 'cores_per_socket',
569 'threads_per_core',
570 'cpu_mhz',
571 'hw_caps',
572 'total_memory',
573 'free_memory',
574 ]
576 return [[k, info[k]] for k in ITEM_ORDER]
578 def xenschedinfo(self):
579 sched_id = self.xc.sched_id_get()
580 if sched_id == xen.lowlevel.xc.XEN_SCHEDULER_SEDF:
581 return 'sedf'
582 elif sched_id == xen.lowlevel.xc.XEN_SCHEDULER_CREDIT:
583 return 'credit'
584 else:
585 return 'unknown'
587 def xeninfo(self):
588 info = self.xc.xeninfo()
589 info['xen_scheduler'] = self.xenschedinfo()
591 ITEM_ORDER = ['xen_major',
592 'xen_minor',
593 'xen_extra',
594 'xen_caps',
595 'xen_scheduler',
596 'xen_pagesize',
597 'platform_params',
598 'xen_changeset',
599 'cc_compiler',
600 'cc_compile_by',
601 'cc_compile_domain',
602 'cc_compile_date',
603 ]
605 return [[k, info[k]] for k in ITEM_ORDER]
607 def xendinfo(self):
608 return [['xend_config_format', 4]]
610 #
611 # utilisation tracking
612 #
614 def get_vcpu_util(self, domid, vcpuid):
615 cpu_loads = self.monitor.get_domain_vcpus_util()
616 if domid in cpu_loads:
617 return cpu_loads[domid].get(vcpuid, 0.0)
618 return 0.0
620 def get_vif_util(self, domid, vifid):
621 vif_loads = self.monitor.get_domain_vifs_util()
622 if domid in vif_loads:
623 return vif_loads[domid].get(vifid, (0.0, 0.0))
624 return (0.0, 0.0)
626 def get_vbd_util(self, domid, vbdid):
627 vbd_loads = self.monitor.get_domain_vbds_util()
628 if domid in vbd_loads:
629 return vbd_loads[domid].get(vbdid, (0.0, 0.0))
630 return (0.0, 0.0)
632 # dictionary version of *info() functions to get rid of
633 # SXPisms.
634 def nodeinfo_dict(self):
635 return dict(self.nodeinfo())
636 def xendinfo_dict(self):
637 return dict(self.xendinfo())
638 def xeninfo_dict(self):
639 return dict(self.xeninfo())
640 def physinfo_dict(self):
641 return dict(self.physinfo())
642 def info_dict(self):
643 return dict(self.info())
646 def refreshBridges(self):
647 for pif in self.pifs.values():
648 pif.refresh(Brctl.get_state())
651 def parse_proc_cpuinfo():
652 cpuinfo = {}
653 f = file('/proc/cpuinfo', 'r')
654 try:
655 p = -1
656 d = {}
657 for line in f:
658 keyvalue = line.split(':')
659 if len(keyvalue) != 2:
660 continue
661 key = keyvalue[0].strip()
662 val = keyvalue[1].strip()
663 if key == 'processor':
664 if p != -1:
665 cpuinfo[p] = d
666 p = int(val)
667 d = {}
668 else:
669 d[key] = val
670 cpuinfo[p] = d
671 return cpuinfo
672 finally:
673 f.close()
676 def instance():
677 global inst
678 try:
679 inst
680 except:
681 inst = XendNode()
682 inst.save()
683 return inst