def test_execute_nowait(self): bu.execute(self.some_command_with_args, False) self.assertTrue(self.popen.mock.popen.called) self.popen.mock.popen.assert_call(self.some_command_with_args) self.assertFalse(self.popen.mock.communicate.called)
def test_execute_error(self): self.popen.mock.returncode = 1 with self.assertRaises(RuntimeError): bu.execute(self.some_command_with_args)
def _update_widgets(self, widgets): zfs_version_path = "/sys/module/zfs/version" # zpool list -H: List all zpools, use script mode (no headers and tabs as separators). try: with open(zfs_version_path, 'r') as zfs_mod_version: zfs_version = zfs_mod_version.readline().rstrip().split('-')[0] except IOError: # ZFS isn't installed or the module isn't loaded, stub the version zfs_version = "0.0.0" logging.error( "ZFS version information not found at {}, check the module is loaded." .format(zfs_version_path)) raw_zpools = execute(('sudo ' if self._usesudo else '') + 'zpool list -H').split('\n') for widget in widgets: widget.set("visited", False) for raw_zpool in raw_zpools: try: # Ignored fields (assigned to _) are "expandsz" and "altroot", also "ckpoint" in ZFS 0.8.0+ if parse_version(zfs_version) < parse_version("0.8.0"): name, size, alloc, free, _, frag, cap, dedup, health, _ = raw_zpool.split( '\t') else: name, size, alloc, free, _, _, frag, cap, dedup, health, _ = raw_zpool.split( '\t') cap = cap.rstrip('%') percentuse = int(cap) percentfree = 100 - percentuse # There is a command, zpool iostat, which is however blocking and was therefore # causing issues. # Instead, we read file `/proc/spl/kstat/zfs/<poolname>/io` which contains # cumulative I/O statistics since boot (or pool creation). We store these values # (and timestamp) during each widget update, and during the next widget update we # use them to compute delta of transferred bytes, and using the last and current # timestamp the rate at which they have been transferred. with open("/proc/spl/kstat/zfs/{}/io".format(name), "r") as f: # Third row provides data we need, we are interested in the first 4 values. # More info about this file can be found here: # https://github.com/zfsonlinux/zfs/blob/master/lib/libspl/include/sys/kstat.h#L580 # The 4 values are: # nread, nwritten, reads, writes iostat = list(map(int, f.readlines()[2].split()[:4])) except (ValueError, IOError): # Unable to parse info about this pool, skip it continue if self._includelist and name not in self._includelist: continue widget = self.widget(name) if not widget: widget = bumblebee.output.Widget(name=name) widget.set("last_iostat", [0, 0, 0, 0]) widget.set("last_timestamp", 0) widgets.append(widget) delta_iostat = [ b - a for a, b in zip(iostat, widget.get("last_iostat")) ] widget.set("last_iostat", iostat) # From docs: # > Note that even though the time is always returned as a floating point number, not # > all systems provide time with a better precision than 1 second. # Be aware that that may affect the precision of reported I/O # Also, during one update cycle the reported I/O may be garbage if the system time # was changed. timestamp = time.time() delta_timestamp = widget.get("last_timestamp") - timestamp widget.set("last_timestamp", time.time()) # abs is there because sometimes the result is -0 rate_iostat = [abs(x / delta_timestamp) for x in delta_iostat] nread, nwritten, reads, writes = rate_iostat # theme.minwidth is not set since these values are not expected to change # rapidly widget.full_text( self._format.format(name=name, used=alloc, left=free, size=size, percentfree=percentfree, percentuse=percentuse, status=health, shortstatus=self._shortstatus(health), fragpercent=frag, deduppercent=dedup)) widget.set("state", health) widget.set("percentfree", percentfree) widget.set("visited", True) if self._showio: wname, rname = [name + x for x in ["__write", "__read"]] widget_w = self.widget(wname) widget_r = self.widget(rname) if not widget_w or not widget_r: widget_r = bumblebee.output.Widget(name=rname) widget_w = bumblebee.output.Widget(name=wname) widgets.extend([widget_r, widget_w]) for w in [widget_r, widget_w]: w.set( "theme.minwidth", self._ioformat.format(ops=9999, band=bytefmt(999.99 * (1024**2)))) w.set("visited", True) widget_w.full_text( self._ioformat.format(ops=round(writes), band=bytefmt(nwritten))) widget_r.full_text( self._ioformat.format(ops=round(reads), band=bytefmt(nread))) for widget in widgets: if widget.get("visited") is False: widgets.remove(widget)