source: trunk/packages/xen-3.1/xen-3.1/tools/python/xen/xend/XendDomainInfo.py @ 34

Last change on this file since 34 was 34, checked in by hartmans, 18 years ago

Add xen and xen-common

File size: 89.5 KB
Line 
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-2007 XenSource Ltd
17#============================================================================
18
19"""Representation of a single domain.
20Includes support for domain construction, using
21open-ended configurations.
22
23Author: Mike Wray <mike.wray@hp.com>
24
25"""
26
27import logging
28import time
29import threading
30import re
31import copy
32import os
33import traceback
34from types import StringTypes
35
36import xen.lowlevel.xc
37from xen.util import asserts
38from xen.util.blkif import blkdev_uname_to_file, blkdev_uname_to_taptype
39from xen.util import security
40
41from xen.xend import balloon, sxp, uuid, image, arch, osdep
42from xen.xend import XendOptions, XendNode, XendConfig
43
44from xen.xend.XendConfig import scrub_password
45from xen.xend.XendBootloader import bootloader, bootloader_tidy
46from xen.xend.XendError import XendError, VmError
47from xen.xend.XendDevices import XendDevices
48from xen.xend.XendTask import XendTask
49from xen.xend.xenstore.xstransact import xstransact, complete
50from xen.xend.xenstore.xsutil import GetDomainPath, IntroduceDomain, ResumeDomain
51from xen.xend.xenstore.xswatch import xswatch
52from xen.xend.XendConstants import *
53from xen.xend.XendAPIConstants import *
54
55from xen.xend.XendVMMetrics import XendVMMetrics
56
57MIGRATE_TIMEOUT = 30.0
58BOOTLOADER_LOOPBACK_DEVICE = '/dev/xvdp'
59
60xc = xen.lowlevel.xc.xc()
61xoptions = XendOptions.instance()
62
63log = logging.getLogger("xend.XendDomainInfo")
64#log.setLevel(logging.TRACE)
65
66
67def create(config):
68    """Creates and start a VM using the supplied configuration.
69
70    @param config: A configuration object involving lists of tuples.
71    @type  config: list of lists, eg ['vm', ['image', 'xen.gz']]
72
73    @rtype:  XendDomainInfo
74    @return: An up and running XendDomainInfo instance
75    @raise VmError: Invalid configuration or failure to start.
76    """
77
78    log.debug("XendDomainInfo.create(%s)", scrub_password(config))
79    vm = XendDomainInfo(XendConfig.XendConfig(sxp_obj = config))
80    try:
81        vm.start()
82    except:
83        log.exception('Domain construction failed')
84        vm.destroy()
85        raise
86
87    return vm
88
89def create_from_dict(config_dict):
90    """Creates and start a VM using the supplied configuration.
91
92    @param config_dict: An configuration dictionary.
93
94    @rtype:  XendDomainInfo
95    @return: An up and running XendDomainInfo instance
96    @raise VmError: Invalid configuration or failure to start.
97    """
98
99    log.debug("XendDomainInfo.create_from_dict(%s)",
100              scrub_password(config_dict))
101    vm = XendDomainInfo(XendConfig.XendConfig(xapi = config_dict))
102    try:
103        vm.start()
104    except:
105        log.exception('Domain construction failed')
106        vm.destroy()
107        raise
108    return vm
109
110def recreate(info, priv):
111    """Create the VM object for an existing domain.  The domain must not
112    be dying, as the paths in the store should already have been removed,
113    and asking us to recreate them causes problems.
114
115    @param xeninfo: Parsed configuration
116    @type  xeninfo: Dictionary
117    @param priv: Is a privileged domain (Dom 0)
118    @type  priv: bool
119
120    @rtype:  XendDomainInfo
121    @return: A up and running XendDomainInfo instance
122    @raise VmError: Invalid configuration.
123    @raise XendError: Errors with configuration.
124    """
125
126    log.debug("XendDomainInfo.recreate(%s)", scrub_password(info))
127
128    assert not info['dying']
129
130    xeninfo = XendConfig.XendConfig(dominfo = info)
131    xeninfo['is_control_domain'] = priv
132    xeninfo['is_a_template'] = False
133    domid = xeninfo['domid']
134    uuid1 = uuid.fromString(xeninfo['uuid'])
135    needs_reinitialising = False
136   
137    dompath = GetDomainPath(domid)
138    if not dompath:
139        raise XendError('No domain path in store for existing '
140                        'domain %d' % domid)
141
142    log.info("Recreating domain %d, UUID %s. at %s" %
143             (domid, xeninfo['uuid'], dompath))
144
145    # need to verify the path and uuid if not Domain-0
146    # if the required uuid and vm aren't set, then that means
147    # we need to recreate the dom with our own values
148    #
149    # NOTE: this is probably not desirable, really we should just
150    #       abort or ignore, but there may be cases where xenstore's
151    #       entry disappears (eg. xenstore-rm /)
152    #
153    try:
154        vmpath = xstransact.Read(dompath, "vm")
155        if not vmpath:
156            if not priv:
157                log.warn('/local/domain/%d/vm is missing. recreate is '
158                         'confused, trying our best to recover' % domid)
159            needs_reinitialising = True
160            raise XendError('reinit')
161       
162        uuid2_str = xstransact.Read(vmpath, "uuid")
163        if not uuid2_str:
164            log.warn('%s/uuid/ is missing. recreate is confused, '
165                     'trying our best to recover' % vmpath)
166            needs_reinitialising = True
167            raise XendError('reinit')
168       
169        uuid2 = uuid.fromString(uuid2_str)
170        if uuid1 != uuid2:
171            log.warn('UUID in /vm does not match the UUID in /dom/%d.'
172                     'Trying out best to recover' % domid)
173            needs_reinitialising = True
174    except XendError:
175        pass # our best shot at 'goto' in python :)
176
177    vm = XendDomainInfo(xeninfo, domid, dompath, augment = True, priv = priv)
178   
179    if needs_reinitialising:
180        vm._recreateDom()
181        vm._removeVm()
182        vm._storeVmDetails()
183        vm._storeDomDetails()
184       
185    vm.image = image.create(vm, vm.info)
186    vm.image.recreate()
187
188    vm._registerWatches()
189    vm.refreshShutdown(xeninfo)
190
191    # register the domain in the list
192    from xen.xend import XendDomain
193    XendDomain.instance().add_domain(vm)
194
195    return vm
196
197
198def restore(config):
199    """Create a domain and a VM object to do a restore.
200
201    @param config: Domain SXP configuration
202    @type  config: list of lists. (see C{create})
203
204    @rtype:  XendDomainInfo
205    @return: A up and running XendDomainInfo instance
206    @raise VmError: Invalid configuration or failure to start.
207    @raise XendError: Errors with configuration.
208    """
209
210    log.debug("XendDomainInfo.restore(%s)", scrub_password(config))
211    vm = XendDomainInfo(XendConfig.XendConfig(sxp_obj = config),
212                        resume = True)
213    try:
214        vm.resume()
215        return vm
216    except:
217        vm.destroy()
218        raise
219
220def createDormant(domconfig):
221    """Create a dormant/inactive XenDomainInfo without creating VM.
222    This is for creating instances of persistent domains that are not
223    yet start.
224
225    @param domconfig: Parsed configuration
226    @type  domconfig: XendConfig object
227   
228    @rtype:  XendDomainInfo
229    @return: A up and running XendDomainInfo instance
230    @raise XendError: Errors with configuration.   
231    """
232   
233    log.debug("XendDomainInfo.createDormant(%s)", scrub_password(domconfig))
234   
235    # domid does not make sense for non-running domains.
236    domconfig.pop('domid', None)
237    vm = XendDomainInfo(domconfig)
238    return vm   
239
240def domain_by_name(name):
241    """Get domain by name
242
243    @params name: Name of the domain
244    @type   name: string
245    @return: XendDomainInfo or None
246    """
247    from xen.xend import XendDomain
248    return XendDomain.instance().domain_lookup_by_name_nr(name)
249
250
251def shutdown_reason(code):
252    """Get a shutdown reason from a code.
253
254    @param code: shutdown code
255    @type  code: int
256    @return: shutdown reason
257    @rtype:  string
258    """
259    return DOMAIN_SHUTDOWN_REASONS.get(code, "?")
260
261def dom_get(dom):
262    """Get info from xen for an existing domain.
263
264    @param dom: domain id
265    @type  dom: int
266    @return: info or None
267    @rtype: dictionary
268    """
269    try:
270        domlist = xc.domain_getinfo(dom, 1)
271        if domlist and dom == domlist[0]['domid']:
272            return domlist[0]
273    except Exception, err:
274        # ignore missing domain
275        log.trace("domain_getinfo(%d) failed, ignoring: %s", dom, str(err))
276    return None
277
278
279class XendDomainInfo:
280    """An object represents a domain.
281
282    @TODO: try to unify dom and domid, they mean the same thing, but
283           xc refers to it as dom, and everywhere else, including
284           xenstore it is domid. The best way is to change xc's
285           python interface.
286
287    @ivar info: Parsed configuration
288    @type info: dictionary
289    @ivar domid: Domain ID (if VM has started)
290    @type domid: int or None
291    @ivar vmpath: XenStore path to this VM.
292    @type vmpath: string
293    @ivar dompath: XenStore path to this Domain.
294    @type dompath: string
295    @ivar image:  Reference to the VM Image.
296    @type image: xen.xend.image.ImageHandler
297    @ivar store_port: event channel to xenstored
298    @type store_port: int
299    @ivar console_port: event channel to xenconsoled
300    @type console_port: int
301    @ivar store_mfn: xenstored mfn
302    @type store_mfn: int
303    @ivar console_mfn: xenconsoled mfn
304    @type console_mfn: int
305    @ivar notes: OS image notes
306    @type notes: dictionary
307    @ivar vmWatch: reference to a watch on the xenstored vmpath
308    @type vmWatch: xen.xend.xenstore.xswatch
309    @ivar shutdownWatch: reference to watch on the xenstored domain shutdown
310    @type shutdownWatch: xen.xend.xenstore.xswatch
311    @ivar shutdownStartTime: UNIX Time when domain started shutting down.
312    @type shutdownStartTime: float or None
313#    @ivar state: Domain state
314#    @type state: enum(DOM_STATE_HALTED, DOM_STATE_RUNNING, ...)
315    @ivar state_updated: lock for self.state
316    @type state_updated: threading.Condition
317    @ivar refresh_shutdown_lock: lock for polling shutdown state
318    @type refresh_shutdown_lock: threading.Condition
319    @ivar _deviceControllers: device controller cache for this domain
320    @type _deviceControllers: dict 'string' to DevControllers
321    """
322   
323    def __init__(self, info, domid = None, dompath = None, augment = False,
324                 priv = False, resume = False):
325        """Constructor for a domain
326
327        @param   info: parsed configuration
328        @type    info: dictionary
329        @keyword domid: Set initial domain id (if any)
330        @type    domid: int
331        @keyword dompath: Set initial dompath (if any)
332        @type    dompath: string
333        @keyword augment: Augment given info with xenstored VM info
334        @type    augment: bool
335        @keyword priv: Is a privileged domain (Dom 0)
336        @type    priv: bool
337        @keyword resume: Is this domain being resumed?
338        @type    resume: bool
339        """
340
341        self.info = info
342        if domid == None:
343            self.domid =  self.info.get('domid')
344        else:
345            self.domid = domid
346       
347        #REMOVE: uuid is now generated in XendConfig
348        #if not self._infoIsSet('uuid'):
349        #    self.info['uuid'] = uuid.toString(uuid.create())
350
351        self.vmpath  = XS_VMROOT + self.info['uuid']
352        self.dompath = dompath
353
354        self.image = None
355        self.store_port = None
356        self.store_mfn = None
357        self.console_port = None
358        self.console_mfn = None
359
360        self.vmWatch = None
361        self.shutdownWatch = None
362        self.shutdownStartTime = None
363        self._resume = resume
364
365        self.state_updated = threading.Condition()
366        self.refresh_shutdown_lock = threading.Condition()
367        self._stateSet(DOM_STATE_HALTED)
368
369        self._deviceControllers = {}
370
371        for state in DOM_STATES_OLD:
372            self.info[state] = 0
373
374        if augment:
375            self._augmentInfo(priv)
376
377        self._checkName(self.info['name_label'])
378
379        self.metrics = XendVMMetrics(uuid.createString(), self)
380           
381
382    #
383    # Public functions available through XMLRPC
384    #
385
386
387    def start(self, is_managed = False):
388        """Attempts to start the VM by do the appropriate
389        initialisation if it not started.
390        """
391        from xen.xend import XendDomain
392
393        if self._stateGet() in (XEN_API_VM_POWER_STATE_HALTED, XEN_API_VM_POWER_STATE_SUSPENDED):
394            try:
395                XendTask.log_progress(0, 30, self._constructDomain)
396                XendTask.log_progress(31, 60, self._initDomain)
397               
398                XendTask.log_progress(61, 70, self._storeVmDetails)
399                XendTask.log_progress(71, 80, self._storeDomDetails)
400                XendTask.log_progress(81, 90, self._registerWatches)
401                XendTask.log_progress(91, 100, self.refreshShutdown)
402
403                xendomains = XendDomain.instance()
404                xennode = XendNode.instance()
405
406                # save running configuration if XendDomains believe domain is
407                # persistent
408                if is_managed:
409                    xendomains.managed_config_save(self)
410
411                if xennode.xenschedinfo() == 'credit':
412                    xendomains.domain_sched_credit_set(self.getDomid(),
413                                                       self.getWeight(),
414                                                       self.getCap())
415            except:
416                log.exception('VM start failed')
417                self.destroy()
418                raise
419        else:
420            raise XendError('VM already running')
421
422    def resume(self):
423        """Resumes a domain that has come back from suspension."""
424        state = self._stateGet()
425        if state in (DOM_STATE_SUSPENDED, DOM_STATE_HALTED):
426            try:
427                self._constructDomain()
428                self._storeVmDetails()
429                self._createDevices()
430                self._createChannels()
431                self._storeDomDetails()
432                self._endRestore()
433            except:
434                log.exception('VM resume failed')
435                self.destroy()
436                raise
437        else:
438            raise XendError('VM is not susupened; it is %s'
439                            % XEN_API_VM_POWER_STATE[state])
440
441    def shutdown(self, reason):
442        """Shutdown a domain by signalling this via xenstored."""
443        log.debug('XendDomainInfo.shutdown(%s)', reason)
444        if self._stateGet() in (DOM_STATE_SHUTDOWN, DOM_STATE_HALTED,):
445            raise XendError('Domain cannot be shutdown')
446
447        if self.domid == 0:
448            raise XendError('Domain 0 cannot be shutdown')
449       
450        if reason not in DOMAIN_SHUTDOWN_REASONS.values():
451            raise XendError('Invalid reason: %s' % reason)
452        self._removeVm('xend/previous_restart_time')
453        self.storeDom("control/shutdown", reason)
454
455        # HVM domain shuts itself down only if it has PV drivers
456        if self.info.is_hvm():
457            hvm_pvdrv = xc.hvm_get_param(self.domid, HVM_PARAM_CALLBACK_IRQ)
458            if not hvm_pvdrv:
459                code = REVERSE_DOMAIN_SHUTDOWN_REASONS[reason]
460                log.info("HVM save:remote shutdown dom %d!", self.domid)
461                xc.domain_shutdown(self.domid, code)
462
463    def pause(self):
464        """Pause domain
465       
466        @raise XendError: Failed pausing a domain
467        """
468        try:
469            xc.domain_pause(self.domid)
470            self._stateSet(DOM_STATE_PAUSED)
471        except Exception, ex:
472            log.exception(ex)
473            raise XendError("Domain unable to be paused: %s" % str(ex))
474
475    def unpause(self):
476        """Unpause domain
477       
478        @raise XendError: Failed unpausing a domain
479        """
480        try:
481            xc.domain_unpause(self.domid)
482            self._stateSet(DOM_STATE_RUNNING)
483        except Exception, ex:
484            log.exception(ex)
485            raise XendError("Domain unable to be unpaused: %s" % str(ex))
486
487    def send_sysrq(self, key):
488        """ Send a Sysrq equivalent key via xenstored."""
489        asserts.isCharConvertible(key)
490        self.storeDom("control/sysrq", '%c' % key)
491
492    def device_create(self, dev_config):
493        """Create a new device.
494
495        @param dev_config: device configuration
496        @type  dev_config: SXP object (parsed config)
497        """
498        log.debug("XendDomainInfo.device_create: %s" % scrub_password(dev_config))
499        dev_type = sxp.name(dev_config)
500        dev_uuid = self.info.device_add(dev_type, cfg_sxp = dev_config)
501        dev_config_dict = self.info['devices'][dev_uuid][1]
502        log.debug("XendDomainInfo.device_create: %s" % scrub_password(dev_config_dict))
503        dev_config_dict['devid'] = devid = \
504             self._createDevice(dev_type, dev_config_dict)
505        self._waitForDevice(dev_type, devid)
506        return self.getDeviceController(dev_type).sxpr(devid)
507
508    def device_configure(self, dev_sxp, devid = None):
509        """Configure an existing device.
510       
511        @param dev_config: device configuration
512        @type  dev_config: SXP object (parsed config)
513        @param devid:      device id
514        @type  devid:      int
515        @return: Returns True if successfully updated device
516        @rtype: boolean
517        """
518
519        # convert device sxp to a dict
520        dev_class = sxp.name(dev_sxp)
521        dev_config = {}
522        for opt_val in dev_sxp[1:]:
523            try:
524                dev_config[opt_val[0]] = opt_val[1]
525            except IndexError:
526                pass
527
528        # use DevController.reconfigureDevice to change device config
529        dev_control = self.getDeviceController(dev_class)
530        dev_uuid = dev_control.reconfigureDevice(devid, dev_config)
531
532        # update XendConfig with new device info
533        if dev_uuid:
534            self.info.device_update(dev_uuid, dev_sxp)
535           
536        return True
537
538    def waitForDevices(self):
539        """Wait for this domain's configured devices to connect.
540
541        @raise VmError: if any device fails to initialise.
542        """
543        for devclass in XendDevices.valid_devices():
544            self.getDeviceController(devclass).waitForDevices()
545
546    def destroyDevice(self, deviceClass, devid, force = False):
547        try:
548            devid = int(devid)
549        except ValueError:
550            # devid is not a number, let's search for it in xenstore.
551            devicePath = '%s/device/%s' % (self.dompath, deviceClass)
552            for entry in xstransact.List(devicePath):
553                backend = xstransact.Read('%s/%s' % (devicePath, entry),
554                                          "backend")
555                devName = xstransact.Read(backend, "dev")
556                if devName == devid:
557                    # We found the integer matching our devid, use it instead
558                    devid = entry
559                    break
560               
561        return self.getDeviceController(deviceClass).destroyDevice(devid, force)
562
563    def getDeviceSxprs(self, deviceClass):
564        if self._stateGet() in (DOM_STATE_RUNNING, DOM_STATE_PAUSED):
565            return self.getDeviceController(deviceClass).sxprs()
566        else:
567            sxprs = []
568            dev_num = 0
569            for dev_type, dev_info in self.info.all_devices_sxpr():
570                if dev_type == deviceClass:
571                    sxprs.append([dev_num, dev_info])
572                    dev_num += 1
573            return sxprs
574
575
576    def setMemoryTarget(self, target):
577        """Set the memory target of this domain.
578        @param target: In MiB.
579        """
580        log.debug("Setting memory target of domain %s (%s) to %d MiB.",
581                  self.info['name_label'], str(self.domid), target)
582       
583        MiB = 1024 * 1024
584        self._safe_set_memory('memory_dynamic_min', target * MiB)
585        self._safe_set_memory('memory_dynamic_max', target * MiB)
586
587        if self.domid >= 0:
588            self.storeVm("memory", target)
589            self.storeDom("memory/target", target << 10)
590        xen.xend.XendDomain.instance().managed_config_save(self)
591
592    def setMemoryMaximum(self, limit):
593        """Set the maximum memory limit of this domain
594        @param limit: In MiB.
595        """
596        log.debug("Setting memory maximum of domain %s (%s) to %d MiB.",
597                  self.info['name_label'], str(self.domid), limit)
598
599        if limit <= 0:
600            raise XendError('Invalid memory size')
601
602        MiB = 1024 * 1024
603        self.info['memory_static_max'] = limit * MiB
604
605        if self.domid >= 0:
606            maxmem = int(limit) * 1024
607            try:
608                return xc.domain_setmaxmem(self.domid, maxmem)
609            except Exception, ex:
610                raise XendError(str(ex))
611        xen.xend.XendDomain.instance().managed_config_save(self)
612
613
614    def getVCPUInfo(self):
615        try:
616            # We include the domain name and ID, to help xm.
617            sxpr = ['domain',
618                    ['domid',      self.domid],
619                    ['name',       self.info['name_label']],
620                    ['vcpu_count', self.info['VCPUs_max']]]
621
622            for i in range(0, self.info['VCPUs_max']):
623                info = xc.vcpu_getinfo(self.domid, i)
624
625                sxpr.append(['vcpu',
626                             ['number',   i],
627                             ['online',   info['online']],
628                             ['blocked',  info['blocked']],
629                             ['running',  info['running']],
630                             ['cpu_time', info['cpu_time'] / 1e9],
631                             ['cpu',      info['cpu']],
632                             ['cpumap',   info['cpumap']]])
633
634            return sxpr
635
636        except RuntimeError, exn:
637            raise XendError(str(exn))
638
639
640    def getDomInfo(self):
641        return dom_get(self.domid)
642
643    #
644    # internal functions ... TODO: re-categorised
645    #
646
647    def _augmentInfo(self, priv):
648        """Augment self.info, as given to us through L{recreate}, with
649        values taken from the store.  This recovers those values known
650        to xend but not to the hypervisor.
651        """
652        augment_entries = XendConfig.LEGACY_XENSTORE_VM_PARAMS[:]
653        if priv:
654            augment_entries.remove('memory')
655            augment_entries.remove('maxmem')
656            augment_entries.remove('vcpus')
657            augment_entries.remove('vcpu_avail')
658
659        vm_config = self._readVMDetails([(k, XendConfig.LEGACY_CFG_TYPES[k])
660                                         for k in augment_entries])
661       
662        # make returned lists into a dictionary
663        vm_config = dict(zip(augment_entries, vm_config))
664       
665        for arg in augment_entries:
666            val = vm_config[arg]
667            if val != None:
668                if arg in XendConfig.LEGACY_CFG_TO_XENAPI_CFG:
669                    xapiarg = XendConfig.LEGACY_CFG_TO_XENAPI_CFG[arg]
670                    self.info[xapiarg] = val
671                elif arg == "memory":
672                    self.info["static_memory_min"] = val
673                elif arg == "maxmem":
674                    self.info["static_memory_max"] = val
675                else:
676                    self.info[arg] = val
677
678        # For dom0, we ignore any stored value for the vcpus fields, and
679        # read the current value from Xen instead.  This allows boot-time
680        # settings to take precedence over any entries in the store.
681        if priv:
682            xeninfo = dom_get(self.domid)
683            self.info['VCPUs_max'] = xeninfo['online_vcpus']
684            self.info['vcpu_avail'] = (1 << xeninfo['online_vcpus']) - 1
685
686        # read image value
687        image_sxp = self._readVm('image')
688        if image_sxp:
689            self.info.update_with_image_sxp(sxp.from_string(image_sxp))
690
691        # read devices
692        devices = []
693        for devclass in XendDevices.valid_devices():
694            devconfig = self.getDeviceController(devclass).configurations()
695            if devconfig:
696                devices.extend(devconfig)
697
698        if not self.info['devices'] and devices is not None:
699            for device in devices:
700                self.info.device_add(device[0], cfg_sxp = device)
701
702        self._update_consoles()
703
704    def _update_consoles(self):
705        if self.domid == None or self.domid == 0:
706            return
707
708        # Update VT100 port if it exists
709        self.console_port = self.readDom('console/port')
710        if self.console_port is not None:
711            serial_consoles = self.info.console_get_all('vt100')
712            if not serial_consoles:
713                cfg = self.info.console_add('vt100', self.console_port)
714                self._createDevice('console', cfg)
715            else:
716                console_uuid = serial_consoles[0].get('uuid')
717                self.info.console_update(console_uuid, 'location',
718                                         self.console_port)
719               
720
721        # Update VNC port if it exists and write to xenstore
722        vnc_port = self.readDom('console/vnc-port')
723        if vnc_port is not None:
724            for dev_uuid, (dev_type, dev_info) in self.info['devices'].items():
725                if dev_type == 'vfb':
726                    old_location = dev_info.get('location')
727                    listen_host = dev_info.get('vnclisten', 'localhost')
728                    new_location = '%s:%s' % (listen_host, str(vnc_port))
729                    if old_location == new_location:
730                        break
731
732                    dev_info['location'] = new_location
733                    self.info.device_update(dev_uuid, cfg_xenapi = dev_info)
734                    vfb_ctrl = self.getDeviceController('vfb')
735                    vfb_ctrl.reconfigureDevice(0, dev_info)
736                    break
737               
738    #
739    # Function to update xenstore /vm/*
740    #
741
742    def _readVm(self, *args):
743        return xstransact.Read(self.vmpath, *args)
744
745    def _writeVm(self, *args):
746        return xstransact.Write(self.vmpath, *args)
747
748    def _removeVm(self, *args):
749        return xstransact.Remove(self.vmpath, *args)
750
751    def _gatherVm(self, *args):
752        return xstransact.Gather(self.vmpath, *args)
753
754    def storeVm(self, *args):
755        return xstransact.Store(self.vmpath, *args)
756
757    #
758    # Function to update xenstore /dom/*
759    #
760
761    def readDom(self, *args):
762        return xstransact.Read(self.dompath, *args)
763
764    def gatherDom(self, *args):
765        return xstransact.Gather(self.dompath, *args)
766
767    def _writeDom(self, *args):
768        return xstransact.Write(self.dompath, *args)
769
770    def _removeDom(self, *args):
771        return xstransact.Remove(self.dompath, *args)
772
773    def storeDom(self, *args):
774        return xstransact.Store(self.dompath, *args)
775
776    def _recreateDom(self):
777        complete(self.dompath, lambda t: self._recreateDomFunc(t))
778
779    def _recreateDomFunc(self, t):
780        t.remove()
781        t.mkdir()
782        t.set_permissions({'dom' : self.domid})
783        t.write('vm', self.vmpath)
784
785    def _storeDomDetails(self):
786        to_store = {
787            'domid':              str(self.domid),
788            'vm':                 self.vmpath,
789            'name':               self.info['name_label'],
790            'console/limit':      str(xoptions.get_console_limit() * 1024),
791            'memory/target':      str(self.info['memory_dynamic_max'] / 1024),
792            }
793
794        def f(n, v):
795            if v is not None:
796                if type(v) == bool:
797                    to_store[n] = v and "1" or "0"
798                else:
799                    to_store[n] = str(v)
800
801        f('console/port',     self.console_port)
802        f('console/ring-ref', self.console_mfn)
803        f('store/port',       self.store_port)
804        f('store/ring-ref',   self.store_mfn)
805
806        if arch.type == "x86":
807            f('control/platform-feature-multiprocessor-suspend', True)
808
809        # elfnotes
810        for n, v in self.info.get_notes().iteritems():
811            n = n.lower().replace('_', '-')
812            if n == 'features':
813                for v in v.split('|'):
814                    v = v.replace('_', '-')
815                    if v.startswith('!'):
816                        f('image/%s/%s' % (n, v[1:]), False)
817                    else:
818                        f('image/%s/%s' % (n, v), True)
819            else:
820                f('image/%s' % n, v)
821
822        to_store.update(self._vcpuDomDetails())
823
824        log.debug("Storing domain details: %s", scrub_password(to_store))
825
826        self._writeDom(to_store)
827
828    def _vcpuDomDetails(self):
829        def availability(n):
830            if self.info['vcpu_avail'] & (1 << n):
831                return 'online'
832            else:
833                return 'offline'
834
835        result = {}
836        for v in range(0, self.info['VCPUs_max']):
837            result["cpu/%d/availability" % v] = availability(v)
838        return result
839
840    #
841    # xenstore watches
842    #
843
844    def _registerWatches(self):
845        """Register a watch on this VM's entries in the store, and the
846        domain's control/shutdown node, so that when they are changed
847        externally, we keep up to date.  This should only be called by {@link
848        #create}, {@link #recreate}, or {@link #restore}, once the domain's
849        details have been written, but before the new instance is returned."""
850        self.vmWatch = xswatch(self.vmpath, self._storeChanged)
851        self.shutdownWatch = xswatch(self.dompath + '/control/shutdown',
852                                     self._handleShutdownWatch)
853
854    def _storeChanged(self, _):
855        log.trace("XendDomainInfo.storeChanged");
856
857        changed = False
858
859        # Check whether values in the configuration have
860        # changed in Xenstore.
861       
862        cfg_vm = ['name', 'on_poweroff', 'on_reboot', 'on_crash',
863                  'rtc/timeoffset']
864       
865        vm_details = self._readVMDetails([(k,XendConfig.LEGACY_CFG_TYPES[k])
866                                           for k in cfg_vm])
867
868        # convert two lists into a python dictionary
869        vm_details = dict(zip(cfg_vm, vm_details))
870
871        if vm_details['rtc/timeoffset'] == None:
872            vm_details['rtc/timeoffset'] = "0"
873
874        for arg, val in vm_details.items():
875            if arg in XendConfig.LEGACY_CFG_TO_XENAPI_CFG:
876                xapiarg = XendConfig.LEGACY_CFG_TO_XENAPI_CFG[arg]
877                if val != None and val != self.info[xapiarg]:
878                    self.info[xapiarg] = val
879                    changed = True
880            elif arg == "memory":
881                if val != None and val != self.info["static_memory_min"]:
882                    self.info["static_memory_min"] = val
883                    changed = True
884            elif arg == "maxmem":
885                if val != None and val != self.info["static_memory_max"]:
886                    self.info["static_memory_max"] = val
887                    changed = True
888
889        # Check whether image definition has been updated
890        image_sxp = self._readVm('image')
891        if image_sxp and image_sxp != sxp.to_string(self.info.image_sxpr()):
892            self.info.update_with_image_sxp(sxp.from_string(image_sxp))
893            changed = True
894
895        # Check if the rtc offset has changes
896        if vm_details.get("rtc/timeoffset", 0) != self.info["platform"].get("rtc_timeoffset", 0):
897            self.info["platform"]["rtc_timeoffset"] = vm_details.get("rtc/timeoffset", 0)
898            changed = True
899 
900        if changed:
901            # Update the domain section of the store, as this contains some
902            # parameters derived from the VM configuration.
903            self._storeDomDetails()
904
905        return 1
906
907    def _handleShutdownWatch(self, _):
908        log.debug('XendDomainInfo.handleShutdownWatch')
909       
910        reason = self.readDom('control/shutdown')
911
912        if reason and reason != 'suspend':
913            sst = self.readDom('xend/shutdown_start_time')
914            now = time.time()
915            if sst:
916                self.shutdownStartTime = float(sst)
917                timeout = float(sst) + SHUTDOWN_TIMEOUT - now
918            else:
919                self.shutdownStartTime = now
920                self.storeDom('xend/shutdown_start_time', now)
921                timeout = SHUTDOWN_TIMEOUT
922
923            log.trace(
924                "Scheduling refreshShutdown on domain %d in %ds.",
925                self.domid, timeout)
926            threading.Timer(timeout, self.refreshShutdown).start()
927           
928        return True
929
930
931    #
932    # Public Attributes for the VM
933    #
934
935
936    def getDomid(self):
937        return self.domid
938
939    def setName(self, name):
940        self._checkName(name)
941        self.info['name_label'] = name
942        self.storeVm("name", name)
943
944    def getName(self):
945        return self.info['name_label']
946
947    def getDomainPath(self):
948        return self.dompath
949
950    def getShutdownReason(self):
951        return self.readDom('control/shutdown')
952
953    def getStorePort(self):
954        """For use only by image.py and XendCheckpoint.py."""
955        return self.store_port
956
957    def getConsolePort(self):
958        """For use only by image.py and XendCheckpoint.py"""
959        return self.console_port
960
961    def getFeatures(self):
962        """For use only by image.py."""
963        return self.info['features']
964
965    def getVCpuCount(self):
966        return self.info['VCPUs_max']
967
968    def setVCpuCount(self, vcpus):
969        if vcpus <= 0:
970            raise XendError('Invalid VCPUs')
971       
972        self.info['vcpu_avail'] = (1 << vcpus) - 1
973        if self.domid >= 0:
974            self.storeVm('vcpu_avail', self.info['vcpu_avail'])
975            # update dom differently depending on whether we are adjusting
976            # vcpu number up or down, otherwise _vcpuDomDetails does not
977            # disable the vcpus
978            if self.info['VCPUs_max'] > vcpus:
979                # decreasing
980                self._writeDom(self._vcpuDomDetails())
981                self.info['VCPUs_live'] = vcpus
982            else:
983                # same or increasing
984                self.info['VCPUs_live'] = vcpus
985                self._writeDom(self._vcpuDomDetails())
986        else:
987            self.info['VCPUs_live'] = vcpus
988            xen.xend.XendDomain.instance().managed_config_save(self)
989        log.info("Set VCPU count on domain %s to %d", self.info['name_label'],
990                 vcpus)
991
992    def getLabel(self):
993        return security.get_security_info(self.info, 'label')
994
995    def getMemoryTarget(self):
996        """Get this domain's target memory size, in KB."""
997        return self.info['memory_dynamic_max'] / 1024
998
999    def getMemoryMaximum(self):
1000        """Get this domain's maximum memory size, in KB."""
1001        # remember, info now stores memory in bytes
1002        return self.info['memory_static_max'] / 1024
1003
1004    def getResume(self):
1005        return str(self._resume)
1006
1007    def getCap(self):
1008        return self.info.get('cpu_cap', 0)
1009
1010    def getWeight(self):
1011        return self.info.get('cpu_weight', 256)
1012
1013    def setResume(self, state):
1014        self._resume = state
1015
1016    def getRestartCount(self):
1017        return self._readVm('xend/restart_count')
1018
1019    def refreshShutdown(self, xeninfo = None):
1020        """ Checks the domain for whether a shutdown is required.
1021
1022        Called from XendDomainInfo and also image.py for HVM images.
1023        """
1024       
1025        # If set at the end of this method, a restart is required, with the
1026        # given reason.  This restart has to be done out of the scope of
1027        # refresh_shutdown_lock.
1028        restart_reason = None
1029
1030        self.refresh_shutdown_lock.acquire()
1031        try:
1032            if xeninfo is None:
1033                xeninfo = dom_get(self.domid)
1034                if xeninfo is None:
1035                    # The domain no longer exists.  This will occur if we have
1036                    # scheduled a timer to check for shutdown timeouts and the
1037                    # shutdown succeeded.  It will also occur if someone
1038                    # destroys a domain beneath us.  We clean up the domain,
1039                    # just in case, but we can't clean up the VM, because that
1040                    # VM may have migrated to a different domain on this
1041                    # machine.
1042                    self.cleanupDomain()
1043                    self._stateSet(DOM_STATE_HALTED)
1044                    return
1045
1046            if xeninfo['dying']:
1047                # Dying means that a domain has been destroyed, but has not
1048                # yet been cleaned up by Xen.  This state could persist
1049                # indefinitely if, for example, another domain has some of its
1050                # pages mapped.  We might like to diagnose this problem in the
1051                # future, but for now all we do is make sure that it's not us
1052                # holding the pages, by calling cleanupDomain.  We can't
1053                # clean up the VM, as above.
1054                self.cleanupDomain()
1055                self._stateSet(DOM_STATE_SHUTDOWN)
1056                return
1057
1058            elif xeninfo['crashed']:
1059                if self.readDom('xend/shutdown_completed'):
1060                    # We've seen this shutdown already, but we are preserving
1061                    # the domain for debugging.  Leave it alone.
1062                    return
1063
1064                log.warn('Domain has crashed: name=%s id=%d.',
1065                         self.info['name_label'], self.domid)
1066                self._writeVm(LAST_SHUTDOWN_REASON, 'crash')
1067
1068                if xoptions.get_enable_dump():
1069                    try:
1070                        self.dumpCore()
1071                    except XendError:
1072                        # This error has been logged -- there's nothing more
1073                        # we can do in this context.
1074                        pass
1075
1076                restart_reason = 'crash'
1077                self._stateSet(DOM_STATE_HALTED)
1078
1079            elif xeninfo['shutdown']:
1080                self._stateSet(DOM_STATE_SHUTDOWN)
1081                if self.readDom('xend/shutdown_completed'):
1082                    # We've seen this shutdown already, but we are preserving
1083                    # the domain for debugging.  Leave it alone.
1084                    return
1085
1086                else:
1087                    reason = shutdown_reason(xeninfo['shutdown_reason'])
1088
1089                    log.info('Domain has shutdown: name=%s id=%d reason=%s.',
1090                             self.info['name_label'], self.domid, reason)
1091                    self._writeVm(LAST_SHUTDOWN_REASON, reason)
1092
1093                    self._clearRestart()
1094
1095                    if reason == 'suspend':
1096                        self._stateSet(DOM_STATE_SUSPENDED)
1097                        # Don't destroy the domain.  XendCheckpoint will do
1098                        # this once it has finished.  However, stop watching
1099                        # the VM path now, otherwise we will end up with one
1100                        # watch for the old domain, and one for the new.
1101                        self._unwatchVm()
1102                    elif reason in ('poweroff', 'reboot'):
1103                        restart_reason = reason
1104                    else:
1105                        self.destroy()
1106
1107            elif self.dompath is None:
1108                # We have yet to manage to call introduceDomain on this
1109                # domain.  This can happen if a restore is in progress, or has
1110                # failed.  Ignore this domain.
1111                pass
1112            else:
1113                # Domain is alive.  If we are shutting it down, then check
1114                # the timeout on that, and destroy it if necessary.
1115                if xeninfo['paused']:
1116                    self._stateSet(DOM_STATE_PAUSED)
1117                else:
1118                    self._stateSet(DOM_STATE_RUNNING)
1119                   
1120                if self.shutdownStartTime:
1121                    timeout = (SHUTDOWN_TIMEOUT - time.time() +
1122                               self.shutdownStartTime)
1123                    if timeout < 0:
1124                        log.info(
1125                            "Domain shutdown timeout expired: name=%s id=%s",
1126                            self.info['name_label'], self.domid)
1127                        self.destroy()
1128        finally:
1129            self.refresh_shutdown_lock.release()
1130
1131        if restart_reason:
1132            threading.Thread(target = self._maybeRestart,
1133                             args = (restart_reason,)).start()
1134
1135
1136    #
1137    # Restart functions - handling whether we come back up on shutdown.
1138    #
1139
1140    def _clearRestart(self):
1141        self._removeDom("xend/shutdown_start_time")
1142
1143
1144    def _maybeRestart(self, reason):
1145        # Dispatch to the correct method based upon the configured on_{reason}
1146        # behaviour.
1147        actions =  {"destroy"        : self.destroy,
1148                    "restart"        : self._restart,
1149                    "preserve"       : self._preserve,
1150                    "rename-restart" : self._renameRestart}
1151
1152        action_conf = {
1153            'poweroff': 'actions_after_shutdown',
1154            'reboot': 'actions_after_reboot',
1155            'crash': 'actions_after_crash',
1156        }
1157
1158        action_target = self.info.get(action_conf.get(reason))
1159        func = actions.get(action_target, None)
1160        if func and callable(func):
1161            func()
1162        else:
1163            self.destroy() # default to destroy
1164
1165    def _renameRestart(self):
1166        self._restart(True)
1167
1168    def _restart(self, rename = False):
1169        """Restart the domain after it has exited.
1170
1171        @param rename True if the old domain is to be renamed and preserved,
1172        False if it is to be destroyed.
1173        """
1174        from xen.xend import XendDomain
1175       
1176        if self._readVm(RESTART_IN_PROGRESS):
1177            log.error('Xend failed during restart of domain %s.  '
1178                      'Refusing to restart to avoid loops.',
1179                      str(self.domid))
1180            self.destroy()
1181            return
1182
1183        old_domid = self.domid
1184        self._writeVm(RESTART_IN_PROGRESS, 'True')
1185
1186        now = time.time()
1187        rst = self._readVm('xend/previous_restart_time')
1188        if rst:
1189            rst = float(rst)
1190            timeout = now - rst
1191            if timeout < MINIMUM_RESTART_TIME:
1192                log.error(
1193                    'VM %s restarting too fast (%f seconds since the last '
1194                    'restart).  Refusing to restart to avoid loops.',
1195                    self.info['name_label'], timeout)
1196                self.destroy()
1197                return
1198
1199        self._writeVm('xend/previous_restart_time', str(now))
1200
1201        try:
1202            if rename:
1203                self._preserveForRestart()
1204            else:
1205                self._unwatchVm()
1206                self.destroyDomain()
1207
1208            # new_dom's VM will be the same as this domain's VM, except where
1209            # the rename flag has instructed us to call preserveForRestart.
1210            # In that case, it is important that we remove the
1211            # RESTART_IN_PROGRESS node from the new domain, not the old one,
1212            # once the new one is available.
1213
1214            new_dom = None
1215            try:
1216                new_dom = XendDomain.instance().domain_create_from_dict(
1217                    self.info)
1218                new_dom.unpause()
1219                rst_cnt = self._readVm('xend/restart_count')
1220                rst_cnt = int(rst_cnt) + 1
1221                self._writeVm('xend/restart_count', str(rst_cnt))
1222                new_dom._removeVm(RESTART_IN_PROGRESS)
1223            except:
1224                if new_dom:
1225                    new_dom._removeVm(RESTART_IN_PROGRESS)
1226                    new_dom.destroy()
1227                else:
1228                    self._removeVm(RESTART_IN_PROGRESS)
1229                raise
1230        except:
1231            log.exception('Failed to restart domain %s.', str(old_domid))
1232
1233    def _preserveForRestart(self):
1234        """Preserve a domain that has been shut down, by giving it a new UUID,
1235        cloning the VM details, and giving it a new name.  This allows us to
1236        keep this domain for debugging, but restart a new one in its place
1237        preserving the restart semantics (name and UUID preserved).
1238        """
1239       
1240        new_uuid = uuid.createString()
1241        new_name = 'Domain-%s' % new_uuid
1242        log.info("Renaming dead domain %s (%d, %s) to %s (%s).",
1243                 self.info['name_label'], self.domid, self.info['uuid'],
1244                 new_name, new_uuid)
1245        self._unwatchVm()
1246        self._releaseDevices()
1247        self.info['name_label'] = new_name
1248        self.info['uuid'] = new_uuid
1249        self.vmpath = XS_VMROOT + new_uuid
1250        self._storeVmDetails()
1251        self._preserve()
1252
1253
1254    def _preserve(self):
1255        log.info("Preserving dead domain %s (%d).", self.info['name_label'],
1256                 self.domid)
1257        self._unwatchVm()
1258        self.storeDom('xend/shutdown_completed', 'True')
1259        self._stateSet(DOM_STATE_HALTED)
1260
1261    #
1262    # Debugging ..
1263    #
1264
1265    def dumpCore(self, corefile = None):
1266        """Create a core dump for this domain.
1267
1268        @raise: XendError if core dumping failed.
1269        """
1270       
1271        try:
1272            if not corefile:
1273                this_time = time.strftime("%Y-%m%d-%H%M.%S", time.localtime())
1274                corefile = "/var/xen/dump/%s-%s.%s.core" % (this_time,
1275                                  self.info['name_label'], self.domid)
1276               
1277            if os.path.isdir(corefile):
1278                raise XendError("Cannot dump core in a directory: %s" %
1279                                corefile)
1280           
1281            xc.domain_dumpcore(self.domid, corefile)
1282        except RuntimeError, ex:
1283            corefile_incomp = corefile+'-incomplete'
1284            os.rename(corefile, corefile_incomp)
1285            log.exception("XendDomainInfo.dumpCore failed: id = %s name = %s",
1286                          self.domid, self.info['name_label'])
1287            raise XendError("Failed to dump core: %s" %  str(ex))
1288
1289    #
1290    # Device creation/deletion functions
1291    #
1292
1293    def _createDevice(self, deviceClass, devConfig):
1294        return self.getDeviceController(deviceClass).createDevice(devConfig)
1295
1296    def _waitForDevice(self, deviceClass, devid):
1297        return self.getDeviceController(deviceClass).waitForDevice(devid)
1298
1299    def _waitForDeviceUUID(self, dev_uuid):
1300        deviceClass, config = self.info['devices'].get(dev_uuid)
1301        self._waitForDevice(deviceClass, config['devid'])
1302
1303    def _reconfigureDevice(self, deviceClass, devid, devconfig):
1304        return self.getDeviceController(deviceClass).reconfigureDevice(
1305            devid, devconfig)
1306
1307    def _createDevices(self):
1308        """Create the devices for a vm.
1309
1310        @raise: VmError for invalid devices
1311        """
1312        ordered_refs = self.info.ordered_device_refs()
1313        for dev_uuid in ordered_refs:
1314            devclass, config = self.info['devices'][dev_uuid]
1315            if devclass in XendDevices.valid_devices():
1316                log.info("createDevice: %s : %s" % (devclass, scrub_password(config)))
1317                dev_uuid = config.get('uuid')
1318                devid = self._createDevice(devclass, config)
1319               
1320                # store devid in XendConfig for caching reasons
1321                if dev_uuid in self.info['devices']:
1322                    self.info['devices'][dev_uuid][1]['devid'] = devid
1323
1324        if self.image:
1325            self.image.createDeviceModel()
1326
1327    def _releaseDevices(self, suspend = False):
1328        """Release all domain's devices.  Nothrow guarantee."""
1329        if suspend and self.image:
1330            self.image.destroy(suspend)
1331            return
1332
1333        while True:
1334            t = xstransact("%s/device" % self.dompath)
1335            for devclass in XendDevices.valid_devices():
1336                for dev in t.list(devclass):
1337                    try:
1338                        t.remove(dev)
1339                    except:
1340                        # Log and swallow any exceptions in removal --
1341                        # there's nothing more we can do.
1342                        log.exception(
1343                           "Device release failed: %s; %s; %s",
1344                           self.info['name_label'], devclass, dev)
1345            if t.commit():
1346                break
1347
1348    def getDeviceController(self, name):
1349        """Get the device controller for this domain, and if it
1350        doesn't exist, create it.
1351
1352        @param name: device class name
1353        @type name: string
1354        @rtype: subclass of DevController
1355        """
1356        if name not in self._deviceControllers:
1357            devController = XendDevices.make_controller(name, self)
1358            if not devController:
1359                raise XendError("Unknown device type: %s" % name)
1360            self._deviceControllers[name] = devController
1361   
1362        return self._deviceControllers[name]
1363
1364    #
1365    # Migration functions (public)
1366    #
1367
1368    def testMigrateDevices(self, network, dst):
1369        """ Notify all device about intention of migration
1370        @raise: XendError for a device that cannot be migrated
1371        """
1372        for (n, c) in self.info.all_devices_sxpr():
1373            rc = self.migrateDevice(n, c, network, dst, DEV_MIGRATE_TEST)
1374            if rc != 0:
1375                raise XendError("Device of type '%s' refuses migration." % n)
1376
1377    def migrateDevices(self, network, dst, step, domName=''):
1378        """Notify the devices about migration
1379        """
1380        ctr = 0
1381        try:
1382            for (dev_type, dev_conf) in self.info.all_devices_sxpr():
1383                self.migrateDevice(dev_type, dev_conf, network, dst,
1384                                   step, domName)
1385                ctr = ctr + 1
1386        except:
1387            for dev_type, dev_conf in self.info.all_devices_sxpr():
1388                if ctr == 0:
1389                    step = step - 1
1390                ctr = ctr - 1
1391                self._recoverMigrateDevice(dev_type, dev_conf, network,
1392                                           dst, step, domName)
1393            raise
1394
1395    def migrateDevice(self, deviceClass, deviceConfig, network, dst,
1396                      step, domName=''):
1397        return self.getDeviceController(deviceClass).migrate(deviceConfig,
1398                                        network, dst, step, domName)
1399
1400    def _recoverMigrateDevice(self, deviceClass, deviceConfig, network,
1401                             dst, step, domName=''):
1402        return self.getDeviceController(deviceClass).recover_migrate(
1403                     deviceConfig, network, dst, step, domName)
1404
1405
1406    ## private:
1407
1408    def _constructDomain(self):
1409        """Construct the domain.
1410
1411        @raise: VmError on error
1412        """
1413
1414        log.debug('XendDomainInfo.constructDomain')
1415
1416        self.shutdownStartTime = None
1417
1418        hvm = self.info.is_hvm()
1419        if hvm:
1420            info = xc.xeninfo()
1421            if 'hvm' not in info['xen_caps']:
1422                raise VmError("HVM guest support is unavailable: is VT/AMD-V "
1423                              "supported by your CPU and enabled in your "
1424                              "BIOS?")
1425
1426        # Hack to pre-reserve some memory for initial domain creation.
1427        # There is an implicit memory overhead for any domain creation. This
1428        # overhead is greater for some types of domain than others. For
1429        # example, an x86 HVM domain will have a default shadow-pagetable
1430        # allocation of 1MB. We free up 2MB here to be on the safe side.
1431        balloon.free(2*1024) # 2MB should be plenty
1432
1433        self.domid = xc.domain_create(
1434            domid = 0,
1435            ssidref = security.get_security_info(self.info, 'ssidref'),
1436            handle = uuid.fromString(self.info['uuid']),
1437            hvm = int(hvm))
1438
1439        if self.domid < 0:
1440            raise VmError('Creating domain failed: name=%s' %
1441                          self.info['name_label'])
1442
1443        self.dompath = GetDomainPath(self.domid)
1444
1445        self._recreateDom()
1446
1447        # Set maximum number of vcpus in domain
1448        xc.domain_max_vcpus(self.domid, int(self.info['VCPUs_max']))
1449
1450        # register the domain in the list
1451        from xen.xend import XendDomain
1452        XendDomain.instance().add_domain(self)
1453
1454    def _introduceDomain(self):
1455        assert self.domid is not None
1456        assert self.store_mfn is not None
1457        assert self.store_port is not None
1458
1459        try:
1460            IntroduceDomain(self.domid, self.store_mfn, self.store_port)
1461        except RuntimeError, exn:
1462            raise XendError(str(exn))
1463
1464
1465    def _initDomain(self):
1466        log.debug('XendDomainInfo.initDomain: %s %s',
1467                  self.domid,
1468                  self.info['cpu_weight'])
1469
1470        self._configureBootloader()
1471
1472        try:
1473            self.image = image.create(self, self.info)
1474
1475            localtime = self.info.get('platform_localtime', False)
1476            if localtime:
1477                xc.domain_set_time_offset(self.domid)
1478
1479            xc.domain_setcpuweight(self.domid, self.info['cpu_weight'])
1480
1481            # repin domain vcpus if a restricted cpus list is provided
1482            # this is done prior to memory allocation to aide in memory
1483            # distribution for NUMA systems.
1484            if self.info['cpus'] is not None and len(self.info['cpus']) > 0:
1485                for v in range(0, self.info['VCPUs_max']):
1486                    xc.vcpu_setaffinity(self.domid, v, self.info['cpus'])
1487
1488            # Use architecture- and image-specific calculations to determine
1489            # the various headrooms necessary, given the raw configured
1490            # values. maxmem, memory, and shadow are all in KiB.
1491            # but memory_static_max etc are all stored in bytes now.
1492            memory = self.image.getRequiredAvailableMemory(
1493                self.info['memory_dynamic_max'] / 1024)
1494            maxmem = self.image.getRequiredAvailableMemory(
1495                self.info['memory_static_max'] / 1024)
1496            shadow = self.image.getRequiredShadowMemory(
1497                self.info['shadow_memory'] / 1024,
1498                self.info['memory_static_max'] / 1024)
1499
1500            log.debug("_initDomain:shadow_memory=0x%x, memory_static_max=0x%x, memory_static_min=0x%x.", self.info['shadow_memory'], self.info['memory_static_max'], self.info['memory_static_min'],)
1501            # Round shadow up to a multiple of a MiB, as shadow_mem_control
1502            # takes MiB and we must not round down and end up under-providing.
1503            shadow = ((shadow + 1023) / 1024) * 1024
1504
1505            # set memory limit
1506            xc.domain_setmaxmem(self.domid, maxmem)
1507
1508            # Make sure there's enough RAM available for the domain
1509            balloon.free(memory + shadow)
1510
1511            # Set up the shadow memory
1512            shadow_cur = xc.shadow_mem_control(self.domid, shadow / 1024)
1513            self.info['shadow_memory'] = shadow_cur
1514
1515            self._createChannels()
1516
1517            channel_details = self.image.createImage()
1518
1519            self.store_mfn = channel_details['store_mfn']
1520            if 'console_mfn' in channel_details:
1521                self.console_mfn = channel_details['console_mfn']
1522            if 'notes' in channel_details:
1523                self.info.set_notes(channel_details['notes'])
1524
1525            self._introduceDomain()
1526
1527            self._createDevices()
1528
1529            self.image.cleanupBootloading()
1530
1531            self.info['start_time'] = time.time()
1532
1533            self._stateSet(DOM_STATE_RUNNING)
1534        except VmError, exn:
1535            log.exception("XendDomainInfo.initDomain: exception occurred")
1536            if self.image:
1537                self.image.cleanupBootloading()
1538            raise exn
1539        except RuntimeError, exn:
1540            log.exception("XendDomainInfo.initDomain: exception occurred")
1541            if self.image:
1542                self.image.cleanupBootloading()
1543            raise VmError(str(exn))
1544
1545
1546    def cleanupDomain(self):
1547        """Cleanup domain resources; release devices.  Idempotent.  Nothrow
1548        guarantee."""
1549
1550        self.refresh_shutdown_lock.acquire()
1551        try:
1552            self.unwatchShutdown()
1553            self._releaseDevices()
1554            bootloader_tidy(self)
1555
1556            if self.image:
1557                try:
1558                    self.image.destroy()
1559                except:
1560                    log.exception(
1561                        "XendDomainInfo.cleanup: image.destroy() failed.")
1562                self.image = None
1563
1564            try:
1565                self._removeDom()
1566            except:
1567                log.exception("Removing domain path failed.")
1568
1569            self._stateSet(DOM_STATE_HALTED)
1570        finally:
1571            self.refresh_shutdown_lock.release()
1572
1573
1574    def unwatchShutdown(self):
1575        """Remove the watch on the domain's control/shutdown node, if any.
1576        Idempotent.  Nothrow guarantee.  Expects to be protected by the
1577        refresh_shutdown_lock."""
1578
1579        try:
1580            try:
1581                if self.shutdownWatch:
1582                    self.shutdownWatch.unwatch()
1583            finally:
1584                self.shutdownWatch = None
1585        except:
1586            log.exception("Unwatching control/shutdown failed.")
1587
1588    def waitForShutdown(self):
1589        self.state_updated.acquire()
1590        try:
1591            while self._stateGet() in (DOM_STATE_RUNNING,DOM_STATE_PAUSED):
1592                self.state_updated.wait()
1593        finally:
1594            self.state_updated.release()
1595
1596    #
1597    # TODO: recategorise - called from XendCheckpoint
1598    #
1599
1600    def completeRestore(self, store_mfn, console_mfn):
1601
1602        log.debug("XendDomainInfo.completeRestore")
1603
1604        self.store_mfn = store_mfn
1605        self.console_mfn = console_mfn
1606
1607        self._introduceDomain()
1608        if self.info.is_hvm():
1609            self.image = image.create(self, self.info)
1610            if self.image:
1611                self.image.createDeviceModel(True)
1612        self._storeDomDetails()
1613        self._registerWatches()
1614        self.refreshShutdown()
1615
1616        log.debug("XendDomainInfo.completeRestore done")
1617
1618
1619    def _endRestore(self):
1620        self.setResume(False)
1621
1622    #
1623    # VM Destroy
1624    #
1625
1626    def _prepare_phantom_paths(self):
1627        # get associated devices to destroy
1628        # build list of phantom devices to be removed after normal devices
1629        plist = []
1630        if self.domid is not None:
1631            from xen.xend.xenstore.xstransact import xstransact
1632            t = xstransact("%s/device/vbd" % GetDomainPath(self.domid))
1633            for dev in t.list():
1634                backend_phantom_vbd = xstransact.Read("%s/device/vbd/%s/phantom_vbd" \
1635                                      % (self.dompath, dev))
1636                if backend_phantom_vbd is not None:
1637                    frontend_phantom_vbd =  xstransact.Read("%s/frontend" \
1638                                      % backend_phantom_vbd)
1639                    plist.append(backend_phantom_vbd)
1640                    plist.append(frontend_phantom_vbd)
1641        return plist
1642
1643    def _cleanup_phantom_devs(self, plist):
1644        # remove phantom devices
1645        if not plist == []:
1646            time.sleep(2)
1647        for paths in plist:
1648            if paths.find('backend') != -1:
1649                from xen.xend.server import DevController
1650                # Modify online status /before/ updating state (latter is watched by
1651                # drivers, so this ordering avoids a race).
1652                xstransact.Write(paths, 'online', "0")
1653                xstransact.Write(paths, 'state', str(DevController.xenbusState['Closing']))
1654            # force
1655            xstransact.Remove(paths)
1656
1657    def destroy(self):
1658        """Cleanup VM and destroy domain.  Nothrow guarantee."""
1659
1660        log.debug("XendDomainInfo.destroy: domid=%s", str(self.domid))
1661
1662        paths = self._prepare_phantom_paths()
1663
1664        self._cleanupVm()
1665        if self.dompath is not None:
1666            self.destroyDomain()
1667
1668        self._cleanup_phantom_devs(paths)
1669
1670        if "transient" in self.info["other_config"] \
1671           and bool(self.info["other_config"]["transient"]):
1672            from xen.xend import XendDomain
1673            XendDomain.instance().domain_delete_by_dominfo(self)
1674
1675
1676    def destroyDomain(self):
1677        log.debug("XendDomainInfo.destroyDomain(%s)", str(self.domid))
1678
1679        paths = self._prepare_phantom_paths()
1680
1681        try:
1682            if self.domid is not None:
1683                xc.domain_destroy(self.domid)
1684                self.domid = None
1685                for state in DOM_STATES_OLD:
1686                    self.info[state] = 0
1687                self._stateSet(DOM_STATE_HALTED)
1688        except:
1689            log.exception("XendDomainInfo.destroy: xc.domain_destroy failed.")
1690
1691        from xen.xend import XendDomain
1692        XendDomain.instance().remove_domain(self)
1693
1694        self.cleanupDomain()
1695        self._cleanup_phantom_devs(paths)
1696
1697
1698    def resumeDomain(self):
1699        log.debug("XendDomainInfo.resumeDomain(%s)", str(self.domid))
1700
1701        if self.domid is None:
1702            return
1703        try:
1704            # could also fetch a parsed note from xenstore
1705            fast = self.info.get_notes().get('SUSPEND_CANCEL') and 1 or 0
1706            if not fast:
1707                self._releaseDevices()
1708                self.testDeviceComplete()
1709                self.testvifsComplete()
1710                log.debug("XendDomainInfo.resumeDomain: devices released")
1711
1712                self._resetChannels()
1713
1714                self._removeDom('control/shutdown')
1715                self._removeDom('device-misc/vif/nextDeviceID')
1716
1717                self._createChannels()
1718                self._introduceDomain()
1719                self._storeDomDetails()
1720
1721                self._createDevices()
1722                log.debug("XendDomainInfo.resumeDomain: devices created")
1723
1724            xc.domain_resume(self.domid, fast)
1725            ResumeDomain(self.domid)
1726        except:
1727            log.exception("XendDomainInfo.resume: xc.domain_resume failed on domain %s." % (str(self.domid)))
1728
1729    #
1730    # Channels for xenstore and console
1731    #
1732
1733    def _createChannels(self):
1734        """Create the channels to the domain.
1735        """
1736        self.store_port = self._createChannel()
1737        self.console_port = self._createChannel()
1738
1739
1740    def _createChannel(self):
1741        """Create an event channel to the domain.
1742        """
1743        try:
1744            if self.domid != None:
1745                return xc.evtchn_alloc_unbound(domid = self.domid,
1746                                               remote_dom = 0)
1747        except:
1748            log.exception("Exception in alloc_unbound(%s)", str(self.domid))
1749            raise
1750
1751    def _resetChannels(self):
1752        """Reset all event channels in the domain.
1753        """
1754        try:
1755            if self.domid != None:
1756                return xc.evtchn_reset(dom = self.domid)
1757        except:
1758            log.exception("Exception in evtcnh_reset(%s)", str(self.domid))
1759            raise
1760
1761
1762    #
1763    # Bootloader configuration
1764    #
1765
1766    def _configureBootloader(self):
1767        """Run the bootloader if we're configured to do so."""
1768
1769        blexec          = self.info['PV_bootloader']
1770        bootloader_args = self.info['PV_bootloader_args']
1771        kernel          = self.info['PV_kernel']
1772        ramdisk         = self.info['PV_ramdisk']
1773        args            = self.info['PV_args']
1774        boot            = self.info['HVM_boot_policy']
1775
1776        if boot:
1777            # HVM booting.
1778            pass
1779        elif not blexec and kernel:
1780            # Boot from dom0.  Nothing left to do -- the kernel and ramdisk
1781            # will be picked up by image.py.
1782            pass
1783        else:
1784            # Boot using bootloader
1785            if not blexec or blexec == 'pygrub':
1786                blexec = osdep.pygrub_path
1787
1788            blcfg = None
1789            disks = [x for x in self.info['vbd_refs']
1790                     if self.info['devices'][x][1]['bootable']]
1791
1792            if not disks:
1793                msg = "Had a bootloader specified, but no disks are bootable"
1794                log.error(msg)
1795                raise VmError(msg)
1796
1797            devinfo = self.info['devices'][disks[0]]
1798            devtype = devinfo[0]
1799            disk = devinfo[1]['uname']
1800
1801            fn = blkdev_uname_to_file(disk)
1802            taptype = blkdev_uname_to_taptype(disk)
1803            mounted = devtype == 'tap' and taptype != 'aio' and taptype != 'sync' and not os.stat(fn).st_rdev
1804            if mounted:
1805                # This is a file, not a device.  pygrub can cope with a
1806                # file if it's raw, but if it's QCOW or other such formats
1807                # used through blktap, then we need to mount it first.
1808
1809                log.info("Mounting %s on %s." %
1810                         (fn, BOOTLOADER_LOOPBACK_DEVICE))
1811
1812                vbd = {
1813                    'mode': 'RO',
1814                    'device': BOOTLOADER_LOOPBACK_DEVICE,
1815                    }
1816
1817                from xen.xend import XendDomain
1818                dom0 = XendDomain.instance().privilegedDomain()
1819                dom0._waitForDeviceUUID(dom0.create_vbd(vbd, disk))
1820                fn = BOOTLOADER_LOOPBACK_DEVICE
1821
1822            try:
1823                blcfg = bootloader(blexec, fn, self, False,
1824                                   bootloader_args, kernel, ramdisk, args)
1825            finally:
1826                if mounted:
1827                    log.info("Unmounting %s from %s." %
1828                             (fn, BOOTLOADER_LOOPBACK_DEVICE))
1829
1830                    dom0.destroyDevice('tap', BOOTLOADER_LOOPBACK_DEVICE)
1831
1832            if blcfg is None:
1833                msg = "Had a bootloader specified, but can't find disk"
1834                log.error(msg)
1835                raise VmError(msg)
1836       
1837            self.info.update_with_image_sxp(blcfg, True)
1838
1839
1840    #
1841    # VM Functions
1842    #
1843
1844    def _readVMDetails(self, params):
1845        """Read the specified parameters from the store.
1846        """
1847        try:
1848            return self._gatherVm(*params)
1849        except ValueError:
1850            # One of the int/float entries in params has a corresponding store
1851            # entry that is invalid.  We recover, because older versions of
1852            # Xend may have put the entry there (memory/target, for example),
1853            # but this is in general a bad situation to have reached.
1854            log.exception(
1855                "Store corrupted at %s!  Domain %d's configuration may be "
1856                "affected.", self.vmpath, self.domid)
1857            return []
1858
1859    def _cleanupVm(self):
1860        """Cleanup VM resources.  Idempotent.  Nothrow guarantee."""
1861
1862        self._unwatchVm()
1863
1864        try:
1865            self._removeVm()
1866        except:
1867            log.exception("Removing VM path failed.")
1868
1869
1870    def checkLiveMigrateMemory(self):
1871        """ Make sure there's enough memory to migrate this domain """
1872        overhead_kb = 0
1873        if arch.type == "x86":
1874            # 1MB per vcpu plus 4Kib/Mib of RAM.  This is higher than
1875            # the minimum that Xen would allocate if no value were given.
1876            overhead_kb = self.info['VCPUs_max'] * 1024 + \
1877                          (self.info['memory_static_max'] / 1024 / 1024) * 4
1878            overhead_kb = ((overhead_kb + 1023) / 1024) * 1024
1879            # The domain might already have some shadow memory
1880            overhead_kb -= xc.shadow_mem_control(self.domid) * 1024
1881        if overhead_kb > 0:
1882            balloon.free(overhead_kb)
1883
1884    def _unwatchVm(self):
1885        """Remove the watch on the VM path, if any.  Idempotent.  Nothrow
1886        guarantee."""
1887        try:
1888            try:
1889                if self.vmWatch:
1890                    self.vmWatch.unwatch()
1891            finally:
1892                self.vmWatch = None
1893        except:
1894            log.exception("Unwatching VM path failed.")
1895
1896    def testDeviceComplete(self):
1897        """ For Block IO migration safety we must ensure that
1898        the device has shutdown correctly, i.e. all blocks are
1899        flushed to disk
1900        """
1901        start = time.time()
1902        while True:
1903            test = 0
1904            diff = time.time() - start
1905            for i in self.getDeviceController('vbd').deviceIDs():
1906                test = 1
1907                log.info("Dev %s still active, looping...", i)
1908                time.sleep(0.1)
1909               
1910            if test == 0:
1911                break
1912            if diff >= MIGRATE_TIMEOUT:
1913                log.info("Dev still active but hit max loop timeout")
1914                break
1915
1916    def testvifsComplete(self):
1917        """ In case vifs are released and then created for the same
1918        domain, we need to wait the device shut down.
1919        """
1920        start = time.time()
1921        while True:
1922            test = 0
1923            diff = time.time() - start
1924            for i in self.getDeviceController('vif').deviceIDs():
1925                test = 1
1926                log.info("Dev %s still active, looping...", i)
1927                time.sleep(0.1)
1928               
1929            if test == 0:
1930                break
1931            if diff >= MIGRATE_TIMEOUT:
1932                log.info("Dev still active but hit max loop timeout")
1933                break
1934
1935    def _storeVmDetails(self):
1936        to_store = {}
1937
1938        for key in XendConfig.LEGACY_XENSTORE_VM_PARAMS:
1939            info_key = XendConfig.LEGACY_CFG_TO_XENAPI_CFG.get(key, key)
1940            if self._infoIsSet(info_key):
1941                to_store[key] = str(self.info[info_key])
1942
1943        if self._infoIsSet("static_memory_min"):
1944            to_store["memory"] = str(self.info["static_memory_min"])
1945        if self._infoIsSet("static_memory_max"):
1946            to_store["maxmem"] = str(self.info["static_memory_max"])
1947
1948        image_sxpr = self.info.image_sxpr()
1949        if image_sxpr:
1950            to_store['image'] = sxp.to_string(image_sxpr)
1951
1952        if self._infoIsSet('security'):
1953            secinfo = self.info['security']
1954            to_store['security'] = sxp.to_string(secinfo)
1955            for idx in range(0, len(secinfo)):
1956                if secinfo[idx][0] == 'access_control':
1957                    to_store['security/access_control'] = sxp.to_string(
1958                        [secinfo[idx][1], secinfo[idx][2]])
1959                    for aidx in range(1, len(secinfo[idx])):
1960                        if secinfo[idx][aidx][0] == 'label':
1961                            to_store['security/access_control/label'] = \
1962                                secinfo[idx][aidx][1]
1963                        if secinfo[idx][aidx][0] == 'policy':
1964                            to_store['security/access_control/policy'] = \
1965                                secinfo[idx][aidx][1]
1966                if secinfo[idx][0] == 'ssidref':
1967                    to_store['security/ssidref'] = str(secinfo[idx][1])
1968
1969
1970        if not self._readVm('xend/restart_count'):
1971            to_store['xend/restart_count'] = str(0)
1972
1973        log.debug("Storing VM details: %s", scrub_password(to_store))
1974
1975        self._writeVm(to_store)
1976        self._setVmPermissions()
1977
1978
1979    def _setVmPermissions(self):
1980        """Allow the guest domain to read its UUID.  We don't allow it to
1981        access any other entry, for security."""
1982        xstransact.SetPermissions('%s/uuid' % self.vmpath,
1983                                  { 'dom' : self.domid,
1984                                    'read' : True,
1985                                    'write' : False })
1986
1987    #
1988    # Utility functions
1989    #
1990
1991    def __getattr__(self, name):
1992         if name == "state":
1993             log.warn("Somebody tried to read XendDomainInfo.state... should us _stateGet()!!!")
1994             log.warn("".join(traceback.format_stack()))
1995             return self._stateGet()
1996         else:
1997             raise AttributeError()
1998
1999    def __setattr__(self, name, value):
2000        if name == "state":
2001            log.warn("Somebody tried to set XendDomainInfo.state... should us _stateGet()!!!")
2002            log.warn("".join(traceback.format_stack()))
2003            self._stateSet(value)
2004        else:
2005            self.__dict__[name] = value
2006
2007    def _stateSet(self, state):
2008        self.state_updated.acquire()
2009        try:
2010            # TODO Not sure this is correct...
2011            # _stateGet is live now. Why not fire event
2012            # even when it hasn't changed?
2013            if self._stateGet() != state:
2014                self.state_updated.notifyAll()
2015                import XendAPI
2016                XendAPI.event_dispatch('mod', 'VM', self.info['uuid'],
2017                                       'power_state')
2018        finally:
2019            self.state_updated.release()
2020
2021    def _stateGet(self):
2022        # Lets try and reconsitute the state from xc
2023        # first lets try and get the domain info
2024        # from xc - this will tell us if the domain
2025        # exists
2026        info = dom_get(self.getDomid())
2027        if info is None or info['shutdown']:
2028            # We are either HALTED or SUSPENDED
2029            # check saved image exists
2030            from xen.xend import XendDomain
2031            managed_config_path = \
2032                XendDomain.instance()._managed_check_point_path( \
2033                    self.get_uuid())
2034            if os.path.exists(managed_config_path):
2035                return XEN_API_VM_POWER_STATE_SUSPENDED
2036            else:
2037                return XEN_API_VM_POWER_STATE_HALTED
2038        else:
2039            # We are either RUNNING or PAUSED
2040            if info['paused']:
2041                return XEN_API_VM_POWER_STATE_PAUSED
2042            else:
2043                return XEN_API_VM_POWER_STATE_RUNNING
2044
2045    def _infoIsSet(self, name):
2046        return name in self.info and self.info[name] is not None
2047
2048    def _checkName(self, name):
2049        """Check if a vm name is valid. Valid names contain alphabetic
2050        characters, digits, or characters in '_-.:/+'.
2051        The same name cannot be used for more than one vm at the same time.
2052
2053        @param name: name
2054        @raise: VmError if invalid
2055        """
2056        from xen.xend import XendDomain
2057       
2058        if name is None or name == '':
2059            raise VmError('Missing VM Name')
2060
2061        if not re.search(r'^[A-Za-z0-9_\-\.\:\/\+]+$', name):
2062            raise VmError('Invalid VM Name')
2063
2064        dom =  XendDomain.instance().domain_lookup_nr(name)
2065        if dom and dom.info['uuid'] != self.info['uuid']:
2066            raise VmError("VM name '%s' already exists%s" %
2067                          (name,
2068                           dom.domid is not None and
2069                           (" as domain %s" % str(dom.domid)) or ""))
2070       
2071
2072    def update(self, info = None, refresh = True):
2073        """Update with info from xc.domain_getinfo().
2074        """
2075        log.trace("XendDomainInfo.update(%s) on domain %s", info,
2076                  str(self.domid))
2077       
2078        if not info:
2079            info = dom_get(self.domid)
2080            if not info:
2081                return
2082
2083        if info["maxmem_kb"] < 0:
2084            info["maxmem_kb"] = XendNode.instance() \
2085                                .physinfo_dict()['total_memory'] * 1024
2086
2087        #manually update ssidref / security fields
2088        if security.on() and info.has_key('ssidref'):
2089            if (info['ssidref'] != 0) and self.info.has_key('security'):
2090                security_field = self.info['security']
2091                if not security_field:
2092                    #create new security element
2093                    self.info.update({'security':
2094                                      [['ssidref', str(info['ssidref'])]]})
2095                   
2096        #ssidref field not used any longer
2097        if 'ssidref' in info:
2098            info.pop('ssidref')
2099
2100        # make sure state is reset for info
2101        # TODO: we should eventually get rid of old_dom_states
2102
2103        self.info.update_config(info)
2104        self._update_consoles()
2105       
2106        if refresh:
2107            self.refreshShutdown(info)
2108
2109        log.trace("XendDomainInfo.update done on domain %s: %s",
2110                  str(self.domid), self.info)
2111
2112    def sxpr(self, ignore_store = False, legacy_only = True):
2113        result = self.info.to_sxp(domain = self,
2114                                  ignore_devices = ignore_store,
2115                                  legacy_only = legacy_only)
2116
2117        #if not ignore_store and self.dompath:
2118        #    vnc_port = self.readDom('console/vnc-port')
2119        #    if vnc_port is not None:
2120        #        result.append(['device',
2121        #                       ['console', ['vnc-port', str(vnc_port)]]])
2122
2123        return result
2124
2125    # Xen API
2126    # ----------------------------------------------------------------
2127
2128    def get_uuid(self):
2129        dom_uuid = self.info.get('uuid')
2130        if not dom_uuid: # if it doesn't exist, make one up
2131            dom_uuid = uuid.createString()
2132            self.info['uuid'] = dom_uuid
2133        return dom_uuid
2134   
2135    def get_memory_static_max(self):
2136        return self.info.get('memory_static_max', 0)
2137    def get_memory_static_min(self):
2138        return self.info.get('memory_static_min', 0)
2139    def get_memory_dynamic_max(self):
2140        return self.info.get('memory_dynamic_max', 0)
2141    def get_memory_dynamic_min(self):
2142        return self.info.get('memory_dynamic_min', 0)
2143
2144    # only update memory-related config values if they maintain sanity
2145    def _safe_set_memory(self, key, newval):
2146        oldval = self.info.get(key, 0)
2147        try:
2148            self.info[key] = newval
2149            self.info._memory_sanity_check()
2150        except Exception, ex:
2151            self.info[key] = oldval
2152            raise 
2153   
2154    def set_memory_static_max(self, val):
2155        self._safe_set_memory('memory_static_max', val)
2156    def set_memory_static_min(self, val):
2157        self._safe_set_memory('memory_static_min', val)
2158    def set_memory_dynamic_max(self, val):
2159        self._safe_set_memory('memory_dynamic_max', val)
2160    def set_memory_dynamic_min(self, val):
2161        self._safe_set_memory('memory_dynamic_min', val)
2162   
2163    def get_vcpus_params(self):
2164        if self.getDomid() is None:
2165            return self.info['vcpus_params']
2166
2167        retval = xc.sched_credit_domain_get(self.getDomid())
2168        return retval
2169    def get_power_state(self):
2170        return XEN_API_VM_POWER_STATE[self._stateGet()]
2171    def get_platform(self):
2172        return self.info.get('platform', {})   
2173    def get_pci_bus(self):
2174        return self.info.get('pci_bus', '')
2175    def get_tools_version(self):
2176        return self.info.get('tools_version', {})
2177    def get_metrics(self):
2178        return self.metrics.get_uuid();
2179   
2180    def get_on_shutdown(self):
2181        after_shutdown = self.info.get('actions_after_shutdown')
2182        if not after_shutdown or after_shutdown not in XEN_API_ON_NORMAL_EXIT:
2183            return XEN_API_ON_NORMAL_EXIT[-1]
2184        return after_shutdown
2185
2186    def get_on_reboot(self):
2187        after_reboot = self.info.get('actions_after_reboot')
2188        if not after_reboot or after_reboot not in XEN_API_ON_NORMAL_EXIT:
2189            return XEN_API_ON_NORMAL_EXIT[-1]
2190        return after_reboot
2191
2192    def get_on_suspend(self):
2193        # TODO: not supported       
2194        after_suspend = self.info.get('actions_after_suspend') 
2195        if not after_suspend or after_suspend not in XEN_API_ON_NORMAL_EXIT:
2196            return XEN_API_ON_NORMAL_EXIT[-1]
2197        return after_suspend       
2198
2199    def get_on_crash(self):
2200        after_crash = self.info.get('actions_after_crash')
2201        if not after_crash or after_crash not in XEN_API_ON_CRASH_BEHAVIOUR:
2202            return XEN_API_ON_CRASH_BEHAVIOUR[0]
2203        return after_crash
2204
2205    def get_dev_config_by_uuid(self, dev_class, dev_uuid):
2206        """ Get's a device configuration either from XendConfig or
2207        from the DevController.
2208
2209        @param dev_class: device class, either, 'vbd' or 'vif'
2210        @param dev_uuid: device UUID
2211
2212        @rtype: dictionary
2213        """
2214        dev_type, dev_config = self.info['devices'].get(dev_uuid, (None, None))
2215
2216        # shortcut if the domain isn't started because
2217        # the devcontrollers will have no better information
2218        # than XendConfig.
2219        if self._stateGet() in (XEN_API_VM_POWER_STATE_HALTED,):
2220            if dev_config:
2221                return copy.deepcopy(dev_config)
2222            return None
2223
2224        # instead of using dev_class, we use the dev_type
2225        # that is from XendConfig.
2226        controller = self.getDeviceController(dev_type)
2227        if not controller:
2228            return None
2229           
2230        all_configs = controller.getAllDeviceConfigurations()
2231        if not all_configs:
2232            return None
2233
2234        updated_dev_config = copy.deepcopy(dev_config)
2235        for _devid, _devcfg in all_configs.items():
2236            if _devcfg.get('uuid') == dev_uuid:
2237                updated_dev_config.update(_devcfg)
2238                updated_dev_config['id'] = _devid
2239                return updated_dev_config
2240
2241        return updated_dev_config
2242                   
2243    def get_dev_xenapi_config(self, dev_class, dev_uuid):
2244        config = self.get_dev_config_by_uuid(dev_class, dev_uuid)
2245        if not config:
2246            return {}
2247       
2248        config['VM'] = self.get_uuid()
2249       
2250        if dev_class == 'vif':
2251            if not config.has_key('name'):
2252                config['name'] = config.get('vifname', '')
2253            if not config.has_key('MAC'):
2254                config['MAC'] = config.get('mac', '')
2255            if not config.has_key('type'):
2256                config['type'] = 'paravirtualised'
2257            if not config.has_key('device'):
2258                devid = config.get('id')
2259                if devid != None:
2260                    config['device'] = 'eth%d' % devid
2261                else:
2262                    config['device'] = ''
2263
2264            if not config.has_key('network'):
2265                try:
2266                    bridge = config.get('bridge', None)
2267                    if bridge is None:
2268                        from xen.util import Brctl
2269                        if_to_br = dict([(i,b)
2270                            for (b,ifs) in Brctl.get_state().items()
2271                                for i in ifs])
2272                        vifname = "vif%s.%s" % (self.getDomid(),
2273                                                config.get('id'))
2274                        bridge = if_to_br.get(vifname, None)
2275                    config['network'] = \
2276                        XendNode.instance().bridge_to_network(
2277                        config.get('bridge')).get_uuid()
2278                except Exception:
2279                    log.exception('bridge_to_network')
2280                    # Ignore this for now -- it may happen if the device
2281                    # has been specified using the legacy methods, but at
2282                    # some point we're going to have to figure out how to
2283                    # handle that properly.
2284
2285            config['MTU'] = 1500 # TODO
2286           
2287            if self._stateGet() not in (XEN_API_VM_POWER_STATE_HALTED,):
2288                xennode = XendNode.instance()
2289                rx_bps, tx_bps = xennode.get_vif_util(self.domid, devid)
2290                config['io_read_kbs'] = rx_bps/1024
2291                config['io_write_kbs'] = tx_bps/1024
2292            else:
2293                config['io_read_kbs'] = 0.0
2294                config['io_write_kbs'] = 0.0               
2295
2296        if dev_class == 'vbd':
2297
2298            if self._stateGet() not in (XEN_API_VM_POWER_STATE_HALTED,):
2299                controller = self.getDeviceController(dev_class)
2300                devid, _1, _2 = controller.getDeviceDetails(config)
2301                xennode = XendNode.instance()
2302                rd_blkps, wr_blkps = xennode.get_vbd_util(self.domid, devid)
2303                config['io_read_kbs'] = rd_blkps
2304                config['io_write_kbs'] = wr_blkps
2305            else:
2306                config['io_read_kbs'] = 0.0
2307                config['io_write_kbs'] = 0.0               
2308           
2309            config['VDI'] = config.get('VDI', '')
2310            config['device'] = config.get('dev', '')
2311            if ':' in config['device']:
2312                vbd_name, vbd_type = config['device'].split(':', 1)
2313                config['device'] = vbd_name
2314                if vbd_type == 'cdrom':
2315                    config['type'] = XEN_API_VBD_TYPE[0]
2316                else:
2317                    config['type'] = XEN_API_VBD_TYPE[1]
2318
2319            config['driver'] = 'paravirtualised' # TODO
2320            config['image'] = config.get('uname', '')
2321
2322            if config.get('mode', 'r') == 'r':
2323                config['mode'] = 'RO'
2324            else:
2325                config['mode'] = 'RW'
2326
2327        if dev_class == 'vtpm':
2328            if not config.has_key('type'):
2329                config['type'] = 'paravirtualised' # TODO
2330            if not config.has_key('backend'):
2331                config['backend'] = "00000000-0000-0000-0000-000000000000"
2332
2333        return config
2334
2335    def get_dev_property(self, dev_class, dev_uuid, field):
2336        config = self.get_dev_xenapi_config(dev_class, dev_uuid)
2337        try:
2338            return config[field]
2339        except KeyError:
2340            raise XendError('Invalid property for device: %s' % field)
2341
2342    def set_dev_property(self, dev_class, dev_uuid, field, value):
2343        self.info['devices'][dev_uuid][1][field] = value
2344
2345    def get_vcpus_util(self):
2346        vcpu_util = {}
2347        xennode = XendNode.instance()
2348        if 'VCPUs_max' in self.info and self.domid != None:
2349            for i in range(0, self.info['VCPUs_max']):
2350                util = xennode.get_vcpu_util(self.domid, i)
2351                vcpu_util[str(i)] = util
2352               
2353        return vcpu_util
2354
2355    def get_consoles(self):
2356        return self.info.get('console_refs', [])
2357
2358    def get_vifs(self):
2359        return self.info.get('vif_refs', [])
2360
2361    def get_vbds(self):
2362        return self.info.get('vbd_refs', [])
2363
2364    def get_vtpms(self):
2365        return self.info.get('vtpm_refs', [])
2366
2367    def create_vbd(self, xenapi_vbd, vdi_image_path):
2368        """Create a VBD using a VDI from XendStorageRepository.
2369
2370        @param xenapi_vbd: vbd struct from the Xen API
2371        @param vdi_image_path: VDI UUID
2372        @rtype: string
2373        @return: uuid of the device
2374        """
2375        xenapi_vbd['image'] = vdi_image_path
2376        if vdi_image_path.startswith('tap'):
2377            dev_uuid = self.info.device_add('tap', cfg_xenapi = xenapi_vbd)
2378        else:
2379            dev_uuid = self.info.device_add('vbd', cfg_xenapi = xenapi_vbd)
2380           
2381        if not dev_uuid:
2382            raise XendError('Failed to create device')
2383
2384        if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING,
2385                                XEN_API_VM_POWER_STATE_PAUSED):
2386            _, config = self.info['devices'][dev_uuid]
2387           
2388            if vdi_image_path.startswith('tap'):
2389                dev_control = self.getDeviceController('tap')
2390            else:
2391                dev_control = self.getDeviceController('vbd')
2392
2393            try:
2394                devid = dev_control.createDevice(config)
2395                dev_control.waitForDevice(devid)
2396                self.info.device_update(dev_uuid,
2397                                        cfg_xenapi = {'devid': devid})
2398            except Exception, exn:
2399                log.exception(exn)
2400                del self.info['devices'][dev_uuid]
2401                self.info['vbd_refs'].remove(dev_uuid)
2402                raise
2403           
2404        return dev_uuid
2405
2406    def create_phantom_vbd_with_vdi(self, xenapi_vbd, vdi_image_path):
2407        """Create a VBD using a VDI from XendStorageRepository.
2408
2409        @param xenapi_vbd: vbd struct from the Xen API
2410        @param vdi_image_path: VDI UUID
2411        @rtype: string
2412        @return: uuid of the device
2413        """
2414        xenapi_vbd['image'] = vdi_image_path
2415        dev_uuid = self.info.phantom_device_add('tap', cfg_xenapi = xenapi_vbd)
2416        if not dev_uuid:
2417            raise XendError('Failed to create device')
2418
2419        if self._stateGet() == XEN_API_VM_POWER_STATE_RUNNING:
2420            _, config = self.info['devices'][dev_uuid]
2421            config['devid'] = self.getDeviceController('tap').createDevice(config)
2422
2423        return config['devid']
2424
2425    def create_vif(self, xenapi_vif):
2426        """Create VIF device from the passed struct in Xen API format.
2427
2428        @param xenapi_vif: Xen API VIF Struct.
2429        @rtype: string
2430        @return: UUID
2431        """
2432        dev_uuid = self.info.device_add('vif', cfg_xenapi = xenapi_vif)
2433        if not dev_uuid:
2434            raise XendError('Failed to create device')
2435       
2436        if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING,
2437                                XEN_API_VM_POWER_STATE_PAUSED):
2438
2439            _, config = self.info['devices'][dev_uuid]
2440            dev_control = self.getDeviceController('vif')
2441
2442            try:
2443                devid = dev_control.createDevice(config)
2444                dev_control.waitForDevice(devid)
2445                self.info.device_update(dev_uuid,
2446                                        cfg_xenapi = {'devid': devid})
2447            except Exception, exn:
2448                log.exception(exn)
2449                del self.info['devices'][dev_uuid]
2450                self.info['vif_refs'].remove(dev_uuid)
2451                raise           
2452 
2453        return dev_uuid
2454
2455    def create_vtpm(self, xenapi_vtpm):
2456        """Create a VTPM device from the passed struct in Xen API format.
2457
2458        @return: uuid of the device
2459        @rtype: string
2460        """
2461
2462        if self._stateGet() not in (DOM_STATE_HALTED,):
2463            raise VmError("Can only add vTPM to a halted domain.")
2464        if self.get_vtpms() != []:
2465            raise VmError('Domain already has a vTPM.')
2466        dev_uuid = self.info.device_add('vtpm', cfg_xenapi = xenapi_vtpm)
2467        if not dev_uuid:
2468            raise XendError('Failed to create device')
2469
2470        return dev_uuid
2471
2472    def create_console(self, xenapi_console):
2473        """ Create a console device from a Xen API struct.
2474
2475        @return: uuid of device
2476        @rtype: string
2477        """
2478        if self._stateGet() not in (DOM_STATE_HALTED,):
2479            raise VmError("Can only add console to a halted domain.")
2480
2481        dev_uuid = self.info.device_add('console', cfg_xenapi = xenapi_console)
2482        if not dev_uuid:
2483            raise XendError('Failed to create device')
2484
2485        return dev_uuid
2486
2487    def destroy_device_by_uuid(self, dev_type, dev_uuid):
2488        if dev_uuid not in self.info['devices']:
2489            raise XendError('Device does not exist')
2490
2491        try:
2492            if self._stateGet() in (XEN_API_VM_POWER_STATE_RUNNING,
2493                                    XEN_API_VM_POWER_STATE_PAUSED):
2494                _, config = self.info['devices'][dev_uuid]
2495                devid = config.get('devid')
2496                if devid != None:
2497                    self.getDeviceController(dev_type).destroyDevice(devid, force = False)
2498                else:
2499                    raise XendError('Unable to get devid for device: %s:%s' %
2500                                    (dev_type, dev_uuid))
2501        finally:
2502            del self.info['devices'][dev_uuid]
2503            self.info['%s_refs' % dev_type].remove(dev_uuid)
2504
2505    def destroy_vbd(self, dev_uuid):
2506        self.destroy_device_by_uuid('vbd', dev_uuid)
2507
2508    def destroy_vif(self, dev_uuid):
2509        self.destroy_device_by_uuid('vif', dev_uuid)
2510
2511    def destroy_vtpm(self, dev_uuid):
2512        self.destroy_device_by_uuid('vtpm', dev_uuid)
2513           
2514    def has_device(self, dev_class, dev_uuid):
2515        return (dev_uuid in self.info['%s_refs' % dev_class.lower()])
2516
2517    def __str__(self):
2518        return '<domain id=%s name=%s memory=%s state=%s>' % \
2519               (str(self.domid), self.info['name_label'],
2520                str(self.info['memory_dynamic_max']), DOM_STATES[self._stateGet()])
2521
2522    __repr__ = __str__
2523
Note: See TracBrowser for help on using the repository browser.