1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 import string
21 import sys
22
23 from rhn.UserDictCase import UserDictCase
24 from spacewalk.common.usix import raise_with_tb
25 from spacewalk.common.rhnLog import log_debug, log_error
26 from spacewalk.common.rhnException import rhnFault
27 from spacewalk.common.rhnTB import Traceback
28 from spacewalk.server import rhnSQL
29
30
32 """ this is a class we use to get the mapping for a kudzu entry """
33
34 mapping = {
35 'desc': 'description',
36 }
37
38 if not dict:
39 return mapping
40 if not type(dict) == type({}) and not isinstance(dict, UserDictCase):
41 return mapping
42 hw_bus = dict.get("bus")
43
44 if not hw_bus:
45 return mapping
46 hw_bus = string.lower(hw_bus)
47 extra = {}
48 if hw_bus == "ddc":
49 extra = {
50 "id": None,
51 "horizsyncmin": "prop1",
52 "horizsyncmax": "prop2",
53 "vertrefreshmin": "prop3",
54 "vertrefreshmax": "prop4",
55 "modes": None,
56 "mem": None,
57 }
58 elif hw_bus == "ide":
59 extra = {
60 "physical": "prop1",
61 "logical": "prop2",
62 }
63 elif hw_bus in ["isapnp", "isa"]:
64 extra = {
65 "pdeviceid": "prop1",
66 "deviceid": "prop2",
67 "compat": "prop3",
68 "native": None,
69 "active": None,
70 "cardnum": None,
71 "logdev": "prop4",
72 "io": "prop2",
73 "irq": "prop1",
74 "dma": "prop3",
75 "mem": "prop4",
76 }
77 elif hw_bus == "keyboard":
78 extra = {}
79 elif hw_bus == "psaux":
80 extra = {}
81 elif hw_bus == "parallel":
82 extra = {
83 'pnpmfr': 'prop1',
84 'pnpdesc': 'prop2',
85 'pnpmodel': 'prop3',
86 'pnpmodes': 'prop4',
87 'pinfo': None,
88 'pinfo.xres': None,
89 'pinfo.yres': None,
90 'pinfo.color': None,
91 'pinfo.ascii': None,
92 }
93 elif hw_bus == "pci":
94 extra = {
95 'vendorid': 'prop1',
96 'deviceid': 'prop2',
97 'subvendorid': 'prop3',
98 'subdeviceid': 'prop4',
99 'network.hwaddr': None,
100 'pcibus': None,
101 'pcidev': None,
102 'pcifn': None,
103 'pcidom': None,
104 }
105 elif hw_bus == "sbus":
106 extra = {
107 "monitor": "prop1",
108 "width": "prop2",
109 "height": "prop3",
110 "freq": "prop4",
111 }
112 elif hw_bus == "scsi":
113 extra = {
114 'host': 'prop1',
115 'id': 'prop2',
116 'channel': 'prop3',
117 'lun': 'prop4',
118 'generic': None,
119 }
120 elif hw_bus == "serial":
121 extra = {
122 'pnpmfr': 'prop1',
123 'pnpdesc': 'prop2',
124 'pnpmodel': 'prop3',
125 'pnpcompat': "prop4",
126 }
127 elif hw_bus == "usb":
128 extra = {
129 "vendorid": "prop1",
130 "deviceid": "prop2",
131 "usbclass": "prop3",
132 "usbbus": "prop4",
133 "usblevel": "pciType",
134 "usbdev": None,
135 "usbprod": None,
136 "usbsubclass": None,
137 "usbprotocol": None,
138 "usbport": None,
139 "usbmfr": None,
140 "productname": None,
141 "productrevision": None,
142 'network.hwaddr': None,
143 }
144 elif hw_bus == "firewire":
145 extra = {
146 'vendorid': 'prop1',
147 'deviceid': 'prop2',
148 'subvendorid': 'prop3',
149 'subdeviceid': 'prop4',
150 }
151 elif hw_bus == 'pcmcia':
152 extra = {
153 'vendorid': 'prop1',
154 'deviceid': 'prop2',
155 'function': 'prop3',
156 'slot': 'prop4',
157 'network.hwaddr': None,
158 }
159 mapping.update(extra)
160 return mapping
161
162
164 """ Cleans up things like 127.00.00.01 """
165 if ip_addr is None:
166 return None
167
168 ip_addr = str(ip_addr)
169
170 if not len(ip_addr):
171 return ''
172 arr = ip_addr.split('.')
173
174
175 return '.'.join([x.lstrip('0') or '0' for x in arr])
176
177
179
180 """ A generic device class """
181 table = "override-GenericDevice"
182
184 self.id = 0
185 self.status = 1
186 self.data = {}
187
188 self.sequence = "rhn_hw_dev_id_seq"
189 self._autonull = ("description", "board")
190
192 if self.id == 0:
193 self.id = rhnSQL.Sequence(self.sequence)()
194 return self.id
195
197 if self.id == 0 and self.status == 2:
198 return 0
199 if self.status == 0:
200 return 0
201 return 1
202
203 - def save(self, sysid):
225
227 """ reload from rhnDevice table based on devid """
228 if not devid:
229 return -1
230 t = rhnSQL.Table(self.table, "id")
231 self.data = t[devid]
232
233 if self.data:
234 for k in ["created", "modified"]:
235 if self.data.has_key(k):
236 del self.data[k]
237 self.id = devid
238 self.status = 0
239 return 0
240
242 """ Method searches for empty string in params dict with names
243 defined in names list and replaces them with None value which
244 is translated to NULL in SQL.
245
246 We do not allow empty strings in database for compatibility
247 reasons between Oracle and PostgreSQL.
248 """
249
250 for param in params:
251 for name in names:
252 if name in param and param[name] == '':
253 param[name] = None
254
255
257
258 """ This is the base Device class that supports instantiation from a
259 dictionarry. the __init__ takes the dictionary as its argument,
260 together with a list of valid fields to recognize and with a mapping
261 for dictionary keys into valid field names for self.data
262
263 The fields are required to know what fields we have in the
264 table. The mapping allows transformation from whatever comes in to
265 valid fields in the table Looks complicated but it isn't -- gafton
266 """
267
268 - def __init__(self, fields, dict=None, mapping=None):
269 GenericDevice.__init__(self)
270 x = {}
271 for k in fields:
272 x[k] = None
273 self.data = UserDictCase(x)
274 if not dict:
275 return
276
277 if type(dict) == type({}):
278 dict = UserDictCase(dict)
279 if mapping is None or type(mapping) == type({}):
280 mapping = UserDictCase(mapping)
281 if not isinstance(dict, UserDictCase) or \
282 not isinstance(mapping, UserDictCase):
283 log_error("Argument passed is not a dictionary", dict, mapping)
284 raise TypeError("Argument passed is not a dictionary",
285 dict, mapping)
286
287 for k in dict.keys():
288 if dict[k] == '':
289 dict[k] = None
290 if self.data.has_key(k):
291 self.data[k] = dict[k]
292 continue
293 if mapping.has_key(k):
294
295 if mapping[k] is not None:
296 self.data[mapping[k]] = dict[k]
297 else:
298 log_error("Unknown HW key =`%s'" % k,
299 dict.dict(), mapping.dict())
300
301 try:
302 raise KeyError("Don't know how to parse key `%s''" % k,
303 dict.dict())
304 except:
305 Traceback(mail=1)
306
307 continue
308
309 try:
310 for k in self.data.keys():
311 if type(self.data[k]) == type("") and len(self.data[k]):
312 self.data[k] = string.strip(self.data[k])
313 if not len(self.data[k]):
314 continue
315 if self.data[k][0] == '"' and self.data[k][-1] == '"':
316 self.data[k] = self.data[k][1:-1]
317 except IndexError:
318 raise_with_tb(IndexError("Can not process data = %s, key = %s" % (
319 repr(self.data), k)), sys.exc_info()[2])
320
321
323
324 """ A more specific device based on the Device class """
325 table = "rhnDevice"
326
328 fields = ['class', 'bus', 'device', 'driver', 'detached',
329 'description', 'pcitype', 'prop1', 'prop2',
330 'prop3', 'prop4']
331
332 mapping = kudzu_mapping(dict)
333
334 Device.__init__(self, fields, dict, mapping)
335
336 self.sequence = "rhn_hw_dev_id_seq"
337
338
340
341 """ A class for handling CPU - mirrors the rhnCPU structure """
342 table = "rhnCPU"
343
345 fields = ['cpu_arch_id', 'architecture', 'bogomips', 'cache',
346 'family', 'mhz', 'stepping', 'flags', 'model',
347 'version', 'vendor', 'nrcpu', 'acpiVersion',
348 'apic', 'apmVersion', 'chipset', 'nrsocket']
349 mapping = {
350 "bogomips": "bogomips",
351 "cache": "cache",
352 "model": "model",
353 "platform": "architecture",
354 "type": "vendor",
355 "model_rev": "stepping",
356 "model_number": "family",
357 "model_ver": "version",
358 "model_version": "version",
359 "speed": "mhz",
360 "count": "nrcpu",
361 "socket_count": "nrsocket",
362 "other": "flags",
363 "desc": None,
364 'class': None,
365 }
366
367 Device.__init__(self, fields, dict, mapping)
368 self.sequence = "rhn_cpu_id_seq"
369 if not dict:
370 return
371 if self.data.get("cpu_arch_id") is not None:
372 return
373
374 if not self.data.has_key("architecture"):
375 log_error("hash does not have a platform member: %s" % dict)
376 raise AttributeError("Expected a hash value for member `platform'")
377
378 arch = self.data["architecture"]
379 row = rhnSQL.Table("rhnCpuArch", "label")[arch]
380 if row is None or not row.has_key("id"):
381 log_error("Can not find arch %s in rhnCpuArch" % arch)
382 raise AttributeError("Invalid architecture for CPU: `%s'" % arch)
383 self.data["cpu_arch_id"] = row["id"]
384 del self.data["architecture"]
385 if self.data.has_key("nrcpu"):
386 try:
387 self.data["nrcpu"] = int(self.data["nrcpu"])
388 except:
389 self.data["nrcpu"] = 1
390 if self.data["nrcpu"] == 0:
391 self.data["nrcpu"] = 1
392
393
409
410
593
594
596 key_mapping = {
597 'netmask': 'netmask',
598 'address': 'address',
599 }
600 unique = ['address']
601 table = 'rhnServerNetAddress'
602
604 log_debug(4, list_ifaces)
605 self.ifaces = {}
606 self.db_ifaces = []
607
608 self._autonull = ('address', 'netmask')
609 self.sequence = "rhn_srv_net_iface_id_seq"
610 if not list_ifaces:
611 return
612 for info in list_ifaces:
613 if not isinstance(info, type({})):
614 raise rhnFault(53, "Unexpected format for interface %s" %
615 info)
616 vdict = {}
617 for key, mapping in self.key_mapping.items():
618
619 if mapping in info:
620 k = mapping
621 else:
622 k = key
623 if k not in info:
624 raise rhnFault(53, "Unable to find required field %s"
625 % (key))
626 val = info[k]
627 if mapping in ['ip_addr', 'netmask', 'broadcast', 'address']:
628
629
630 val = self.cleanse_ip_addr(val)
631 vdict[mapping] = val
632 self.ifaces[vdict['address']] = vdict
633
635 return "<%s Class at %d: %s>\n" % (
636 self.__class__.__name__,
637 id(self), {
638 "self.ifaces": self.ifaces,
639 "self.db_ifaces": self.db_ifaces,
640 })
641 __repr__ = __str__
642
644 """ to be overriden by child """
645 return val
646
647 - def save(self, interface_id):
648 log_debug(4, self.ifaces)
649 self.reload(interface_id)
650 log_debug(4, "Net addresses in DB", self.db_ifaces)
651
652
653 inserts = []
654 updates = []
655 deletes = []
656 ifaces = self.ifaces.copy()
657 for iface in self.db_ifaces:
658 address = iface['address']
659 if iface['address'] not in self.ifaces:
660
661
662 iface = dict((column, iface[column]) for column in self.unique)
663 deletes.append(iface)
664 continue
665 uploaded_iface = ifaces[address]
666 del ifaces[address]
667
668 if _hash_eq(uploaded_iface, iface):
669
670 continue
671 uploaded_iface.update({'interface_id': interface_id})
672 updates.append(uploaded_iface)
673
674
675 for name, iface in ifaces.items():
676 iface['address'] = iface['address']
677 iface['interface_id'] = interface_id
678 inserts.append(iface)
679
680 log_debug(4, "Deletes", deletes)
681 log_debug(4, "Updates", updates)
682 log_debug(4, "Inserts", inserts)
683
684 self._delete(deletes)
685 self._update(updates)
686 self._insert(inserts)
687
689 q = """insert into %s
690 (%s) values (%s)"""
691 self._null_columns(params, self._autonull)
692
693 columns = list(self.key_mapping.values()) + ['interface_id']
694 columns.sort()
695 bind_params = string.join([':' + x for x in columns], ", ")
696 h = rhnSQL.prepare(q % (self.table, string.join(columns, ", "), bind_params))
697 return _dml(h, params)
698
700 q = """delete from %s
701 where %s"""
702
703 columns = self.unique
704 wheres = ['%s = :%s' % (x, x) for x in columns]
705 h = rhnSQL.prepare(q % (self.table, string.join(wheres, " and ")))
706 return _dml(h, params)
707
709 q = """update %s
710 set %s
711 where %s"""
712 self._null_columns(params, self._autonull)
713
714 wheres = self.unique
715 wheres = ['%s = :%s' % (x, x) for x in wheres]
716 wheres = string.join(wheres, " and ")
717
718 updates = list(self.key_mapping.values())
719 updates.sort()
720 updates = ['%s = :%s' % (x, x) for x in updates]
721 updates = string.join(updates, ", ")
722
723 h = rhnSQL.prepare(q % (self.table, updates, wheres))
724 return _dml(h, params)
725
726 - def reload(self, interface_id):
727 h = rhnSQL.prepare("""
728 select *
729 from %s
730 where interface_id = :interface_id
731 order by interface_id
732 """ % self.table)
733 h.execute(interface_id=interface_id)
734 self.db_ifaces = []
735 while 1:
736 row = h.fetchone_dict()
737 if not row:
738 break
739 hval = {'interface_id': row['interface_id']}
740 for key in self.key_mapping.values():
741 hval[key] = row[key]
742 self.db_ifaces.append(hval)
743
744 self.status = 0
745 return 0
746
747
749
750 """ IPv6 Network interface """
751 key_mapping = {
752 'netmask': 'netmask',
753 'addr': 'address',
754 'scope': 'scope',
755 }
756 table = 'rhnServerNetAddress6'
757 unique = ['interface_id', 'address', 'scope']
758
762
763
765
766 """ IPv4 Network interface """
767 key_mapping = {
768 'netmask': 'netmask',
769 'ipaddr': 'address',
770 'broadcast': 'broadcast',
771 }
772 table = 'rhnServerNetAddress4'
773 unique = ['interface_id']
774
778
781
782
784 """ Compares two hashes and return 1 if the first is a subset of the second """
785 log_debug(5, h1, h2)
786 for k, v in h1.items():
787 if k not in h2:
788 return 0
789 if h2[k] != v:
790 return 0
791 return 1
792
793
794 -def _dml(statement, params):
795 log_debug(5, params)
796 if not params:
797 return 0
798 params = _transpose(params)
799 rowcount = statement.executemany(**params)
800 log_debug(5, "Affected rows", rowcount)
801 return rowcount
802
803
821
822
845
846
869
870
886
887
889
890 """ Support for the hardware items """
891
893 self.__hardware = {}
894 self.__loaded = 0
895 self.__changed = 0
896
898 return self.__hardware[device_class]
899
901 """ add new hardware """
902 log_debug(4, hardware)
903 if not hardware:
904 return -1
905 if type(hardware) == type({}):
906 hardware = UserDictCase(hardware)
907 if not isinstance(hardware, UserDictCase):
908 log_error("argument type is not hash: %s" % hardware)
909 raise TypeError("This function requires a hash as an argument")
910
911 hw_class = hardware.get("class")
912 if hw_class is None:
913 return -1
914 hw_class = string.lower(hw_class)
915
916 class_type = None
917
918 if hw_class in ["video", "audio", "audio_hd", "usb", "other", "hd", "floppy",
919 "mouse", "modem", "network", "cdrom", "scsi",
920 "unspec", "scanner", "tape", "capture", "raid",
921 "socket", "keyboard", "printer", "firewire", "ide"]:
922 class_type = HardwareDevice
923 elif hw_class == "cpu":
924 class_type = CPUDevice
925 elif hw_class == "netinfo":
926 class_type = NetworkInformation
927 elif hw_class == "memory":
928 class_type = MemoryInformation
929 elif hw_class == "dmi":
930 class_type = DMIInformation
931 elif hw_class == "installinfo":
932 class_type = InstallInformation
933 elif hw_class == "netinterfaces":
934 class_type = NetIfaceInformation
935 else:
936 log_error("UNKNOWN CLASS TYPE `%s'" % hw_class)
937
938
939 try:
940 raise KeyError("Unknwon class type `%s' for hardware '%s'" % (
941 hw_class, hardware))
942 except:
943 Traceback(mail=1)
944 return
945
946
947 new_dev = class_type(hardware)
948
949 if class_type in self.__hardware:
950 _l = self.__hardware[class_type]
951 else:
952 _l = self.__hardware[class_type] = []
953 _l.append(new_dev)
954 self.__changed = 1
955 return 0
956
958 """ This function deletes all hardware. """
959 log_debug(4, sysid)
960 if not self.__loaded:
961 self.reload_hardware_byid(sysid)
962 hardware = self.__hardware
963 if hardware == {}:
964
965 return 0
966 self.__changed = 1
967
968 for device_type in hardware.keys():
969 for hw in hardware[device_type]:
970 hw.status = 2
971
972
973
974 hardware[device_type] = [a for a in hardware[device_type] if not (a.status == 2 and hasattr(a, "id") and a.id == 0)]
975 return 0
976
978 """Save the hardware list """
979 log_debug(3, sysid, "changed = %s" % self.__changed)
980 hardware = self.__hardware
981 if hardware == {}:
982 return 0
983 if not self.__changed:
984 return 0
985 for device_type, hw_list in hardware.items():
986 for hw in hw_list:
987 hw.save(sysid)
988 self.__changed = 0
989 return 0
990
992 """ Load a certain hardware class from the database """
993 if DevClass not in self.__hardware:
994 self.__hardware[DevClass] = []
995
996 h = rhnSQL.prepare("select id from %s where server_id = :sysid" % DevClass.table)
997 h.execute(sysid=sysid)
998 rows = h.fetchall_dict() or []
999
1000 for device in rows:
1001 dev_id = device['id']
1002 dev = DevClass()
1003 dev.reload(dev_id)
1004 self.__hardware[DevClass].append(dev)
1005
1028