/[smeserver]/cdrom.image/sme9/updates/storage/devicelibs/edd.py
ViewVC logotype

Contents of /cdrom.image/sme9/updates/storage/devicelibs/edd.py

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.2 - (show annotations) (download) (as text)
Sun Dec 22 04:27:52 2013 UTC (10 years, 10 months ago) by wellsi
Branch: MAIN
CVS Tags: HEAD
Changes since 1.1: +0 -0 lines
Content type: text/x-python
FILE REMOVED
anaconda updates now handled via patches

1 #
2 # edd.py
3 # BIOS EDD data parsing functions
4 #
5 # Copyright (C) 2010 Red Hat, Inc. All rights reserved.
6 #
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #
20 # Author(s): Hans de Goede <hdegoede@redhat.com>
21 # Ales Kozumplik <akozumpl@redhat.com>
22 #
23
24 import glob
25 import logging
26 import os
27 import re
28 import struct
29
30 log = logging.getLogger("storage")
31
32 re_host_bus = re.compile(r'^PCI\s*(\S*)\s*channel: (\S*)\s*$')
33 re_interface_scsi = re.compile(r'^SCSI\s*id: (\S*)\s*lun: (\S*)\s*$')
34 re_interface_ata = re.compile(r'^ATA\s*device: (\S*)\s*$')
35
36 class EddEntry(object):
37 """ This object merely collects what the /sys/firmware/edd/* entries can
38 provide.
39 """
40 def __init__(self, sysfspath):
41 self.type = None
42
43 self.ata_device = None
44 self.channel = None
45 self.mbr_signature = None
46 self.pci_dev = None
47 self.scsi_id = None
48 self.scsi_lun = None
49 self.sectors = None
50
51 self.load(sysfspath)
52
53 def __str__(self):
54 return \
55 "\ttype: %(type)s, ata_device: %(ata_device)s\n" \
56 "\tchannel: %(channel)s, mbr_signature: %(mbr_signature)s\n" \
57 "\tpci_dev: %(pci_dev)s, scsi_id: %(scsi_id)s\n" \
58 "\tscsi_lun: %(scsi_lun)s, sectors: %(sectors)s" % self.__dict__
59
60 def _read_file(self, filename):
61 contents = None
62 if os.path.exists(filename):
63 with open(filename) as f:
64 contents = f.read().rstrip()
65 return contents
66
67 def load(self, sysfspath):
68 interface = self._read_file(os.path.join(sysfspath, "interface"))
69 if interface:
70 self.type = interface.split()[0]
71 if self.type == "SCSI":
72 match = re_interface_scsi.match(interface)
73 self.scsi_id = int(match.group(1))
74 self.scsi_lun = int(match.group(2))
75 elif self.type == "ATA":
76 match = re_interface_ata.match(interface)
77 self.ata_device = int(match.group(1))
78
79 self.mbr_signature = self._read_file(
80 os.path.join(sysfspath, "mbr_signature"))
81 sectors = self._read_file(os.path.join(sysfspath, "sectors"))
82 if sectors:
83 self.sectors = int(sectors)
84 hbus = self._read_file(os.path.join(sysfspath, "host_bus"))
85 if hbus:
86 match = re_host_bus.match(hbus)
87 if match:
88 self.pci_dev = match.group(1)
89 self.channel = int(match.group(2))
90 else:
91 log.warning("edd: can not match host_bus: %s" % hbus)
92
93 class EddMatcher(object):
94 """ This object tries to match given entry to a disk device name.
95
96 Assuming, heuristic analysis and guessing hapens here.
97 """
98 def __init__(self, edd_entry):
99 self.edd = edd_entry
100
101 def devname_from_pci_dev(self):
102 name = None
103 if self.edd.type == "ATA" and \
104 self.edd.channel is not None and \
105 self.edd.ata_device is not None:
106 path = "/sys/devices/pci0000:00/0000:%(pci_dev)s/host%(chan)d/"\
107 "target%(chan)d:0:%(dev)d/%(chan)d:0:%(dev)d:0/block" % {
108 'pci_dev' : self.edd.pci_dev,
109 'chan' : self.edd.channel,
110 'dev' : self.edd.ata_device
111 }
112 if os.path.isdir(path):
113 block_entries = os.listdir(path)
114 if len(block_entries) == 1:
115 name = block_entries[0]
116 else:
117 log.warning("edd: directory does not exist: %s" % path)
118 elif self.edd.type == "SCSI":
119 pattern = "/sys/devices/pci0000:00/0000:%(pci_dev)s/virtio*/block" % \
120 {'pci_dev' : self.edd.pci_dev}
121 matching_paths = glob.glob(pattern)
122 if len(matching_paths) != 1 or not os.path.exists(matching_paths[0]):
123 return None
124 block_entries = os.listdir(matching_paths[0])
125 if len(block_entries) == 1:
126 name = block_entries[0]
127 return name
128
129 def match_via_mbrsigs(self, mbr_dict):
130 """ Try to match the edd entry based on its mbr signature.
131
132 This will obviously fail for a fresh drive/image, but in extreme
133 cases can also show false positives for randomly matching data.
134 """
135 for (name, mbr_signature) in mbr_dict.items():
136 if mbr_signature == self.edd.mbr_signature:
137 return name
138 return None
139
140 def biosdev_to_edd_dir(biosdev):
141 return "/sys/firmware/edd/int13_dev%x" % biosdev
142
143 def collect_edd_data():
144 edd_data_dict = {}
145 # the hard drive numbering starts at 0x80 (128 decimal):
146 for biosdev in range(0x80, 0x80+16):
147 sysfspath = biosdev_to_edd_dir(biosdev)
148 if not os.path.exists(sysfspath):
149 break
150 edd_data_dict[biosdev] = EddEntry(sysfspath)
151 return edd_data_dict
152
153 def collect_mbrs(devices):
154 """ Read MBR signatures from devices.
155
156 Returns a dict mapping device names to their MBR signatures. It is not
157 guaranteed this will succeed, with a new disk for instance.
158 """
159 mbr_dict = {}
160 for dev in devices:
161 try:
162 fd = os.open(dev.path, os.O_RDONLY)
163 # The signature is the unsigned integer at byte 440:
164 os.lseek(fd, 440, 0)
165 mbrsig = struct.unpack('I', os.read(fd, 4))
166 os.close(fd)
167 except OSError as e:
168 log.warning("edd: error reading mbrsig from disk %s: %s" %
169 (dev.name, str(e)))
170 continue
171
172 mbrsig_str = "0x%08x" % mbrsig
173 # sanity check
174 if mbrsig_str == '0x00000000':
175 log.info("edd: MBR signature on %s is zero. new disk image?" % dev.name)
176 continue
177 else:
178 for (dev_name, mbrsig_str_old) in mbr_dict.items():
179 if mbrsig_str_old == mbrsig_str:
180 log.error("edd: dupicite MBR signature %s for %s and %s" %
181 (mbrsig_str, dev_name, dev.name))
182 # this actually makes all the other data useless
183 return {}
184 # update the dictionary
185 mbr_dict[dev.name] = mbrsig_str
186 log.info("edd: collected mbr signatures: %s" % mbr_dict)
187 return mbr_dict
188
189 def get_edd_dict(devices):
190 """ Generates the 'device name' -> 'edd number' mapping.
191
192 The EDD kernel module that exposes /sys/firmware/edd is thoroughly
193 broken, the information there is incomplete and sometimes downright
194 wrong. So after we mine out all useful information that the files under
195 /sys/firmware/edd/int13_*/ can provide, we resort to heuristics and
196 guessing. Our first attempt is, by looking at the device type int
197 'interface', attempting to map pci device number, channel number etc. to
198 a sysfs path, check that the path really exists, then read the device
199 name (e.g 'sda') from there. Should this fail we try to match contents
200 of 'mbr_signature' to a real MBR signature found on the existing block
201 devices.
202 """
203 mbr_dict = collect_mbrs(devices)
204 edd_entries_dict = collect_edd_data()
205 edd_dict = {}
206 for (edd_number, edd_entry) in edd_entries_dict.items():
207 log.debug("edd: data extracted from 0x%x:\n%s" % (edd_number, edd_entry))
208 matcher = EddMatcher(edd_entry)
209 # first try to match through the pci dev etc.
210 name = matcher.devname_from_pci_dev()
211 # next try to compare mbr signatures
212 if name:
213 log.debug("edd: matched 0x%x to %s using pci_dev" % (edd_number, name))
214 else:
215 name = matcher.match_via_mbrsigs(mbr_dict)
216 if name:
217 log.info("edd: matched 0x%x to %s using MBR sig" % (edd_number, name))
218
219 if name:
220 old_edd_number = edd_dict.get(name)
221 if old_edd_number:
222 log.info("edd: both edd entries 0x%x and 0x%x seem to map to %s" %
223 (old_edd_number, edd_number, name))
224 # this means all the other data can be confused and useless
225 return {}
226 edd_dict[name] = edd_number
227 continue
228 log.error("edd: unable to match edd entry 0x%x" % edd_number)
229 return edd_dict

admin@koozali.org
ViewVC Help
Powered by ViewVC 1.2.1 RSS 2.0 feed