diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/xenmgr/lib/EventTypes.py | 2 | ||||
-rw-r--r-- | tools/xenmgr/lib/XendClient.py | 4 | ||||
-rw-r--r-- | tools/xenmgr/lib/XendDomain.py | 91 | ||||
-rw-r--r-- | tools/xenmgr/lib/XendDomainInfo.py | 789 | ||||
-rw-r--r-- | tools/xenmgr/lib/server/SrvConsoleServer.py | 30 | ||||
-rw-r--r-- | tools/xenmgr/lib/server/SrvDomain.py | 6 | ||||
-rwxr-xr-x | tools/xenmgr/lib/server/blkif.py | 118 | ||||
-rwxr-xr-x | tools/xenmgr/lib/server/controller.py | 29 | ||||
-rw-r--r-- | tools/xenmgr/lib/server/messages.py | 3 | ||||
-rwxr-xr-x | tools/xenmgr/lib/server/netif.py | 59 | ||||
-rw-r--r-- | tools/xenmgr/lib/xm/create.py | 180 | ||||
-rw-r--r-- | tools/xenmgr/lib/xm/main.py | 424 | ||||
-rw-r--r-- | tools/xenmgr/lib/xm/opts.py | 213 | ||||
-rw-r--r-- | tools/xenmgr/lib/xm/shutdown.py | 3 |
14 files changed, 1257 insertions, 694 deletions
diff --git a/tools/xenmgr/lib/EventTypes.py b/tools/xenmgr/lib/EventTypes.py index c8d5e62aa7..6350baa5dd 100644 --- a/tools/xenmgr/lib/EventTypes.py +++ b/tools/xenmgr/lib/EventTypes.py @@ -8,7 +8,7 @@ ## xend.domain.unpause: dom ## xend.domain.pause: dom ## xend.domain.shutdown: dom -## xend.domain.halt: dom +## xend.domain.destroy: dom ## xend.domain.migrate.begin: dom, to ## Begin tells: src host, src domain uri, dst host. Dst id known? diff --git a/tools/xenmgr/lib/XendClient.py b/tools/xenmgr/lib/XendClient.py index fb4a5b4421..664835a155 100644 --- a/tools/xenmgr/lib/XendClient.py +++ b/tools/xenmgr/lib/XendClient.py @@ -199,9 +199,9 @@ class Xend: return xend_call(self.domainurl(id), {'op' : 'shutdown'}) - def xend_domain_halt(self, id): + def xend_domain_destroy(self, id): return xend_call(self.domainurl(id), - {'op' : 'halt'}) + {'op' : 'destroy'}) def xend_domain_save(self, id, filename): return xend_call(self.domainurl(id), diff --git a/tools/xenmgr/lib/XendDomain.py b/tools/xenmgr/lib/XendDomain.py index f236a43d80..106af33b61 100644 --- a/tools/xenmgr/lib/XendDomain.py +++ b/tools/xenmgr/lib/XendDomain.py @@ -6,6 +6,8 @@ """ import sys +from twisted.internet import defer + import Xc; xc = Xc.new() import xenctl.ip @@ -59,21 +61,26 @@ class XendDomain: for d in domlist: domid = str(d['dom']) doms[domid] = d + dlist = [] for config in self.domain_db.values(): domid = str(sxp.child_value(config, 'id')) print "dom=", domid, "config=", config if domid in doms: print "dom=", domid, "new" - self._new_domain(config) + deferred = self._new_domain(config, doms[domid]) + dlist.append(deferred) else: print "dom=", domid, "del" self._delete_domain(domid) - print "doms:" - for d in self.domain.values(): print 'dom', d - print "refresh..." - self.refresh() - print "doms:" - for d in self.domain.values(): print 'dom', d + deferred = defer.DeferredList(dlist, fireOnOneErrback=1) + def cbok(val): + print "doms:" + for d in self.domain.values(): print 'dom', d + print "refresh..." + self.refresh() + print "doms:" + for d in self.domain.values(): print 'dom', d + deferred.addCallback(cbok) def sync(self): """Sync domain db to disk. @@ -90,31 +97,36 @@ class XendDomain: def close(self): pass - def _new_domain(self, info): + def _new_domain(self, savedinfo, info): """Create a domain entry from saved info. """ - console = None - kernel = None - id = sxp.child_value(info, 'id') - dom = int(id) - name = sxp.child_value(info, 'name') - memory = int(sxp.child_value(info, 'memory')) - consoleinfo = sxp.child(info, 'console') - if consoleinfo: - consoleid = sxp.child_value(consoleinfo, 'id') - console = self.xconsole.console_get(consoleid) - if dom and console is None: - # Try to connect a console. - console = self.xconsole.console_create(dom) - config = sxp.child(info, 'config') - if config: - image = sxp.child(info, 'image') - if image: - image = sxp.child0(image) - kernel = sxp.child_value(image, 'kernel') - dominfo = XendDomainInfo.XendDomainInfo( - config, dom, name, memory, kernel, console) - self.domain[id] = dominfo +## console = None +## kernel = None +## id = sxp.child_value(info, 'id') +## dom = int(id) +## name = sxp.child_value(info, 'name') +## memory = int(sxp.child_value(info, 'memory')) +## consoleinfo = sxp.child(info, 'console') +## if consoleinfo: +## consoleid = sxp.child_value(consoleinfo, 'id') +## console = self.xconsole.console_get(consoleid) +## if dom and console is None: +## # Try to connect a console. +## console = self.xconsole.console_create(dom) +## config = sxp.child(info, 'config') +## if config: +## image = sxp.child(info, 'image') +## if image: +## image = sxp.child0(image) +## kernel = sxp.child_value(image, 'kernel') +## dominfo = XendDomainInfo.XendDomainInfo( +## config, dom, name, memory, kernel, console) + config = sxp.child_value(savedinfo, 'config') + deferred = XendDomainInfo.vm_recreate(config, info) + def fn(dominfo): + self.domain[dominfo.id] = dominfo + deferred.addCallback(fn) + return deferred def _add_domain(self, id, info, notify=1): self.domain[id] = info @@ -143,10 +155,13 @@ class XendDomain: doms[id] = d if id not in self.domain: config = None - image = None - newinfo = XendDomainInfo.XendDomainInfo( - config, d['dom'], d['name'], d['mem_kb']/1024, image=image, info=d) - self._add_domain(newinfo.id, newinfo) + #image = None + #newinfo = XendDomainInfo.XendDomainInfo( + # config, d['dom'], d['name'], d['mem_kb']/1024, image=image, info=d) + deferred = XendDomainInfo.vm_recreate(config, d) + def fn(dominfo): + self._add_domain(dominfo.id, dominfo) + deferred.addCallback(fn) # Remove entries for domains that no longer exist. for d in self.domain.values(): dominfo = doms.get(d.id) @@ -217,13 +232,13 @@ class XendDomain: self.refresh() return val - def domain_halt(self, id): + def domain_destroy(self, id): """Terminate domain immediately. """ dom = int(id) if dom <= 0: return 0 - eserver.inject('xend.domain.halt', id) + eserver.inject('xend.domain.destroy', id) val = xc.domain_destroy(dom=dom) self.refresh() return val @@ -235,14 +250,14 @@ class XendDomain: pass def domain_save(self, id, dst, progress=0): - """Save domain state to file, halt domain. + """Save domain state to file, destroy domain. """ dom = int(id) self.domain_pause(id) eserver.inject('xend.domain.save', id) rc = xc.linux_save(dom=dom, state_file=dst, progress=progress) if rc == 0: - self.domain_halt(id) + self.domain_destroy(id) return rc def domain_restore(self, src, config, progress=0): diff --git a/tools/xenmgr/lib/XendDomainInfo.py b/tools/xenmgr/lib/XendDomainInfo.py index 3828fcf2ec..0dfd1a71ee 100644 --- a/tools/xenmgr/lib/XendDomainInfo.py +++ b/tools/xenmgr/lib/XendDomainInfo.py @@ -60,160 +60,6 @@ class VmError(ValueError): return self.value -class XendDomainInfo: - """Virtual machine object.""" - - def __init__(self, config, dom, name, memory, image=None, console=None, info=None): - """Construct a virtual machine object. - - config configuration - dom domain id - name name - memory memory size (in MB) - image image object - """ - #todo: add info: runtime, state, ... - self.config = config - self.id = str(dom) - self.dom = dom - self.name = name - self.memory = memory - self.image = image - self.console = console - self.devices = {} - self.configs = [] - self.info = info - self.ipaddrs = [] - self.block_controller = 0 - self.net_controller = 0 - - #todo: state: running, suspended - self.state = 'running' - #todo: set to migrate info if migrating - self.migrate = None - - def update(self, info): - """Update with info from xc.domain_getinfo(). - """ - self.info = info - - def __str__(self): - s = "domain" - s += " id=" + self.id - s += " name=" + self.name - s += " memory=" + str(self.memory) - if self.console: - s += " console=" + self.console.id - if self.image: - s += " image=" + self.image - s += "" - return s - - __repr__ = __str__ - - def sxpr(self): - sxpr = ['domain', - ['id', self.id], - ['name', self.name], - ['memory', self.memory] ] - if self.info: - run = (self.info['running'] and 'R') or 'r' - block = (self.info['blocked'] and 'B') or 'b' - stop = (self.info['paused'] and 'P') or 'p' - susp = (self.info['shutdown'] and 'S') or 's' - crash = (self.info['crashed'] and 'C') or 'c' - state = run + block + stop + susp + crash - sxpr.append(['state', state]) - if self.info['shutdown']: - reasons = ["poweroff", "reboot", "suspend"] - reason = reasons[self.info['shutdown_reason']] - sxpr.append(['shutdown_reason', reason]) - sxpr.append(['cpu', self.info['cpu']]) - sxpr.append(['cpu_time', self.info['cpu_time']/1e9]) - if self.console: - sxpr.append(self.console.sxpr()) - if self.config: - sxpr.append(['config', self.config]) - return sxpr - - def add_device(self, type, dev): - """Add a device to a virtual machine. - - dev device to add - """ - dl = self.devices.get(type, []) - dl.append(dev) - self.devices[type] = dl - - def get_devices(self, type): - val = self.devices.get(type, []) - print 'get_devices', type; sxp.show(val); print - return val - - def get_device_by_id(self, type, id): - """Get the device with the given id. - - id device id - - returns device or None - """ - return sxp.child_with_id(self.get_devices(type), id) - - def get_device_by_index(self, type, idx): - """Get the device with the given index. - - idx device index - - returns device or None - """ - dl = self.get_devices(type) - if 0 <= idx < len(dl): - return dl[idx] - else: - return None - - def add_config(self, val): - """Add configuration data to a virtual machine. - - val data to add - """ - self.configs.append(val) - - def destroy(self): - if self.dom <= 0: - return 0 - return xc.domain_destroy(dom=self.dom) - - def died(self): - print 'died>', self.dom - self.release_vifs() - - def release_vifs(self): - print 'release_vifs>', self.dom - vifs = self.get_devices('vif') - for v in vifs: - vif = sxp.child_value(v, 'vif') - bridge = sxp.child_value(v, 'bridge') - XendBridge.vif_bridge_rem(self.dom, vif, bridge) - - def show(self): - """Print virtual machine info. - """ - print "[VM dom=%d name=%s memory=%d" % (self.dom, self.name, self.memory) - print "image:" - sxp.show(self.image) - print - for dl in self.devices: - for dev in dl: - print "device:" - sxp.show(dev) - print - for val in self.configs: - print "config:" - sxp.show(val) - print - print "]" - def blkdev_name_to_number(name): """Take the given textual block-device name (e.g., '/dev/sda1', 'hda') and return the device number used by the OS. """ @@ -264,7 +110,7 @@ def lookup_raw_partn(partition): return None -def lookup_disk_uname( uname ): +def lookup_disk_uname(uname): """Lookup a list of segments for a physical device. uname [string]: name of the device in the format \'phy:dev\' for a physical device returns [list of dicts]: list of extents that make up the named device @@ -277,7 +123,7 @@ def lookup_disk_uname( uname ): segments = None return segments -def make_disk(dom, uname, dev, mode): +def make_disk(dom, uname, dev, mode, recreate=0): """Create a virtual disk device for a domain. @returns Deferred @@ -289,21 +135,21 @@ def make_disk(dom, uname, dev, mode): raise VmError("vbd: Multi-segment vdisk: uname=%s" % uname) segment = segments[0] vdev = blkdev_name_to_number(dev) - ctrl = xend.blkif_create(dom) + ctrl = xend.blkif_create(dom, recreate=recreate) def fn(ctrl): - return xend.blkif_dev_create(dom, vdev, mode, segment) + return xend.blkif_dev_create(dom, vdev, mode, segment, recreate=recreate) ctrl.addCallback(fn) return ctrl -def make_vif(dom, vif, vmac): +def make_vif(dom, vif, vmac, recreate=0): """Create a virtual network device for a domain. @returns Deferred """ - xend.netif_create(dom) - d = xend.netif_dev_create(dom, vif, vmac) + xend.netif_create(dom, recreate=recreate) + d = xend.netif_dev_create(dom, vif, vmac, recreate=recreate) return d def vif_up(iplist): @@ -338,49 +184,6 @@ def vif_up(iplist): finally: if not nlb: set_ip_nonlocal_bind(0) -def xen_domain_create(config, ostype, name, memory, kernel, ramdisk, cmdline, vifs_n): - """Create a domain. Builds the image but does not configure it. - - config configuration - ostype OS type - name domain name - memory domain memory (MB) - kernel kernel image - ramdisk kernel ramdisk - cmdline kernel commandline - vifs_n number of network interfaces - returns vm - """ - flags = 0 - if not os.path.isfile(kernel): - raise VmError('Kernel image does not exist: %s' % kernel) - if ramdisk and not os.path.isfile(ramdisk): - raise VMError('Kernel ramdisk does not exist: %s' % ramdisk) - - cpu = int(sxp.child_value(config, 'cpu', '-1')) - print 'xen_domain_create> create ', memory, name, cpu - dom = xc.domain_create(mem_kb= memory * 1024, name= name, cpu= cpu) - if dom <= 0: - raise VmError('Creating domain failed: name=%s memory=%d kernel=%s' - % (name, memory, kernel)) - console = xendConsole.console_create(dom) - buildfn = getattr(xc, '%s_build' % ostype) - - print 'xen_domain_create> build ', ostype, dom, kernel, cmdline, ramdisk - if len(cmdline) >= 256: - print 'Warning: kernel cmdline too long' - err = buildfn(dom = dom, - image = kernel, - control_evtchn = console.port2, - cmdline = cmdline, - ramdisk = ramdisk, - flags = flags) - if err != 0: - raise VmError('Building domain failed: type=%s dom=%d err=%d' - % (ostype, dom, err)) - vm = XendDomainInfo(config, dom, name, memory, kernel, console) - return vm - config_handlers = {} def add_config_handler(name, h): @@ -450,34 +253,27 @@ def vm_create(config): returns Deferred raises VmError for invalid configuration """ - # todo - add support for scheduling params? print 'vm_create>' - vm = None - try: - name = sxp.child_value(config, 'name') - memory = int(sxp.child_value(config, 'memory', '128')) - image = sxp.child_value(config, 'image') - - image_name = sxp.name(image) - image_handler = get_image_handler(image_name) - if image_handler is None: - raise VmError('unknown image type: ' + image_name) - vm = image_handler(config, name, memory, image) - deferred = vm_configure(vm, config) - except StandardError, ex: - # Catch errors, cleanup and re-raise. - if vm: - vm.destroy() - raise - def cbok(x): - print 'vm_create> cbok', x - return x - deferred.addCallback(cbok) - print 'vm_create<' - return deferred + vm = XendDomainInfo() + return vm.construct(config) + +def vm_recreate(config, info): + """Create the VM object for an existing domain. + """ + vm = XendDomainInfo() + vm.recreate = 1 + vm.setdom(info['dom']) + vm.name = info['name'] + vm.memory = info['mem_kb']/1024 + if config: + d = vm.construct(config) + else: + d = defer.Deferred() + d.callback(vm) + return d def vm_restore(src, config, progress=0): - """Restore a VM. + """Restore a VM from a disk image. src saved state to restore config configuration @@ -485,11 +281,14 @@ def vm_restore(src, config, progress=0): returns deferred raises VmError for invalid configuration """ + vm = XendDomainInfo() + vm.config = config ostype = "linux" #todo set from config restorefn = getattr(xc, "%s_restore" % ostype) dom = restorefn(state_file=src, progress=progress) - if dom < 0: return dom - deferred = dom_configure(dom, config) + if dom < 0: + raise VMError('restore failed') + deferred = vm.dom_configure(dom) def vifs_cb(val, vm): vif_up(vm.ipaddrs) deferred.addCallback(vifs_cb, vm) @@ -501,115 +300,25 @@ def dom_get(dom): return domlist[0] return None -def dom_configure(dom, config): - """Configure a domain. - - dom domain id - config configuration - returns deferred - """ - d = dom_get(dom) - if not d: - raise VMError("Domain not found: %d" % dom) - try: - name = d['name'] - memory = d['memory']/1024 - image = None - vm = VM(config, dom, name, memory, image) - deferred = vm_configure(vm, config) - except StandardError, ex: - if vm: - vm.destroy() - raise - return deferred def append_deferred(dlist, v): if isinstance(v, defer.Deferred): dlist.append(v) -def vm_create_devices(vm, config): - """Create the devices for a vm. - - vm virtual machine - config configuration - - returns Deferred - raises VmError for invalid devices - """ - print '>vm_create_devices' - dlist = [] - devices = sxp.children(config, 'device') - index = {} - for d in devices: - dev = sxp.child0(d) - if dev is None: - raise VmError('invalid device') - dev_name = sxp.name(dev) - dev_index = index.get(dev_name, 0) - dev_handler = get_device_handler(dev_name) - if dev_handler is None: - raise VmError('unknown device type: ' + dev_name) - v = dev_handler(vm, dev, dev_index) - append_deferred(dlist, v) - index[dev_name] = dev_index + 1 - deferred = defer.DeferredList(dlist, fireOnOneErrback=1) - print '<vm_create_devices' - return deferred - -def config_controllers(vm, config): - for c in sxp.children(config, 'controller'): - name = sxp.name(c) - if name == 'block': - vm.block_controller = 1 - xend.blkif_set_control_domain(vm.dom) - elif name == 'net': - vm.net_controller = 1 - xend.netif_set_control_domain(vm.dom) - else: - raise VmError('invalid controller type:' + str(name)) - -def vm_configure(vm, config): - """Configure a vm. - - vm virtual machine - config configuration - - returns Deferred - calls callback with vm - """ - config_controllers(vm, config) - if vm.block_controller: - d = defer.Deferred() - d.callback(1) - else: - d = xend.blkif_create(vm.dom) - d.addCallback(_vm_configure1, vm, config) - return d - -def _vm_configure1(val, vm, config): - d = vm_create_devices(vm, config) +def _vm_configure1(val, vm): + d = vm.create_devices() print '_vm_configure1> made devices...' def cbok(x): print '_vm_configure1> cbok', x return x d.addCallback(cbok) - d.addCallback(_vm_configure2, vm, config) + d.addCallback(_vm_configure2, vm) print '_vm_configure1<' return d -def _vm_configure2(val, vm, config): +def _vm_configure2(val, vm): print '>callback _vm_configure2...' - dlist = [] - index = {} - for field in sxp.children(config): - field_name = sxp.name(field) - field_index = index.get(field_name, 0) - field_handler = get_config_handler(field_name) - # Ignore unknown fields. Warn? - if field_handler: - v = field_handler(vm, config, field, field_index) - append_deferred(dlist, v) - index[field_name] = field_index + 1 - d = defer.DeferredList(dlist, fireOnOneErrback=1) + d = vm.configure_fields() def cbok(results): print '_vm_configure2> cbok', results return vm @@ -622,22 +331,369 @@ def _vm_configure2(val, vm, config): print '<_vm_configure2' return d -def config_devices(config, name): - """Get a list of the 'device' nodes of a given type from a config. +class XendDomainInfo: + """Virtual machine object.""" - config configuration - name device type - return list of device configs - """ - devices = [] - for d in sxp.children(config, 'device'): - dev = sxp.child0(d) - if dev is None: continue - if name == sxp.name(dev): - devices.append(dev) - return devices + def __init__(self): + self.recreate = 0 + self.config = None + self.id = None + self.dom = None + self.name = None + self.memory = None + self.image = None + self.ramdisk = None + self.cmdline = None + self.console = None + self.devices = {} + self.configs = [] + self.info = None + self.ipaddrs = [] + self.blkif_backend = 0 + self.netif_backend = 0 + #todo: state: running, suspended + self.state = 'running' + #todo: set to migrate info if migrating + self.migrate = None + + def setdom(self, dom): + self.dom = int(dom) + self.id = str(dom) -def vm_image_linux(config, name, memory, image): + def update(self, info): + """Update with info from xc.domain_getinfo(). + """ + self.info = info + + def __str__(self): + s = "domain" + s += " id=" + self.id + s += " name=" + self.name + s += " memory=" + str(self.memory) + if self.console: + s += " console=" + self.console.id + if self.image: + s += " image=" + self.image + s += "" + return s + + __repr__ = __str__ + + def sxpr(self): + sxpr = ['domain', + ['id', self.id], + ['name', self.name], + ['memory', self.memory] ] + if self.info: + run = (self.info['running'] and 'r') or '-' + block = (self.info['blocked'] and 'b') or '-' + stop = (self.info['paused'] and 'p') or '-' + susp = (self.info['shutdown'] and 's') or '-' + crash = (self.info['crashed'] and 'c') or '-' + state = run + block + stop + susp + crash + sxpr.append(['state', state]) + if self.info['shutdown']: + reasons = ["poweroff", "reboot", "suspend"] + reason = reasons[self.info['shutdown_reason']] + sxpr.append(['shutdown_reason', reason]) + sxpr.append(['cpu', self.info['cpu']]) + sxpr.append(['cpu_time', self.info['cpu_time']/1e9]) + if self.console: + sxpr.append(self.console.sxpr()) + if self.config: + sxpr.append(['config', self.config]) + return sxpr + + def construct(self, config): + # todo - add support for scheduling params? + self.config = config + try: + self.name = sxp.child_value(config, 'name') + self.memory = int(sxp.child_value(config, 'memory', '128')) + self.configure_backends() + image = sxp.child_value(config, 'image') + image_name = sxp.name(image) + image_handler = get_image_handler(image_name) + if image_handler is None: + raise VmError('unknown image type: ' + image_name) + image_handler(self, image) + deferred = self.configure() + except StandardError, ex: + # Catch errors, cleanup and re-raise. + self.destroy() + raise + def cbok(x): + print 'vm_create> cbok', x + return x + deferred.addCallback(cbok) + print 'vm_create<' + return deferred + + def config_devices(self, name): + """Get a list of the 'device' nodes of a given type from the config. + + name device type + return list of device configs + """ + devices = [] + for d in sxp.children(self.config, 'device'): + dev = sxp.child0(d) + if dev is None: continue + if name == sxp.name(dev): + devices.append(dev) + return devices + + def add_device(self, type, dev): + """Add a device to a virtual machine. + + dev device to add + """ + dl = self.devices.get(type, []) + dl.append(dev) + self.devices[type] = dl + + def get_devices(self, type): + val = self.devices.get(type, []) + return val + + def get_device_by_id(self, type, id): + """Get the device with the given id. + + id device id + + returns device or None + """ + dl = self.get_devices(type) + for d in dl: + if d.getprop('id') == id: + return d + return None + + def get_device_by_index(self, type, idx): + """Get the device with the given index. + + idx device index + + returns device or None + """ + dl = self.get_devices(type) + if 0 <= idx < len(dl): + return dl[idx] + else: + return None + + def add_config(self, val): + """Add configuration data to a virtual machine. + + val data to add + """ + self.configs.append(val) + + def destroy(self): + if self.dom <= 0: + return 0 + return xc.domain_destroy(dom=self.dom) + + def died(self): + print 'died>', self.dom + self.release_devices() + + def release_devices(self): + print 'release_devices>', self.dom + self.release_vifs() + self.release_vbds() + self.devices = {} + + def release_vifs(self): + print 'release_vifs>', self.dom + if self.dom is None: return + ctrl = xend.netif_get(self.dom) + if ctrl: + ctrl.destroy() + + def release_vbds(self): + print 'release_vbds>', self.dom + if self.dom is None: return + ctrl = xend.blkif_get(self.dom) + if ctrl: + ctrl.destroy() + + def show(self): + """Print virtual machine info. + """ + print "[VM dom=%d name=%s memory=%d" % (self.dom, self.name, self.memory) + print "image:" + sxp.show(self.image) + print + for dl in self.devices: + for dev in dl: + print "device:" + sxp.show(dev) + print + for val in self.configs: + print "config:" + sxp.show(val) + print + print "]" + + def init_domain(self): + """Initialize the domain memory. + """ + if self.recreate: return + memory = self.memory + name = self.name + cpu = int(sxp.child_value(self.config, 'cpu', '-1')) + print 'init_domain>', memory, name, cpu + dom = xc.domain_create(mem_kb= memory * 1024, name= name, cpu= cpu) + if dom <= 0: + raise VmError('Creating domain failed: name=%s memory=%d' + % (name, memory)) + self.setdom(dom) + + def build_domain(self, ostype, kernel, ramdisk, cmdline, vifs_n): + """Build the domain boot image. + """ + if self.recreate: return + if len(cmdline) >= 256: + print 'Warning: kernel cmdline too long' + dom = self.dom + buildfn = getattr(xc, '%s_build' % ostype) + print 'build_domain>', ostype, dom, kernel, cmdline, ramdisk + flags = 0 + if self.netif_backend: flags |= SIF_NET_BE_DOMAIN + if self.blkif_backend: flags |= SIF_BLK_BE_DOMAIN + err = buildfn(dom = dom, + image = kernel, + control_evtchn = self.console.port2, + cmdline = cmdline, + ramdisk = ramdisk, + flags = flags) + if err != 0: + raise VmError('Building domain failed: type=%s dom=%d err=%d' + % (ostype, dom, err)) + + def create_domain(self, ostype, kernel, ramdisk, cmdline, vifs_n): + """Create a domain. Builds the image but does not configure it. + + ostype OS type + kernel kernel image + ramdisk kernel ramdisk + cmdline kernel commandline + vifs_n number of network interfaces + """ + print 'create_domain>', ostype, kernel + if not self.recreate: + if not os.path.isfile(kernel): + raise VmError('Kernel image does not exist: %s' % kernel) + if ramdisk and not os.path.isfile(ramdisk): + raise VMError('Kernel ramdisk does not exist: %s' % ramdisk) + print 'create-domain> init_domain...' + self.init_domain() + print 'create_domain>', 'dom=', self.dom + self.console = xendConsole.console_create(self.dom) + self.build_domain(ostype, kernel, ramdisk, cmdline, vifs_n) + self.image = kernel + self.ramdisk = ramdisk + self.cmdline = cmdline + + def create_devices(self): + """Create the devices for a vm. + + returns Deferred + raises VmError for invalid devices + """ + print '>create_devices' + dlist = [] + devices = sxp.children(self.config, 'device') + index = {} + for d in devices: + dev = sxp.child0(d) + if dev is None: + raise VmError('invalid device') + dev_name = sxp.name(dev) + dev_index = index.get(dev_name, 0) + dev_handler = get_device_handler(dev_name) + if dev_handler is None: + raise VmError('unknown device type: ' + dev_name) + v = dev_handler(self, dev, dev_index) + append_deferred(dlist, v) + index[dev_name] = dev_index + 1 + deferred = defer.DeferredList(dlist, fireOnOneErrback=1) + print '<create_devices' + return deferred + + def configure_backends(self): + """Set configuration flags if the vm is a backend for netif of blkif. + """ + for c in sxp.children(self.config, 'backend'): + name = sxp.name(c) + if name == 'blkif': + self.blkif_backend = 1 + elif name == 'netif': + self.netif_backend = 1 + else: + raise VmError('invalid backend type:' + str(name)) + + def create_backends(self): + """Setup the netif and blkif backends. + """ + if self.blkif_backend: + xend.blkif_set_control_domain(self.dom, recreate=self.recreate) + if self.netif_backend: + xend.netif_set_control_domain(self.dom, recreate=self.recreate) + + def configure(self): + """Configure a vm. + + vm virtual machine + config configuration + + returns Deferred - calls callback with vm + """ + if self.blkif_backend: + d = defer.Deferred() + d.callback(1) + else: + d = xend.blkif_create(self.dom, recreate=self.recreate) + d.addCallback(_vm_configure1, self) + return d + + def dom_configure(self, dom): + """Configure a domain. + + dom domain id + returns deferred + """ + d = dom_get(dom) + if not d: + raise VMError("Domain not found: %d" % dom) + try: + self.setdom(dom) + self.name = d['name'] + self.memory = d['memory']/1024 + deferred = self.configure() + except StandardError, ex: + self.destroy() + raise + return deferred + + def configure_fields(self): + dlist = [] + index = {} + for field in sxp.children(self.config): + field_name = sxp.name(field) + field_index = index.get(field_name, 0) + field_handler = get_config_handler(field_name) + # Ignore unknown fields. Warn? + if field_handler: + v = field_handler(self, self.config, field, field_index) + append_deferred(dlist, v) + index[field_name] = field_index + 1 + d = defer.DeferredList(dlist, fireOnOneErrback=1) + return d + + +def vm_image_linux(vm, image): """Create a VM for a linux image. name vm name @@ -658,12 +714,11 @@ def vm_image_linux(config, name, memory, image): if args: cmdline += " " + args ramdisk = sxp.child_value(image, "ramdisk", '') - vifs = config_devices(config, "vif") - vm = xen_domain_create(config, "linux", name, memory, kernel, - ramdisk, cmdline, len(vifs)) + vifs = vm.config_devices("vif") + vm.create_domain("linux", kernel, ramdisk, cmdline, len(vifs)) return vm -def vm_image_netbsd(config, name, memory, image): +def vm_image_netbsd(vm, image): """Create a VM for a bsd image. name vm name @@ -685,9 +740,8 @@ def vm_image_netbsd(config, name, memory, image): if args: cmdline += " " + args ramdisk = sxp.child_value(image, "ramdisk") - vifs = config_devices(config, "vif") - vm = xen_domain_create(config, "netbsd", name, memory, kernel, - ramdisk, cmdline, len(vifs)) + vifs = vm.config_devices("vif") + vm.create_domain("netbsd", kernel, ramdisk, cmdline, len(vifs)) return vm @@ -698,18 +752,18 @@ def vm_dev_vif(vm, val, index): val vif config index vif index """ - if vm.net_controller: - raise VmError('vif: vif in control domain') + if vm.netif_backend: + raise VmError('vif: vif in netif backend domain') vif = index #todo vmac = sxp.child_value(val, "mac") - defer = make_vif(vm.dom, vif, vmac) + defer = make_vif(vm.dom, vif, vmac, vm.recreate) def fn(id): + dev = xend.netif_dev(vm.dom, vif) + devid = sxp.attribute(val, 'id') + if devid: + dev.setprop('id', devid) bridge = sxp.child_value(val, "bridge") - bridge = XendBridge.vif_bridge_add(vm.dom, vif, bridge) - dev = ['vif', ['vif', vif], ['bridge', bridge] ] - netdev = xend.netif_dev(vm.dom, vif) - if netdev and netdev.mac: - dev += [ 'mac', netdev.mac ] + dev.bridge_add(bridge) vm.add_device('vif', dev) print 'vm_dev_vif> created', dev return id @@ -723,8 +777,9 @@ def vm_dev_vbd(vm, val, index): val vbd config index vbd index """ - if vm.block_controller: - raise VmError('vbd: vbd in control domain') + if vm.blkif_backend: + raise VmError('vbd: vbd in blkif backend domain') + vdev = index uname = sxp.child_value(val, 'uname') if not uname: raise VMError('vbd: Missing uname') @@ -732,9 +787,10 @@ def vm_dev_vbd(vm, val, index): if not dev: raise VMError('vbd: Missing dev') mode = sxp.child_value(val, 'mode', 'r') - defer = make_disk(vm.dom, uname, dev, mode) + defer = make_disk(vm.dom, uname, dev, mode, vm.recreate) def fn(vbd): - vm.add_device('vbd', val) + dev = xend.blkif_dev(vm.dom, vdev) + vm.add_device('vbd', dev) return vbd defer.addCallback(fn) return defer @@ -765,7 +821,8 @@ def vm_dev_pci(vm, val, index): func = parse_pci(func) except: raise VMError('pci: invalid parameter') - rc = xc.physdev_pci_access_modify(dom=vm.dom, bus=bus, dev=dev, func=func, enable=1) + rc = xc.physdev_pci_access_modify(dom=vm.dom, bus=bus, dev=dev, + func=func, enable=1) if rc < 0: #todo non-fatal raise VMError('pci: Failed to configure device: bus=%s dev=%s func=%s' % @@ -833,13 +890,11 @@ def vm_field_vnet(vm, config, val, index): if id is None: raise VmError('vnet: missing vif id') dev = vm.get_device_by_id('vif', id) - if not sxp.elementp(dev, 'vif'): - raise VmError('vnet: invalid vif id %s' % id) - vnet = sxp.child_value(v, 'vnet', 1) - mac = sxp.child_value(dev, 'mac') - vif = sxp.child_value(dev, 'vif') - vnet_bridge(vnet, mac, vm.dom, 0) - vm.add_config([ 'vif.vnet', ['id', id], ['vnet', vnet], ['mac', mac]]) + #vnet = sxp.child_value(v, 'vnet', 1) + #mac = sxp.child_value(dev, 'mac') + #vif = sxp.child_value(dev, 'vif') + #vnet_bridge(vnet, mac, vm.dom, 0) + #vm.add_config([ 'vif.vnet', ['id', id], ['vnet', vnet], ['mac', mac]]) # Register image handlers for linux and bsd. add_image_handler('linux', vm_image_linux) diff --git a/tools/xenmgr/lib/server/SrvConsoleServer.py b/tools/xenmgr/lib/server/SrvConsoleServer.py index 7179a0d1d4..94b7c8e6c8 100644 --- a/tools/xenmgr/lib/server/SrvConsoleServer.py +++ b/tools/xenmgr/lib/server/SrvConsoleServer.py @@ -584,28 +584,31 @@ class Daemon: reactor.diconnectAll() sys.exit(0) - def blkif_set_control_domain(self, dom): + def blkif_set_control_domain(self, dom, recreate=0): """Set the block device backend control domain. """ - return self.blkifCF.setControlDomain(dom) + return self.blkifCF.setControlDomain(dom, recreate=recreate) def blkif_get_control_domain(self, dom): """Get the block device backend control domain. """ return self.blkifCF.getControlDomain() - def blkif_create(self, dom): + def blkif_create(self, dom, recreate=0): """Create a block device interface controller. Returns Deferred """ - d = self.blkifCF.createInstance(dom) + d = self.blkifCF.createInstance(dom, recreate=recreate) return d + def blkif_get(self, dom): + return self.blkifCF.getInstanceByDom(dom) + def blkif_dev(self, dom, vdev): return self.blkifCF.getDomainDevice(dom, vdev) - def blkif_dev_create(self, dom, vdev, mode, segment): + def blkif_dev_create(self, dom, vdev, mode, segment, recreate=0): """Create a block device. Returns Deferred @@ -614,26 +617,29 @@ class Daemon: if not ctrl: raise ValueError('No blkif controller: %d' % dom) print 'blkif_dev_create>', dom, vdev, mode, segment - d = ctrl.attach_device(vdev, mode, segment) + d = ctrl.attachDevice(vdev, mode, segment, recreate=recreate) return d - def netif_set_control_domain(self, dom): + def netif_set_control_domain(self, dom, recreate=0): """Set the network interface backend control domain. """ - return self.netifCF.setControlDomain(dom) + return self.netifCF.setControlDomain(dom, recreate=recreate) def netif_get_control_domain(self, dom): """Get the network interface backend control domain. """ return self.netifCF.getControlDomain() - def netif_create(self, dom): + def netif_create(self, dom, recreate=0): """Create a network interface controller. """ - return self.netifCF.createInstance(dom) + return self.netifCF.createInstance(dom, recreate=recreate) + + def netif_get(self, dom): + return self.netifCF.getInstanceByDom(dom) - def netif_dev_create(self, dom, vif, vmac): + def netif_dev_create(self, dom, vif, vmac, recreate=0): """Create a network device. todo @@ -641,7 +647,7 @@ class Daemon: ctrl = self.netifCF.getInstanceByDom(dom) if not ctrl: raise ValueError('No netif controller: %d' % dom) - d = ctrl.attach_device(vif, vmac) + d = ctrl.attachDevice(vif, vmac, recreate=recreate) return d def netif_dev(self, dom, vif): diff --git a/tools/xenmgr/lib/server/SrvDomain.py b/tools/xenmgr/lib/server/SrvDomain.py index 5034869e63..d8287e05ea 100644 --- a/tools/xenmgr/lib/server/SrvDomain.py +++ b/tools/xenmgr/lib/server/SrvDomain.py @@ -32,8 +32,8 @@ class SrvDomain(SrvDir): req.setHeader("Location", "%s/.." % req.prePathURL()) return val - def op_halt(self, op, req): - val = self.xd.domain_halt(self.dom.id) + def op_destroy(self, op, req): + val = self.xd.domain_destroy(self.dom.id) req.setHeader("Location", "%s/.." % req.prePathURL()) return val @@ -196,7 +196,7 @@ class SrvDomain(SrvDir): req.write('<input type="submit" name="op" value="unpause">') req.write('<input type="submit" name="op" value="pause">') req.write('<input type="submit" name="op" value="shutdown">') - req.write('<input type="submit" name="op" value="halt">') + req.write('<input type="submit" name="op" value="destroy">') req.write('<br><input type="submit" name="op" value="migrate">') req.write('To: <input type="text" name="destination">') req.write('</form>') diff --git a/tools/xenmgr/lib/server/blkif.py b/tools/xenmgr/lib/server/blkif.py index 8da827baa9..92706316b1 100755 --- a/tools/xenmgr/lib/server/blkif.py +++ b/tools/xenmgr/lib/server/blkif.py @@ -1,3 +1,5 @@ +from twisted.internet import defer + import channel import controller from messages import * @@ -22,7 +24,7 @@ class BlkifControllerFactory(controller.ControllerFactory): self.attached = 1 self.registerChannel() - def createInstance(self, dom): + def createInstance(self, dom, recreate=0): d = self.addDeferred() blkif = self.getInstanceByDom(dom) if blkif: @@ -30,7 +32,10 @@ class BlkifControllerFactory(controller.ControllerFactory): else: blkif = BlkifController(self, dom) self.addInstance(blkif) - blkif.send_be_create() + if recreate: + self.callDeferred(blkif) + else: + blkif.send_be_create() return d def getDomainDevices(self, dom): @@ -41,10 +46,11 @@ class BlkifControllerFactory(controller.ControllerFactory): blkif = self.getInstanceByDom(dom) return (blkif and blkif.getDevice(vdev)) or None - def setControlDomain(self, dom): + def setControlDomain(self, dom, recreate=0): if self.dom == dom: return self.deregisterChannel() - self.attached = 0 + if not recreate: + self.attached = 0 self.dom = dom self.registerChannel() # @@ -55,6 +61,28 @@ class BlkifControllerFactory(controller.ControllerFactory): def getControlDomain(self): return self.dom + def reattachDevice(self, dom, vdev): + blkif = self.getInstanceByDom(dom) + if blkif: + blkif.reattachDevice(vdev) + self.attached = self.devicesAttached() + if self.attached: + self.reattached() + + def devicesAttached(self): + """Check if all devices are attached. + """ + attached = 1 + for blkif in self.getInstances(): + if not blkif.attached: + attached = 0 + break + return attached + + def reattached(self): + for blkif in self.getInstances(): + blkif.reattached() + def recv_be_create(self, msg, req): #print 'recv_be_create>' val = unpackMsg('blkif_be_create_t', msg) @@ -86,29 +114,7 @@ class BlkifControllerFactory(controller.ControllerFactory): if self.attached: self.callDeferred(0) else: - self.reattach_device(val['domid'], val['vdevice']) - - def reattach_device(self, dom, vdev): - blkif = self.getInstanceByDom(dom) - if blkif: - blkif.reattach_device(vdev) - self.attached = self.devices_attached() - if self.attached: - self.reattached() - - def devices_attached(self): - """Check if all devices are attached. - """ - attached = 1 - for blkif in self.getInstances(): - if not blkif.attached: - attached = 0 - break - return attached - - def reattached(self): - for blkif in self.getInstances(): - blkif.reattached() + self.reattachDevice(val['domid'], val['vdevice']) def recv_be_driver_status_changed(self, msg, req): val = unpackMsg('blkif_be_driver_status_changed_t', msg) @@ -117,11 +123,12 @@ class BlkifControllerFactory(controller.ControllerFactory): for blkif in self.getInstances(): blkif.detach() -class BlkDev: +class BlkDev(controller.Dev): """Info record for a block device. """ - def __init__(self, vdev, mode, segment): + def __init__(self, ctrl, vdev, mode, segment): + controller.Dev.__init__(self, ctrl) self.vdev = vdev self.mode = mode self.device = segment['device'] @@ -131,6 +138,14 @@ class BlkDev: def readonly(self): return 'w' not in self.mode + + def sxpr(self): + print 'BlkDev>sxpr>', vars(self) + val = ['blkif', ['vdev', self.vdev], ['mode', self.mode] ] + return val + + def destroy(self): + self.controller.send_be_vbd_destroy(self.vdev) class BlkifController(controller.Controller): """Block device interface controller. Handles all block devices @@ -154,21 +169,43 @@ class BlkifController(controller.Controller): self.registerChannel() #print 'BlkifController<', 'dom=', self.dom, 'idx=', self.idx + def lostChannel(self): + print 'BlkifController>lostChannel>', 'dom=', self.dom + #self.destroyDevices() + controller.Controller.lostChannel(self) + def getDevices(self): return self.devices.values() def getDevice(self, vdev): return self.devices.get(vdev) - def attach_device(self, vdev, mode, segment): + def addDevice(self, vdev, mode, segment): + if vdev in self.devices: return None + dev = BlkDev(self, vdev, mode, segment) + self.devices[vdev] = dev + return dev + + def attachDevice(self, vdev, mode, segment, recreate=0): """Attach a device to the specified interface. """ #print 'BlkifController>attach_device>', self.dom, vdev, mode, segment - if vdev in self.devices: return -1 - dev = BlkDev(vdev, mode, segment) - self.devices[vdev] = dev - self.send_be_vbd_create(vdev) - return self.factory.addDeferred() + dev = self.addDevice(vdev, mode, segment) + if not dev: return -1 + if recreate: + d = defer.Deferred() + d.callback(self) + else: + self.send_be_vbd_create(vdev) + d = self.factory.addDeferred() + return d + + def destroy(self): + self.destroyDevices() + + def destroyDevices(self): + for dev in self.getDevices(): + dev.destroy() def detach(self): """Detach all devices, when the back-end control domain has changed. @@ -178,7 +215,7 @@ class BlkifController(controller.Controller): dev.attached = 0 self.send_be_vbd_create(vdev) - def reattach_device(self, vdev): + def reattachDevice(self, vdev): """Reattach a device, when the back-end control domain has changed. """ dev = self.devices[vdev] @@ -254,4 +291,13 @@ class BlkifController(controller.Controller): 'extent.sector_start' : dev.start_sector, 'extent.sector_length' : dev.nr_sectors }) self.factory.writeRequest(msg) + + def send_be_vbd_destroy(self, vdev): + dev = self.devices[vdev] + msg = packMsg('blkif_be_vbd_destroy_t', + { 'domid' : self.dom, + 'blkif_handle' : 0, + 'vdevice' : dev.vdev }) + del self.devices[vdev] + self.factory.writeRequest(msg) diff --git a/tools/xenmgr/lib/server/controller.py b/tools/xenmgr/lib/server/controller.py index 791e987511..cb543fa57c 100755 --- a/tools/xenmgr/lib/server/controller.py +++ b/tools/xenmgr/lib/server/controller.py @@ -95,7 +95,7 @@ class ControllerFactory(CtrlMsgRcvr): if instance.idx in self.instances: del self.instances[instance.idx] - def createInstance(self, dom): + def createInstance(self, dom, recreate=0): raise NotImplementedError() def instanceClosed(self, instance): @@ -136,3 +136,30 @@ class Controller(CtrlMsgRcvr): def lostChannel(self): self.factory.instanceClosed(self) + +class Dev: + + def __init__(self, controller): + self.controller = controller + self.props = {} + + def setprop(self, k, v): + self.props[k] = v + + def getprop(self, k, v=None): + return self.props.get(k, v) + + def hasprop(self, k): + return k in self.props + + def delprop(self, k): + if k in self.props: + del self.props[k] + + #def __repr__(self): + # return str(self.sxpr()) + + def sxpr(self): + raise NotImplementedError() + + diff --git a/tools/xenmgr/lib/server/messages.py b/tools/xenmgr/lib/server/messages.py index 78bc24526f..0313670279 100644 --- a/tools/xenmgr/lib/server/messages.py +++ b/tools/xenmgr/lib/server/messages.py @@ -76,6 +76,9 @@ blkif_formats = { 'blkif_be_vbd_grow_t': (CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_GROW), + 'blkif_be_vbd_destroy_t': + (CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_DESTROY), + 'blkif_fe_interface_status_changed_t': (CMSG_BLKIF_FE, CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED), diff --git a/tools/xenmgr/lib/server/netif.py b/tools/xenmgr/lib/server/netif.py index f3da86ba82..d2a6a53860 100755 --- a/tools/xenmgr/lib/server/netif.py +++ b/tools/xenmgr/lib/server/netif.py @@ -1,5 +1,9 @@ import random +from twisted.internet import defer + +from xenmgr import XendBridge + import channel import controller from messages import * @@ -22,7 +26,7 @@ class NetifControllerFactory(controller.ControllerFactory): self.attached = 1 self.registerChannel() - def createInstance(self, dom): + def createInstance(self, dom, recreate=0): """Create or find the network interface controller for a domain. """ #print 'netif>createInstance> dom=', dom @@ -40,12 +44,13 @@ class NetifControllerFactory(controller.ControllerFactory): netif = self.getInstanceByDom(dom) return (netif and netif.getDevice(vif)) or None - def setControlDomain(self, dom): + def setControlDomain(self, dom, recreate=0): """Set the 'back-end' device driver domain. """ if self.dom == dom: return self.deregisterChannel() - self.attached = 0 + if not recreate: + self.attached = 0 self.dom = dom self.registerChannel() # @@ -98,20 +103,38 @@ class NetifControllerFactory(controller.ControllerFactory): ## print "Done notifying guests" ## recovery = False -class NetDev: +class NetDev(controller.Dev): """Info record for a network device. """ - def __init__(self, vif, mac): + def __init__(self, ctrl, vif, mac): + controller.Dev.__init__(self, ctrl) self.vif = vif self.mac = mac self.evtchn = None + self.bridge = None def sxpr(self): vif = str(self.vif) mac = ':'.join(map(lambda x: "%x" % x, self.mac)) - return ['netif', ['vif', vif], ['mac', mac]] - + val = ['netif', ['vif', vif], ['mac', mac]] + if self.bridge: + val += ['bridge', self.bridge] + return val + + def bridge_add(self, bridge): + self.bridge = XendBridge.vif_bridge_add(self.controller.dom, self.vif, bridge) + + def bridge_rem(self): + if not self.bridge: return + XendBridge.vif_bridge_rem(self.controller.dom, self.vif, self.bridge) + self.bridge = None + + def destroy(self): + self.bridge_rem() + self.controller.send_be_destroy(self.vif) + + class NetifController(controller.Controller): """Network interface controller. Handles all network devices for a domain. """ @@ -150,8 +173,7 @@ class NetifController(controller.Controller): def lostChannel(self): print 'NetifController>lostChannel>', 'dom=', self.dom - #for vif in self.devices: - # self.send_be_destroy(vif) + #self.destroyDevices() controller.Controller.lostChannel(self) def getDevices(self): @@ -167,11 +189,18 @@ class NetifController(controller.Controller): mac = [ int(x, 16) for x in vmac.split(':') ] if len(mac) != 6: raise ValueError("invalid mac") #print "attach_device>", "vif=", vif, "mac=", mac - dev = NetDev(vif, mac) + dev = NetDev(self, vif, mac) self.devices[vif] = dev return dev - def attach_device(self, vif, vmac): + def destroy(self): + self.destroyDevices() + + def destroyDevices(self): + for dev in self.getDevices(): + dev.destroy() + + def attachDevice(self, vif, vmac, recreate=0): """Attach a network device. If vmac is None a random mac address is assigned. @@ -179,8 +208,12 @@ class NetifController(controller.Controller): @param vmac mac address (string) """ self.addDevice(vif, vmac) - d = self.factory.addDeferred() - self.send_be_create(vif) + if recreate: + d = defer.Deferred() + d.callback(self) + else: + d = self.factory.addDeferred() + self.send_be_create(vif) return d def reattach_devices(self): diff --git a/tools/xenmgr/lib/xm/create.py b/tools/xenmgr/lib/xm/create.py index 29b4c7b6dc..e3ccad20b7 100644 --- a/tools/xenmgr/lib/xm/create.py +++ b/tools/xenmgr/lib/xm/create.py @@ -1,3 +1,6 @@ +# Copyright (C) 2004 Mike Wray <mike.wray@hp.com> +"""Domain creation. +""" import string import sys @@ -13,7 +16,7 @@ Create a domain. """) gopts.opt('help', short='h', - fn=set_value, default=0, + fn=set_true, default=0, use="Print this help.") gopts.opt('quiet', short='q', @@ -36,21 +39,19 @@ gopts.opt('load', short='L', val='FILE', fn=set_value, default=None, use='Domain saved state to load.') -def set_var(opt, k, v): - opt.set(v) - for d in string.split(v, ';' ): - (k, v) = string.split(d, '=') - opt.opts.setvar(k, v) - gopts.opt('define', short='D', val='VAR=VAL', fn=set_var, default=None, - use="""Set variables before loading defaults, e.g. '-D vmid=3;ip=1.2.3.4' - to set vmid and ip.""") + use="""Set a variable before loading defaults, e.g. '-D vmid=3' + to set vmid. May be repeated to set more thanone variable.""") gopts.opt('dryrun', short='n', fn=set_true, default=0, use="Dry run - print the config but don't create the domain.") +gopts.opt('name', short='N', val='NAME', + fn=set_value, default=None, + use="Domain name.") + gopts.opt('console', short='c', fn=set_true, default=0, use="Connect to console after domain is created.") @@ -71,34 +72,48 @@ gopts.opt('memory', short='m', val='MEMORY', fn=set_value, default=128, use="Domain memory in MB.") +gopts.opt('blkif', + fn=set_true, default=0, + use="Make the domain a block device backend.") + +gopts.opt('netif', + fn=set_true, default=0, + use="Make the domain a network interface backend.") + gopts.opt('disk', short='d', val='phy:DEV,VDEV,MODE', fn=append_value, default=[], use="""Add a disk device to a domain. The physical device is DEV, which - is exported to the domain as VDEV. The disk is read-only if MODE is r, - read-write if mode is 'w'.""") + is exported to the domain as VDEV. The disk is read-only if MODE + is 'r', read-write if MODE is 'w'. + The option may be repeated to add more than one disk. + """) gopts.opt('pci', val='BUS,DEV,FUNC', fn=append_value, default=[], - use="""Add a PCI device to a domain, using given params (in hex).""") + use="""Add a PCI device to a domain, using given params (in hex). + For example '-pci c0,02,1a'. + The option may be repeated to add more than one pci device. + """) gopts.opt('ipaddr', short='i', val="IPADDR", fn=append_value, default=[], use="Add an IP address to the domain.") -gopts.opt('mac', short='M', val="MAC", +gopts.opt('vif', val="mac=MAC,bridge=BRIDGE", fn=append_value, default=[], - use="""Add a network interface with the given mac address to the domain. - More than one interface may be specified. Interfaces with unspecified MAC addresses - are allocated a random address.""") + use="""Add a network interface with the given MAC address and bridge. + If mac is not specified a random MAC address is used. + If bridge is not specified the default bridge is used. + This option may be repeated to add more than one vif. + Specifying vifs will increase the number of interfaces as needed. + """) -gopts.opt('nics', val="N", +gopts.opt('nics', val="NUM", fn=set_int, default=1, - use="Set the number of network interfaces.") - -gopts.opt('vnet', val='VNET', - fn=append_value, default=[], - use="""Define the vnets for the network interfaces. - More than one vnet may be given, they are used in order. + use="""Set the number of network interfaces. + Use the vif option to define interface parameters, otherwise + defaults are used. Specifying vifs will increase the + number of interfaces as needed. """) gopts.opt('root', short='R', val='DEVICE', @@ -116,15 +131,15 @@ gopts.opt('ip', short='I', val='IPADDR', gopts.opt('gateway', val="IPADDR", fn=set_value, default='', - use="Set kernel IP gateway.") + use="Set the kernel IP gateway.") gopts.opt('netmask', val="MASK", fn=set_value, default = '', - use="Set kernel IP netmask.") + use="Set the kernel IP netmask.") gopts.opt('hostname', val="NAME", fn=set_value, default='', - use="Set kernel IP hostname.") + use="Set the kernel IP hostname.") gopts.opt('interface', val="INTF", fn=set_value, default="eth0", @@ -132,7 +147,7 @@ gopts.opt('interface', val="INTF", gopts.opt('dhcp', val="off|dhcp", fn=set_value, default='off', - use="Set kernel dhcp option.") + use="Set the kernel dhcp option.") gopts.opt('nfs_server', val="IPADDR", fn=set_value, default=None, @@ -143,19 +158,16 @@ gopts.opt('nfs_root', val="PATH", use="Set the path of the root NFS directory.") def strip(pre, s): + """Strip prefix 'pre' if present. + """ if s.startswith(pre): return s[len(pre):] else: return s -def make_config(opts): - - config = ['config', - ['name', opts.name ], - ['memory', opts.memory ] ] - if opts.cpu: - config.append(['cpu', opts.cpu]) - +def configure_image(config, opts): + """Create the image config. + """ config_image = [ opts.builder ] config_image.append([ 'kernel', os.path.abspath(opts.kernel) ]) if opts.ramdisk: @@ -169,8 +181,10 @@ def make_config(opts): if opts.extra: config_image.append(['args', opts.extra]) config.append(['image', config_image ]) - - config_devs = [] + +def configure_disks(config_devs, opts): + """Create the config for disks (virtual block devices). + """ for (uname, dev, mode) in opts.disk: config_vbd = ['vbd', ['uname', uname], @@ -178,18 +192,34 @@ def make_config(opts): ['mode', mode ] ] config_devs.append(['device', config_vbd]) +def configure_pci(config_devs, opts): + """Create the config for pci devices. + """ for (bus, dev, func) in opts.pci: config_pci = ['pci', ['bus', bus], ['dev', dev], ['func', func]] config_devs.append(['device', config_pci]) - for idx in range(0, opts.nics): - config_vif = ['vif', ['@', ['id', 'vif%d' % idx]]] - if idx < len(opts.mac): - config_vif.append(['mac', opts.mac[idx]]) +def configure_vifs(config_devs, opts): + """Create the config for virtual network interfaces. + """ + vifs = opts.vif + vifs_n = max(opts.nics, len(vifs)) + + for idx in range(0, vifs_n): + if idx < len(vifs): + d = vifs[idx] + mac = d.get('mac') + bridge = d.get('bridge') + else: + mac = None + bridge = None + config_vif = ['vif'] + if mac: + config_vif.append(['mac', mac]) + if bridge: + config_vif.append(['bridge', bridge]) config_devs.append(['device', config_vif]) - config += config_devs - ## if vfr_ipaddr: ## config_vfr = ['vfr'] ## idx = 0 # No way of saying which IP is for which vif? @@ -197,15 +227,27 @@ def make_config(opts): ## config_vfr.append(['vif', ['id', idx], ['ip', ip]]) ## config.append(config_vfr) - if opts.vnet: - config_vnet = ['vnet'] - idx = 0 - for vnet in opts.vnet: - config_vif = ['vif', ['id', 'vif%d' % idx], ['vnet', vnet]] - config_vnet.append(config_vif) - idx += 1 - config.append(config_vnet) - + +def make_config(opts): + """Create the domain configuration. + """ + + config = ['vm', + ['name', opts.name ], + ['memory', opts.memory ] ] + if opts.cpu: + config.append(['cpu', opts.cpu]) + if opts.blkif: + config.append(['backend', ['blkif']]) + if opts.netif: + config.append(['backend', ['netif']]) + + configure_image(config, opts) + config_devs = [] + configure_disks(config_devs, opts) + configure_pci(config_devs, opts) + configure_vifs(config_devs, opts) + config += config_devs return config def preprocess_disk(opts): @@ -213,7 +255,6 @@ def preprocess_disk(opts): disk = [] for v in opts.disk: d = v.split(',') - print 'disk', v, d if len(d) != 3: opts.err('Invalid disk specifier: ' + v) disk.append(d) @@ -231,6 +272,22 @@ def preprocess_pci(opts): pci.append(hexd) opts.pci = pci +def preprocess_vifs(opts): + if not opts.vif: return + vifs = [] + for vif in opts.vif: + d = {} + a = vif.split(',') + for b in a: + (k, v) = b.strip().split('=') + k = k.strip() + v = v.strip() + if k not in ['mac', 'bridge']: + opts.err('Invalid vif specifier: ' + vif) + d[k] = v + vifs.append(d) + opts.vif = vifs + def preprocess_ip(opts): setip = (opts.hostname or opts.netmask or opts.gateway or opts.dhcp or opts.interface) @@ -259,6 +316,7 @@ def preprocess(opts): opts.err("No kernel specified") preprocess_disk(opts) preprocess_pci(opts) + preprocess_vifs(opts) preprocess_ip(opts) preprocess_nfs(opts) @@ -266,8 +324,8 @@ def make_domain(opts, config): """Create, build and start a domain. Returns: [int] the ID of the new domain. """ - if opts.load: - filename = os.path.abspath(opts.load) + if opts.vals.load: + filename = os.path.abspath(opts.vals.load) dominfo = server.xend_domain_restore(filename, config) else: dominfo = server.xend_domain_create(config) @@ -280,7 +338,7 @@ def make_domain(opts, config): console_port = None if server.xend_domain_unpause(dom) < 0: - server.xend_domain_halt(dom) + server.xend_domain_destroy(dom) opts.err("Failed to start domain %d" % dom) opts.info("Started domain %d, console on port %d" % (dom, console_port)) @@ -289,16 +347,16 @@ def make_domain(opts, config): def main(argv): opts = gopts args = opts.parse(argv) - if opts.help: + if opts.vals.help: opts.usage() return - if opts.config: + if opts.vals.config: pass else: opts.load_defaults() - preprocess(opts) - config = make_config(opts) - if opts.dryrun: + preprocess(opts.vals) + config = make_config(opts.vals) + if opts.vals.dryrun: PrettyPrint.prettyprint(config) else: make_domain(opts, config) diff --git a/tools/xenmgr/lib/xm/main.py b/tools/xenmgr/lib/xm/main.py index f124d63518..86f606fd83 100644 --- a/tools/xenmgr/lib/xm/main.py +++ b/tools/xenmgr/lib/xm/main.py @@ -10,27 +10,72 @@ from xenmgr import sxp from xenmgr.XendClient import server from xenmgr.xm import create, shutdown +class Prog: + """Base class for sub-programs. + """ + + """Program group it belongs to""" + group = 'all' + """Program name.""" + name = '??' + """Short program info.""" + info = '' + + def __init__(self, xm): + self.xm = xm + + def err(self, msg): + self.xm.err(msg) + + def help(self, args): + self.shortHelp(args) + + def shortHelp(self, args): + print "%-14s %s" % (self.name, self.info) + + def main(self, args): + """Program main entry point. + """ + pass + + +class ProgUnknown(Prog): + + name = 'unknown' + info = '' + + def help(self, args): + self.xm.err("Unknown command: %s\nTry '%s help' for more information." + % (args[0], self.xm.name)) + + main = help + class Xm: + """Main application. + """ def __init__(self): - self.prog = 'xm' - pass + self.name = 'xm' + self.unknown = ProgUnknown(self) + self.progs = {} def err(self, msg): print >>sys.stderr, "Error:", msg sys.exit(1) def main(self, args): - """Main entry point. Dispatches to the xm_ methods. + """Main entry point. Dispatches to the progs. """ - self.prog = args[0] + self.name = args[0] if len(args) < 2: self.err("Missing command\nTry '%s help' for more information." - % self.prog) - prog = 'xm_' + args[1] + % self.name) help = self.helparg(args) - fn = getattr(self, prog, self.unknown) - fn(help, args[1:]) + p = self.getprog(args[1], self.unknown) + if help: + p.help(args[1:]) + else: + p.main(args[1:]) def helparg(self, args): for a in args: @@ -38,39 +83,87 @@ class Xm: return 1 return 0 - def unknown(self, help, args): - if help and len(args) == 1: - self.xm_help(help, args) - else: - self.err("Unknown command: %s\nTry '%s help' for more information." - % (args[0], self.prog)) + def prog(self, pklass): + """Add a sub-program. + + pklass program class (Prog subclass) + """ + p = pklass(self) + self.progs[p.name] = p + return p + + def getprog(self, name, val=None): + """Get a sub-program. + """ + return self.progs.get(name, val) - def help(self, meth, args): - """Print help on an xm_ method. - Uses the method documentation string if there is one. + def proglist(self): + """Get a list of sub-programs, ordered by group. """ - name = meth[3:] - f = getattr(self, meth) - print "%-14s %s" % (name, f.__doc__ or '') - - def xm_help(self, help, args): - """Print help.""" - for k in dir(self): - if not k.startswith('xm_'): continue - self.help(k, args) - print "\nTry '%s CMD -h' for help on CMD" % self.prog - - def xm_create(self, help, args): - """Create a domain.""" + groups = {} + for p in self.progs.values(): + l = groups.get(p.group, []) + l.append(p) + groups[p.group] = l + kl = groups.keys() + kl.sort() + pl = [] + for k in kl: + l = groups[k] + l.sort() + pl += l + return pl + +# Create the application object, then add the sub-program classes. +xm = Xm() + +class ProgHelp(Prog): + + name = "help" + info = "Print help." + + def help(self, args): + if len(args) == 2: + name = args[1] + p = self.xm.getprog(name) + if p: + p.help(args) + else: + print '%s: Unknown command: %s' % (self.name, name) + else: + for p in self.xm.proglist(): + p.shortHelp(args) + print "\nTry '%s help CMD' for help on CMD" % self.xm.name + + main = help + +xm.prog(ProgHelp) + +class ProgCreate(Prog): + + group = 'domain' + name = "create" + info = """Create a domain.""" + + def help(self, args): + create.main([args[0], '-h']) + + def main(self, args): create.main(args) - def xm_save(self, help, args): - """Save domain state (and config) to file.""" - if help: - print args[0], "DOM FILE [CONFIG]" - print """\nSave domain with id DOM to FILE. - Optionally save config to CONFIG.""" - return +xm.prog(ProgCreate) + +class ProgSave(Prog): + group = 'domain' + name = "save" + info = """Save domain state (and config) to file.""" + + def help(self, args): + print args[0], "DOM FILE [CONFIG]" + print """\nSave domain with id DOM to FILE. + Optionally save config to CONFIG.""" + + def main(self, args): if len(args) < 3: self.err("%s: Missing arguments" % args[0]) dom = args[1] savefile = os.path.abspath(args[2]) @@ -83,23 +176,45 @@ class Xm: PrettyPrint.prettyprint(config, out=out) out.close() server.xend_domain_save(dom, savefile) - - def xm_restore(self, help, args): - """Create a domain from a saved state.""" - if help: - print args[0], "FILE CONFIG" - print "\nRestore a domain from FILE using configuration CONFIG." - return + +xm.prog(ProgSave) + +class ProgRestore(Prog): + group = 'domain' + name = "restore" + info = """Create a domain from a saved state.""" + + def help(self, args): + print args[0], "FILE CONFIG" + print "\nRestore a domain from FILE using configuration CONFIG." + + def main(self, help, args): if len(args) < 3: self.err("%s: Missing arguments" % args[0]) savefile = os.path.abspath(args[1]) configfile = os.path.abspath(args[2]) info = server.xend_domain_restore(savefile, configfile) PrettyPrint.prettyprint(info) - def xm_domains(self, help, args): - """List domains.""" - if help: self.help('xm_' + args[0], args); return - doms = server.xend_domains() +xm.prog(ProgRestore) + +class ProgList(Prog): + group = 'domain' + name = "list" + info = """List info about domains.""" + + def help(self, args): + if help: + print args[0], '[DOM...]' + print """\nGet information about domains. + Either all domains or the domains given.""" + return + + def main(self, args): + n = len(args) + if n == 1: + doms = server.xend_domains() + else: + doms = map(int, args[1:]) doms.sort() print 'Dom Name Mem(MB) CPU State Time(s)' for dom in doms: @@ -113,111 +228,167 @@ class Xm: d['cpu_time'] = float(sxp.child_value(info, 'cpu_time', '0')) print ("%(dom)-4d %(name)-16s %(mem)7d %(cpu)3d %(state)5s %(cpu_time)7.1f" % d) - def xm_domain(self, help, args): - """Get information about a domain.""" - if help: - print args[0], 'DOM' - print '\nGet information about domain DOM.' - return - if len(args) < 2: self.err("%s: Missing domain" % args[0]) - dom = args[1] - info = server.xend_domain(dom) - PrettyPrint.prettyprint(info) - print +xm.prog(ProgList) - def xm_halt(self, help, args): - """Terminate a domain immediately.""" - if help: - print args[0], 'DOM' - print '\nTerminate domain DOM immediately.' - return +class ProgDestroy(Prog): + group = 'domain' + name = "destroy" + info = """Terminate a domain immediately.""" + + def help(self, args): + print args[0], 'DOM' + print '\nTerminate domain DOM immediately.' + + def main(self, args): if len(args) < 2: self.err("%s: Missing domain" % args[0]) dom = args[1] - server.xend_domain_halt(dom) + server.xend_domain_destroy(dom) + +xm.prog(ProgDestroy) + +class ProgShutdown(Prog): + group = 'domain' + name = "shutdown" + info = """Shutdown a domain.""" - def xm_shutdown(self, help, args): - """Shutdown a domain.""" + def help(self, args): + print args[0], 'DOM' + print '\nSignal domain DOM to shutdown.' + + def main(self, args): shutdown.main(args) - def xm_pause(self, help, args): - """Pause execution of a domain.""" - if help: - print args[0], 'DOM' - print '\nPause execution of domain DOM.' - return +xm.prog(ProgShutdown) + +class ProgPause(Prog): + group = 'domain' + name = "pause" + info = """Pause execution of a domain.""" + + def help(self, args): + print args[0], 'DOM' + print '\nPause execution of domain DOM.' + + def main(self, args): if len(args) < 2: self.err("%s: Missing domain" % args[0]) dom = args[1] server.xend_domain_pause(dom) - def xm_unpause(self, help, args): - """Unpause a paused domain.""" - if help: - print args[0], 'DOM' - print '\nUnpause execution of domain DOM.' - return +xm.prog(ProgPause) + +class ProgUnpause(Prog): + group = 'domain' + name = "unpause" + info = """Unpause a paused domain.""" + + def help(self, args): + print args[0], 'DOM' + print '\nUnpause execution of domain DOM.' + + def main(self, args): if len(args) < 2: self.err("%s: Missing domain" % args[0]) dom = args[1] server.xend_domain_unpause(dom) - def xm_pincpu(self, help, args): - """Pin a domain to a cpu. """ - if help: - print args[0],'DOM CPU' - print '\nPin domain DOM to cpu CPU.' - return +xm.prog(ProgUnpause) + +class ProgPincpu(Prog): + group = 'domain' + name = "pincpu" + info = """Pin a domain to a cpu. """ + + def help(self, args): + print args[0],'DOM CPU' + print '\nPin domain DOM to cpu CPU.' + + def main(self, args): if len(args) != 3: self.err("%s: Invalid argument(s)" % args[0]) v = map(int, args[1:3]) server.xend_domain_pincpu(*v) - def xm_bvt(self, help, args): - """Set BVT scheduler parameters.""" - if help: - print args[0], "DOM MCUADV WARP WARPL WARPU" - print '\nSet Borrowed Virtual Time scheduler parameters.' - return +xm.prog(ProgPincpu) + +class ProgBvt(Prog): + group = 'scheduler' + name = "bvt" + info = """Set BVT scheduler parameters.""" + + def help(self, args): + print args[0], "DOM MCUADV WARP WARPL WARPU" + print '\nSet Borrowed Virtual Time scheduler parameters.' + + def main(self, args): if len(args) != 6: self.err("%s: Invalid argument(s)" % args[0]) v = map(int, args[1:6]) server.xend_domain_cpu_bvt_set(*v) - def xm_bvtslice(self, help, args): - """Set the BVT scheduler slice.""" - if help: - print args[0], 'SLICE' - print '\nSet Borrowed Virtual Time scheduler slice.' - return +xm.prog(ProgBvt) + +class ProgBvtslice(Prog): + group = 'scheduler' + name = "bvtslice" + info = """Set the BVT scheduler slice.""" + + def help(self, args): + print args[0], 'SLICE' + print '\nSet Borrowed Virtual Time scheduler slice.' + + def main(self, args): if len(args) < 2: self.err('%s: Missing slice' % args[0]) server.xend_node_cpu_bvt_slice_set(slice) - def xm_atropos(self, help, args): - """Set atropos parameters.""" - if help: - print args[0], "DOM PERIOD SLICE LATENCY XTRATIME" - print "\nSet atropos parameters." - return +xm.prog(ProgBvtslice) + +class ProgAtropos(Prog): + group = 'scheduler' + name= "atropos" + info = """Set atropos parameters.""" + + def help(self, args): + print args[0], "DOM PERIOD SLICE LATENCY XTRATIME" + print "\nSet atropos parameters." + + def main(self, args): if len(args) != 5: self.err("%s: Invalid argument(s)" % args[0]) v = map(int, args[1:5]) server.xend_domain_cpu_atropos_set(*v) - def xm_rrobin(self, help, args): - """Set round robin slice.""" - if help: - print args[0], "SLICE" - print "\nSet round robin scheduler slice." - return +xm.prog(ProgAtropos) + +class ProgRrobin(Prog): + group = 'scheduler' + name = "rrobin" + info = """Set round robin slice.""" + + def help(self, args): + print args[0], "SLICE" + print "\nSet round robin scheduler slice." + + def main(self, args): if len(args) != 2: self.err("%s: Invalid argument(s)" % args[0]) rrslice = int(args[1]) server.xend_node_rrobin_set(rrslice) - def xm_info(self, help, args): - """Get information about the xen host.""" - if help: self.help('xm_' + args[0], args); return +xm.prog(ProgRrobin) + +class ProgInfo(Prog): + group = 'host' + name = "info" + info = """Get information about the xen host.""" + + def main(self, args): info = server.xend_node() for x in info[1:]: print "%-23s:" % x[0], x[1] - def xm_consoles(self, help, args): - """Get information about domain consoles.""" - if help: self.help('xm_' + args[0], args); return +xm.prog(ProgInfo) + +class ProgConsoles(Prog): + group = 'console' + name = "consoles" + info = """Get information about domain consoles.""" + + def main(self, args): l = server.xend_consoles() print "Dom Port Id" for x in l: @@ -228,12 +399,18 @@ class Xm: d['id'] = sxp.child_value(info, 'id', '?') print "%(dom)3s %(port)4s %(id)3s" % d - def xm_console(self, help, args): - """Open a console to a domain.""" - if help: - print "console DOM" - print "\nOpen a console to domain DOM." - return +xm.prog(ProgConsoles) + +class ProgConsole(Prog): + group = 'console' + name = "console" + info = """Open a console to a domain.""" + + def help(self, args): + print "console DOM" + print "\nOpen a console to domain DOM." + + def main(self, args): if len(args) < 2: self.err("%s: Missing domain" % args[0]) dom = args[1] info = server.xend_domain(dom) @@ -244,6 +421,7 @@ class Xm: from xenctl import console_client console_client.connect("localhost", int(port)) +xm.prog(ProgConsole) + def main(args): - xm = Xm() xm.main(args) diff --git a/tools/xenmgr/lib/xm/opts.py b/tools/xenmgr/lib/xm/opts.py index 426a6d24d1..b2cb2c7463 100644 --- a/tools/xenmgr/lib/xm/opts.py +++ b/tools/xenmgr/lib/xm/opts.py @@ -1,3 +1,6 @@ +# Copyright (C) 2004 Mike Wray <mike.wray@hp.com> +"""Object-oriented command-line option support. +""" from getopt import getopt import os import os.path @@ -5,8 +8,22 @@ import sys import types class Opt: + """An individual option. + """ def __init__(self, opts, name, short=None, long=None, val=None, fn=None, use=None, default=None): + """Create an option. + + opts parent options object + name name of the field it controls + short short (1-char) command line switch (optional) + long long command-line switch. Defaults to option name. + val string used to print option args in help. + If val is not specified the option has no arg. + fn function to call when the option is specified. + use usage (help) string + default default value if not specified on command-line + """ self.opts = opts self.name = name self.short = short @@ -24,18 +41,34 @@ class Opt: self.fn = fn self.specified_opt = None self.specified_val = None + self.value = None self.set(default) + def __repr__(self): + return self.name + '=' + str(self.specified_val) + + __str__ = __repr__ + def set(self, value): - setattr(self.opts, self.name, value) + """Set the option value. + """ + self.opts.setopt(self.name, value) def get(self): - return getattr(self.opts, self.name) + """Get the option value. + """ + return self.opts.getopt(self.name) def append(self, value): - self.set(self.get().append(value)) + """Append a value to the option value. + """ + v = self.get() or [] + v.append(value) + self.set(v) def short_opt(self): + """Short option spec. + """ if self.short: if self.val: return self.short + ':' @@ -45,6 +78,8 @@ class Opt: return None def long_opt(self): + """Long option spec. + """ if self.long: if self.val: return self.long + '=' @@ -68,6 +103,12 @@ class Opt: print '\tDefault', self.default or 'None' def specify(self, k, v): + """Specify the option. Called when the option is set + from the command line. + + k option switch used + v optional value given (if any) + """ if k in self.optkeys: if self.val is None and v: self.opts.err("Option '%s' does not take a value" % k) @@ -80,52 +121,113 @@ class Opt: return 0 def specified(self): + """Test whether the option has been specified: set + from the command line. + """ return self.specified_opt +class OptVals: + """Class to hold option values. + """ + pass + class Opts: + """Container for options. + """ def __init__(self, use=None): - self._usage = use - self._options = [] - self._options_map = {} - self._argv = [] - self._vals = {} - self._globals = {} - self._locals = {} - self.quiet = 0 + """Options constructor. + + use usage string + """ + self.use = use + # List of options. + self.options = [] + # Options indexed by name. + self.options_map = {} + # Command-line arguments. + self.argv = [] + # Option values. + self.vals = OptVals() + self.vals.quiet = 0 + # Variables for default scripts. + self.vars = {} + + def __repr__(self): + return '\n'.join(map(str, self.options)) + + __str__ = __repr__ def opt(self, name, **args): + """Add an option. + + name option name + **args keyword params for option constructor + """ x = Opt(self, name, **args) - self._options.append(x) - self._options_map[name] = x + self.options.append(x) + self.options_map[name] = x return x + def setvar(self, var, val): + """Set a default script variable. + """ + self.vars[var] = val + + def getvar(self, var): + """Get a default script variable. + """ + return self.vars.get(var) + + def option(self, name): + """Get an option (object). + """ + return self.options_map.get(name) + + def setopt(self, name, val): + """Set an option value. + An option can also be set using 'opts.vals.name = val'. + """ + setattr(self.vals, name, val) + def getopt(self, name): - return self._options_map.get(name) + """Get an option value. + An option value can also be got using 'opts.vals.name'. + """ + getattr(self.vals, name) def specified(self, name): - opt = self.getopt(name) + """Test if an option has been specified. + """ + opt = self.option(name) return opt and opt.specified() - def setvar(self, name, val): - self._globals[name] = val - def err(self, msg): + """Print an error to stderr and exit. + """ print >>sys.stderr, "Error:", msg sys.exit(1) def info(self, msg): - if self.quiet: return + """Print a message to stdout (unless quiet is set). + """ + if self.vals.quiet: return print msg def warn(self, msg): + """Print a warning to stdout. + """ print >>sys.stderr, "Warning:", msg def parse(self, argv): - self._argv = argv + """Parse arguments argv using the options. + + return remaining arguments + """ + self.argv = argv (vals, args) = getopt(argv[1:], self.short_opts(), self.long_opts()) - self._args = args + self.args = args for (k, v) in vals: - for opt in self._options: + for opt in self.options: if opt.specify(k, v): break else: print >>sys.stderr, "Error: Unknown option:", k @@ -133,63 +235,92 @@ class Opts: return args def short_opts(self): + """Get short options specifier for getopt. + """ l = [] - for x in self._options: + for x in self.options: y = x.short_opt() if not y: continue l.append(y) return ''.join(l) def long_opts(self): + """Get long options specifier for getopt. + """ l = [] - for x in self._options: + for x in self.options: y = x.long_opt() if not y: continue l.append(y) - return ''.join(l) + return l def usage(self): - print 'Usage: ', self._argv[0], self._usage or 'OPTIONS' - for opt in self._options: + print 'Usage: ', self.argv[0], self.use or 'OPTIONS' + for opt in self.options: opt.show() def load_defaults(self): - for x in [ '' ] + self.path.split(':'): + """Load a defaults script. Assumes these options set: + 'path' search path + 'default' script name + """ + for x in [ '' ] + self.vals.path.split(':'): if x: - p = os.path.join(x, self.defaults) + p = os.path.join(x, self.vals.defaults) else: - p = self.defaults + p = self.vals.defaults if os.path.exists(p): self.load(p) break else: self.err("Cannot open defaults file %s" % self.defaults) - def load(self, defaults): - self._globals['sys'] = sys - self._globals['config_file'] = defaults - execfile(defaults, self._globals, self._locals) + def load(self, defaults, help=0): + """Load a defaults file. Local variables in the file + are used to set options with the same names. + Variables are not used to set options that are already specified. + """ + # Create global and lobal dicts for the file. + # Initialize locals to the vars. + # Use exec to do the standard imports and + # define variables we are passing to the script. + globals = {} + locals = {} + locals.update(self.vars) + cmd = '\n'.join(["import sys", + "import os", + "import os.path", + "xm_file = '%s'" % defaults, + "xm_help = %d" % help ]) + exec cmd in globals, locals + execfile(defaults, globals, locals) + if help: return + # Extract the values set by the script and set the corresponding + # options, if not set on the command line. vtypes = [ types.StringType, types.ListType, types.IntType, types.FloatType ] - for (k, v) in self._locals.items(): + for (k, v) in locals.items(): if self.specified(k): continue if not(type(v) in vtypes): continue - print 'SET ', k, v - setattr(self, k, v) + self.setopt(k, v) def set_true(opt, k, v): + """Set an option true.""" opt.set(1) def set_false(opt, k, v): + """Set an option false.""" opt.set(0) def set_value(opt, k, v): + """Set an option to a valoue.""" opt.set(v) def set_int(opt, k, v): + """Set an option to an integer value.""" try: v = int(v) except: @@ -197,4 +328,12 @@ def set_int(opt, k, v): opt.set(v) def append_value(opt, k, v): + """Append a value to a list option.""" opt.append(v) + +def set_var(opt, k, v): + """Set a default script variable. + """ + (var, val) = v.strip().split('=') + opt.opts.setvar(var.strip(), val.strip()) + diff --git a/tools/xenmgr/lib/xm/shutdown.py b/tools/xenmgr/lib/xm/shutdown.py index 0108a8df0f..90fff21f6a 100644 --- a/tools/xenmgr/lib/xm/shutdown.py +++ b/tools/xenmgr/lib/xm/shutdown.py @@ -1,3 +1,6 @@ +# Copyright (C) 2004 Mike Wray <mike.wray@hp.com> +"""Domain shutdown. +""" import string import sys import time |