blob: e81a5c1e0497bfa0e941cc44770d2965b9bbb37c [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 kdanilov3d2bc4f2016-11-12 18:31:18 +02008from .node_interfaces import IRPCNode
koder aka kdanilovf86d7af2015-05-06 04:01:54 +03009
10
koder aka kdanilov962ee5f2016-12-19 02:40:08 +020011logger = logging.getLogger("wally")
12
13
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030014def get_data(rr: str, data: str) -> str:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030015 match_res = re.search("(?ims)" + rr, data)
16 return match_res.group(0)
17
18
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030019class HWInfo:
koder aka kdanilov22d134e2016-11-08 11:33:19 +020020 def __init__(self) -> None:
21 self.hostname = None # type: str
22 self.cores = [] # type: List[Tuple[str, int]]
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030023
24 # /dev/... devices
koder aka kdanilov22d134e2016-11-08 11:33:19 +020025 self.disks_info = {} # type: Dict[str, Tuple[str, int]]
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030026
27 # real disks on raid controller
koder aka kdanilov22d134e2016-11-08 11:33:19 +020028 self.disks_raw_info = {} # type: Dict[str, str]
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030029
koder aka kdanilov6ab4d432015-06-22 00:26:28 +030030 # name => (speed, is_full_diplex, ip_addresses)
koder aka kdanilov73084622016-11-16 21:51:08 +020031 self.net_info = {} # type: Dict[str, Tuple[Optional[int], Optional[bool], List[str]]]
koder aka kdanilov6ab4d432015-06-22 00:26:28 +030032
koder aka kdanilov22d134e2016-11-08 11:33:19 +020033 self.ram_size = 0 # type: int
34 self.sys_name = None # type: str
35 self.mb = None # type: str
36 self.raw = None # type: str
koder aka kdanilov6ab4d432015-06-22 00:26:28 +030037
koder aka kdanilov22d134e2016-11-08 11:33:19 +020038 self.storage_controllers = [] # type: List[str]
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030039
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030040 def get_hdd_count(self) -> Iterable[int]:
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +030041 # SATA HDD COUNT, SAS 10k HDD COUNT, SAS SSD count, PCI-E SSD count
42 return []
43
koder aka kdanilov22d134e2016-11-08 11:33:19 +020044 def get_summary(self) -> Dict[str, int]:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030045 cores = sum(count for _, count in self.cores)
46 disks = sum(size for _, size in self.disks_info.values())
47
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +030048 return {'cores': cores,
49 'ram': self.ram_size,
50 'storage': disks,
51 'disk_count': len(self.disks_info)}
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030052
53 def __str__(self):
54 res = []
55
56 summ = self.get_summary()
57 summary = "Simmary: {cores} cores, {ram}B RAM, {disk}B storage"
58 res.append(summary.format(cores=summ['cores'],
59 ram=utils.b2ssize(summ['ram']),
60 disk=utils.b2ssize(summ['storage'])))
61 res.append(str(self.sys_name))
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030062 if self.mb:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030063 res.append("Motherboard: " + self.mb)
64
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030065 if not self.ram_size:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030066 res.append("RAM: Failed to get RAM size")
67 else:
68 res.append("RAM " + utils.b2ssize(self.ram_size) + "B")
69
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030070 if not self.cores:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030071 res.append("CPU cores: Failed to get CPU info")
72 else:
73 res.append("CPU cores:")
74 for name, count in self.cores:
75 if count > 1:
76 res.append(" {0} * {1}".format(count, name))
77 else:
78 res.append(" " + name)
79
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030080 if self.storage_controllers:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030081 res.append("Disk controllers:")
82 for descr in self.storage_controllers:
83 res.append(" " + descr)
84
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030085 if self.disks_info:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030086 res.append("Storage devices:")
87 for dev, (model, size) in sorted(self.disks_info.items()):
88 ssize = utils.b2ssize(size) + "B"
89 res.append(" {0} {1} {2}".format(dev, ssize, model))
90 else:
91 res.append("Storage devices's: Failed to get info")
92
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +030093 if self.disks_raw_info:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +030094 res.append("Disks devices:")
95 for dev, descr in sorted(self.disks_raw_info.items()):
96 res.append(" {0} {1}".format(dev, descr))
97 else:
98 res.append("Disks devices's: Failed to get info")
99
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300100 if self.net_info:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300101 res.append("Net adapters:")
koder aka kdanilov6ab4d432015-06-22 00:26:28 +0300102 for name, (speed, dtype, _) in self.net_info.items():
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300103 res.append(" {0} {2} duplex={1}".format(name, dtype, speed))
104 else:
105 res.append("Net adapters: Failed to get net info")
106
107 return str(self.hostname) + ":\n" + "\n".join(" " + i for i in res)
108
109
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300110class SWInfo:
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200111 def __init__(self) -> None:
koder aka kdanilov962ee5f2016-12-19 02:40:08 +0200112 self.mtab = None # type: str
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200113 self.kernel_version = None # type: str
koder aka kdanilov962ee5f2016-12-19 02:40:08 +0200114 self.libvirt_version = None # type: Optional[str]
115 self.qemu_version = None # type: Optional[str]
koder aka kdanilov73084622016-11-16 21:51:08 +0200116 self.OS_version = None # type: utils.OSRelease
koder aka kdanilov962ee5f2016-12-19 02:40:08 +0200117 self.ceph_version = None # type: Optional[str]
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300118
119
koder aka kdanilov3d2bc4f2016-11-12 18:31:18 +0200120def get_sw_info(node: IRPCNode) -> SWInfo:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300121 res = SWInfo()
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300122
koder aka kdanilov22d134e2016-11-08 11:33:19 +0200123 res.OS_version = utils.get_os(node)
koder aka kdanilov73084622016-11-16 21:51:08 +0200124 res.kernel_version = node.get_file_content('/proc/version').decode('utf8').strip()
koder aka kdanilov962ee5f2016-12-19 02:40:08 +0200125 res.mtab = node.get_file_content('/etc/mtab').decode('utf8').strip()
126
127 try:
128 res.libvirt_version = node.run("virsh -v", nolog=True).strip()
129 except OSError:
130 res.libvirt_version = None
131
koder aka kdanilov23e6bdf2016-12-24 02:18:54 +0200132 # try:
133 # # dpkg -l ??
134 # res.libvirt_version = node.run("virsh -v", nolog=True).strip()
135 # except OSError:
136 # res.libvirt_version = None
137
koder aka kdanilov962ee5f2016-12-19 02:40:08 +0200138 try:
139 res.qemu_version = node.run("qemu-system-x86_64 --version", nolog=True).strip()
140 except OSError:
141 res.qemu_version = None
142
143 try:
144 res.ceph_version = node.run("ceph --version", nolog=True).strip()
145 except OSError:
146 res.ceph_version = None
koder aka kdanilov4af1c1d2015-05-18 15:48:58 +0300147
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300148 return res
149
150
koder aka kdanilov962ee5f2016-12-19 02:40:08 +0200151def get_hw_info(node: IRPCNode) -> Optional[HWInfo]:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300152
koder aka kdanilov962ee5f2016-12-19 02:40:08 +0200153 try:
154 lshw_out = node.run('sudo lshw -xml 2>/dev/null')
155 except Exception as exc:
156 logger.warning("lshw failed on node %s: %s", node.info.node_id(), exc)
157 return None
158
159 res = HWInfo()
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300160 res.raw = lshw_out
161 lshw_et = ET.fromstring(lshw_out)
162
163 try:
koder aka kdanilov73084622016-11-16 21:51:08 +0200164 res.hostname = cast(str, lshw_et.find("node").attrib['id'])
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300165 except Exception:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300166 pass
167
168 try:
koder aka kdanilov73084622016-11-16 21:51:08 +0200169
170 res.sys_name = cast(str, lshw_et.find("node/vendor").text) + " " + \
171 cast(str, lshw_et.find("node/product").text)
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300172 res.sys_name = res.sys_name.replace("(To be filled by O.E.M.)", "")
173 res.sys_name = res.sys_name.replace("(To be Filled by O.E.M.)", "")
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300174 except Exception:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300175 pass
176
177 core = lshw_et.find("node/node[@id='core']")
178 if core is None:
koder aka kdanilov73084622016-11-16 21:51:08 +0200179 return res
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300180
181 try:
koder aka kdanilov73084622016-11-16 21:51:08 +0200182 res.mb = " ".join(cast(str, core.find(node).text)
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300183 for node in ['vendor', 'product', 'version'])
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300184 except Exception:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300185 pass
186
187 for cpu in core.findall("node[@class='processor']"):
188 try:
koder aka kdanilov73084622016-11-16 21:51:08 +0200189 model = cast(str, cpu.find('product').text)
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300190 threads_node = cpu.find("configuration/setting[@id='threads']")
191 if threads_node is None:
192 threads = 1
193 else:
194 threads = int(threads_node.attrib['value'])
195 res.cores.append((model, threads))
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300196 except Exception:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300197 pass
198
199 res.ram_size = 0
200 for mem_node in core.findall(".//node[@class='memory']"):
201 descr = mem_node.find('description')
202 try:
203 if descr is not None and descr.text == 'System Memory':
204 mem_sz = mem_node.find('size')
205 if mem_sz is None:
206 for slot_node in mem_node.find("node[@class='memory']"):
207 slot_sz = slot_node.find('size')
208 if slot_sz is not None:
209 assert slot_sz.attrib['units'] == 'bytes'
210 res.ram_size += int(slot_sz.text)
211 else:
212 assert mem_sz.attrib['units'] == 'bytes'
213 res.ram_size += int(mem_sz.text)
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300214 except Exception:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300215 pass
216
217 for net in core.findall(".//node[@class='network']"):
218 try:
219 link = net.find("configuration/setting[@id='link']")
220 if link.attrib['value'] == 'yes':
koder aka kdanilov73084622016-11-16 21:51:08 +0200221 name = cast(str, net.find("logicalname").text)
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300222 speed_node = net.find("configuration/setting[@id='speed']")
223
224 if speed_node is None:
225 speed = None
226 else:
koder aka kdanilov73084622016-11-16 21:51:08 +0200227 speed = int(speed_node.attrib['value'])
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300228
229 dup_node = net.find("configuration/setting[@id='duplex']")
230 if dup_node is None:
231 dup = None
232 else:
koder aka kdanilov73084622016-11-16 21:51:08 +0200233 dup = cast(str, dup_node.attrib['value']).lower() == 'yes'
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300234
koder aka kdanilov73084622016-11-16 21:51:08 +0200235 ips = [] # type: List[str]
236 res.net_info[name] = (speed, dup, ips)
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300237 except Exception:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300238 pass
239
240 for controller in core.findall(".//node[@class='storage']"):
241 try:
242 description = getattr(controller.find("description"), 'text', "")
243 product = getattr(controller.find("product"), 'text', "")
244 vendor = getattr(controller.find("vendor"), 'text', "")
245 dev = getattr(controller.find("logicalname"), 'text', "")
246 if dev != "":
247 res.storage_controllers.append(
248 "{0}: {1} {2} {3}".format(dev, description,
249 vendor, product))
250 else:
251 res.storage_controllers.append(
252 "{0} {1} {2}".format(description,
253 vendor, product))
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300254 except Exception:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300255 pass
256
257 for disk in core.findall(".//node[@class='disk']"):
258 try:
259 lname_node = disk.find('logicalname')
260 if lname_node is not None:
koder aka kdanilov73084622016-11-16 21:51:08 +0200261 dev = cast(str, lname_node.text).split('/')[-1]
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300262
263 if dev == "" or dev[-1].isdigit():
264 continue
265
266 sz_node = disk.find('size')
267 assert sz_node.attrib['units'] == 'bytes'
268 sz = int(sz_node.text)
269 res.disks_info[dev] = ('', sz)
270 else:
271 description = disk.find('description').text
272 product = disk.find('product').text
273 vendor = disk.find('vendor').text
274 version = disk.find('version').text
275 serial = disk.find('serial').text
276
277 full_descr = "{0} {1} {2} {3} {4}".format(
278 description, product, vendor, version, serial)
279
koder aka kdanilov73084622016-11-16 21:51:08 +0200280 businfo = cast(str, disk.find('businfo').text)
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300281 res.disks_raw_info[businfo] = full_descr
koder aka kdanilov3b4da8b2016-10-17 00:17:53 +0300282 except Exception:
koder aka kdanilovf86d7af2015-05-06 04:01:54 +0300283 pass
284
285 return res