31
import CapabilitiesParser
32
from VirtualDisk import VirtualDisk
30
33
from virtinst import _virtinst as _
39
DRIVER_TAP_RAW = "aio"
40
DRIVER_TAP_QCOW = "qcow"
41
DRIVER_TAP_VMDK = "vmdk"
44
DEVICE_CDROM = "cdrom"
45
DEVICE_FLOPPY = "floppy"
50
def __init__(self, path = None, size = None, transient=False, type=None, device=DEVICE_DISK, driverName=None, driverType=None, readOnly=False, sparse=True):
51
"""@path is the path to the disk image.
52
@size is the size of the disk image in gigabytes."""
55
self.transient = transient
58
self._readOnly = readOnly
60
self._driverName = driverName
61
self._driverType = driverType
64
if self.path is not None:
65
# Check that the basics are valid
66
if __builtin__.type(self.path) is not __builtin__.type("string"):
67
raise ValueError, _("The %s path must be a string or None.") % self._device
68
self.path = os.path.abspath(self.path)
69
if os.path.isdir(self.path):
70
raise ValueError, _("The %s path must be a file or a device, not a directory") % self._device
71
if not os.path.exists(os.path.dirname(self.path)):
72
raise ValueError, _("The specified path's root directory must exist.")
74
if (self._device == self.DEVICE_FLOPPY or \
75
self._device == self.DEVICE_CDROM) and \
76
not os.path.exists(self.path):
77
raise ValueError, _("The %s path must exist.") % self._device
79
# If no disk type specified, attempt to determine from path
80
if self._type is None:
81
if not os.path.exists(self.path):
83
"Disk path not found: Assuming file disk type.");
84
self._type = VirtualDisk.TYPE_FILE
86
if stat.S_ISBLK(os.stat(self.path)[stat.ST_MODE]):
88
"Path is block file: Assuming Block disk type.");
89
self._type = VirtualDisk.TYPE_BLOCK
91
self._type = VirtualDisk.TYPE_FILE
93
if self._type == VirtualDisk.TYPE_FILE:
94
if self.size is None and not os.path.exists(self.path):
96
_("A size must be provided for non-existent disks")
97
if os.path.exists(self.path) and \
98
stat.S_ISBLK(os.stat(self.path)[stat.ST_MODE]):
99
raise ValueError, _("The specified path is a block device, not a regular file.")
100
if self.size is not None and \
101
(__builtin__.type(self.size) is not __builtin__.type(1) and __builtin__.type(self.size) is not __builtin__.type(1.0)):
102
raise ValueError, _("Disk size must be an int or a float.")
103
if self.size < 0 and self.size is not None:
104
raise ValueError, _("Disk size must not be less than 0.")
105
elif self._type == VirtualDisk.TYPE_BLOCK:
106
if not os.path.exists(self.path):
108
_("The specified block device does not exist.")
109
if not stat.S_ISBLK(os.stat(self.path)[stat.ST_MODE]):
111
_("The specified path is not a block device.")
114
# Only floppy or cdrom can be created w/o media
115
if device != self.DEVICE_FLOPPY and \
116
device != self.DEVICE_CDROM:
117
raise ValueError, _("Disk type '%s' requires a path") % device
121
type = property(get_type)
123
def get_transient(self):
124
return self._transient
125
transient = property(get_transient)
127
def get_device(self):
129
device = property(get_device)
131
def get_driver_name(self):
132
return self._driverName
133
driver_name = property(get_driver_name)
135
def get_driver_type(self):
136
return self._driverType
137
driver_type = property(get_driver_type)
139
def get_read_only(self):
140
return self._readOnly
141
read_only = property(get_read_only)
143
def setup(self, progresscb):
144
if self._type == VirtualDisk.TYPE_FILE and self.path is not None \
145
and not os.path.exists(self.path):
146
size_bytes = long(self.size * 1024L * 1024L * 1024L)
147
progresscb.start(filename=self.path,size=long(size_bytes), \
148
text=_("Creating storage file..."))
152
fd = os.open(self.path, os.O_WRONLY | os.O_CREAT)
154
os.lseek(fd, size_bytes, 0)
156
progresscb.update(self.size)
158
buf = '\x00' * 1024 * 1024 # 1 meg of nulls
159
for i in range(0, long(self.size * 1024L)):
161
progresscb.update(long(i * 1024L * 1024L))
162
except OSError, detail:
163
raise RuntimeError, "Error creating diskimage " + self.path + ": " + detail.strerror
167
progresscb.end(size_bytes)
168
# FIXME: set selinux context?
170
def get_xml_config(self, disknode):
172
if self.type == VirtualDisk.TYPE_BLOCK:
175
ret = " <disk type='%(type)s' device='%(device)s'>\n" % { "type": self.type, "device": self.device }
176
if not(self.driver_name is None):
177
if self.driver_type is None:
178
ret += " <driver name='%(name)s'/>\n" % { "name": self.driver_name }
180
ret += " <driver name='%(name)s' type='%(type)s'/>\n" % { "name": self.driver_name, "type": self.driver_type }
181
if self.path is not None:
182
path = util.xml_escape(self.path)
183
ret += " <source %(typeattr)s='%(disk)s'/>\n" % { "typeattr": typeattr, "disk": path }
184
if self.target is not None:
185
disknode = self.target
186
ret += " <target dev='%(disknode)s'/>\n" % { "disknode": disknode }
188
ret += " <readonly/>\n"
192
def is_conflict_disk(self, conn):
194
# get working domain's name
195
ids = conn.listDomainsID();
198
vm = conn.lookupByID(id)
200
except libvirt.libvirtError:
201
# guest probably in process of dieing
202
logging.warn("Failed to lookup domain id %d" % id)
204
names = conn.listDefinedDomains()
207
vm = conn.lookupByName(name)
209
except libvirt.libvirtError:
210
# guest probably in process of dieing
211
logging.warn("Failed to lookup domain name %s" % name)
217
doc = libxml2.parseDoc(vm.XMLDesc(0))
220
ctx = doc.xpathNewContext()
223
count += ctx.xpathEval("count(/domain/devices/disk/source[@dev='%s'])" % self.path)
224
count += ctx.xpathEval("count(/domain/devices/disk/source[@file='%s'])" % self.path)
229
ctx.xpathFreeContext()
238
return "%s:%s" %(self.type, self.path)
240
# Back compat class to avoid ABI break
241
class XenDisk(VirtualDisk):
244
class VirtualNetworkInterface:
245
def __init__(self, macaddr = None, type="bridge", bridge = None, network=None):
37
class VirtualNetworkInterface(object):
39
TYPE_BRIDGE = "bridge"
40
TYPE_VIRTUAL = "network"
43
def __init__(self, macaddr=None, type=TYPE_BRIDGE, bridge=None,
44
network=None, model=None):
247
46
if macaddr is not None and \
248
47
__builtin__.type(macaddr) is not __builtin__.type("string"):
258
57
self.bridge = bridge
259
58
self.network = network
260
if self.type == "network":
60
if self.type == self.TYPE_VIRTUAL:
261
61
if network is None:
262
62
raise ValueError, _("A network name was not provided")
264
raise ValueError, _("Bridge name is not required for %s") % ("type=network",)
265
elif self.type == "bridge":
267
raise ValueError, _("Network name is not required for %s") % ("type=bridge",)
268
elif self.type == "user":
270
raise ValueError, _("Network name is not required for %s") % ("type=bridge",)
272
raise ValueError, _("Bridge name is not required for %s") % ("type=network",)
63
elif self.type == self.TYPE_BRIDGE:
65
elif self.type == self.TYPE_USER:
274
68
raise ValueError, _("Unknown network type %s") % (type,)
276
70
def is_conflict_net(self, conn):
277
71
"""is_conflict_net: determines if mac conflicts with others in system
279
returns a two element tuple:
73
returns a two element tuple:
280
74
first element is True if fatal collision occured
281
75
second element is a string description of the collision.
282
76
Non fatal collisions (mac addr collides with inactive guest) will
283
77
return (False, "description of collision")"""
78
if self.macaddr is None:
284
80
# get Running Domains
285
81
ids = conn.listDomainsID();
338
130
self.bridge = util.default_bridge()
340
132
def get_xml_config(self):
341
if self.type == "bridge":
342
return (" <interface type='bridge'>\n" + \
343
" <source bridge='%(bridge)s'/>\n" + \
344
" <mac address='%(mac)s'/>\n" + \
345
" </interface>\n") % \
346
{ "bridge": self.bridge, "mac": self.macaddr }
347
elif self.type == "network":
348
return (" <interface type='network'>\n" + \
349
" <source network='%(network)s'/>\n" + \
350
" <mac address='%(mac)s'/>\n" + \
351
" </interface>\n") % \
352
{ "network": self.network, "mac": self.macaddr }
353
elif self.type == "user":
354
return (" <interface type='user'>\n" + \
355
" <mac address='%(mac)s'/>\n" + \
356
" </interface>\n") % \
357
{ "mac": self.macaddr }
135
if self.type == self.TYPE_BRIDGE:
136
src_xml = " <source bridge='%s'/>\n" % self.bridge
137
elif self.type == self.TYPE_VIRTUAL:
138
src_xml = " <source network='%s'/>\n" % self.network
141
model_xml = " <model type='%s'/>\n" % self.model
143
return " <interface type='%s'>\n" % self.type + \
145
" <mac address='%s'/>\n" % self.macaddr + \
359
149
def countMACaddr(self, vms):
170
class VirtualAudio(object):
172
MODELS = [ "es1370", "sb16", "pcspk" ]
174
def __init__(self, model):
179
def set_model(self, new_model):
180
if type(new_model) != str:
181
raise ValueError, _("'model' must be a string, "
182
" was '%s'." % type(new_model))
183
if not self.MODELS.count(new_model):
184
raise ValueError, _("Unsupported sound model '%s'" % new_model)
185
self._model = new_model
186
model = property(get_model, set_model)
188
def get_xml_config(self):
189
return " <sound model='%s'/>" % self.model
383
191
# Back compat class to avoid ABI break
384
192
class XenNetworkInterface(VirtualNetworkInterface):
387
class VirtualGraphics:
388
def __init__(self, *args):
391
def get_xml_config(self):
394
# Back compat class to avoid ABI break
395
class XenGraphics(VirtualGraphics):
398
class VNCVirtualGraphics(XenGraphics):
399
def __init__(self, *args):
401
if len(args) >= 1 and not args[0] is None:
402
if args[0] < 5900 or args[0] > 65535:
403
raise ValueError, _("Invalid value for vnc port, port number must be in between 5900 and 65535")
407
if len(args) >= 2 and args[1]:
408
self.keymap = args[1]
412
def get_xml_config(self):
413
if self.keymap == None:
416
keymapstr = "keymap='"+self.keymap+"' "
417
return " <graphics type='vnc' port='%(port)d' %(keymapstr)s/>" % {"port":self.port, "keymapstr":keymapstr}
419
# Back compat class to avoid ABI break
420
class XenVNCGraphics(VNCVirtualGraphics):
423
class SDLVirtualGraphics(XenGraphics):
424
def __init__(self, *args):
427
def get_xml_config(self):
428
return " <graphics type='sdl'/>"
430
# Back compat class to avoid ABI break
431
class XenSDLGraphics(SDLVirtualGraphics):
195
class VirtualGraphics(object):
200
def __init__(self, type=TYPE_VNC, port=-1, listen=None, passwd=None,
203
if type != self.TYPE_VNC and type != self.TYPE_SDL:
204
raise ValueError(_("Unknown graphics type"))
207
self.set_keymap(keymap)
208
self.set_listen(listen)
209
self.set_passwd(passwd)
213
type = property(get_type)
215
def get_keymap(self):
217
def set_keymap(self, val):
219
val = util.default_keymap()
220
if not val or type(val) != type("string"):
221
raise ValueError, _("Keymap must be a string")
223
raise ValueError, _("Keymap must be less than 16 characters")
224
if re.match("^[a-zA-Z0-9_-]*$", val) == None:
225
raise ValueError, _("Keymap can only contain alphanumeric, '_', or '-' characters")
227
keymap = property(get_keymap, set_keymap)
231
def set_port(self, val):
234
elif type(val) is not int \
235
or (val != -1 and (val < 5900 or val > 65535)):
236
raise ValueError, _("VNC port must be a number between 5900 and 65535, or -1 for auto allocation")
238
port = property(get_port, set_port)
240
def get_listen(self):
242
def set_listen(self, val):
244
listen = property(get_listen, set_listen)
246
def get_passwd(self):
248
def set_passwd(self, val):
250
passwd = property(get_passwd, set_passwd)
252
def get_xml_config(self):
253
if self._type == self.TYPE_SDL:
254
return " <graphics type='sdl'/>"
259
keymapxml = " keymap='%s'" % self._keymap
261
listenxml = " listen='%s'" % self._listen
263
passwdxml = " passwd='%s'" % self._passwd
264
xml = " <graphics type='vnc' " + \
265
"port='%(port)d'" % { "port" : self._port } + \
266
"%(keymapxml)s" % { "keymapxml" : keymapxml } + \
267
"%(listenxml)s" % { "listenxml" : listenxml } + \
268
"%(passwdxml)s" % { "passwdxml" : passwdxml } + \
434
272
class Installer(object):
435
def __init__(self, type = "xen", location = None, boot = None, extraargs = None, os_type = None):
273
def __init__(self, type = "xen", location = None, boot = None,
274
extraargs = None, os_type = None, conn = None):
436
275
self._location = None
437
276
self._extraargs = None
438
277
self._boot = None
439
278
self._cdrom = False
440
279
self._os_type = os_type
441
281
self._install_disk = None # VirtualDisk that contains install media
657
509
self._cpuset = val
658
510
cpuset = property(get_cpuset, set_cpuset)
512
def get_graphics_dev(self):
513
return self._graphics_dev
514
def set_graphics_dev(self, val):
515
self._graphics_dev = val
516
graphics_dev = property(get_graphics_dev, set_graphics_dev)
518
# DEPRECATED PROPERTIES
519
# Deprecated: Should set graphics_dev.keymap directly
520
def get_keymap(self):
521
if self._graphics_dev is None:
523
return self._graphics_dev.keymap
524
def set_keymap(self, val):
525
if self._graphics_dev is not None:
526
self._graphics_dev.keymap = val
527
keymap = property(get_keymap, set_keymap)
529
# Deprecated: Should set guest.graphics_dev = VirtualGraphics(...)
661
530
def get_graphics(self):
662
return self._graphics
531
if self._graphics_dev is None:
532
return { "enabled " : False }
533
return { "enabled" : True, "type" : self._graphics_dev, \
534
"keymap" : self._graphics_dev.keymap}
663
535
def set_graphics(self, val):
664
def validate_keymap(keymap):
667
if type(keymap) != type("string"):
668
raise ValueError, _("Keymap must be a string")
670
raise ValueError, _("Keymap must be less than 16 characters")
671
if re.match("^[a-zA-Z0-9_-]*$", keymap) == None:
672
raise ValueError, _("Keymap can only contain alphanumeric, '_', or '-' characters")
538
# a dictionary with keys: enabled, type, port, keymap
539
# a tuple of the form : (enabled, type, port, keymap)
541
# : "vnc", "sdl", or false
677
547
if type(val) == dict:
678
548
if not val.has_key("enabled"):
679
549
raise ValueError, _("Must specify whether graphics are enabled")
680
self._graphics["enabled"] = val["enabled"]
550
enabled = val["enabled"]
681
551
if val.has_key("type"):
683
553
if val.has_key("opts"):
685
555
elif type(val) == tuple:
686
if len(val) >= 1: self._graphics["enabled"] = val[0]
687
if len(val) >= 2: t = val[1]
688
if len(val) >= 3: opts = val[2]
689
if len(val) >= 4: self._graphics["keymap"] = validate_keymap(val[3])
556
if len(val) >= 1: enabled = val[0]
557
if len(val) >= 2: gtype = val[1]
558
if len(val) >= 3: port = val[2]
559
if len(val) >= 4: keymap = val[3]
691
561
if val in ("vnc", "sdl"):
693
self._graphics["enabled"] = True
695
self._graphics["enabled"] = val
697
if self._graphics["enabled"] not in (True, False):
567
if enabled not in (True, False):
698
568
raise ValueError, _("Graphics enabled must be True or False")
700
if self._graphics["enabled"] == True:
702
if self.graphics.has_key("keymap"):
703
gt = VNCVirtualGraphics(opts, self._graphics["keymap"])
705
gt = VNCVirtualGraphics(opts)
707
gt = SDLVirtualGraphics(opts)
709
raise ValueError, _("Unknown graphics type")
710
self._graphics["type"] = gt
571
gdev = VirtualGraphics(type=gtype)
576
self._graphics_dev = gdev
712
578
graphics = property(get_graphics, set_graphics)
580
# Deprecated: Should be called from the installer directly
581
def get_location(self):
582
return self._installer.location
583
def set_location(self, val):
584
self._installer.location = val
585
location = property(get_location, set_location)
715
# Legacy, deprecated properties
587
# Deprecated: Should be called from the installer directly
716
588
def get_scratchdir(self):
717
589
return self._installer.scratchdir
718
590
scratchdir = property(get_scratchdir)
592
# Deprecated: Should be called from the installer directly
720
593
def get_boot(self):
721
594
return self._installer.boot
722
595
def set_boot(self, val):
723
596
self._installer.boot = val
724
597
boot = property(get_boot, set_boot)
726
def get_location(self):
727
return self._installer.location
728
def set_location(self, val):
729
self._installer.location = val
730
location = property(get_location, set_location)
599
# Deprecated: Should be called from the installer directly
732
600
def get_extraargs(self):
733
601
return self._installer.extraargs
734
602
def set_extraargs(self, val):
735
603
self._installer.extraargs = val
736
604
extraargs = property(get_extraargs, set_extraargs)
606
# Deprecated: Should set the installer values directly
738
607
def get_cdrom(self):
739
608
return self._installer.location
740
609
def set_cdrom(self, val):
761
631
def _get_network_xml(self, install = True):
762
632
"""Get the network config in the libvirt XML format"""
634
for n in self._install_nics:
765
637
ret += n.get_xml_config()
768
640
def _get_graphics_xml(self, install = True):
769
641
"""Get the graphics config in the libvirt XML format."""
771
if self.graphics["enabled"] == False:
773
gt = self.graphics["type"]
774
return gt.get_xml_config()
642
if self._graphics_dev is None:
644
return self._graphics_dev.get_xml_config()
776
646
def _get_input_xml(self, install = True):
777
647
"""Get the input device config in libvirt XML format."""
778
648
(type,bus) = self.get_input_device()
779
649
return " <input type='%s' bus='%s'/>" % (type, bus)
651
def _get_sound_xml(self):
652
"""Get the sound device configuration in libvirt XML format."""
654
for sound_dev in self.sound_devs:
657
xml += sound_dev.get_xml_config()
781
660
def _get_device_xml(self, install = True):
785
%(graphics)s""" % { "disks": self._get_disk_xml(install), \
786
"networks": self._get_network_xml(install), \
787
"input": self._get_input_xml(install), \
788
"graphics": self._get_graphics_xml(install) }
663
diskxml = self._get_disk_xml(install)
664
netxml = self._get_network_xml(install)
665
inputxml = self._get_input_xml(install)
666
graphicsxml = self._get_graphics_xml(install)
667
soundxml = self._get_sound_xml()
668
for devxml in [diskxml, netxml, inputxml, graphicsxml, soundxml]:
790
676
def get_config_xml(self, install = True, disk_boot = False):
843
730
self._prepare_install(meter)
845
return self._do_install(consolecb, meter)
732
return self._do_install(consolecb, meter, removeOld, wait)
847
734
self._installer.cleanup()
849
736
def _prepare_install(self, meter):
850
737
self._install_disks = self.disks[:]
851
738
self._install_nics = self.nics[:]
853
def _do_install(self, consolecb, meter):
741
def _do_install(self, consolecb, meter, removeOld=False, wait=True):
855
if self.conn.lookupByName(self.name) is not None:
744
vm = self.conn.lookupByName(self.name)
745
except libvirt.libvirtError:
752
logging.info("Destroying image %s" %(self.name))
754
logging.info("Removing old definition for image %s" %(self.name))
756
except libvirt.libvirtError, e:
757
raise RuntimeError, _("Could not remove old vm '%s': %s") %(self.name, str(e))
856
759
raise RuntimeError, _("Domain named %s already exists!") %(self.name,)
857
except libvirt.libvirtError:
861
762
self._create_devices(meter)