blob: 764d1266bcdb37d2cfe164ebb0cf8bec3533e4a7 [file] [log] [blame]
koder aka kdanilovf86d7af2015-05-06 04:01:54 +03001import re
koder aka kdanilov22d134e2016-11-08 11:33:19 +02002from typing import Dict, Iterable
koder aka kdanilovf86d7af2015-05-06 04:01:54 +03003import xml.etree.ElementTree as ET
koder aka kdanilov73084622016-11-16 21:51:08 +02004from typing import List, Tuple, cast, Optional
koder aka kdanilovf86d7af2015-05-06 04:01:54 +03005
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +03006from . import utils
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +02007from .node_interfaces import IRPCNode
koder aka kdanilovf86d7af2015-05-06 04:01:54 +03008
9
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030010def get_data(rr: str, data: str) -> str:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030011 match_res = re.search("(?ims)" + rr, data)
12 return match_res.group(0)
13
14
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030015class HWInfo:
koder aka kdanilov22d134e2016-11-08 11:33:19 +020016 def __init__(self) -> None:
17 self.hostname = None # type: str
18 self.cores = [] # type: List[Tuple[str, int]]
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030019
20 # /dev/... devices
koder aka kdanilov22d134e2016-11-08 11:33:19 +020021 self.disks_info = {} # type: Dict[str, Tuple[str, int]]
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030022
23 # real disks on raid controller
koder aka kdanilov22d134e2016-11-08 11:33:19 +020024 self.disks_raw_info = {} # type: Dict[str, str]
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030025
koder aka kdanilov6ab4d432015-06-22 00:26:28 +030026 # name => (speed, is_full_diplex, ip_addresses)
koder aka kdanilov73084622016-11-16 21:51:08 +020027 self.net_info = {} # type: Dict[str, Tuple[Optional[int], Optional[bool], List[str]]]
koder aka kdanilov6ab4d432015-06-22 00:26:28 +030028
koder aka kdanilov22d134e2016-11-08 11:33:19 +020029 self.ram_size = 0 # type: int
30 self.sys_name = None # type: str
31 self.mb = None # type: str
32 self.raw = None # type: str
koder aka kdanilov6ab4d432015-06-22 00:26:28 +030033
koder aka kdanilov22d134e2016-11-08 11:33:19 +020034 self.storage_controllers = [] # type: List[str]
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030035
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030036 def get_hdd_count(self) -> Iterable[int]:
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +030037 # SATA HDD COUNT, SAS 10k HDD COUNT, SAS SSD count, PCI-E SSD count
38 return []
39
koder aka kdanilov22d134e2016-11-08 11:33:19 +020040 def get_summary(self) -> Dict[str, int]:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030041 cores = sum(count for _, count in self.cores)
42 disks = sum(size for _, size in self.disks_info.values())
43
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +030044 return {'cores': cores,
45 'ram': self.ram_size,
46 'storage': disks,
47 'disk_count': len(self.disks_info)}
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030048
49 def __str__(self):
50 res = []
51
52 summ = self.get_summary()
53 summary = "Simmary: {cores} cores, {ram}B RAM, {disk}B storage"
54 res.append(summary.format(cores=summ['cores'],
55 ram=utils.b2ssize(summ['ram']),
56 disk=utils.b2ssize(summ['storage'])))
57 res.append(str(self.sys_name))
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030058 if self.mb:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030059 res.append("Motherboard: " + self.mb)
60
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030061 if not self.ram_size:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030062 res.append("RAM: Failed to get RAM size")
63 else:
64 res.append("RAM " + utils.b2ssize(self.ram_size) + "B")
65
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030066 if not self.cores:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030067 res.append("CPU cores: Failed to get CPU info")
68 else:
69 res.append("CPU cores:")
70 for name, count in self.cores:
71 if count > 1:
72 res.append(" {0} * {1}".format(count, name))
73 else:
74 res.append(" " + name)
75
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030076 if self.storage_controllers:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030077 res.append("Disk controllers:")
78 for descr in self.storage_controllers:
79 res.append(" " + descr)
80
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030081 if self.disks_info:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030082 res.append("Storage devices:")
83 for dev, (model, size) in sorted(self.disks_info.items()):
84 ssize = utils.b2ssize(size) + "B"
85 res.append(" {0} {1} {2}".format(dev, ssize, model))
86 else:
87 res.append("Storage devices's: Failed to get info")
88
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030089 if self.disks_raw_info:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030090 res.append("Disks devices:")
91 for dev, descr in sorted(self.disks_raw_info.items()):
92 res.append(" {0} {1}".format(dev, descr))
93 else:
94 res.append("Disks devices's: Failed to get info")
95
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030096 if self.net_info:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030097 res.append("Net adapters:")
koder aka kdanilov6ab4d432015-06-22 00:26:28 +030098 for name, (speed, dtype, _) in self.net_info.items():
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030099 res.append(" {0} {2} duplex={1}".format(name, dtype, speed))
100 else:
101 res.append("Net adapters: Failed to get net info")
102
103 return str(self.hostname) + ":\n" + "\n".join(" " + i for i in res)
104
105
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300106class SWInfo:
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200107 def __init__(self) -> None:
108 self.partitions = None # type: str
109 self.kernel_version = None # type: str
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200110 self.libvirt_version = None # type: str
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200111 self.qemu_version = None # type: str
koder aka kdanilov73084622016-11-16 21:51:08 +0200112 self.OS_version = None # type: utils.OSRelease
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200113 self.ceph_version = None # type: str
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300114
115
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +0200116def get_sw_info(node: IRPCNode) -> SWInfo:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300117 res = SWInfo()
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300118
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200119 res.OS_version = utils.get_os(node)
koder aka kdanilov73084622016-11-16 21:51:08 +0200120 res.kernel_version = node.get_file_content('/proc/version').decode('utf8').strip()
121 res.partitions = node.get_file_content('/etc/mtab').decode('utf8').strip()
122 res.libvirt_version = node.run("virsh -v", nolog=True).strip()
123 res.qemu_version = node.run("qemu-system-x86_64 --version", nolog=True).strip()
124 res.ceph_version = node.run("ceph --version", nolog=True).strip()
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300125
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300126 return res
127
128
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +0200129def get_hw_info(node: IRPCNode) -> HWInfo:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300130 res = HWInfo()
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300131 lshw_out = node.run('sudo lshw -xml 2>/dev/null', nolog=True)
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300132
133 res.raw = lshw_out
134 lshw_et = ET.fromstring(lshw_out)
135
136 try:
koder aka kdanilov73084622016-11-16 21:51:08 +0200137 res.hostname = cast(str, lshw_et.find("node").attrib['id'])
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300138 except Exception:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300139 pass
140
141 try:
koder aka kdanilov73084622016-11-16 21:51:08 +0200142
143 res.sys_name = cast(str, lshw_et.find("node/vendor").text) + " " + \
144 cast(str, lshw_et.find("node/product").text)
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300145 res.sys_name = res.sys_name.replace("(To be filled by O.E.M.)", "")
146 res.sys_name = res.sys_name.replace("(To be Filled by O.E.M.)", "")
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300147 except Exception:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300148 pass
149
150 core = lshw_et.find("node/node[@id='core']")
151 if core is None:
koder aka kdanilov73084622016-11-16 21:51:08 +0200152 return res
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300153
154 try:
koder aka kdanilov73084622016-11-16 21:51:08 +0200155 res.mb = " ".join(cast(str, core.find(node).text)
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300156 for node in ['vendor', 'product', 'version'])
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300157 except Exception:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300158 pass
159
160 for cpu in core.findall("node[@class='processor']"):
161 try:
koder aka kdanilov73084622016-11-16 21:51:08 +0200162 model = cast(str, cpu.find('product').text)
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300163 threads_node = cpu.find("configuration/setting[@id='threads']")
164 if threads_node is None:
165 threads = 1
166 else:
167 threads = int(threads_node.attrib['value'])
168 res.cores.append((model, threads))
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300169 except Exception:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300170 pass
171
172 res.ram_size = 0
173 for mem_node in core.findall(".//node[@class='memory']"):
174 descr = mem_node.find('description')
175 try:
176 if descr is not None and descr.text == 'System Memory':
177 mem_sz = mem_node.find('size')
178 if mem_sz is None:
179 for slot_node in mem_node.find("node[@class='memory']"):
180 slot_sz = slot_node.find('size')
181 if slot_sz is not None:
182 assert slot_sz.attrib['units'] == 'bytes'
183 res.ram_size += int(slot_sz.text)
184 else:
185 assert mem_sz.attrib['units'] == 'bytes'
186 res.ram_size += int(mem_sz.text)
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300187 except Exception:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300188 pass
189
190 for net in core.findall(".//node[@class='network']"):
191 try:
192 link = net.find("configuration/setting[@id='link']")
193 if link.attrib['value'] == 'yes':
koder aka kdanilov73084622016-11-16 21:51:08 +0200194 name = cast(str, net.find("logicalname").text)
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300195 speed_node = net.find("configuration/setting[@id='speed']")
196
197 if speed_node is None:
198 speed = None
199 else:
koder aka kdanilov73084622016-11-16 21:51:08 +0200200 speed = int(speed_node.attrib['value'])
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300201
202 dup_node = net.find("configuration/setting[@id='duplex']")
203 if dup_node is None:
204 dup = None
205 else:
koder aka kdanilov73084622016-11-16 21:51:08 +0200206 dup = cast(str, dup_node.attrib['value']).lower() == 'yes'
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300207
koder aka kdanilov73084622016-11-16 21:51:08 +0200208 ips = [] # type: List[str]
209 res.net_info[name] = (speed, dup, ips)
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300210 except Exception:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300211 pass
212
213 for controller in core.findall(".//node[@class='storage']"):
214 try:
215 description = getattr(controller.find("description"), 'text', "")
216 product = getattr(controller.find("product"), 'text', "")
217 vendor = getattr(controller.find("vendor"), 'text', "")
218 dev = getattr(controller.find("logicalname"), 'text', "")
219 if dev != "":
220 res.storage_controllers.append(
221 "{0}: {1} {2} {3}".format(dev, description,
222 vendor, product))
223 else:
224 res.storage_controllers.append(
225 "{0} {1} {2}".format(description,
226 vendor, product))
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300227 except Exception:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300228 pass
229
230 for disk in core.findall(".//node[@class='disk']"):
231 try:
232 lname_node = disk.find('logicalname')
233 if lname_node is not None:
koder aka kdanilov73084622016-11-16 21:51:08 +0200234 dev = cast(str, lname_node.text).split('/')[-1]
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300235
236 if dev == "" or dev[-1].isdigit():
237 continue
238
239 sz_node = disk.find('size')
240 assert sz_node.attrib['units'] == 'bytes'
241 sz = int(sz_node.text)
242 res.disks_info[dev] = ('', sz)
243 else:
244 description = disk.find('description').text
245 product = disk.find('product').text
246 vendor = disk.find('vendor').text
247 version = disk.find('version').text
248 serial = disk.find('serial').text
249
250 full_descr = "{0} {1} {2} {3} {4}".format(
251 description, product, vendor, version, serial)
252
koder aka kdanilov73084622016-11-16 21:51:08 +0200253 businfo = cast(str, disk.find('businfo').text)
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300254 res.disks_raw_info[businfo] = full_descr
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300255 except Exception:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300256 pass
257
258 return res