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

Contents of /trunk/src/GLIStorageDevice.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 197 - (hide annotations) (download) (as text)
Tue Jan 4 03:49:32 2005 UTC (15 years, 9 months ago) by agaffney
File MIME type: text/x-python
File size: 18984 byte(s)
attempting to fix geometry issues

1 agaffney 107 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 agaffney 197 # 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 agaffney 107 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 agaffney 113 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 agaffney 107 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 agaffney 112 break
134 agaffney 107 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 agaffney 183 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 agaffney 184 return 0
224 agaffney 183
225 agaffney 107 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 agaffney 188 if type == "": type = "unknown"
299 agaffney 107 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 agaffney 184 # 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 agaffney 107
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 agaffney 184 if not self.is_extended():
347     return None
348 agaffney 107 logicals = []
349     start = self._start
350 agaffney 184 # 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 agaffney 107 logicals.append(part)
361     logicals.sort()
362     return logicals
363    
364     def get_extended_parent(self):
365 agaffney 184 if not self.is_logical():
366     return None
367     else:
368     return self._device.get_partition_at(self._start, ignore_extended=0)
369 agaffney 107
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