ia64/xen-unstable

view tools/python/xen/xend/XendDomain.py @ 10710:800261a88275

[XEND] Prevent uuid double use.

A check_uuid() in this patch checks on uuid of the
VM configuration definition. If specified uuid is
already used with the others VM, the xm create command
does not create the VM. The xm create command error occurs.

Signed-off-by: Masaki Kanno <kanno.masaki@jp.fujitsu.com>
author kfraser@localhost.localdomain
date Mon Jul 10 15:10:00 2006 +0100 (2006-07-10)
parents 71fa0e0d520c
children 9ddc5be227c1
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) 2005 Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
17 # Copyright (C) 2005 XenSource Ltd
18 #============================================================================
20 """Handler for domain operations.
21 Nothing here is persistent (across reboots).
22 Needs to be persistent for one uptime.
23 """
25 import logging
26 import os
27 import socket
28 import sys
29 import threading
31 import xen.lowlevel.xc
33 import XendDomainInfo
35 from xen.xend import XendRoot
36 from xen.xend import XendCheckpoint
37 from xen.xend.XendError import XendError, XendInvalidDomain
38 from xen.xend.XendLogging import log
39 from xen.xend.xenstore.xstransact import xstransact
40 from xen.xend.xenstore.xswatch import xswatch
41 from xen.util import security
44 xc = xen.lowlevel.xc.xc()
45 xroot = XendRoot.instance()
48 __all__ = [ "XendDomain" ]
50 PRIV_DOMAIN = 0
51 VMROOT = '/vm/'
54 class XendDomain:
55 """Index of all domains. Singleton.
56 """
58 ## public:
60 def __init__(self):
61 self.domains = {}
62 self.domains_lock = threading.RLock()
65 # This must be called only the once, by instance() below. It is separate
66 # from the constructor because XendDomainInfo calls back into this class
67 # in order to check the uniqueness of domain names. This means that
68 # instance() must be able to return a valid instance of this class even
69 # during this initialisation.
70 def init(self):
71 xstransact.Mkdir(VMROOT)
72 xstransact.SetPermissions(VMROOT, { 'dom' : PRIV_DOMAIN })
74 self.domains_lock.acquire()
75 try:
76 self._add_domain(
77 XendDomainInfo.recreate(self.xen_domains()[PRIV_DOMAIN],
78 True))
79 self.dom0_setup()
81 # This watch registration needs to be before the refresh call, so
82 # that we're sure that we haven't missed any releases, but inside
83 # the domains_lock, as we don't want the watch to fire until after
84 # the refresh call has completed.
85 xswatch("@introduceDomain", self.onChangeDomain)
86 xswatch("@releaseDomain", self.onChangeDomain)
88 self.refresh(True)
89 finally:
90 self.domains_lock.release()
93 def list(self):
94 """Get list of domain objects.
96 @return: domain objects
97 """
98 self.domains_lock.acquire()
99 try:
100 self.refresh()
101 return self.domains.values()
102 finally:
103 self.domains_lock.release()
106 def list_sorted(self):
107 """Get list of domain objects, sorted by name.
109 @return: domain objects
110 """
111 doms = self.list()
112 doms.sort(lambda x, y: cmp(x.getName(), y.getName()))
113 return doms
115 def list_names(self):
116 """Get list of domain names.
118 @return: domain names
119 """
120 doms = self.list_sorted()
121 return map(lambda x: x.getName(), doms)
124 ## private:
126 def onChangeDomain(self, _):
127 self.domains_lock.acquire()
128 try:
129 self.refresh()
130 finally:
131 self.domains_lock.release()
132 return 1
135 def xen_domains(self):
136 """Get table of domains indexed by id from xc. Expects to be
137 protected by the domains_lock.
138 """
139 domlist = xc.domain_getinfo()
140 doms = {}
141 for d in domlist:
142 domid = d['dom']
143 doms[domid] = d
144 return doms
147 def dom0_setup(self):
148 """Expects to be protected by the domains_lock."""
149 dom0 = self.domains[PRIV_DOMAIN]
151 # get max number of vcpus to use for dom0 from config
152 target = int(xroot.get_dom0_vcpus())
153 log.debug("number of vcpus to use is %d", target)
155 # target == 0 means use all processors
156 if target > 0:
157 dom0.setVCpuCount(target)
160 def _add_domain(self, info):
161 """Add the given domain entry to this instance's internal cache.
162 Expects to be protected by the domains_lock.
163 """
164 self.domains[info.getDomid()] = info
167 def _delete_domain(self, domid):
168 """Remove the given domain from this instance's internal cache.
169 Expects to be protected by the domains_lock.
170 """
171 info = self.domains.get(domid)
172 if info:
173 del self.domains[domid]
174 info.cleanupDomain()
177 def refresh(self, initialising = False):
178 """Refresh domain list from Xen. Expects to be protected by the
179 domains_lock.
181 @param initialising True if this is the first refresh after starting
182 Xend. This does not change this method's behaviour, except for
183 logging.
184 """
185 doms = self.xen_domains()
186 for d in self.domains.values():
187 info = doms.get(d.getDomid())
188 if info:
189 d.update(info)
190 else:
191 self._delete_domain(d.getDomid())
192 for d in doms:
193 if d not in self.domains:
194 if doms[d]['dying']:
195 log.log(initialising and logging.ERROR or logging.DEBUG,
196 'Cannot recreate information for dying domain %d.'
197 ' Xend will ignore this domain from now on.',
198 doms[d]['dom'])
199 elif d == PRIV_DOMAIN:
200 log.fatal(
201 "No record of privileged domain %d! Terminating.", d)
202 sys.exit(1)
203 else:
204 try:
205 self._add_domain(
206 XendDomainInfo.recreate(doms[d], False))
207 except:
208 log.exception(
209 "Failed to recreate information for domain "
210 "%d. Destroying it in the hope of "
211 "recovery.", d)
212 try:
213 xc.domain_destroy(d)
214 except:
215 log.exception('Destruction of %d failed.', d)
218 ## public:
220 def domain_create(self, config):
221 """Create a domain from a configuration.
223 @param config: configuration
224 @return: domain
225 """
226 self.domains_lock.acquire()
227 try:
228 dominfo = XendDomainInfo.create(config)
229 self._add_domain(dominfo)
230 return dominfo
231 finally:
232 self.domains_lock.release()
235 def domain_configure(self, config):
236 """Configure an existing domain.
238 @param vmconfig: vm configuration
239 """
240 # !!!
241 raise XendError("Unsupported")
243 def domain_restore(self, src):
244 """Restore a domain from file.
246 @param src: source file
247 """
249 try:
250 fd = os.open(src, os.O_RDONLY)
251 try:
252 return self.domain_restore_fd(fd)
253 finally:
254 os.close(fd)
255 except OSError, ex:
256 raise XendError("can't read guest state file %s: %s" %
257 (src, ex[1]))
259 def domain_restore_fd(self, fd):
260 """Restore a domain from the given file descriptor."""
262 try:
263 return XendCheckpoint.restore(self, fd)
264 except:
265 # I don't really want to log this exception here, but the error
266 # handling in the relocation-socket handling code (relocate.py) is
267 # poor, so we need to log this for debugging.
268 log.exception("Restore failed")
269 raise XendError("Restore failed")
272 def restore_(self, config):
273 """Create a domain as part of the restore process. This is called
274 only from {@link XendCheckpoint}.
276 A restore request comes into XendDomain through {@link
277 #domain_restore} or {@link #domain_restore_fd}. That request is
278 forwarded immediately to XendCheckpoint which, when it is ready, will
279 call this method. It is necessary to come through here rather than go
280 directly to {@link XendDomainInfo.restore} because we need to
281 serialise the domain creation process, but cannot lock
282 domain_restore_fd as a whole, otherwise we will deadlock waiting for
283 the old domain to die.
284 """
285 self.domains_lock.acquire()
286 try:
287 security.refresh_ssidref(config)
288 dominfo = XendDomainInfo.restore(config)
289 self._add_domain(dominfo)
290 return dominfo
291 finally:
292 self.domains_lock.release()
295 def domain_lookup(self, domid):
296 self.domains_lock.acquire()
297 try:
298 self.refresh()
299 return self.domains.get(domid)
300 finally:
301 self.domains_lock.release()
304 def domain_lookup_nr(self, domid):
305 self.domains_lock.acquire()
306 try:
307 return self.domains.get(domid)
308 finally:
309 self.domains_lock.release()
312 def domain_lookup_by_name_or_id(self, name):
313 self.domains_lock.acquire()
314 try:
315 self.refresh()
316 return self.domain_lookup_by_name_or_id_nr(name)
317 finally:
318 self.domains_lock.release()
321 def domain_lookup_by_name_or_id_nr(self, name):
322 self.domains_lock.acquire()
323 try:
324 dominfo = self.domain_lookup_by_name_nr(name)
326 if dominfo:
327 return dominfo
328 else:
329 try:
330 return self.domains.get(int(name))
331 except ValueError:
332 return None
333 finally:
334 self.domains_lock.release()
337 def domain_lookup_by_name_nr(self, name):
338 self.domains_lock.acquire()
339 try:
340 matching = filter(lambda d: d.getName() == name,
341 self.domains.values())
342 n = len(matching)
343 if n == 1:
344 return matching[0]
345 return None
346 finally:
347 self.domains_lock.release()
350 def domain_lookup_by_uuid_nr(self, uuid):
351 self.domains_lock.acquire()
352 try:
353 matching = filter(lambda d: d.getUuid() == uuid,
354 self.domains.values())
355 n = len(matching)
356 if n == 1:
357 return matching[0]
358 return None
359 finally:
360 self.domains_lock.release()
363 def privilegedDomain(self):
364 self.domains_lock.acquire()
365 try:
366 return self.domains[PRIV_DOMAIN]
367 finally:
368 self.domains_lock.release()
371 def domain_unpause(self, domid):
372 """Unpause domain execution."""
373 try:
374 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
375 if not dominfo:
376 raise XendInvalidDomain(str(domid))
377 log.info("Domain %s (%d) unpaused.", dominfo.getName(),
378 dominfo.getDomid())
379 return dominfo.unpause()
380 except Exception, ex:
381 raise XendError(str(ex))
384 def domain_pause(self, domid):
385 """Pause domain execution."""
386 try:
387 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
388 if not dominfo:
389 raise XendInvalidDomain(str(domid))
390 log.info("Domain %s (%d) paused.", dominfo.getName(),
391 dominfo.getDomid())
392 return dominfo.pause()
393 except Exception, ex:
394 raise XendError(str(ex))
397 def domain_destroy(self, domid):
398 """Terminate domain immediately."""
400 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
401 if dominfo and dominfo.getDomid() == PRIV_DOMAIN:
402 raise XendError("Cannot destroy privileged domain %s" % domid)
404 if dominfo:
405 val = dominfo.destroy()
406 else:
407 try:
408 val = xc.domain_destroy(domid)
409 except Exception, ex:
410 raise XendError(str(ex))
411 return val
413 def domain_migrate(self, domid, dst, live=False, resource=0, port=0):
414 """Start domain migration."""
416 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
417 if not dominfo:
418 raise XendInvalidDomain(str(domid))
420 if dominfo.getDomid() == PRIV_DOMAIN:
421 raise XendError("Cannot migrate privileged domain %i" % domid)
423 """ The following call may raise a XendError exception """
424 dominfo.testMigrateDevices(True, dst)
426 if port == 0:
427 port = xroot.get_xend_relocation_port()
428 try:
429 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
430 sock.connect((dst, port))
431 except socket.error, err:
432 raise XendError("can't connect: %s" % err[1])
434 sock.send("receive\n")
435 sock.recv(80)
436 XendCheckpoint.save(sock.fileno(), dominfo, True, live, dst)
439 def domain_save(self, domid, dst):
440 """Start saving a domain to file.
442 @param dst: destination file
443 """
445 try:
446 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
447 if not dominfo:
448 raise XendInvalidDomain(str(domid))
450 if dominfo.getDomid() == PRIV_DOMAIN:
451 raise XendError("Cannot save privileged domain %i" % domid)
453 fd = os.open(dst, os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
454 try:
455 # For now we don't support 'live checkpoint'
456 return XendCheckpoint.save(fd, dominfo, False, False, dst)
457 finally:
458 os.close(fd)
459 except OSError, ex:
460 raise XendError("can't write guest state file %s: %s" %
461 (dst, ex[1]))
463 def domain_pincpu(self, domid, vcpu, cpumap):
464 """Set which cpus vcpu can use
466 @param cpumap: string repr of list of usable cpus
467 """
468 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
469 if not dominfo:
470 raise XendInvalidDomain(str(domid))
472 try:
473 return xc.vcpu_setaffinity(dominfo.getDomid(), vcpu, cpumap)
474 except Exception, ex:
475 raise XendError(str(ex))
477 def domain_cpu_bvt_set(self, domid, mcuadv, warpback, warpvalue, warpl,
478 warpu):
479 """Set BVT (Borrowed Virtual Time) scheduler parameters for a domain.
480 """
481 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
482 if not dominfo:
483 raise XendInvalidDomain(str(domid))
484 try:
485 return xc.bvtsched_domain_set(dom=dominfo.getDomid(),
486 mcuadv=mcuadv,
487 warpback=warpback,
488 warpvalue=warpvalue,
489 warpl=warpl, warpu=warpu)
490 except Exception, ex:
491 raise XendError(str(ex))
493 def domain_cpu_bvt_get(self, domid):
494 """Get BVT (Borrowed Virtual Time) scheduler parameters for a domain.
495 """
496 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
497 if not dominfo:
498 raise XendInvalidDomain(str(domid))
499 try:
500 return xc.bvtsched_domain_get(dominfo.getDomid())
501 except Exception, ex:
502 raise XendError(str(ex))
505 def domain_cpu_sedf_set(self, domid, period, slice_, latency, extratime,
506 weight):
507 """Set Simple EDF scheduler parameters for a domain.
508 """
509 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
510 if not dominfo:
511 raise XendInvalidDomain(str(domid))
512 try:
513 return xc.sedf_domain_set(dominfo.getDomid(), period, slice_,
514 latency, extratime, weight)
515 except Exception, ex:
516 raise XendError(str(ex))
518 def domain_cpu_sedf_get(self, domid):
519 """Get Simple EDF scheduler parameters for a domain.
520 """
521 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
522 if not dominfo:
523 raise XendInvalidDomain(str(domid))
524 try:
525 sedf_info = xc.sedf_domain_get(dominfo.getDomid())
526 # return sxpr
527 return ['sedf',
528 ['domain', sedf_info['domain']],
529 ['period', sedf_info['period']],
530 ['slice', sedf_info['slice']],
531 ['latency', sedf_info['latency']],
532 ['extratime', sedf_info['extratime']],
533 ['weight', sedf_info['weight']]]
535 except Exception, ex:
536 raise XendError(str(ex))
538 def domain_sched_credit_get(self, domid):
539 """Get credit scheduler parameters for a domain.
540 """
541 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
542 if not dominfo:
543 raise XendInvalidDomain(str(domid))
544 try:
545 return xc.sched_credit_domain_get(dominfo.getDomid())
546 except Exception, ex:
547 raise XendError(str(ex))
549 def domain_sched_credit_set(self, domid, weight, cap):
550 """Set credit scheduler parameters for a domain.
551 """
552 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
553 if not dominfo:
554 raise XendInvalidDomain(str(domid))
555 try:
556 return xc.sched_credit_domain_set(dominfo.getDomid(), weight, cap)
557 except Exception, ex:
558 raise XendError(str(ex))
560 def domain_maxmem_set(self, domid, mem):
561 """Set the memory limit for a domain.
563 @param mem: memory limit (in MiB)
564 @return: 0 on success, -1 on error
565 """
566 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
567 if not dominfo:
568 raise XendInvalidDomain(str(domid))
569 maxmem = int(mem) * 1024
570 try:
571 return xc.domain_setmaxmem(dominfo.getDomid(), maxmem)
572 except Exception, ex:
573 raise XendError(str(ex))
575 def domain_ioport_range_enable(self, domid, first, last):
576 """Enable access to a range of IO ports for a domain
578 @param first: first IO port
579 @param last: last IO port
580 @return: 0 on success, -1 on error
581 """
582 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
583 if not dominfo:
584 raise XendInvalidDomain(str(domid))
585 nr_ports = last - first + 1
586 try:
587 return xc.domain_ioport_permission(dominfo.getDomid(),
588 first_port = first,
589 nr_ports = nr_ports,
590 allow_access = 1)
591 except Exception, ex:
592 raise XendError(str(ex))
594 def domain_ioport_range_disable(self, domid, first, last):
595 """Disable access to a range of IO ports for a domain
597 @param first: first IO port
598 @param last: last IO port
599 @return: 0 on success, -1 on error
600 """
601 dominfo = self.domain_lookup_by_name_or_id_nr(domid)
602 if not dominfo:
603 raise XendInvalidDomain(str(domid))
604 nr_ports = last - first + 1
605 try:
606 return xc.domain_ioport_permission(dominfo.getDomid(),
607 first_port = first,
608 nr_ports = nr_ports,
609 allow_access = 0)
610 except Exception, ex:
611 raise XendError(str(ex))
614 def instance():
615 """Singleton constructor. Use this instead of the class constructor.
616 """
617 global inst
618 try:
619 inst
620 except:
621 inst = XendDomain()
622 inst.init()
623 return inst