a lot of changes
diff --git a/wally/hw_info.py b/wally/hw_info.py
new file mode 100644
index 0000000..9d38913
--- /dev/null
+++ b/wally/hw_info.py
@@ -0,0 +1,330 @@
+import re
+import xml.etree.ElementTree as ET
+
+from wally import ssh_utils, utils
+
+
+def get_data(rr, data):
+    match_res = re.search("(?ims)" + rr, data)
+    return match_res.group(0)
+
+
+class HWInfo(object):
+    def __init__(self):
+        self.hostname = None
+        self.cores = []
+
+        # /dev/... devices
+        self.disks_info = {}
+
+        # real disks on raid controller
+        self.disks_raw_info = {}
+
+        self.net_info = {}
+        self.ram_size = 0
+        self.sys_name = None
+        self.mb = None
+        self.raw = None
+        self.storage_controllers = []
+
+    def get_summary(self):
+        cores = sum(count for _, count in self.cores)
+        disks = sum(size for _, size in self.disks_info.values())
+
+        return {'cores': cores, 'ram': self.ram_size, 'storage': disks}
+
+    def __str__(self):
+        res = []
+
+        summ = self.get_summary()
+        summary = "Simmary: {cores} cores, {ram}B RAM, {disk}B storage"
+        res.append(summary.format(cores=summ['cores'],
+                                  ram=utils.b2ssize(summ['ram']),
+                                  disk=utils.b2ssize(summ['storage'])))
+        res.append(str(self.sys_name))
+        if self.mb is not None:
+            res.append("Motherboard: " + self.mb)
+
+        if self.ram_size == 0:
+            res.append("RAM: Failed to get RAM size")
+        else:
+            res.append("RAM " + utils.b2ssize(self.ram_size) + "B")
+
+        if self.cores == []:
+            res.append("CPU cores: Failed to get CPU info")
+        else:
+            res.append("CPU cores:")
+            for name, count in self.cores:
+                if count > 1:
+                    res.append("    {0} * {1}".format(count, name))
+                else:
+                    res.append("    " + name)
+
+        if self.storage_controllers != []:
+            res.append("Disk controllers:")
+            for descr in self.storage_controllers:
+                res.append("    " + descr)
+
+        if self.disks_info != {}:
+            res.append("Storage devices:")
+            for dev, (model, size) in sorted(self.disks_info.items()):
+                ssize = utils.b2ssize(size) + "B"
+                res.append("    {0} {1} {2}".format(dev, ssize, model))
+        else:
+            res.append("Storage devices's: Failed to get info")
+
+        if self.disks_raw_info != {}:
+            res.append("Disks devices:")
+            for dev, descr in sorted(self.disks_raw_info.items()):
+                res.append("    {0} {1}".format(dev, descr))
+        else:
+            res.append("Disks devices's: Failed to get info")
+
+        if self.net_info != {}:
+            res.append("Net adapters:")
+            for name, (speed, dtype) in self.net_info.items():
+                res.append("    {0} {2} duplex={1}".format(name, dtype, speed))
+        else:
+            res.append("Net adapters: Failed to get net info")
+
+        return str(self.hostname) + ":\n" + "\n".join("    " + i for i in res)
+
+
+class SWInfo(object):
+    def __init__(self):
+        self.os = None
+        self.partitions = None
+        self.kernel_version = None
+        self.fio_version = None
+        self.libvirt_version = None
+        self.kvm_version = None
+        self.qemu_version = None
+        self.OS_version = None
+        self.ceph_version = None
+
+
+def get_sw_info(conn):
+    res = SWInfo()
+    return res
+
+
+def get_hw_info(conn):
+    res = HWInfo()
+    lshw_out = ssh_utils.run_over_ssh(conn, 'sudo lshw -xml 2>/dev/null',
+                                      nolog=True)
+
+    res.raw = lshw_out
+    lshw_et = ET.fromstring(lshw_out)
+
+    try:
+        res.hostname = lshw_et.find("node").attrib['id']
+    except:
+        pass
+
+    try:
+        res.sys_name = (lshw_et.find("node/vendor").text + " " +
+                        lshw_et.find("node/product").text)
+        res.sys_name = res.sys_name.replace("(To be filled by O.E.M.)", "")
+        res.sys_name = res.sys_name.replace("(To be Filled by O.E.M.)", "")
+    except:
+        pass
+
+    core = lshw_et.find("node/node[@id='core']")
+    if core is None:
+        return
+
+    try:
+        res.mb = " ".join(core.find(node).text
+                          for node in ['vendor', 'product', 'version'])
+    except:
+        pass
+
+    for cpu in core.findall("node[@class='processor']"):
+        try:
+            model = cpu.find('product').text
+            threads_node = cpu.find("configuration/setting[@id='threads']")
+            if threads_node is None:
+                threads = 1
+            else:
+                threads = int(threads_node.attrib['value'])
+            res.cores.append((model, threads))
+        except:
+            pass
+
+    res.ram_size = 0
+    for mem_node in core.findall(".//node[@class='memory']"):
+        descr = mem_node.find('description')
+        try:
+            if descr is not None and descr.text == 'System Memory':
+                mem_sz = mem_node.find('size')
+                if mem_sz is None:
+                    for slot_node in mem_node.find("node[@class='memory']"):
+                        slot_sz = slot_node.find('size')
+                        if slot_sz is not None:
+                            assert slot_sz.attrib['units'] == 'bytes'
+                            res.ram_size += int(slot_sz.text)
+                else:
+                    assert mem_sz.attrib['units'] == 'bytes'
+                    res.ram_size += int(mem_sz.text)
+        except:
+            pass
+
+    for net in core.findall(".//node[@class='network']"):
+        try:
+            link = net.find("configuration/setting[@id='link']")
+            if link.attrib['value'] == 'yes':
+                name = net.find("logicalname").text
+                speed_node = net.find("configuration/setting[@id='speed']")
+
+                if speed_node is None:
+                    speed = None
+                else:
+                    speed = speed_node.attrib['value']
+
+                dup_node = net.find("configuration/setting[@id='duplex']")
+                if dup_node is None:
+                    dup = None
+                else:
+                    dup = dup_node.attrib['value']
+
+                res.net_info[name] = (speed, dup)
+        except:
+            pass
+
+    for controller in core.findall(".//node[@class='storage']"):
+        try:
+            description = getattr(controller.find("description"), 'text', "")
+            product = getattr(controller.find("product"), 'text', "")
+            vendor = getattr(controller.find("vendor"), 'text', "")
+            dev = getattr(controller.find("logicalname"), 'text', "")
+            if dev != "":
+                res.storage_controllers.append(
+                    "{0}: {1} {2} {3}".format(dev, description,
+                                              vendor, product))
+            else:
+                res.storage_controllers.append(
+                    "{0} {1} {2}".format(description,
+                                         vendor, product))
+        except:
+            pass
+
+    for disk in core.findall(".//node[@class='disk']"):
+        try:
+            lname_node = disk.find('logicalname')
+            if lname_node is not None:
+                dev = lname_node.text.split('/')[-1]
+
+                if dev == "" or dev[-1].isdigit():
+                    continue
+
+                sz_node = disk.find('size')
+                assert sz_node.attrib['units'] == 'bytes'
+                sz = int(sz_node.text)
+                res.disks_info[dev] = ('', sz)
+            else:
+                description = disk.find('description').text
+                product = disk.find('product').text
+                vendor = disk.find('vendor').text
+                version = disk.find('version').text
+                serial = disk.find('serial').text
+
+                full_descr = "{0} {1} {2} {3} {4}".format(
+                    description, product, vendor, version, serial)
+
+                businfo = disk.find('businfo').text
+                res.disks_raw_info[businfo] = full_descr
+        except:
+            pass
+
+    return res
+
+# import traceback
+# print ET.tostring(disk)
+# traceback.print_exc()
+
+# print get_hw_info(None)
+
+# def get_hw_info(conn):
+#     res = HWInfo(None)
+#     remote_run = functools.partial(ssh_utils.run_over_ssh, conn, nolog=True)
+
+#     # some data
+#     with conn.open_sftp() as sftp:
+#         proc_data = ssh_utils.read_from_remote(sftp, '/proc/cpuinfo')
+#         mem_data = ssh_utils.read_from_remote(sftp, '/proc/meminfo')
+
+#     # cpu info
+#     curr_core = {}
+#     for line in proc_data.split("\n"):
+#         if line.strip() == "":
+#             if curr_core != {}:
+#                 res.cores.append(curr_core)
+#                 curr_core = {}
+#         else:
+#             param, val = line.split(":", 1)
+#             curr_core[param.strip()] = val.strip()
+
+#     if curr_core != {}:
+#         res.cores.append(curr_core)
+
+#     # RAM info
+#     for line in mem_data.split("\n"):
+#         if line.startswith("MemTotal"):
+#             res.ram_size = int(line.split(":", 1)[1].split()[0]) * 1024
+#             break
+
+#     # HDD info
+#     for dev in remote_run('ls /dev').split():
+#         if dev[-1].isdigit():
+#             continue
+
+#         if dev.startswith('sd') or dev.startswith('hd'):
+#             model = None
+#             size = None
+
+#             for line in remote_run('sudo hdparm -I /dev/' + dev).split("\n"):
+#                 if "Model Number:" in line:
+#                     model = line.split(':', 1)[1]
+#                 elif "device size with M = 1024*1024" in line:
+#                     size = int(line.split(':', 1)[1].split()[0])
+#                     size *= 1024 ** 2
+
+#             res.disks_info[dev] = (model, size)
+
+#     # Network info
+#     separator = '*-network'
+#     net_info = remote_run('sudo lshw -class network')
+#     for net_dev_info in net_info.split(separator):
+#         if net_dev_info.strip().startswith("DISABLED"):
+#             continue
+
+#         if ":" not in net_dev_info:
+#             continue
+
+#         dev_params = {}
+#         for line in net_dev_info.split("\n"):
+#             line = line.strip()
+#             if ':' in line:
+#                 key, data = line.split(":", 1)
+#                 dev_params[key.strip()] = data.strip()
+
+#         if 'configuration' not in dev_params:
+#             print "!!!!!", net_dev_info
+#             continue
+
+#         conf = dev_params['configuration']
+#         if 'link=yes' in conf:
+#             if 'speed=' in conf:
+#                 speed = conf.split('speed=', 1)[1]
+#                 speed = speed.strip().split()[0]
+#             else:
+#                 speed = None
+
+#             if "duplex=" in conf:
+#                 dtype = conf.split("duplex=", 1)[1]
+#                 dtype = dtype.strip().split()[0]
+#             else:
+#                 dtype = None
+
+#             res.net_info[dev_params['logical name']] = (speed, dtype)
+#     return res