blob: 61938ab9645c35be9e25dbcc0e14bfc08a5d1a27 [file] [log] [blame]
koder aka kdanilovf86d7af2015-05-06 04:01:54 +03001import re
koder aka kdanilov962ee5f2016-12-19 02:40:08 +02002import logging
koder aka kdanilov22d134e2016-11-08 11:33:19 +02003from typing import Dict, Iterable
koder aka kdanilovf86d7af2015-05-06 04:01:54 +03004import xml.etree.ElementTree as ET
koder aka kdanilov73084622016-11-16 21:51:08 +02005from typing import List, Tuple, cast, Optional
koder aka kdanilovf86d7af2015-05-06 04:01:54 +03006
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +03007from . import utils
koder aka kdanilova732a602017-02-01 20:29:56 +02008from .node_utils import get_os, OSRelease
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +02009from .node_interfaces import IRPCNode
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030010
11
koder aka kdanilov962ee5f2016-12-19 02:40:08 +020012logger = logging.getLogger("wally")
13
14
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030015def get_data(rr: str, data: str) -> str:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030016 match_res = re.search("(?ims)" + rr, data)
17 return match_res.group(0)
18
19
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030020class HWInfo:
koder aka kdanilov22d134e2016-11-08 11:33:19 +020021 def __init__(self) -> None:
22 self.hostname = None # type: str
23 self.cores = [] # type: List[Tuple[str, int]]
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030024
25 # /dev/... devices
koder aka kdanilov22d134e2016-11-08 11:33:19 +020026 self.disks_info = {} # type: Dict[str, Tuple[str, int]]
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030027
28 # real disks on raid controller
koder aka kdanilov22d134e2016-11-08 11:33:19 +020029 self.disks_raw_info = {} # type: Dict[str, str]
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030030
koder aka kdanilov6ab4d432015-06-22 00:26:28 +030031 # name => (speed, is_full_diplex, ip_addresses)
koder aka kdanilov73084622016-11-16 21:51:08 +020032 self.net_info = {} # type: Dict[str, Tuple[Optional[int], Optional[bool], List[str]]]
koder aka kdanilov6ab4d432015-06-22 00:26:28 +030033
koder aka kdanilov22d134e2016-11-08 11:33:19 +020034 self.ram_size = 0 # type: int
35 self.sys_name = None # type: str
36 self.mb = None # type: str
37 self.raw = None # type: str
koder aka kdanilov6ab4d432015-06-22 00:26:28 +030038
koder aka kdanilov22d134e2016-11-08 11:33:19 +020039 self.storage_controllers = [] # type: List[str]
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030040
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030041 def get_hdd_count(self) -> Iterable[int]:
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +030042 # SATA HDD COUNT, SAS 10k HDD COUNT, SAS SSD count, PCI-E SSD count
43 return []
44
koder aka kdanilov22d134e2016-11-08 11:33:19 +020045 def get_summary(self) -> Dict[str, int]:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030046 cores = sum(count for _, count in self.cores)
47 disks = sum(size for _, size in self.disks_info.values())
48
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +030049 return {'cores': cores,
50 'ram': self.ram_size,
51 'storage': disks,
52 'disk_count': len(self.disks_info)}
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030053
54 def __str__(self):
55 res = []
56
57 summ = self.get_summary()
58 summary = "Simmary: {cores} cores, {ram}B RAM, {disk}B storage"
59 res.append(summary.format(cores=summ['cores'],
60 ram=utils.b2ssize(summ['ram']),
61 disk=utils.b2ssize(summ['storage'])))
62 res.append(str(self.sys_name))
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030063 if self.mb:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030064 res.append("Motherboard: " + self.mb)
65
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030066 if not self.ram_size:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030067 res.append("RAM: Failed to get RAM size")
68 else:
69 res.append("RAM " + utils.b2ssize(self.ram_size) + "B")
70
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030071 if not self.cores:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030072 res.append("CPU cores: Failed to get CPU info")
73 else:
74 res.append("CPU cores:")
75 for name, count in self.cores:
76 if count > 1:
77 res.append(" {0} * {1}".format(count, name))
78 else:
79 res.append(" " + name)
80
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030081 if self.storage_controllers:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030082 res.append("Disk controllers:")
83 for descr in self.storage_controllers:
84 res.append(" " + descr)
85
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030086 if self.disks_info:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030087 res.append("Storage devices:")
88 for dev, (model, size) in sorted(self.disks_info.items()):
89 ssize = utils.b2ssize(size) + "B"
90 res.append(" {0} {1} {2}".format(dev, ssize, model))
91 else:
92 res.append("Storage devices's: Failed to get info")
93
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030094 if self.disks_raw_info:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030095 res.append("Disks devices:")
96 for dev, descr in sorted(self.disks_raw_info.items()):
97 res.append(" {0} {1}".format(dev, descr))
98 else:
99 res.append("Disks devices's: Failed to get info")
100
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300101 if self.net_info:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300102 res.append("Net adapters:")
koder aka kdanilov6ab4d432015-06-22 00:26:28 +0300103 for name, (speed, dtype, _) in self.net_info.items():
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300104 res.append(" {0} {2} duplex={1}".format(name, dtype, speed))
105 else:
106 res.append("Net adapters: Failed to get net info")
107
108 return str(self.hostname) + ":\n" + "\n".join(" " + i for i in res)
109
110
koder aka kdanilov7f59d562016-12-26 01:34:23 +0200111class CephInfo:
112 def __init__(self) -> None:
113 pass
114
115
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300116class SWInfo:
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200117 def __init__(self) -> None:
koder aka kdanilov962ee5f2016-12-19 02:40:08 +0200118 self.mtab = None # type: str
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200119 self.kernel_version = None # type: str
koder aka kdanilov962ee5f2016-12-19 02:40:08 +0200120 self.libvirt_version = None # type: Optional[str]
121 self.qemu_version = None # type: Optional[str]
koder aka kdanilova732a602017-02-01 20:29:56 +0200122 self.OS_version = None # type: OSRelease
koder aka kdanilov7f59d562016-12-26 01:34:23 +0200123 self.ceph_info = None # type: Optional[CephInfo]
124
125
126def get_ceph_services_info(node: IRPCNode) -> CephInfo:
127 # TODO: use ceph-monitoring module
128 return CephInfo()
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300129
130
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +0200131def get_sw_info(node: IRPCNode) -> SWInfo:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300132 res = SWInfo()
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300133
koder aka kdanilov108ac362017-01-19 20:17:16 +0200134 res.OS_version = get_os(node)
koder aka kdanilov73084622016-11-16 21:51:08 +0200135 res.kernel_version = node.get_file_content('/proc/version').decode('utf8').strip()
koder aka kdanilov962ee5f2016-12-19 02:40:08 +0200136 res.mtab = node.get_file_content('/etc/mtab').decode('utf8').strip()
137
138 try:
139 res.libvirt_version = node.run("virsh -v", nolog=True).strip()
140 except OSError:
141 res.libvirt_version = None
142
koder aka kdanilov7f59d562016-12-26 01:34:23 +0200143 # dpkg -l ??
koder aka kdanilov23e6bdf2016-12-24 02:18:54 +0200144
koder aka kdanilov962ee5f2016-12-19 02:40:08 +0200145 try:
146 res.qemu_version = node.run("qemu-system-x86_64 --version", nolog=True).strip()
147 except OSError:
148 res.qemu_version = None
149
koder aka kdanilov7f59d562016-12-26 01:34:23 +0200150 for role in ('ceph-osd', 'ceph-mon', 'ceph-mds'):
151 if role in node.info.roles:
152 res.ceph_info = get_ceph_services_info(node)
153 break
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300154
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300155 return res
156
157
koder aka kdanilov962ee5f2016-12-19 02:40:08 +0200158def get_hw_info(node: IRPCNode) -> Optional[HWInfo]:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300159
koder aka kdanilov962ee5f2016-12-19 02:40:08 +0200160 try:
161 lshw_out = node.run('sudo lshw -xml 2>/dev/null')
162 except Exception as exc:
koder aka kdanilov108ac362017-01-19 20:17:16 +0200163 logger.warning("lshw failed on node %s: %s", node.node_id, exc)
koder aka kdanilov962ee5f2016-12-19 02:40:08 +0200164 return None
165
166 res = HWInfo()
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300167 res.raw = lshw_out
168 lshw_et = ET.fromstring(lshw_out)
169
170 try:
koder aka kdanilov73084622016-11-16 21:51:08 +0200171 res.hostname = cast(str, lshw_et.find("node").attrib['id'])
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300172 except Exception:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300173 pass
174
175 try:
koder aka kdanilov73084622016-11-16 21:51:08 +0200176
177 res.sys_name = cast(str, lshw_et.find("node/vendor").text) + " " + \
178 cast(str, lshw_et.find("node/product").text)
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300179 res.sys_name = res.sys_name.replace("(To be filled by O.E.M.)", "")
180 res.sys_name = res.sys_name.replace("(To be Filled by O.E.M.)", "")
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300181 except Exception:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300182 pass
183
184 core = lshw_et.find("node/node[@id='core']")
185 if core is None:
koder aka kdanilov73084622016-11-16 21:51:08 +0200186 return res
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300187
188 try:
koder aka kdanilov73084622016-11-16 21:51:08 +0200189 res.mb = " ".join(cast(str, core.find(node).text)
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300190 for node in ['vendor', 'product', 'version'])
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300191 except Exception:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300192 pass
193
194 for cpu in core.findall("node[@class='processor']"):
195 try:
koder aka kdanilov73084622016-11-16 21:51:08 +0200196 model = cast(str, cpu.find('product').text)
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300197 threads_node = cpu.find("configuration/setting[@id='threads']")
198 if threads_node is None:
199 threads = 1
200 else:
201 threads = int(threads_node.attrib['value'])
202 res.cores.append((model, threads))
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300203 except Exception:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300204 pass
205
206 res.ram_size = 0
207 for mem_node in core.findall(".//node[@class='memory']"):
208 descr = mem_node.find('description')
209 try:
210 if descr is not None and descr.text == 'System Memory':
211 mem_sz = mem_node.find('size')
212 if mem_sz is None:
213 for slot_node in mem_node.find("node[@class='memory']"):
214 slot_sz = slot_node.find('size')
215 if slot_sz is not None:
216 assert slot_sz.attrib['units'] == 'bytes'
217 res.ram_size += int(slot_sz.text)
218 else:
219 assert mem_sz.attrib['units'] == 'bytes'
220 res.ram_size += int(mem_sz.text)
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300221 except Exception:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300222 pass
223
224 for net in core.findall(".//node[@class='network']"):
225 try:
226 link = net.find("configuration/setting[@id='link']")
227 if link.attrib['value'] == 'yes':
koder aka kdanilov73084622016-11-16 21:51:08 +0200228 name = cast(str, net.find("logicalname").text)
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300229 speed_node = net.find("configuration/setting[@id='speed']")
230
231 if speed_node is None:
232 speed = None
233 else:
koder aka kdanilov73084622016-11-16 21:51:08 +0200234 speed = int(speed_node.attrib['value'])
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300235
236 dup_node = net.find("configuration/setting[@id='duplex']")
237 if dup_node is None:
238 dup = None
239 else:
koder aka kdanilov73084622016-11-16 21:51:08 +0200240 dup = cast(str, dup_node.attrib['value']).lower() == 'yes'
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300241
koder aka kdanilov73084622016-11-16 21:51:08 +0200242 ips = [] # type: List[str]
243 res.net_info[name] = (speed, dup, ips)
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300244 except Exception:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300245 pass
246
247 for controller in core.findall(".//node[@class='storage']"):
248 try:
249 description = getattr(controller.find("description"), 'text', "")
250 product = getattr(controller.find("product"), 'text', "")
251 vendor = getattr(controller.find("vendor"), 'text', "")
252 dev = getattr(controller.find("logicalname"), 'text', "")
253 if dev != "":
254 res.storage_controllers.append(
255 "{0}: {1} {2} {3}".format(dev, description,
256 vendor, product))
257 else:
258 res.storage_controllers.append(
259 "{0} {1} {2}".format(description,
260 vendor, product))
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300261 except Exception:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300262 pass
263
264 for disk in core.findall(".//node[@class='disk']"):
265 try:
266 lname_node = disk.find('logicalname')
267 if lname_node is not None:
koder aka kdanilov73084622016-11-16 21:51:08 +0200268 dev = cast(str, lname_node.text).split('/')[-1]
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300269
270 if dev == "" or dev[-1].isdigit():
271 continue
272
273 sz_node = disk.find('size')
274 assert sz_node.attrib['units'] == 'bytes'
275 sz = int(sz_node.text)
276 res.disks_info[dev] = ('', sz)
277 else:
278 description = disk.find('description').text
279 product = disk.find('product').text
280 vendor = disk.find('vendor').text
281 version = disk.find('version').text
282 serial = disk.find('serial').text
283
284 full_descr = "{0} {1} {2} {3} {4}".format(
285 description, product, vendor, version, serial)
286
koder aka kdanilov73084622016-11-16 21:51:08 +0200287 businfo = cast(str, disk.find('businfo').text)
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300288 res.disks_raw_info[businfo] = full_descr
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300289 except Exception:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300290 pass
291
292 return res