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

Contents of /trunk/src/GLIStorageDevice.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 198 - (show annotations) (download) (as text)
Tue Jan 4 03:51:30 2005 UTC (15 years, 9 months ago) by agaffney
File MIME type: text/x-python
File size: 18976 byte(s)
undo geometry change

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

Properties

Name Value
svn:eol-style native

  ViewVC Help
Powered by ViewVC 1.1.20