/[gli]/trunk/src/GLIStorageDevice.py
Gentoo

Contents of /trunk/src/GLIStorageDevice.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 492 - (show annotations) (download) (as text)
Sat Apr 9 06:22:00 2005 UTC (15 years, 6 months ago) by agaffney
File MIME type: text/x-python
File size: 18901 byte(s)
Major GLIStorageDevice overhaul...all MB now instead of sectors.

1 import commands, string, re, os, parted
2
3 MEGABYTE = 1024 * 1024
4
5 class Device:
6 "Class representing a partitionable device."
7
8 _device = None
9 _partitions = None
10 _geometry = None
11 _total_bytes = 0
12 _total_sectors = 0
13 _cylinder_bytes = 0
14 _sectors_in_cylinder = 0
15 _parted_dev = None
16 _parted_disk = None
17 _sector_bytes = 0
18 _total_mb = 0
19 _arch = None
20
21 archinfo = { 'sparc': { 'fixedparts': [ { 'minor': 3, 'type': "wholedisk" } ], 'disklabel': 'sun', 'extended': False },
22 'hppa': { 'fixedparts': [ { 'minor': 1, 'type': "boot" } ], 'disklabel': 'msdos', 'extended': False },
23 'x86': { 'fixedparts': [], 'disklabel': 'msdos', 'extended': True },
24 'ppc': { 'fixedparts': [ { 'minor': 1, 'type': "metadata" } ], 'disklabel': 'mac', 'extended': False }
25 }
26
27 def __init__(self, device, arch="x86"):
28 self._device = device
29 self._partitions = {}
30 self._geometry = {'cylinders': 0, 'heads': 0, 'sectors': 0, 'sectorsize': 512}
31 self._total_bytes = 0
32 self._cylinder_bytes = 0
33 self._arch = arch
34 self._parted_dev = parted.PedDevice.get(self._device)
35 self._parted_disk = parted.PedDisk.new(self._parted_dev)
36 self.set_disk_geometry_from_disk()
37
38 def set_disk_geometry_from_disk(self):
39 self._total_bytes = self._parted_dev.length * self._parted_dev.sector_size
40 self._geometry['heads'], self._geometry['sectors'], self._geometry['cylinders'] = self._parted_dev.heads, self._parted_dev.sectors, self._parted_dev.cylinders
41 self._sector_bytes = self._parted_dev.sector_size
42 self._cylinder_bytes = self._geometry['heads'] * self._geometry['sectors'] * self._sector_bytes
43 self._total_sectors = self._parted_dev.length
44 self._sectors_in_cylinder = self._geometry['heads'] * self._geometry['sectors']
45 self._total_mb = int(self._total_bytes / MEGABYTE)
46
47 def set_partitions_from_disk(self):
48 last_part = 0
49 last_log_part = 4
50 parted_part = self._parted_disk.next_partition()
51 while parted_part:
52 part_mb = int((parted_part.geom.end - parted_part.geom.start + 1) * self._sector_bytes / MEGABYTE)
53 if parted_part.num >= 1:
54 fs_type = ""
55 if parted_part.fs_type != None: fs_type = parted_part.fs_type.name
56 if parted_part.type == 2: fs_type = "extended"
57 # Insert code to calculate MB here ---------------------------------------v
58 self._partitions[int(parted_part.num)] = Partition(self, parted_part.num, part_mb, parted_part.geom.start, parted_part.geom.end, fs_type, format=False, existing=True)
59 elif parted_part.type_name == "free":
60 parent_part = self.get_partition_at(parted_part.geom.start, ignore_extended=0)
61 if parent_part:
62 # And here ----------------------------------------------------------------------------v
63 self._partitions[float(last_log_part+0.9)] = Partition(self, float(last_log_part+0.9), part_mb, parted_part.geom.start, parted_part.geom.end, "free", format=False, existing=False)
64 last_log_part += 1
65 else:
66 # And here ----------------------------------------------------------------------------v
67 self._partitions[float(last_log_part+0.1)] = Partition(self, float(last_log_part+0.1), part_mb, parted_part.geom.start, parted_part.geom.end, "free", format=False, existing=False)
68 last_part += 1
69 parted_part = self._parted_disk.next_partition(parted_part)
70
71 def set_partitions_from_install_profile_structure(self, ips):
72 pass
73 # for part in ips:
74 # tmppart = ips[part]
75 # existing = False
76 # parted_part = self._parted_disk.get_partition(part)
77 # if parted_part != None:
78 # start = parted_part.geom.start / self._sectors_in_cylinder
79 # end = parted_part.geom.end / self._sectors_in_cylinder
80 # fs_type = ""
81 # if parted_part.fs_type != None: fs_type = parted_part.fs_type.name
82 # if parted_part.type == 2: fs_type = "extended"
83 # if int(tmppart['start']) == int(start) and int(tmppart['end']) == int(end) and tmppart['type'] == fs_type and tmppart['format'] == False:
84 # existing = True
85 # self._partitions[int(part)] = Partition(self, part, '', tmppart['start'], tmppart['end'], 0, tmppart['type'], mountopts=tmppart['mountopts'], mountpoint=tmppart['mountpoint'], format=tmppart['format'], existing=(not tmppart['format']))
86
87 def get_device(self):
88 return self._device
89
90 def add_partition(self, free_minor, mb, start, end, type, mountpoint='', mountopts=''):
91 free_minor = float(free_minor)
92 new_minor = int(free_minor) + 1
93 if self._partitions.has_key(new_minor):
94 parts = self._partitions.keys()
95 parts.sort()
96 parts.reverse()
97 hole_at = 0
98 for i in range(1, parts[0]+1):
99 if i <= new_minor: continue
100 if not self._partitions.has_key(i):
101 hole_at = i
102 break
103 stopscooting = 0
104 for i in parts:
105 if stopscooting: break
106 if i >= hole_at and hole_at: continue
107 if i >= new_minor:
108 self._partitions[i].set_minor(i+1)
109 self._partitions[i+1] = self._partitions[i]
110 if i == new_minor: stopscooting = 1
111 del self._partitions[free_minor]
112 self._partitions[new_minor] = Partition(self, new_minor, mb, start, end, type, mountpoint=mountpoint, mountopts=mountopts)
113
114 def remove_partition(self, minor):
115 tmppart = self._partitions[int(minor)]
116 free_minor = 0
117 if tmppart.is_logical():
118 free_minor = float(minor)-0.1
119 else:
120 free_minor = float(minor)-0.9
121 self._partitions[free_minor] = Partition(self, free_minor, tmppart.get_mb(), 0, 0, "free", format=False, existing=False)
122 del self._partitions[int(minor)]
123
124 def get_free_space(self, start):
125 GAP_SIZE = 100
126 parts = self._partitions.keys()
127 parts.sort()
128 lastend_pri = 0
129 lastend_log = 0
130 free_start = -1
131 free_end = -1
132 if start > self._total_sectors: return (-1, -1)
133 for part in parts:
134 if part > 4: break
135 tmppart = self._partitions[part]
136 if (tmppart.get_start() > (lastend_pri + GAP_SIZE)) and (lastend_pri >= start):
137 free_start = lastend_pri
138 free_end = tmppart.get_start() - 1
139 break
140 if tmppart.is_extended() and start < tmppart.get_end():
141 lastend_log = tmppart.get_start()
142 for part_log in parts:
143 if part_log < 5: continue
144 tmppart_log = self._partitions[part_log]
145 if (tmppart_log.get_start() > (lastend_log + GAP_SIZE)) and (lastend_log >= start):
146 free_start = lastend_log
147 free_end = tmppart_log.get_start() - 1
148 break
149 lastend_log = tmppart_log.get_end() + 1
150 if free_start == -1 and lastend_log < tmppart.get_end():
151 free_start = lastend_log
152 free_end = tmppart.get_end()
153 break
154 lastend_pri = tmppart.get_end() + 1
155 if free_start == -1 and lastend_pri < self._total_sectors:
156 free_start = lastend_pri
157 free_end = self._total_sectors
158 return (free_start, free_end)
159
160 def get_partition_at(self, sector, ignore_extended=1):
161 parts = self._partitions.keys()
162 parts.sort()
163 for part in parts:
164 tmppart = self._partitions[part]
165 if ignore_extended and tmppart.is_extended(): continue
166 if (sector >= tmppart.get_start()) and (sector <= tmppart.get_end()):
167 return part
168 return 0
169
170 def get_free_minor_at(self, start, end):
171 parts = self._partitions.keys()
172 parts.sort()
173 minor = 1
174 lastpart = 0
175 for part in parts:
176 if part > 4: break
177 tmppart = self._partitions[part]
178 if end < tmppart.get_start():
179 minor = part
180 if (minor - 1) > lastpart: minor = lastpart + 1
181 break
182 if tmppart.is_extended() and start < tmppart.get_end():
183 minor = 5
184 lastpart = 4
185 for part_log in parts:
186 if part_log < 5: continue
187 tmppart_log = self._partitions[part_log]
188 if end < tmppart_log.get_start():
189 minor = part_log
190 if (minor - 1) > lastpart: minor = lastpart + 1
191 break
192 minor = part_log + 1
193 lastpart = part_log
194 break
195 minor = part + 1
196 lastpart = part
197 return minor
198
199 def get_ordered_partition_list(self):
200 parts = self._partitions.keys()
201 parts.sort()
202 partlist = []
203 tmppart = None
204 tmppart_log = None
205 for part in parts:
206 if self.archinfo[self._arch]['extended'] and part > 4.1: break
207 tmppart = self._partitions[part]
208 partlist.append(part)
209 if tmppart.is_extended():
210 for part_log in parts:
211 if part_log < 4.9: continue
212 tmppart_log = self._partitions[part_log]
213 partlist.append(part_log)
214 return partlist
215
216 def get_install_profile_structure(self):
217 devdic = {}
218 for part in self._partitions:
219 tmppart = self._partitions[part]
220 devdic[part] = { 'mb': tmppart.get_mb(), 'minor': float(part), 'origminor': tmppart.get_orig_minor(), 'start': tmppart.get_start(), 'end': tmppart.get_end(), 'type': tmppart.get_type(), 'mountpoint': tmppart.get_mountpoint(), 'mountopts': tmppart.get_mountopts(), 'format': tmppart.get_format() }
221 return devdic
222
223 def get_extended_partition(self):
224 for part in self._partitions:
225 tmppart = self._partitions[part]
226 if tmppart.is_extended():
227 return part
228 return 0
229
230 def get_num_sectors(self):
231 return int(self._total_sectors)
232
233 def get_cylinder_size(self):
234 return int(self._cylinder_bytes)
235
236 def get_sector_size(self):
237 return int(self._sector_bytes)
238
239 def get_num_cylinders(self):
240 return int(self._geometry['cylinders'])
241
242 def get_drive_bytes(self):
243 return int(self._total_bytes)
244
245 def get_total_mb(self):
246 return self._total_mb
247
248 def get_partitions(self):
249 return self._partitions
250
251 # def print_partitions(self):
252 # for part in self._partitions.keys():
253 # print self._partitions[part].return_info()
254
255 def print_geometry(self):
256 print self._total_bytes, self._geometry
257
258 def _error(self, message):
259 "Raises an exception"
260 raise "DeviceObjectError", message
261
262 def _run(self, cmd):
263 "Runs a command and returns the output"
264
265 # Run command
266 output_string = commands.getoutput(cmd)
267
268 # What we will return
269 output_list = []
270
271 # As long as there is a new line in the output_string
272 while output_string.find("\n") != -1:
273
274 # Find the \n in the string
275 index = output_string.find("\n") + 1
276
277 # Add the line to the output and remove it from
278 # the output_string
279 output_list.append(output_string[:index])
280 output_string = output_string[index:]
281
282 # return output
283 return output_list
284
285
286 class Partition:
287 "Class representing a single partition within a Device object"
288
289 _device = None
290 _minor = 0
291 _orig_minor = 0
292 _start = 0
293 _end = 0
294 _type = None
295 _mountpoint = None
296 _mountopts = None
297 _format = None
298 _resizeable = None
299 _min_sectors_for_resize = 0
300 _mb = 0
301
302 def __init__(self, device, minor, mb, start, end, type, mountpoint='', mountopts='', format=True, existing=False):
303 self._device = device
304 self._minor = float(minor)
305 if existing: self._orig_minor = int(minor)
306 self._start = int(start)
307 self._end = int(end)
308 self._type = type or "unknown"
309 self._mountpoint = mountpoint
310 self._mountopts = mountopts
311 self._format = format
312 self._mb = mb
313 if existing:
314 self._orig_minor = self._minor
315 parted_part = device._parted_disk.get_partition(minor)
316 label_type = device._parted_disk.type.name
317 if label_type == "loop":
318 dev_node = device._device
319 else:
320 dev_node = device._device + str(minor)
321 if type == "ntfs":
322 min_bytes = int(commands.getoutput("ntfsresize -f --info " + dev_node + " | grep -e '^You might resize' | sed -e 's/You might resize at //' -e 's/ bytes or .\+//'"))
323 self._min_cylinders_for_resize = int(min_bytes / self._device._cylinder_bytes) + 1
324 self._resizeable == True
325 elif type == "ext2" or type == "ext3":
326 block_size = string.strip(commands.getoutput("dumpe2fs -h " + dev_node + r" 2>&1 | grep -e '^Block size:' | sed -e 's/^Block size:\s\+//'"))
327 free_blocks = string.strip(commands.getoutput("dumpe2fs -h " + dev_node + r" 2>&1 | grep -e '^Free blocks:' | sed -e 's/^Free blocks:\s\+//'"))
328 free_sec = int(int(block_size) * int(free_blocks) / self._device._sector_bytes)
329 free_sec = free_sec - 2000 # just to be safe
330 self._min_sectors_for_resize = (self._end - self._start + 1) - free_sec
331 self._resizeable == True
332 else:
333 parted_part = self._device._parted_disk.get_partition(int(self._minor))
334 try:
335 parted_fs = parted_part.geom.file_system_open()
336 except:
337 self._resizeable = False
338 return
339 resize_constraint = parted_fs.get_resize_constraint()
340 min_size = resize_constraint.min_size
341 if int(min_size) != min_size: min_size = int(min_size) + 1
342 self._min_sectors_for_resize = min_size
343 self._resizeable = True
344
345 def is_extended(self):
346 if self._type == "extended":
347 return True
348 else:
349 return False
350
351 def is_logical(self):
352 if self._type == "free":
353 if int(self._minor) + 0.9 == self._minor:
354 return True
355 else:
356 return False
357 elif self._device.archinfo[self._device._arch]['extended'] and self._minor > 4:
358 return True
359 else:
360 return False
361
362 def get_logicals(self):
363 if not self.is_extended():
364 return None
365 logicals = []
366 start = self._start
367 parts = self._device._partitions.keys()
368 parts.sort()
369 for part in parts:
370 if part < 5: continue
371 logicals.append(part)
372 logicals.sort()
373 return logicals
374
375 def get_extended_parent(self):
376 if not self.is_logical():
377 return None
378 else:
379 return self._device.get_partition_at(self._start, ignore_extended=0)
380
381 def set_start(self, start):
382 self._start = int(start)
383 self._blocks = ((self._end - self._start) * self._device.get_cylinder_size()) / 512
384
385 def get_start(self):
386 return int(self._start)
387
388 def set_end(self, end):
389 self._end = int(end)
390 self._blocks = ((self._end - self._start) * self._device.get_cylinder_size()) / 512
391
392 def get_end(self):
393 return int(self._end)
394
395 def get_mb(self):
396 return int(self._mb)
397
398 def set_type(self, type):
399 self._type = type
400
401 def get_type(self):
402 return self._type
403
404 def get_device(self):
405 return self._device
406
407 def set_minor(self, minor):
408 self._minor = float(minor)
409
410 def get_minor(self):
411 return float(self._minor)
412
413 def set_orig_minor(self, orig_minor):
414 self._orig_minor = int(orig_minor)
415
416 def get_orig_minor(self):
417 return self._orig_minor
418
419 def set_mountpoint(self, mountpoint):
420 self._mountpoint = mountpoint
421
422 def get_mountpoint(self):
423 return self._mountpoint
424
425 def set_mountopts(self, mountopts):
426 self._mountopts = mountopts
427
428 def get_mountopts(self):
429 return self._mountopts
430
431 def set_format(self, format):
432 self._format = format
433
434 def get_format(self):
435 return self._format
436
437 def get_blocks(self):
438 return int(self._blocks)
439
440 def get_min_sectors_for_resize(self):
441 if self.is_extended():
442 min_size = self._start
443 for part in self._device._partitions:
444 if part < 5: continue
445 if part.get_end > min_size: min_size = part.get_end()
446 return min_size
447 else:
448 return self._min_sectors_for_resize
449
450 def get_max_sectors_for_resize(self):
451 free_start, free_end = self._device.get_free_space(self._end)
452 if free_end == -1: return self._end
453 if free_start - 1 == self._end:
454 if self.is_logical():
455 if free_end <= self._device._partitions[self.get_extended_parent()]._end:
456 return free_end - self._start
457 else:
458 return self._end - self._start
459 else:
460 return free_end - self._start
461
462 def resize(self, start, end):
463 part_at_start = self._device.get_partition_at(int(start))
464 part_at_end = self._device.get_partition_at(int(end))
465 logicals = None
466 if self.is_logical():
467 parent = self.get_extended_parent()
468 parentstart = int(self._device._partitions[parent].get_start())
469 parentend = int(self._device._partitions[parent].get_end())
470 if (start < parentstart) or (end > parentend): return 0
471 if self.is_extended():
472 logicals = self.get_logicals()
473 if len(logicals):
474 logicals_start = self._device._partitions[logicals[0]].get_start()
475 logicals_end = self._device._partitions[logicals[len(logicals)-1]].get_end()
476 if (start > logicals_start) or (end < logicals_end): return 0
477 if part_at_start in logicals: part_at_start = 0
478 if part_at_end in logicals: part_at_end = 0
479 if ((not part_at_start == 0) and (part_at_start != self._minor)) or ((not part_at_end == 0) and (part_at_end != self._minor)):
480 return 0
481 self.set_start(start)
482 self.set_end(end)
483 return 1
484
485 def _error(self, message):
486 "Raises an exception"
487 raise "PartitionObjectError", message
488
489
490 def detect_devices():
491 "Returns a list of partitionable devices on the system"
492
493 devices = []
494
495 # Make sure sysfs exists
496 if not os.path.exists("/sys/bus"):
497 raise Exception, "no sysfs found (you MUST use a kernel >2.6)"
498 # Make sure /proc/partitions exists
499 if not os.path.exists("/proc/partitions"):
500 raise Exception, "/proc/partitions does not exist!"
501
502 # Load /proc/partitions into the variable 'partitions'
503 partitions = []
504 for line in open("/proc/partitions"):
505 if len(line.split()) < 4 or not line.split()[0].isdigit() or \
506 not line.split()[1].isdigit():
507 continue
508
509 # Get the major, minor and device name
510 major = line.split()[0]
511 minor = line.split()[1]
512 device = "/dev/" + line.split()[3]
513
514 if not major.isdigit() or not minor.isdigit():
515 continue
516
517 major = int(major)
518 minor = int(minor)
519
520 # If there is no /dev/'device_name', then scan
521 # all the devices in /dev to try and find a
522 # devices with the same major and minor
523 if not os.path.exists(device):
524 device = None
525 for path, dirs, files in os.walk("/dev"):
526 for file in files:
527 full_file = os.path.join(path, file)
528 if not os.path.exists(full_file):
529 continue
530 statres = os.stat(full_file)
531 fmaj = os.major(statres.st_rdev)
532 fmin = os.minor(statres.st_rdev)
533 if fmaj == major and fmin == minor:
534 device = full_file
535 break
536 if not device:
537 continue
538
539 partitions.append(( major, minor, device ))
540
541 # Scan sysfs for the devices of type 'x'
542 # 'x' being a member of the list below:
543 for dev_type in [ "ide", "scsi" ]: # Other device types? usb? fw?
544 if os.path.exists("/sys/bus/" + dev_type):
545 sysfs_devices = os.listdir("/sys/bus/"+dev_type+"/devices")
546
547 # For each device in the devices on that bus
548 for sysfs_device in sysfs_devices:
549 dev_file = "/sys/bus/" + dev_type + "/devices/"\
550 + sysfs_device + "/block/dev"
551
552 # If the file is not a block device, loop
553 if not os.path.exists(dev_file):
554 continue
555
556 # Get the major and minor info
557 try:
558 major, minor = open(dev_file).read().split(":")
559 major = int(major)
560 minor = int(minor)
561 except:
562 raise Exception, "invalid major minor in "\
563 + dev_file
564
565 # Find a device listed in /proc/partitions
566 # that has the same minor and major as our
567 # current block device.
568 for record in partitions:
569 if major == record[0] and minor == record[1]:
570 devices.append(record[2])
571
572 # We have assembled the list of devices, so return it
573 return devices

Properties

Name Value
svn:eol-style native

  ViewVC Help
Powered by ViewVC 1.1.20