2
# Utility functions used for guest installation
4
# Copyright 2006 Red Hat, Inc.
5
# Jeremy Katz <katzj@redhat.com>
7
# This program is free software; you can redistribute it and/or modify
8
# it under the terms of the GNU General Public License as published by
9
# the Free Software Foundation; either version 2 of the License, or
10
# (at your option) any later version.
12
# This program is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
17
# You should have received a copy of the GNU General Public License
18
# along with this program; if not, write to the Free Software
19
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
23
# WARNING: the contents of this file, somewhat unfortunately, are legacy
24
# API. No incompatible changes are allowed to this file, and no new
25
# code should be added here (utility functions live in _util.py).
26
# Clients of virtinst shouldn't use these functions: if you think you
27
# need to, tell us why.
37
from sys import stderr
40
from virtinst import _virtinst as _
42
import CapabilitiesParser
46
KEYBOARD_DIR = "/etc/sysconfig/keyboard"
47
XORG_CONF = "/etc/X11/xorg.conf"
48
CONSOLE_SETUP_CONF = "/etc/default/console-setup"
50
def default_route(nic = None):
51
if platform.system() == 'SunOS':
52
cmd = [ '/usr/bin/netstat', '-rn' ]
55
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
56
stderr=subprocess.PIPE)
57
for line in proc.stdout.readlines():
59
if len(vals) > 1 and vals[0] == 'default':
63
route_file = "/proc/net/route"
67
for line in d.xreadlines():
69
if (len(info) != 11): # 11 = typical num of fields in the file
70
print >> stderr, _("Invalid line length while parsing %s.") %(route_file)
71
print >> stderr, _("Defaulting bridge to xenbr%d") % (defn)
74
route = int(info[1],16)
83
ret = virtinst._util.default_bridge2(None)
85
# Maintain this behavior for back compat
92
def default_network(conn):
93
ret = virtinst._util.default_bridge2(conn)
95
# FIXME: Check that this exists
96
ret = ["network", "default"]
100
def default_connection():
101
if os.path.exists('/var/lib/xend'):
102
if os.path.exists('/dev/xen/evtchn'):
104
if os.path.exists("/proc/xen"):
107
if os.path.exists("/usr/bin/qemu") or \
108
os.path.exists("/usr/bin/qemu-kvm") or \
109
os.path.exists("/usr/bin/kvm") or \
110
os.path.exists("/usr/bin/xenner"):
111
if User.User.current().has_priv(User.User.PRIV_QEMU_SYSTEM):
112
return "qemu:///system"
114
return "qemu:///session"
118
if platform.system() == 'SunOS':
119
raise OSError('CPU flags not available')
121
f = open("/proc/cpuinfo")
122
lines = f.readlines()
125
if not line.startswith("flags"):
127
# get the actual flags
128
flags = line[:-1].split(":", 1)[1]
130
flst = flags.split(" ")
134
def is_pae_capable(conn = None):
135
"""Determine if a machine is PAE capable or not."""
137
conn = libvirt.open('')
138
return "pae" in conn.getCapabilities()
140
def is_hvm_capable():
141
"""Determine if a machine is HVM capable or not."""
142
if platform.system() == 'SunOS':
143
raise OSError('HVM capability not determinible')
146
if os.path.exists("/sys/hypervisor/properties/capabilities"):
147
caps = open("/sys/hypervisor/properties/capabilities").read()
148
if caps.find("hvm") != -1:
152
def is_kqemu_capable():
153
return os.path.exists("/dev/kqemu")
155
def is_kvm_capable():
156
return os.path.exists("/dev/kvm")
158
def is_blktap_capable():
159
if platform.system() == 'SunOS':
162
#return os.path.exists("/dev/xen/blktapctrl")
163
f = open("/proc/modules")
164
lines = f.readlines()
167
if line.startswith("blktap ") or line.startswith("xenblktap "):
171
def get_default_arch():
177
# this function is directly from xend/server/netif.py and is thus
178
# available under the LGPL,
179
# Copyright 2004, 2005 Mike Wray <mike.wray@hp.com>
180
# Copyright 2005 XenSource Ltd
181
def randomMAC(type = "xen"):
182
"""Generate a random MAC address.
184
00-16-3E allocated to xensource
185
52-54-00 used by qemu/kvm
187
The OUI list is available at http://standards.ieee.org/regauth/oui/oui.txt.
189
The remaining 3 fields are random, with the first bit of the first
192
>>> randomMAC().startswith("00:16:36")
194
>>> randomMAC("foobar").startswith("00:16:36")
196
>>> randomMAC("xen").startswith("00:16:36")
198
>>> randomMAC("qemu").startswith("52:54:00")
201
@return: MAC address string
203
ouis = { 'xen': [ 0x00, 0x16, 0x36 ], 'qemu': [ 0x52, 0x54, 0x00 ] }
211
random.randint(0x00, 0xff),
212
random.randint(0x00, 0xff),
213
random.randint(0x00, 0xff) ]
214
return ':'.join(map(lambda x: "%02x" % x, mac))
216
# the following three functions are from xend/uuid.py and are thus
217
# available under the LGPL,
218
# Copyright 2005 Mike Wray <mike.wray@hp.com>
219
# Copyright 2005 XenSource Ltd
221
"""Generate a random UUID."""
223
return [ random.randint(0, 255) for dummy in range(0, 16) ]
226
return "-".join(["%02x" * 4, "%02x" * 2, "%02x" * 2, "%02x" * 2,
227
"%02x" * 6]) % tuple(u)
229
def uuidFromString(s):
230
s = s.replace('-', '')
231
return [ int(s[i : i + 2], 16) for i in range(0, 32, 2) ]
233
# the following function quotes from python2.5/uuid.py
234
def get_host_network_devices():
236
for dirname in ['', '/sbin/', '/usr/sbin']:
237
executable = os.path.join(dirname, "ifconfig")
238
if not os.path.exists(executable):
241
cmd = 'LC_ALL=C %s -a 2>/dev/null' % (executable)
246
if line.find("encap:Ethernet") > 0:
247
words = line.lower().split()
248
for i in range(len(words)):
249
if words[i] == "hwaddr":
253
def get_max_vcpus(conn, type=None):
254
"""@conn libvirt connection to poll for max possible vcpus
255
@type optional guest type (kvm, etc.)"""
257
type = conn.getType()
259
m = conn.getMaxVcpus(type.lower())
260
except libvirt.libvirtError:
264
def get_phy_cpus(conn):
265
"""Get number of physical CPUs."""
266
hostinfo = conn.getInfo()
267
pcpus = hostinfo[4] * hostinfo[5] * hostinfo[6] * hostinfo[7]
272
if os.WIFEXITED(st) and os.WEXITSTATUS(st) != 0:
273
raise OSError("Failed to run %s, exited with %d" %
274
(cmd, os.WEXITSTATUS(st)))
277
"""Replaces chars ' " < > & with xml safe counterparts"""
278
str = str.replace("&", "&")
279
str = str.replace("'", "'")
280
str = str.replace("\"", """)
281
str = str.replace("<", "<")
282
str = str.replace(">", ">")
285
def compareMAC(p, q):
286
"""Compare two MAC addresses"""
290
if len(pa) != len(qa):
296
for i in xrange(len(pa)):
297
n = int(pa[i], 0x10) - int(qa[i], 0x10)
305
"""Look in /etc/X11/xorg.conf for the host machine's keymap, and attempt to
306
map it to a keymap supported by qemu"""
310
f = open(XORG_CONF, "r")
312
logging.debug('Could not open "%s": %s ' % (XORG_CONF, str(e)))
314
keymap_re = re.compile(r'\s*Option\s+"XkbLayout"\s+"(?P<kt>[a-z-]+)"')
316
m = keymap_re.match(line)
321
logging.debug("Didn't find keymap in '%s'!" % XORG_CONF)
325
def _console_setup_keymap():
326
"""Look in /etc/default/console-setup for the host machine's keymap, and attempt to
327
map it to a keymap supported by qemu"""
331
f = open(CONSOLE_SETUP_CONF, "r")
333
logging.debug('Could not open "%s": %s ' % (CONSOLE_SETUP_CONF, str(e)))
335
keymap_re = re.compile(r'\s*XKBLAYOUT="(?P<kt>[a-z-]+)"')
337
m = keymap_re.match(line)
342
logging.debug("Didn't find keymap in '%s'!" % XORG_CONF)
346
def default_keymap():
347
"""Look in /etc/sysconfig for the host machine's keymap, and attempt to
348
map it to a keymap supported by qemu"""
350
# Set keymap to same as hosts
356
f = open(KEYBOARD_DIR, "r")
358
logging.debug('Could not open "/etc/sysconfig/keyboard" ' + str(e))
361
kt = _console_setup_keymap()
367
if re.search("KEYTABLE", s) != None or \
368
(re.search("KEYBOARD", s) != None and
369
re.search("KEYBOARDTYPE", s) == None):
376
kt = s.split(delim)[1].strip()
380
logging.debug("Did not parse any usable keymapping.")
385
keymap = check_keytable(kt)
388
logging.debug("Didn't match keymap '%s' in keytable!" % kt)
393
def pygrub_path(conn=None):
395
Return the pygrub path for the current host, or connection if
398
# FIXME: This should be removed/deprecated when capabilities are
399
# fixed to provide bootloader info
401
cap = CapabilitiesParser.parse(conn.getCapabilities())
402
if (cap.host.arch == "i86pc"):
403
return "/usr/lib/xen/bin/pygrub"
405
return "/usr/lib/xen-default/bin/pygrub"
407
if platform.system() == "SunOS":
408
return "/usr/lib/xen/bin/pygrub"
409
return "/usr/lib/xen-default/bin/pygrub"
413
Parse a libvirt hypervisor uri into it's individual parts
414
@returns: tuple of the form (scheme (ex. 'qemu', 'xen+ssh'), username,
415
hostname, path (ex. '/system'), query,
418
def splitnetloc(url, start=0):
419
for c in '/?#': # the order is important!
420
delim = url.find(c, start)
425
return url[start:delim], url[delim:]
427
username = netloc = query = fragment = ''
430
scheme, uri = uri[:i].lower(), uri[i+1:]
432
netloc, uri = splitnetloc(uri, 2)
433
offset = netloc.find("@")
435
username = netloc[0:offset]
436
netloc = netloc[offset+1:]
438
uri, fragment = uri.split('#', 1)
440
uri, query = uri.split('?', 1)
443
return scheme, username, netloc, uri, query, fragment
446
def is_uri_remote(uri):
448
split_uri = uri_split(uri)
449
netloc = split_uri[2]
455
logging.exception("Error parsing URI in is_remote: %s" % e)
458
def get_uri_hostname(uri):
460
split_uri = uri_split(uri)
461
netloc = split_uri[2]
466
logging.warning("Cannot parse URI %s: %s" % (uri, str(e)))
469
def get_uri_transport(uri):
471
split_uri = uri_split(uri)
472
scheme = split_uri[0]
473
username = split_uri[1]
476
offset = scheme.index("+")
478
return [scheme[offset+1:], username]
483
def get_uri_driver(uri):
485
split_uri = uri_split(uri)
486
scheme = split_uri[0]
489
offset = scheme.find("+")
491
return scheme[:offset]
497
def is_storage_capable(conn):
498
"""check if virConnectPtr passed has storage API support"""
499
return support.check_conn_support(conn, support.SUPPORT_CONN_STORAGE)
501
def get_xml_path(xml, path=None, func=None):
503
Return the content from the passed xml xpath, or return the result
504
of a passed function (receives xpathContext as its only arg)
511
doc = libxml2.parseDoc(xml)
512
ctx = doc.xpathNewContext()
515
ret = ctx.xpathEval(path)
517
if type(ret) == list:
519
result = ret[0].content
527
raise ValueError(_("'path' or 'func' is required."))
532
ctx.xpathFreeContext()
535
def lookup_pool_by_path(conn, path):
537
Return the first pool with matching matching target path.
538
return the first we find, active or inactive. This iterates over
539
all pools and dumps their xml, so it is NOT quick.
540
Favor running pools over inactive pools.
541
@return virStoragePool object if found, None otherwise
543
if not is_storage_capable(conn):
546
def check_pool(poolname, path):
547
pool = conn.storagePoolLookupByName(poolname)
548
xml_path = get_xml_path(pool.XMLDesc(0), "/pool/target/path")
549
if os.path.abspath(xml_path) == path:
552
running_list = conn.listStoragePools()
553
inactive_list = conn.listDefinedStoragePools()
554
for plist in [running_list, inactive_list]:
556
p = check_pool(name, path)
561
def check_keytable(kt):
564
# Try a simple lookup in the keytable
565
if keytable.keytable.has_key(kt.lower()):
566
return keytable.keytable[kt]
568
# Try a more intelligent lookup: strip out all '-' and '_', sort
569
# the keytable keys putting the longest first, then compare
572
return len(b) - len(a)
574
clean_kt = kt.replace("-", "").replace("_", "")
575
sorted_keys = sorted(keytable.keytable.keys(), len_cmp)
577
for key in sorted_keys:
579
key = key.replace("-", "").replace("_","")
581
if clean_kt.startswith(key):
582
return keytable.keytable[origkey]
590
if __name__ == "__main__":