ia64/xen-unstable

view tools/xc/py/XenoUtil.py @ 1052:cc2a9e8d1568

bitkeeper revision 1.691 (4011c6d3g_Q_DeG3-esCCO1ROch6Rw)

XenoUtil.py, VBD-HOWTO.txt:
Fix location of xen vbd dataase file.
author kaf24@scramble.cl.cam.ac.uk
date Sat Jan 24 01:13:55 2004 +0000 (2004-01-24)
parents 7f65f6292176
children 4d8a0cc41eb6
line source
1 import string, re, os, sys
3 ##### Module variables
5 """Location of the Virtual Disk management database.
6 defaults to /var/lib/xen_vdisks.sqlite
7 """
8 VD_DB_FILE = "/var/lib/xen_vdisks.sqlite"
10 """VBD expertise level - determines the strictness of the sanity checking.
11 This mode determines the level of complaints when disk sharing occurs
12 through the current VBD mappings.
13 0 - only allow shared mappings if both domains have r/o access (always OK)
14 1 - also allow sharing with one dom r/w and the other r/o
15 2 - allow sharing with both doms r/w
16 """
17 VBD_EXPERT_MODE = 0
19 ##### Module initialisation
21 try:
22 # try to import sqlite (not everyone will have it installed)
23 import sqlite
24 except ImportError:
25 # on failure, just catch the error, don't do anything
26 pass
29 ##### Networking-related functions
31 def get_current_ipaddr(dev='eth0'):
32 """Return a string containing the primary IP address for the given
33 network interface (default 'eth0').
34 """
35 fd = os.popen( '/sbin/ifconfig ' + dev + ' 2>/dev/null' )
36 lines = fd.readlines()
37 for line in lines:
38 m = re.search( '^\s+inet addr:([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+).*',
39 line )
40 if m:
41 return m.group(1)
42 return None
44 def get_current_ipmask(dev='eth0'):
45 """Return a string containing the primary IP netmask for the given
46 network interface (default 'eth0').
47 """
48 fd = os.popen( '/sbin/ifconfig ' + dev + ' 2>/dev/null' )
49 lines = fd.readlines()
50 for line in lines:
51 m = re.search( '^.+Mask:([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+).*',
52 line )
53 if m:
54 return m.group(1)
55 return None
57 def get_current_ipgw(dev='eth0'):
58 """Return a string containing the IP gateway for the given
59 network interface (default 'eth0').
60 """
61 fd = os.popen( '/sbin/route -n' )
62 lines = fd.readlines()
63 for line in lines:
64 m = re.search( '^\S+\s+([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)' +
65 '\s+\S+\s+\S*G.*' + dev + '.*', line )
66 if m:
67 return m.group(1)
68 return None
70 def setup_vfr_rules_for_vif(dom,vif,addr):
71 """Takes a tuple ( domain-id, vif-id, ip-addr ), where the ip-addr
72 is expressed as a textual dotted quad, and set up appropriate routing
73 rules in Xen. No return value.
74 """
75 fd = os.open( '/proc/xeno/vfr', os.O_WRONLY )
76 if ( re.search( '169\.254', addr) ):
77 os.write( fd, 'ADD ACCEPT srcaddr=' + addr +
78 ' srcaddrmask=255.255.255.255' +
79 ' srcdom=' + str(dom) + ' srcidx=' + str(vif) +
80 ' dstdom=0 dstidx=0 proto=any\n' )
81 else:
82 os.write( fd, 'ADD ACCEPT srcaddr=' + addr +
83 ' srcaddrmask=255.255.255.255' +
84 ' srcdom=' + str(dom) + ' srcidx=' + str(vif) +
85 ' dst=PHYS proto=any\n' )
86 os.write( fd, 'ADD ACCEPT dstaddr=' + addr +
87 ' dstaddrmask=255.255.255.255' +
88 ' src=ANY' +
89 ' dstdom=' + str(dom) + ' dstidx=' + str(vif) +
90 ' proto=any\n' )
91 os.close( fd )
92 return None
94 def add_offset_to_ip( ip, off ):
95 l = string.split(ip,'.')
96 a = ( (string.atoi(l[0])<<24) | (string.atoi(l[1])<<16) |
97 (string.atoi(l[2])<<8) | string.atoi(l[3]) ) + off
99 return '%d.%d.%d.%d' % ( ((a>>24)&0xff), ((a>>16)&0xff),
100 ((a>>8)&0xff), (a&0xff) )
102 ##### VBD-related Functions
104 def blkdev_name_to_number(name):
105 """Take the given textual block-device name (e.g., '/dev/sda1',
106 'hda') and return the device number used by the OS. """
108 if not re.match( '/dev/', name ):
109 name = '/dev/' + name
111 return os.stat(name).st_rdev
113 # lookup_blkdev_partn_info( '/dev/sda3' )
114 def lookup_raw_partn(partition):
115 """Take the given block-device name (e.g., '/dev/sda1', 'hda')
116 and return a dictionary { device, start_sector,
117 nr_sectors, type }
118 device: Device number of the given partition
119 start_sector: Index of first sector of the partition
120 nr_sectsors: Number of sectors comprising this partition
121 type: 'Disk' or identifying name for partition type
122 """
124 if not re.match( '/dev/', partition ):
125 partition = '/dev/' + partition
127 drive = re.split( '[0-9]', partition )[0]
129 if drive == partition:
130 fd = os.popen( '/sbin/sfdisk -s ' + drive + ' 2>/dev/null' )
131 line = fd.readline()
132 if line:
133 return [ { 'device' : blkdev_name_to_number(drive),
134 'start_sector' : 0,
135 'nr_sectors' : int(line) * 2,
136 'type' : 'Disk' } ]
137 return None
139 # determine position on disk
140 fd = os.popen( '/sbin/sfdisk -d ' + drive + ' 2>/dev/null' )
142 #['/dev/sda3 : start= 16948575, size=16836120, Id=83, bootable\012']
143 lines = fd.readlines()
144 for line in lines:
145 m = re.search( '^' + partition + '\s*: start=\s*([0-9]+), ' +
146 'size=\s*([0-9]+), Id=\s*(\S+).*$', line)
147 if m:
148 return [ { 'device' : blkdev_name_to_number(drive),
149 'start_sector' : int(m.group(1)),
150 'nr_sectors' : int(m.group(2)),
151 'type' : m.group(3) } ]
153 return None
155 def lookup_disk_uname( uname ):
156 """Lookup a list of segments for either a physical or a virtual device.
157 uname [string]: name of the device in the format \'vd:id\' for a virtual
158 disk, or \'phy:dev\' for a physical device
159 returns [list of dicts]: list of extents that make up the named device
160 """
161 ( type, d_name ) = string.split( uname, ':' )
163 if type == "phy":
164 segments = lookup_raw_partn( d_name )
165 elif type == "vd":
166 segments = vd_lookup( d_name )
168 return segments
172 ##### VD Management-related functions
176 def __vd_no_database():
177 """Called when no database found - exits with an error
178 """
179 print >> sys.stderr, "ERROR: Could not locate the database file at " + VD_DB_FILE
180 sys.exit(1)
182 def vd_format(partition, extent_size_mb):
183 """Format a partition or drive for use a virtual disk storage.
184 partition [string]: device file representing the partition
185 extent_size_mb [string]: extent size in megabytes to use on this disk
186 """
188 if not os.path.isfile(VD_DB_FILE):
189 vd_init_db(VD_DB_FILE)
191 if not re.match( '/dev/', partition ):
192 partition = '/dev/' + partition
194 cx = sqlite.connect(VD_DB_FILE)
195 cu = cx.cursor()
197 cu.execute("select * from vdisk_part where partition = \'"
198 + partition + "\'")
199 row = cu.fetchone()
201 extent_size = extent_size_mb * 2048 # convert megabytes to sectors
203 if not row:
204 part_info = lookup_raw_partn(partition)[0]
206 cu.execute("INSERT INTO vdisk_part(partition, part_id, extent_size) " +
207 "VALUES ( \'" + partition + "\', "
208 + str(blkdev_name_to_number(partition))
209 + ", " + str(extent_size) + ")")
212 cu.execute("SELECT max(vdisk_extent_no) FROM vdisk_extents "
213 + "WHERE vdisk_id = 0")
215 max_id, = cu.fetchone()
217 if max_id != None:
218 new_id = max_id + 1
219 else:
220 new_id = 0
222 for i in range(part_info['nr_sectors'] / extent_size):
223 sql ="""INSERT INTO vdisk_extents(vdisk_extent_no, vdisk_id,
224 part_id, part_extent_no)
225 VALUES ("""+ str(new_id + i) + ", 0, "\
226 + str(blkdev_name_to_number(partition))\
227 + ", " + str(i) + ")"
228 cu.execute(sql)
230 cx.commit()
231 cx.close()
232 return 0
235 def vd_create(size_mb, expiry):
236 """Create a new virtual disk.
237 size_mb [int]: size in megabytes for the new virtual disk
238 expiry [int]: expiry time in seconds from now
239 """
241 if not os.path.isfile(VD_DB_FILE):
242 __vd_no_database()
244 cx = sqlite.connect(VD_DB_FILE)
245 cu = cx.cursor()
247 size = size_mb * 2048
249 cu.execute("SELECT max(vdisk_id) FROM vdisks")
250 max_id, = cu.fetchone()
251 new_id = int(max_id) + 1
253 # fetch a list of extents from the expired disks, along with information
254 # about their size
255 cu.execute("""SELECT vdisks.vdisk_id, vdisk_extent_no, part_extent_no,
256 vdisk_extents.part_id, extent_size
257 FROM vdisks NATURAL JOIN vdisk_extents
258 NATURAL JOIN vdisk_part
259 WHERE expires AND expiry_time <= datetime('now')
260 ORDER BY expiry_time asc, vdisk_extent_no desc
261 """) # aims to reuse the last extents
262 # from the longest-expired disks first
264 allocated = 0
266 if expiry:
267 expiry_ts = "datetime('now', '" + str(expiry) + " seconds')"
268 expires = 1
269 else:
270 expiry_ts = "NULL"
271 expires = 0
273 # we'll use this to build the SQL statement we want
274 building_sql = "INSERT INTO vdisks(vdisk_id, size, expires, expiry_time)" \
275 +" VALUES ("+str(new_id)+", "+str(size)+ ", " \
276 + str(expires) + ", " + expiry_ts + "); "
278 counter = 0
280 while allocated < size:
281 row = cu.fetchone()
282 if not row:
283 print "ran out of space, having allocated %d meg of %d" % (allocated, size)
284 cx.close()
285 return -1
288 (vdisk_id, vdisk_extent_no, part_extent_no, part_id, extent_size) = row
289 allocated += extent_size
290 building_sql += "UPDATE vdisk_extents SET vdisk_id = " + str(new_id) \
291 + ", " + "vdisk_extent_no = " + str(counter) \
292 + " WHERE vdisk_extent_no = " + str(vdisk_extent_no) \
293 + " AND vdisk_id = " + str(vdisk_id) + "; "
295 counter += 1
298 # this will execute the SQL query we build to store details of the new
299 # virtual disk and allocate space to it print building_sql
300 cu.execute(building_sql)
302 cx.commit()
303 cx.close()
304 return str(new_id)
307 def vd_lookup(id):
308 """Lookup a Virtual Disk by ID.
309 id [string]: a virtual disk identifier
310 Returns [list of dicts]: a list of extents as dicts, containing fields:
311 device : Linux device number of host disk
312 start_sector : within the device
313 nr_sectors : size of this extent
314 type : set to \'VD Extent\'
316 part_device : Linux device no of host partition
317 part_start_sector : within the partition
318 """
320 if not os.path.isfile(VD_DB_FILE):
321 __vd_no_database()
323 cx = sqlite.connect(VD_DB_FILE)
324 cu = cx.cursor()
326 cu.execute("-- types int")
327 cu.execute("""SELECT COUNT(*)
328 FROM vdisks
329 WHERE (expiry_time > datetime('now') OR NOT expires)
330 AND vdisk_id = """ + id)
331 count, = cu.fetchone()
333 if not count:
334 cx.close()
335 return -1
337 cu.execute("SELECT size from vdisks WHERE vdisk_id = " + id)
338 real_size, = cu.fetchone()
340 # This query tells PySQLite how to convert the data returned from the
341 # following query - the use of the multiplication confuses it otherwise ;-)
342 # This row is significant to PySQLite but is syntactically an SQL comment.
344 cu.execute("-- types str, int, int, int")
346 # This SQL statement is designed so that when the results are fetched they
347 # will be in the right format to return immediately.
348 cu.execute("""SELECT partition, vdisk_part.part_id,
349 round(part_extent_no * extent_size) as start,
350 extent_size
352 FROM vdisks NATURAL JOIN vdisk_extents
353 NATURAL JOIN vdisk_part
355 WHERE vdisk_extents.vdisk_id = """ + id
356 )
358 extent_tuples = cu.fetchall()
360 # use this function to map the results from the database into a dict
361 # list of extents, for consistency with the rest of the code
362 def transform ((partition, part_device, part_offset, nr_sectors)):
363 return {
364 # the disk device this extent is on - for passing to Xen
365 'device' : lookup_raw_partn(partition)[0]['device'],
366 # the offset of this extent within the disk - for passing to Xen
367 'start_sector' : int(part_offset + lookup_raw_partn(partition)[0]['start_sector']),
368 # extent size, in sectors
369 'nr_sectors' : nr_sectors,
370 # partition device this extent is on (useful to know for XenoUtil fns)
371 'part_device' : part_device,
372 # start sector within this partition (useful to know for XenoUtil fns)
373 'part_start_sector' : part_offset,
374 # type of this extent - handy to know
375 'type' : 'VD Extent' }
377 cx.commit()
378 cx.close()
380 extent_dicts = map(transform, extent_tuples)
382 # calculate the over-allocation in sectors (happens because
383 # we allocate whole extents)
384 allocated_size = 0
385 for i in extent_dicts:
386 allocated_size += i['nr_sectors']
388 over_allocation = allocated_size - real_size
390 # trim down the last extent's length so the resulting VBD will be the
391 # size requested, rather than being rounded up to the nearest extent
392 extent_dicts[len(extent_dicts) - 1]['nr_sectors'] -= over_allocation
394 return extent_dicts
397 def vd_enlarge(vdisk_id, extra_size_mb):
398 """Create a new virtual disk.
399 vdisk_id [string] : ID of the virtual disk to enlarge
400 extra_size_mb [int]: size in megabytes to increase the allocation by
401 returns [int] : 0 on success, otherwise non-zero
402 """
404 if not os.path.isfile(VD_DB_FILE):
405 __vd_no_database()
407 cx = sqlite.connect(VD_DB_FILE)
408 cu = cx.cursor()
410 extra_size = extra_size_mb * 2048
412 cu.execute("-- types int")
413 cu.execute("SELECT COUNT(*) FROM vdisks WHERE vdisk_id = " + vdisk_id
414 + " AND (expiry_time > datetime('now') OR NOT expires)")
415 count, = cu.fetchone()
417 if not count: # no such vdisk
418 cx.close()
419 return -1
421 cu.execute("-- types int")
422 cu.execute("""SELECT SUM(extent_size)
423 FROM vdisks NATURAL JOIN vdisk_extents
424 NATURAL JOIN vdisk_part
425 WHERE vdisks.vdisk_id = """ + vdisk_id)
427 real_size, = cu.fetchone() # get the true allocated size
429 cu.execute("-- types int")
430 cu.execute("SELECT size FROM vdisks WHERE vdisk_id = " + vdisk_id)
432 old_size, = cu.fetchone()
435 cu.execute("--- types int")
436 cu.execute("""SELECT MAX(vdisk_extent_no)
437 FROM vdisk_extents
438 WHERE vdisk_id = """ + vdisk_id)
440 counter = cu.fetchone()[0] + 1 # this stores the extent numbers
443 # because of the extent-based allocation, the VD may already have more
444 # allocated space than they asked for. Find out how much we really
445 # need to add.
446 add_size = extra_size + old_size - real_size
448 # fetch a list of extents from the expired disks, along with information
449 # about their size
450 cu.execute("""SELECT vdisks.vdisk_id, vdisk_extent_no, part_extent_no,
451 vdisk_extents.part_id, extent_size
452 FROM vdisks NATURAL JOIN vdisk_extents
453 NATURAL JOIN vdisk_part
454 WHERE expires AND expiry_time <= datetime('now')
455 ORDER BY expiry_time asc, vdisk_extent_no desc
456 """) # aims to reuse the last extents
457 # from the longest-expired disks first
459 allocated = 0
461 building_sql = "UPDATE vdisks SET size = " + str(old_size + extra_size)\
462 + " WHERE vdisk_id = " + vdisk_id + "; "
464 while allocated < add_size:
465 row = cu.fetchone()
466 if not row:
467 cx.close()
468 return -1
470 (dead_vd_id, vdisk_extent_no, part_extent_no, part_id, extent_size) = row
471 allocated += extent_size
472 building_sql += "UPDATE vdisk_extents SET vdisk_id = " + vdisk_id \
473 + ", " + "vdisk_extent_no = " + str(counter) \
474 + " WHERE vdisk_extent_no = " + str(vdisk_extent_no) \
475 + " AND vdisk_id = " + str(dead_vd_id) + "; "
477 counter += 1
480 # this will execute the SQL query we build to store details of the new
481 # virtual disk and allocate space to it print building_sql
482 cu.execute(building_sql)
484 cx.commit()
485 cx.close()
486 return 0
489 def vd_undelete(vdisk_id, expiry_time):
490 """Create a new virtual disk.
491 vdisk_id [int]: size in megabytes for the new virtual disk
492 expiry_time [int]: expiry time, in seconds from now
493 returns [int]: zero on success, non-zero on failure
494 """
496 if not os.path.isfile(VD_DB_FILE):
497 __vd_no_database()
499 cx = sqlite.connect(VD_DB_FILE)
500 cu = cx.cursor()
502 cu.execute("-- types int")
503 cu.execute("SELECT COUNT(*) FROM vdisks WHERE vdisk_id = " + vdisk_id)
504 count, = cu.fetchone()
506 if not count:
507 cx.close()
508 return -1
510 cu.execute("-- types int")
511 cu.execute("""SELECT SUM(extent_size)
512 FROM vdisks NATURAL JOIN vdisk_extents
513 NATURAL JOIN vdisk_part
514 WHERE vdisks.vdisk_id = """ + vdisk_id)
516 real_size, = cu.fetchone() # get the true allocated size
519 cu.execute("-- types int")
520 cu.execute("SELECT size FROM vdisks WHERE vdisk_id = " + vdisk_id)
522 old_size, = cu.fetchone()
524 if real_size < old_size:
525 cx.close()
526 return -1
528 if expiry_time == 0:
529 expires = '0'
530 else:
531 expires = '1'
533 # this will execute the SQL query we build to store details of the new
534 # virtual disk and allocate space to it print building_sql
535 cu.execute("UPDATE vdisks SET expiry_time = datetime('now','"
536 + str(expiry_time) + " seconds'), expires = " + expires
537 + " WHERE vdisk_id = " + vdisk_id)
539 cx.commit()
540 cx.close()
541 return 0
546 def vd_list():
547 """Lists all the virtual disks registered in the system.
548 returns [list of dicts]
549 """
551 if not os.path.isfile(VD_DB_FILE):
552 __vd_no_database()
554 cx = sqlite.connect(VD_DB_FILE)
555 cu = cx.cursor()
557 cu.execute("""SELECT vdisk_id, size, expires, expiry_time
558 FROM vdisks
559 WHERE (NOT expires) OR expiry_time > datetime('now')
560 """)
562 ret = cu.fetchall()
564 cx.close()
566 def makedicts((vdisk_id, size, expires, expiry_time)):
567 return { 'vdisk_id' : str(vdisk_id), 'size': size,
568 'expires' : expires, 'expiry_time' : expiry_time }
570 return map(makedicts, ret)
573 def vd_refresh(id, expiry):
574 """Change the expiry time of a virtual disk.
575 id [string] : a virtual disk identifier
576 expiry [int] : expiry time in seconds from now (0 = never expire)
577 returns [int]: zero on success, non-zero on failure
578 """
580 if not os.path.isfile(VD_DB_FILE):
581 __vd_no_database()
583 cx = sqlite.connect(VD_DB_FILE)
584 cu = cx.cursor()
586 cu.execute("-- types int")
587 cu.execute("SELECT COUNT(*) FROM vdisks WHERE vdisk_id = " + id
588 + " AND (expiry_time > datetime('now') OR NOT expires)")
589 count, = cu.fetchone()
591 if not count:
592 cx.close()
593 return -1
595 if expiry:
596 expires = 1
597 expiry_ts = "datetime('now', '" + str(expiry) + " seconds')"
598 else:
599 expires = 0
600 expiry_ts = "NULL"
602 cu.execute("UPDATE vdisks SET expires = " + str(expires)
603 + ", expiry_time = " + expiry_ts
604 + " WHERE (expiry_time > datetime('now') OR NOT expires)"
605 + " AND vdisk_id = " + id)
607 cx.commit()
608 cx.close()
610 return 0
613 def vd_delete(id):
614 """Deletes a Virtual Disk, making its extents available for future VDs.
615 id [string] : identifier for the virtual disk to delete
616 returns [int] : 0 on success, -1 on failure (VD not found
617 or already deleted)
618 """
620 if not os.path.isfile(VD_DB_FILE):
621 __vd_no_database()
623 cx = sqlite.connect(VD_DB_FILE)
624 cu = cx.cursor()
626 cu.execute("-- types int")
627 cu.execute("SELECT COUNT(*) FROM vdisks WHERE vdisk_id = " + id
628 + " AND (expiry_time > datetime('now') OR NOT expires)")
629 count, = cu.fetchone()
631 if not count:
632 cx.close()
633 return -1
635 cu.execute("UPDATE vdisks SET expires = 1, expiry_time = datetime('now')"
636 + " WHERE vdisk_id = " + id)
638 cx.commit()
639 cx.close()
641 return 0
644 def vd_freespace():
645 """Returns the amount of free space available for new virtual disks, in MB
646 returns [int] : free space for VDs in MB
647 """
649 if not os.path.isfile(VD_DB_FILE):
650 __vd_no_database()
652 cx = sqlite.connect(VD_DB_FILE)
653 cu = cx.cursor()
655 cu.execute("-- types int")
657 cu.execute("""SELECT SUM(extent_size)
658 FROM vdisks NATURAL JOIN vdisk_extents
659 NATURAL JOIN vdisk_part
660 WHERE expiry_time <= datetime('now') AND expires""")
662 sum, = cu.fetchone()
664 cx.close()
666 return sum / 2048
669 def vd_init_db(path):
670 """Initialise the VD SQLite database
671 path [string]: path to the SQLite database file
672 """
674 cx = sqlite.connect(path)
675 cu = cx.cursor()
677 cu.execute(
678 """CREATE TABLE vdisk_extents
679 ( vdisk_extent_no INT,
680 vdisk_id INT,
681 part_id INT,
682 part_extent_no INT )
683 """)
685 cu.execute(
686 """CREATE TABLE vdisk_part
687 ( part_id INT,
688 partition VARCHAR,
689 extent_size INT )
690 """)
692 cu.execute(
693 """CREATE TABLE vdisks
694 ( vdisk_id INT,
695 size INT,
696 expires BOOLEAN,
697 expiry_time TIMESTAMP )
698 """)
701 cu.execute(
702 """INSERT INTO vdisks ( vdisk_id, size, expires, expiry_time )
703 VALUES ( 0, 0, 1, datetime('now') )
704 """)
706 cx.commit()
707 cx.close()
709 VD_DB_FILE = path
713 def vd_cp_to_file(vdisk_id,filename):
714 """Writes the contents of a specified vdisk out into a disk file, leaving
715 the original copy in the virtual disk pool."""
717 cx = sqlite.connect(VD_DB_FILE)
718 cu = cx.cursor()
720 extents = vd_lookup(vdisk_id)
722 if extents < 0:
723 return -1
725 file_idx = 0 # index into source file, in sectors
727 for i in extents:
728 cu.execute("""SELECT partition, extent_size FROM vdisk_part
729 WHERE part_id = """ + str(i['part_device']))
731 (partition, extent_size) = cu.fetchone()
733 os.system("dd bs=1b if=" + partition + " of=" + filename
734 + " skip=" + str(i['part_start_sector'])
735 + " seek=" + str(file_idx)
736 + " count=" + str(i['nr_sectors'])
737 + " > /dev/null")
739 file_idx += i['nr_sectors']
741 cx.close()
743 return 0 # should return -1 if something breaks
746 def vd_mv_to_file(vdisk_id,filename):
747 """Writes a vdisk out into a disk file and frees the space originally
748 taken within the virtual disk pool.
749 vdisk_id [string]: ID of the vdisk to write out
750 filename [string]: file to write vdisk contents out to
751 returns [int]: zero on success, nonzero on failure
752 """
754 if vd_cp_to_file(vdisk_id,filename):
755 return -1
757 if vd_delete(vdisk_id):
758 return -1
760 return 0
763 def vd_read_from_file(filename,expiry):
764 """Reads the contents of a file directly into a vdisk, which is
765 automatically allocated to fit.
766 filename [string]: file to read disk contents from
767 returns [string] : vdisk ID for the destination vdisk
768 """
770 size_sectors = os.stat(filename).st_size / 512
772 vdisk_id = vd_create(size_sectors / ( 2 * 1024 ),expiry)
774 if vdisk_id < 0:
775 return -1
777 cx = sqlite.connect(VD_DB_FILE)
778 cu = cx.cursor()
780 cu.execute("""SELECT partition, extent_size, part_extent_no
781 FROM vdisk_part NATURAL JOIN vdisk_extents
782 WHERE vdisk_id = """ + vdisk_id + """
783 ORDER BY vdisk_extent_no""")
785 extents = cu.fetchall()
787 file_idx = 0 # index into source file, in sectors
789 def write_extent_to_vd((partition, extent_size, part_extent_no),
790 file_idx, filename):
791 """Write an extent out to disk and update file_idx"""
793 os.system("dd bs=512 if=" + filename + " of=" + partition
794 + " skip=" + str(file_idx)
795 + " seek=" + str(part_extent_no * extent_size)
796 + " count=" + str(min(extent_size, size_sectors - file_idx))
797 + " > /dev/null")
799 return file_idx + extent_size
801 for i in extents:
802 file_idx += write_extent_to_vd(i, file_idx, filename)
804 cx.close()
806 return vdisk_id
811 def vd_extents_validate(new_extents,new_writeable):
812 """Validate the extents against the existing extents.
813 Complains if the list supplied clashes against the extents that
814 are already in use in the system.
815 new_extents [list of dicts]: list of new extents, as dicts
816 new_writeable [int]: 1 if they are to be writeable, 0 otherwise
817 returns [int]: either the expertise level of the mapping if it doesn't
818 exceed VBD_EXPERT_MODE or -1 if it does (error)
819 """
821 import Xc # this is only needed in this function
823 xc = Xc.new()
825 ##### Probe for explicitly created virtual disks and build a list
826 ##### of extents for comparison with the ones that are being added
828 probe = xc.vbd_probe()
830 old_extents = [] # this will hold a list of all existing extents and
831 # their writeable status, as a list of (device,
832 # start, size, writeable?) tuples
834 for vbd in probe:
835 this_vbd_extents = xc.vbd_getextents(vbd['dom'],vbd['vbd'])
836 for vbd_ext in this_vbd_extents:
837 vbd_ext['writeable'] = vbd['writeable']
838 old_extents.append(vbd_ext)
840 ##### Now scan /proc/mounts for compile a list of extents corresponding to
841 ##### any devices mounted in DOM0. This list is added on to old_extents
843 regexp = re.compile("/dev/(\S*) \S* \S* (..).*")
844 fd = open('/proc/mounts', "r")
846 while True:
847 line = fd.readline()
848 if not line: # if we've run out of lines then stop reading
849 break
851 m = regexp.match(line)
853 # if the regexp didn't match then it's probably a line we don't
854 # care about - skip to next line
855 if not m:
856 continue
858 # lookup the device
859 ext_list = lookup_raw_partn(m.group(1))
861 # if lookup failed, skip to next mounted device
862 if not ext_list:
863 continue
865 # set a writeable flag as appropriate
866 for ext in ext_list:
867 ext['writeable'] = m.group(2) == 'rw'
869 # now we've got here, the contents of ext_list are in a
870 # suitable format to be added onto the old_extents list, ready
871 # for checking against the new extents
873 old_extents.extend(ext_list)
875 fd.close() # close /proc/mounts
877 ##### By this point, old_extents contains a list of extents, in
878 ##### dictionary format corresponding to every extent of physical
879 ##### disk that's either part of an explicitly created VBD, or is
880 ##### mounted under DOM0. We now check these extents against the
881 ##### proposed additions in new_extents, to see if a conflict will
882 ##### happen if they are added with write status new_writeable
884 level = 0 # this'll accumulate the max warning level
886 # Search for clashes between the new extents and the old ones
887 # Takes time O(len(new_extents) * len(old_extents))
888 for new_ext in new_extents:
889 for old_ext in old_extents:
890 if(new_ext['device'] == old_ext['device']):
892 new_ext_start = new_ext['start_sector']
893 new_ext_end = new_ext_start + new_ext['nr_sectors'] - 1
895 old_ext_start = old_ext['start_sector']
896 old_ext_end = old_ext_start + old_ext['nr_sectors'] - 1
898 if((old_ext_start <= new_ext_start <= old_ext_end) or
899 (old_ext_start <= new_ext_end <= old_ext_end)):
900 if (not old_ext['writeable']) and new_writeable:
901 level = max(1,level)
902 elif old_ext['writeable'] and (not new_writeable):
903 level = max(1,level)
904 elif old_ext['writeable'] and new_writeable:
905 level = max(2,level)
908 ##### level now holds the warning level incurred by the current
909 ##### VBD setup and we complain appropriately to the user
912 if level == 1:
913 print >> sys.stderr, """Warning: one or more hard disk extents
914 writeable by one domain are also readable by another."""
915 elif level == 2:
916 print >> sys.stderr, """Warning: one or more hard disk extents are
917 writeable by two or more domains simultaneously."""
919 if level > VBD_EXPERT_MODE:
920 print >> sys.stderr, """ERROR: This kind of disk sharing is not allowed
921 at the current safety level (%d).""" % VBD_EXPERT_MODE
922 level = -1
924 return level