1
import string, re, os, sys
5
"""Location of the Virtual Disk management database.
6
defaults to /var/db/xen_vdisks.sqlite
8
VD_DB_FILE = "/var/db/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
19
##### Module initialisation
22
# try to import sqlite (not everyone will have it installed)
25
# on failure, just catch the error, don't do anything
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').
35
fd = os.popen( '/sbin/ifconfig ' + dev + ' 2>/dev/null' )
36
lines = fd.readlines()
38
m = re.search( '^\s+inet addr:([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+).*',
44
def get_current_ipmask(dev='eth0'):
45
"""Return a string containing the primary IP netmask for the given
46
network interface (default 'eth0').
48
fd = os.popen( '/sbin/ifconfig ' + dev + ' 2>/dev/null' )
49
lines = fd.readlines()
51
m = re.search( '^.+Mask:([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+).*',
57
def get_current_ipgw(dev='eth0'):
58
"""Return a string containing the IP gateway for the given
59
network interface (default 'eth0').
61
fd = os.popen( '/sbin/route -n' )
62
lines = fd.readlines()
64
m = re.search( '^\S+\s+([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)' +
65
'\s+\S+\s+\S*G.*' + dev + '.*', line )
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.
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' )
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' +
89
' dstdom=' + str(dom) + ' dstidx=' + str(vif) +
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
def check_subnet( ip, network, netmask ):
103
l = string.split(ip,'.')
104
n_ip = ( (string.atoi(l[0])<<24) | (string.atoi(l[1])<<16) |
105
(string.atoi(l[2])<<8) | string.atoi(l[3]) )
107
l = string.split(network,'.')
108
n_net = ( (string.atoi(l[0])<<24) | (string.atoi(l[1])<<16) |
109
(string.atoi(l[2])<<8) | string.atoi(l[3]) )
111
l = string.split(netmask,'.')
112
n_mask = ( (string.atoi(l[0])<<24) | (string.atoi(l[1])<<16) |
113
(string.atoi(l[2])<<8) | string.atoi(l[3]) )
115
return (n_ip&n_mask)==(n_net&n_mask)
118
##### VBD-related Functions
120
def blkdev_name_to_number(name):
121
"""Take the given textual block-device name (e.g., '/dev/sda1',
122
'hda') and return the device number used by the OS. """
124
if not re.match( '/dev/', name ):
125
name = '/dev/' + name
127
return os.stat(name).st_rdev
129
# lookup_blkdev_partn_info( '/dev/sda3' )
130
def lookup_raw_partn(partition):
131
"""Take the given block-device name (e.g., '/dev/sda1', 'hda')
132
and return a dictionary { device, start_sector,
134
device: Device number of the given partition
135
start_sector: Index of first sector of the partition
136
nr_sectors: Number of sectors comprising this partition
137
type: 'Disk' or identifying name for partition type
140
if not re.match( '/dev/', partition ):
141
partition = '/dev/' + partition
143
drive = re.split( '[0-9]', partition )[0]
145
if drive == partition:
146
fd = os.popen( '/sbin/sfdisk -s ' + drive + ' 2>/dev/null' )
149
return [ { 'device' : blkdev_name_to_number(drive),
150
'start_sector' : long(0),
151
'nr_sectors' : long(line) * 2,
155
# determine position on disk
156
fd = os.popen( '/sbin/sfdisk -d ' + drive + ' 2>/dev/null' )
158
#['/dev/sda3 : start= 16948575, size=16836120, Id=83, bootable\012']
159
lines = fd.readlines()
161
m = re.search( '^' + partition + '\s*: start=\s*([0-9]+), ' +
162
'size=\s*([0-9]+), Id=\s*(\S+).*$', line)
164
return [ { 'device' : blkdev_name_to_number(drive),
165
'start_sector' : long(m.group(1)),
166
'nr_sectors' : long(m.group(2)),
167
'type' : m.group(3) } ]
171
def lookup_disk_uname( uname ):
172
"""Lookup a list of segments for either a physical or a virtual device.
173
uname [string]: name of the device in the format \'vd:id\' for a virtual
174
disk, or \'phy:dev\' for a physical device
175
returns [list of dicts]: list of extents that make up the named device
177
( type, d_name ) = string.split( uname, ':' )
180
segments = lookup_raw_partn( d_name )
182
segments = vd_lookup( d_name )
188
##### VD Management-related functions
190
##### By Mark Williamson, <mark.a.williamson@intel.com>
191
##### (C) Intel Research Cambridge
195
# Plenty of room for enhancement to this functionality (contributions
196
# welcome - and then you get to have your name in the source ;-)...
198
# vd_unformat() : want facilities to unallocate virtual disk
199
# partitions, possibly migrating virtual disks of them, with checks to see if
200
# it's safe and options to force it anyway
202
# vd_create() : should have an optional argument specifying a physical
203
# disk preference - useful to allocate for guest doms to do RAID
205
# vd_undelete() : add ability to "best effort" undelete as much of a
206
# vdisk as is left in the case that some of it has already been
207
# reallocated. Some people might still be able to recover some of
208
# their data this way, even if some of the disk has disappeared.
210
# It'd be nice if we could wipe virtual disks for security purposes -
211
# should be easy to do this using dev if=/dev/{zero,random} on each
212
# extent in turn. There could be another optional flag to vd_create
213
# in order to allow this.
215
# Error codes could be more expressive - i.e. actually tell why the
216
# error occurred rather than "it broke". Currently the code avoids
217
# using exceptions to make control scripting simpler and more
218
# accessible to beginners - therefore probably should just use more
221
# Enhancements / additions to the example scripts are also welcome:
222
# some people will interact with this code mostly through those
225
# More documentation of how this stuff should be used is always nice -
226
# if you have a novel configuration that you feel isn't discussed
227
# enough in the HOWTO (which is currently a work in progress), feel
228
# free to contribute a walkthrough, or something more substantial.
232
def __vd_no_database():
233
"""Called when no database found - exits with an error
235
print >> sys.stderr, "ERROR: Could not locate the database file at " + VD_DB_FILE
239
def vd_format(partition, extent_size_mb):
240
"""Format a partition or drive for use a virtual disk storage.
241
partition [string]: device file representing the partition
242
extent_size_mb [string]: extent size in megabytes to use on this disk
245
if not os.path.isfile(VD_DB_FILE):
246
vd_init_db(VD_DB_FILE)
248
if not re.match( '/dev/', partition ):
249
partition = '/dev/' + partition
251
cx = sqlite.connect(VD_DB_FILE)
254
cu.execute("select * from vdisk_part where partition = \'"
258
extent_size = extent_size_mb * 2048 # convert megabytes to sectors
261
part_info = lookup_raw_partn(partition)[0]
263
cu.execute("INSERT INTO vdisk_part(partition, part_id, extent_size) " +
264
"VALUES ( \'" + partition + "\', "
265
+ str(blkdev_name_to_number(partition))
266
+ ", " + str(extent_size) + ")")
269
cu.execute("SELECT max(vdisk_extent_no) FROM vdisk_extents "
270
+ "WHERE vdisk_id = 0")
272
max_id, = cu.fetchone()
279
num_extents = part_info['nr_sectors'] / extent_size
281
for i in range(num_extents):
282
sql ="""INSERT INTO vdisk_extents(vdisk_extent_no, vdisk_id,
283
part_id, part_extent_no)
284
VALUES ("""+ str(new_id + i) + ", 0, "\
285
+ str(blkdev_name_to_number(partition))\
286
+ ", " + str(num_extents - (i + 1)) + ")"
294
def vd_create(size_mb, expiry):
295
"""Create a new virtual disk.
296
size_mb [int]: size in megabytes for the new virtual disk
297
expiry [int]: expiry time in seconds from now
300
if not os.path.isfile(VD_DB_FILE):
303
cx = sqlite.connect(VD_DB_FILE)
306
size = size_mb * 2048
308
cu.execute("SELECT max(vdisk_id) FROM vdisks")
309
max_id, = cu.fetchone()
310
new_id = int(max_id) + 1
312
# fetch a list of extents from the expired disks, along with information
314
cu.execute("""SELECT vdisks.vdisk_id, vdisk_extent_no, part_extent_no,
315
vdisk_extents.part_id, extent_size
316
FROM vdisks NATURAL JOIN vdisk_extents
317
NATURAL JOIN vdisk_part
318
WHERE expires AND expiry_time <= datetime('now')
319
ORDER BY expiry_time ASC, vdisk_extent_no DESC
320
""") # aims to reuse the last extents
321
# from the longest-expired disks first
326
expiry_ts = "datetime('now', '" + str(expiry) + " seconds')"
332
# we'll use this to build the SQL statement we want
333
building_sql = "INSERT INTO vdisks(vdisk_id, size, expires, expiry_time)" \
334
+" VALUES ("+str(new_id)+", "+str(size)+ ", " \
335
+ str(expires) + ", " + expiry_ts + "); "
339
while allocated < size:
342
print "ran out of space, having allocated %d meg of %d" % (allocated, size)
347
(vdisk_id, vdisk_extent_no, part_extent_no, part_id, extent_size) = row
348
allocated += extent_size
349
building_sql += "UPDATE vdisk_extents SET vdisk_id = " + str(new_id) \
350
+ ", " + "vdisk_extent_no = " + str(counter) \
351
+ " WHERE vdisk_extent_no = " + str(vdisk_extent_no) \
352
+ " AND vdisk_id = " + str(vdisk_id) + "; "
357
# this will execute the SQL query we build to store details of the new
358
# virtual disk and allocate space to it print building_sql
359
cu.execute(building_sql)
367
"""Lookup a Virtual Disk by ID.
368
id [string]: a virtual disk identifier
369
Returns [list of dicts]: a list of extents as dicts, containing fields:
370
device : Linux device number of host disk
371
start_sector : within the device
372
nr_sectors : size of this extent
373
type : set to \'VD Extent\'
375
part_device : Linux device no of host partition
376
part_start_sector : within the partition
379
if not os.path.isfile(VD_DB_FILE):
382
cx = sqlite.connect(VD_DB_FILE)
385
cu.execute("-- types int")
386
cu.execute("""SELECT COUNT(*)
388
WHERE (expiry_time > datetime('now') OR NOT expires)
389
AND vdisk_id = """ + id)
390
count, = cu.fetchone()
396
cu.execute("SELECT size from vdisks WHERE vdisk_id = " + id)
397
real_size, = cu.fetchone()
399
# This query tells PySQLite how to convert the data returned from the
400
# following query - the use of the multiplication confuses it otherwise ;-)
401
# This row is significant to PySQLite but is syntactically an SQL comment.
403
cu.execute("-- types str, int, int, int")
405
# This SQL statement is designed so that when the results are fetched they
406
# will be in the right format to return immediately.
407
cu.execute("""SELECT partition, vdisk_part.part_id,
408
round(part_extent_no * extent_size) as start,
411
FROM vdisks NATURAL JOIN vdisk_extents
412
NATURAL JOIN vdisk_part
414
WHERE vdisk_extents.vdisk_id = """ + id
415
+ " ORDER BY vdisk_extents.vdisk_extent_no ASC"
418
extent_tuples = cu.fetchall()
420
# use this function to map the results from the database into a dict
421
# list of extents, for consistency with the rest of the code
422
def transform ((partition, part_device, part_offset, nr_sectors)):
424
# the disk device this extent is on - for passing to Xen
425
'device' : lookup_raw_partn(partition)[0]['device'],
426
# the offset of this extent within the disk - for passing to Xen
427
'start_sector' : long(part_offset + lookup_raw_partn(partition)[0]['start_sector']),
428
# extent size, in sectors
429
'nr_sectors' : nr_sectors,
430
# partition device this extent is on (useful to know for XenoUtil fns)
431
'part_device' : part_device,
432
# start sector within this partition (useful to know for XenoUtil fns)
433
'part_start_sector' : part_offset,
434
# type of this extent - handy to know
435
'type' : 'VD Extent' }
440
extent_dicts = map(transform, extent_tuples)
442
# calculate the over-allocation in sectors (happens because
443
# we allocate whole extents)
445
for i in extent_dicts:
446
allocated_size += i['nr_sectors']
448
over_allocation = allocated_size - real_size
450
# trim down the last extent's length so the resulting VBD will be the
451
# size requested, rather than being rounded up to the nearest extent
452
extent_dicts[len(extent_dicts) - 1]['nr_sectors'] -= over_allocation
457
def vd_enlarge(vdisk_id, extra_size_mb):
458
"""Create a new virtual disk.
459
vdisk_id [string] : ID of the virtual disk to enlarge
460
extra_size_mb [int]: size in megabytes to increase the allocation by
461
returns [int] : 0 on success, otherwise non-zero
464
if not os.path.isfile(VD_DB_FILE):
467
cx = sqlite.connect(VD_DB_FILE)
470
extra_size = extra_size_mb * 2048
472
cu.execute("-- types int")
473
cu.execute("SELECT COUNT(*) FROM vdisks WHERE vdisk_id = " + vdisk_id
474
+ " AND (expiry_time > datetime('now') OR NOT expires)")
475
count, = cu.fetchone()
477
if not count: # no such vdisk
481
cu.execute("-- types int")
482
cu.execute("""SELECT SUM(extent_size)
483
FROM vdisks NATURAL JOIN vdisk_extents
484
NATURAL JOIN vdisk_part
485
WHERE vdisks.vdisk_id = """ + vdisk_id)
487
real_size, = cu.fetchone() # get the true allocated size
489
cu.execute("-- types int")
490
cu.execute("SELECT size FROM vdisks WHERE vdisk_id = " + vdisk_id)
492
old_size, = cu.fetchone()
495
cu.execute("--- types int")
496
cu.execute("""SELECT MAX(vdisk_extent_no)
498
WHERE vdisk_id = """ + vdisk_id)
500
counter = cu.fetchone()[0] + 1 # this stores the extent numbers
503
# because of the extent-based allocation, the VD may already have more
504
# allocated space than they asked for. Find out how much we really
506
add_size = extra_size + old_size - real_size
508
# fetch a list of extents from the expired disks, along with information
510
cu.execute("""SELECT vdisks.vdisk_id, vdisk_extent_no, part_extent_no,
511
vdisk_extents.part_id, extent_size
512
FROM vdisks NATURAL JOIN vdisk_extents
513
NATURAL JOIN vdisk_part
514
WHERE expires AND expiry_time <= datetime('now')
515
ORDER BY expiry_time ASC, vdisk_extent_no DESC
516
""") # aims to reuse the last extents
517
# from the longest-expired disks first
521
building_sql = "UPDATE vdisks SET size = " + str(old_size + extra_size)\
522
+ " WHERE vdisk_id = " + vdisk_id + "; "
524
while allocated < add_size:
530
(dead_vd_id, vdisk_extent_no, part_extent_no, part_id, extent_size) = row
531
allocated += extent_size
532
building_sql += "UPDATE vdisk_extents SET vdisk_id = " + vdisk_id \
533
+ ", " + "vdisk_extent_no = " + str(counter) \
534
+ " WHERE vdisk_extent_no = " + str(vdisk_extent_no) \
535
+ " AND vdisk_id = " + str(dead_vd_id) + "; "
540
# this will execute the SQL query we build to store details of the new
541
# virtual disk and allocate space to it print building_sql
542
cu.execute(building_sql)
549
def vd_undelete(vdisk_id, expiry_time):
550
"""Create a new virtual disk.
551
vdisk_id [int]: size in megabytes for the new virtual disk
552
expiry_time [int]: expiry time, in seconds from now
553
returns [int]: zero on success, non-zero on failure
556
if not os.path.isfile(VD_DB_FILE):
559
if vdisk_id == '0': # undeleting vdisk 0 isn't sane!
562
cx = sqlite.connect(VD_DB_FILE)
565
cu.execute("-- types int")
566
cu.execute("SELECT COUNT(*) FROM vdisks WHERE vdisk_id = " + vdisk_id)
567
count, = cu.fetchone()
573
cu.execute("-- types int")
574
cu.execute("""SELECT SUM(extent_size)
575
FROM vdisks NATURAL JOIN vdisk_extents
576
NATURAL JOIN vdisk_part
577
WHERE vdisks.vdisk_id = """ + vdisk_id)
579
real_size, = cu.fetchone() # get the true allocated size
582
cu.execute("-- types int")
583
cu.execute("SELECT size FROM vdisks WHERE vdisk_id = " + vdisk_id)
585
old_size, = cu.fetchone()
587
if real_size < old_size:
596
# this will execute the SQL query we build to store details of the new
597
# virtual disk and allocate space to it print building_sql
598
cu.execute("UPDATE vdisks SET expiry_time = datetime('now','"
599
+ str(expiry_time) + " seconds'), expires = " + expires
600
+ " WHERE vdisk_id = " + vdisk_id)
610
"""Lists all the virtual disks registered in the system.
611
returns [list of dicts]
614
if not os.path.isfile(VD_DB_FILE):
617
cx = sqlite.connect(VD_DB_FILE)
620
cu.execute("""SELECT vdisk_id, size, expires, expiry_time
622
WHERE (NOT expires) OR expiry_time > datetime('now')
629
def makedicts((vdisk_id, size, expires, expiry_time)):
630
return { 'vdisk_id' : str(vdisk_id), 'size': size,
631
'expires' : expires, 'expiry_time' : expiry_time }
633
return map(makedicts, ret)
636
def vd_refresh(id, expiry):
637
"""Change the expiry time of a virtual disk.
638
id [string] : a virtual disk identifier
639
expiry [int] : expiry time in seconds from now (0 = never expire)
640
returns [int]: zero on success, non-zero on failure
643
if not os.path.isfile(VD_DB_FILE):
646
cx = sqlite.connect(VD_DB_FILE)
649
cu.execute("-- types int")
650
cu.execute("SELECT COUNT(*) FROM vdisks WHERE vdisk_id = " + id
651
+ " AND (expiry_time > datetime('now') OR NOT expires)")
652
count, = cu.fetchone()
660
expiry_ts = "datetime('now', '" + str(expiry) + " seconds')"
665
cu.execute("UPDATE vdisks SET expires = " + str(expires)
666
+ ", expiry_time = " + expiry_ts
667
+ " WHERE (expiry_time > datetime('now') OR NOT expires)"
668
+ " AND vdisk_id = " + id)
677
"""Deletes a Virtual Disk, making its extents available for future VDs.
678
id [string] : identifier for the virtual disk to delete
679
returns [int] : 0 on success, -1 on failure (VD not found
683
if not os.path.isfile(VD_DB_FILE):
686
cx = sqlite.connect(VD_DB_FILE)
689
cu.execute("-- types int")
690
cu.execute("SELECT COUNT(*) FROM vdisks WHERE vdisk_id = " + id
691
+ " AND (expiry_time > datetime('now') OR NOT expires)")
692
count, = cu.fetchone()
698
cu.execute("UPDATE vdisks SET expires = 1, expiry_time = datetime('now')"
699
+ " WHERE vdisk_id = " + id)
708
"""Returns the amount of free space available for new virtual disks, in MB
709
returns [int] : free space for VDs in MB
712
if not os.path.isfile(VD_DB_FILE):
715
cx = sqlite.connect(VD_DB_FILE)
718
cu.execute("-- types int")
720
cu.execute("""SELECT SUM(extent_size)
721
FROM vdisks NATURAL JOIN vdisk_extents
722
NATURAL JOIN vdisk_part
723
WHERE expiry_time <= datetime('now') AND expires""")
732
def vd_init_db(path):
733
"""Initialise the VD SQLite database
734
path [string]: path to the SQLite database file
737
cx = sqlite.connect(path)
741
"""CREATE TABLE vdisk_extents
742
( vdisk_extent_no INT,
749
"""CREATE TABLE vdisk_part
756
"""CREATE TABLE vdisks
760
expiry_time TIMESTAMP )
765
"""INSERT INTO vdisks ( vdisk_id, size, expires, expiry_time )
766
VALUES ( 0, 0, 1, datetime('now') )
776
def vd_cp_to_file(vdisk_id,filename):
777
"""Writes the contents of a specified vdisk out into a disk file, leaving
778
the original copy in the virtual disk pool."""
780
cx = sqlite.connect(VD_DB_FILE)
783
extents = vd_lookup(vdisk_id)
788
file_idx = 0 # index into source file, in sectors
791
cu.execute("""SELECT partition, extent_size FROM vdisk_part
792
WHERE part_id = """ + str(i['part_device']))
794
(partition, extent_size) = cu.fetchone()
796
os.system("dd bs=1b if=" + partition + " of=" + filename
797
+ " skip=" + str(i['part_start_sector'])
798
+ " seek=" + str(file_idx)
799
+ " count=" + str(i['nr_sectors'])
802
file_idx += i['nr_sectors']
806
return 0 # should return -1 if something breaks
809
def vd_mv_to_file(vdisk_id,filename):
810
"""Writes a vdisk out into a disk file and frees the space originally
811
taken within the virtual disk pool.
812
vdisk_id [string]: ID of the vdisk to write out
813
filename [string]: file to write vdisk contents out to
814
returns [int]: zero on success, nonzero on failure
817
if vd_cp_to_file(vdisk_id,filename):
820
if vd_delete(vdisk_id):
826
def vd_read_from_file(filename,expiry):
827
"""Reads the contents of a file directly into a vdisk, which is
828
automatically allocated to fit.
829
filename [string]: file to read disk contents from
830
returns [string] : vdisk ID for the destination vdisk
833
size_bytes = os.stat(filename).st_size
835
(size_mb,leftover) = divmod(size_bytes,1048580) # size in megabytes
836
if leftover > 0: size_mb += 1 # round up if not an exact number of MB
838
vdisk_id = vd_create(size_mb, expiry)
843
cx = sqlite.connect(VD_DB_FILE)
846
cu.execute("""SELECT partition, extent_size, part_extent_no
847
FROM vdisk_part NATURAL JOIN vdisk_extents
848
WHERE vdisk_id = """ + vdisk_id + """
849
ORDER BY vdisk_extent_no ASC""")
851
extents = cu.fetchall()
853
size_sectors = size_mb * 2048 # for feeding to dd
855
file_idx = 0 # index into source file, in sectors
857
def write_extent_to_vd((partition, extent_size, part_extent_no),
859
"""Write an extent out to disk and update file_idx"""
861
os.system("dd bs=512 if=" + filename + " of=" + partition
862
+ " skip=" + str(file_idx)
863
+ " seek=" + str(part_extent_no * extent_size)
864
+ " count=" + str(min(extent_size, size_sectors - file_idx))
870
file_idx += write_extent_to_vd(i, file_idx, filename)
879
def vd_extents_validate(new_extents,new_writeable):
880
"""Validate the extents against the existing extents.
881
Complains if the list supplied clashes against the extents that
882
are already in use in the system.
883
new_extents [list of dicts]: list of new extents, as dicts
884
new_writeable [int]: 1 if they are to be writeable, 0 otherwise
885
returns [int]: either the expertise level of the mapping if it doesn't
886
exceed VBD_EXPERT_MODE or -1 if it does (error)
889
import Xc # this is only needed in this function
893
##### Probe for explicitly created virtual disks and build a list
894
##### of extents for comparison with the ones that are being added
896
probe = xc.vbd_probe()
898
old_extents = [] # this will hold a list of all existing extents and
899
# their writeable status, as a list of (device,
900
# start, size, writeable?) tuples
903
this_vbd_extents = xc.vbd_getextents(vbd['dom'],vbd['vbd'])
904
for vbd_ext in this_vbd_extents:
905
vbd_ext['writeable'] = vbd['writeable']
906
old_extents.append(vbd_ext)
908
##### Now scan /proc/mounts for compile a list of extents corresponding to
909
##### any devices mounted in DOM0. This list is added on to old_extents
911
regexp = re.compile("/dev/(\S*) \S* \S* (..).*")
912
fd = open('/proc/mounts', "r")
916
if not line: # if we've run out of lines then stop reading
919
m = regexp.match(line)
921
# if the regexp didn't match then it's probably a line we don't
922
# care about - skip to next line
927
ext_list = lookup_raw_partn(m.group(1))
929
# if lookup failed, skip to next mounted device
933
# set a writeable flag as appropriate
935
ext['writeable'] = m.group(2) == 'rw'
937
# now we've got here, the contents of ext_list are in a
938
# suitable format to be added onto the old_extents list, ready
939
# for checking against the new extents
941
old_extents.extend(ext_list)
943
fd.close() # close /proc/mounts
945
##### By this point, old_extents contains a list of extents, in
946
##### dictionary format corresponding to every extent of physical
947
##### disk that's either part of an explicitly created VBD, or is
948
##### mounted under DOM0. We now check these extents against the
949
##### proposed additions in new_extents, to see if a conflict will
950
##### happen if they are added with write status new_writeable
952
level = 0 # this'll accumulate the max warning level
954
# Search for clashes between the new extents and the old ones
955
# Takes time O(len(new_extents) * len(old_extents))
956
for new_ext in new_extents:
957
for old_ext in old_extents:
958
if(new_ext['device'] == old_ext['device']):
960
new_ext_start = new_ext['start_sector']
961
new_ext_end = new_ext_start + new_ext['nr_sectors'] - 1
963
old_ext_start = old_ext['start_sector']
964
old_ext_end = old_ext_start + old_ext['nr_sectors'] - 1
966
if((old_ext_start <= new_ext_start <= old_ext_end) or
967
(old_ext_start <= new_ext_end <= old_ext_end)):
968
if (not old_ext['writeable']) and new_writeable:
970
elif old_ext['writeable'] and (not new_writeable):
972
elif old_ext['writeable'] and new_writeable:
976
##### level now holds the warning level incurred by the current
977
##### VBD setup and we complain appropriately to the user
981
print >> sys.stderr, """Warning: one or more hard disk extents
982
writeable by one domain are also readable by another."""
984
print >> sys.stderr, """Warning: one or more hard disk extents are
985
writeable by two or more domains simultaneously."""
987
if level > VBD_EXPERT_MODE:
988
print >> sys.stderr, """ERROR: This kind of disk sharing is not allowed
989
at the current safety level (%d).""" % VBD_EXPERT_MODE