#!/usr/local/bin/python # vxtop v.0.0.1 wrapper for vxstat # Copyright (c) 2002, George Schlossnagle . # All rights reserved # # This program is free software and can be redistibuted and modified # as you wish, as long as this header remains in place. # This program is provided "as is" and without any express or implied # warranties. If it breaks, you can have both pieces. import curses import os import popen2 import UserDict import select import threading import time #import traceback import types class Popen: def __init__(self, cmd, capturestderr=0, bufsize=-1): p2cread, p2cwrite = os.pipe() c2pread, c2pwrite = os.pipe() if capturestderr: errout, errin = os.pipe() self.pid = os.fork() if self.pid == 0: # Child os.dup2(p2cread, 0) os.dup2(c2pwrite, 1) if capturestderr: os.dup2(errin, 2) self._run_child(cmd) os.close(p2cread) self.tochild = os.fdopen(p2cwrite, 'w', bufsize) os.close(c2pwrite) self.fromchild = os.fdopen(c2pread, 'r', bufsize) if capturestderr: os.close(errin) self.childerr = os.fdopen(errout, 'r', bufsize) else: self.childerr = None def _run_child(self, cmd): if isinstance(cmd, types.StringTypes): cmd = cmd.split() for i in range(3, 1024): try: os.close(i) except: pass try: os.execvp(cmd[0], cmd) finally: os._exit(1) class VxObject: __name__ = 'VxObject' def __init__(self, name, parent,type,dg): self.name = name; self.parent = parent self.type = type self.dg = dg self.info = "" self.dfinfo = [] self.children = [] self.r_ops = 0 self.w_ops = 0 self.r_blk = 0 self.w_blk = 0 self.r_avg = 0.0 self.w_avg = 0.0 def __repr__(self): r_avg = "%4.2f" % self.r_avg w_avg = "%4.2f" % self.w_avg return "%-4s %-22s %6d %6d %8d %8d %7s %7s" % \ (self.type, \ self.name+':'+self.dg, \ self.r_ops, \ self.w_ops, \ self.r_blk, \ self.w_blk, \ r_avg, \ w_avg ) __self__ = __repr__ def addChildren(self, child): self.children.append(child) def printRecurse(self): print self for child in self.children: child.printRecurse() def returnChildren(self): array = [self,] for child in self.children: array = array + child.returnChildren() return array def updateStats(self, arr): try: self.r_ops = long(arr[2]) self.w_ops = long(arr[3]) self.r_blk = long(arr[4]) self.w_blk = long(arr[5]) self.r_avg = float(arr[6]) self.w_avg = float(arr[7]) except ValueError: pass def description(self): array = [] array.append("Volume: "+self.name+" "+self.info) if len(self.dfinfo) == 5: str = "Mount Point: %s Size: %dM Capacity: %s" % \ (self.dfinfo[4], long(self.dfinfo[0])/1000, self.dfinfo[3]) array.append(str) else: array.append("Unmounted volume") return array class VxDict: def __init__(self): self.byType = {} self.byName = {} self.refreshInterval = 1 self.dgs = self._getDg() self.df = self._getDf() for dg in self.dgs: self._build(dg) self._buildTree() for dg in self.dgs: self._buildDisks(dg) def returnZoom(self, name): return self.byName[name].returnChildren() def returnType(self, id): array = [] for i in self.byType[id]: array.append(self.byName[i]) return array def _getDf(self): fd = os.popen("df -k") array = {} fd.readline() # pop off first line while 1: line = fd.readline() if not line: break rdev, kbytes, used, avail, cap, mnt = line.split() path = rdev.split('/') if len(path) > 3 and path[2] == 'vx': if len(path) == 6: # dg specified identifier = path[5]+':'+path[4] else: identifier = path[4]+':rootdg' array[identifier] = [kbytes, used, avail, cap, mnt] else: pass return array def _getDg(self): fd = os.popen("vxdg -q list", "r") array = [] while 1: line = fd.readline() if not line: break fields = line.split() array.append(fields[0]) return array def _build(self, dg): fd = os.popen("vxprint -htqQ -g "+dg, "r") while 1: line = fd.readline() if not line: break fields = line.strip().split() if len(fields): if fields[0] == 'v': type = 'vol' else: type = fields[0] if type == 'vol': self.byName[fields[1]] = VxObject(name=fields[1], \ parent=None, type=type, dg=dg) try: self.byName[fields[1]].dfinfo = self.df[fields[1]+':'+dg] except KeyError: pass else: self.byName[fields[1]] = VxObject(name=fields[1], \ parent=fields[2], type=type, dg=dg) if type == 'pl': size = fields[5] layout = fields[6] details = fields[7] if layout == 'STRIPE': stripes,width = details.split('/') info = "Layout: %s Num Stripes: %s Stripe Width: %sk" % (layout, stripes, width) else: info = "Layout: %s" % (layout,) try: self.byName[fields[2]].info = info except KeyError: pass try: self.byType[type].append(fields[1]) except KeyError: self.byType[type] = [fields[1],] else: pass def _buildDisks(self,dg): fd = os.popen("vxprint -s -F \"%name %dm_name\" -g "+dg) while 1: line = fd.readline() if not line: break sd,dm = line.strip().split() sdObj = self.byName[sd] self.byName[dm].addChildren(sdObj) def _buildTree(self): for key in self.byName.iterkeys(): myObj = self.byName[key] if self.byName.has_key(myObj.parent): self.byName[myObj.parent].addChildren(myObj) #### sort functions ##### def _sortName(a,b): return cmp(a.name,b.name) def _sortRops(a,b): return cmp(b.r_ops,a.r_ops) def _sortWops(a,b): return cmp(b.w_ops,a.w_ops) def _sortRblk(a,b): return cmp(b.r_blk,a.r_blk) def _sortWblk(a,b): return cmp(b.w_blk,a.w_blk) def _sortRavg(a,b): return cmp(b.r_avg,a.r_avg) def _sortWavg(a,b): return cmp(b.w_avg,a.w_avg) class StatsWindow: def __init__(self, scr,vxinfo): self.scr = scr self.ctrlwin = curses.newwin(4,curses.COLS - 1,0,0) self.ctrlwin.leaveok(1) self.mainwin = curses.newwin(curses.LINES - 5,curses.COLS - 2,4,0) self.mainwin.leaveok(1) self.bottomwin = curses.newwin(1,curses.COLS-2,curses.LINES - 1,0) self.bottomwin.leaveok(1) self.vxinfo = vxinfo self.currentLine = 0 self.type = 'vol' self.menu = self.defaultMenu self.zoom = None self.array = [] self.sort = None self.curObj = [] def processKeys(self, c): if c in 'vV': # display volumes self.type = 'vol' elif c in 'dD': # display disks self.type = 'dm' elif c in 'sS': # sort self.scr.nodelay(0) self.eraseMenu() self.sortMenu() self.ctrlwin.refresh() c = self.scr.getch() self.eraseMenu() self.scr.nodelay(1) self.processSortKeys(c) elif c == '?': # display help self.menu = self.helpMenu elif c == ' ': self.eraseMenu() self.menu = self.defaultMenu else: pass # self.ctrlwin.clear() self.mainwin.clear() self.currentLine = 0 def processSortKeys(self, c): if 0 0 and not self.zoom: self.currentLine = self.currentLine - 1 elif c == curses.KEY_RIGHT and 0 < self.currentLine < len(self.array) + 1: self.zoom = self.array[self.currentLine - 1].name self.mainwin.clear() elif c == curses.KEY_LEFT: self.zoom = None self.mainwin.clear() else: pass def display(self): i = 0 self.menu() title ='typ name:dg r_ops w_ops r_blk w_blk r_avg w_avg' self.mainwin.addstr(0,0,title, curses.A_BOLD) if self.zoom: self.array = self.vxinfo.returnZoom(self.zoom) else: self.array = self.vxinfo.returnType(self.type) if self.sort and not self.zoom: self.array.sort(self.sort) for obj in self.array[0:curses.LINES - 6]: i = i + 1 if i != self.currentLine or self.zoom: self.mainwin.move(i,0) self.mainwin.clrtoeol() if self.zoom and i == 1: self.mainwin.addstr(i,0,str(obj), curses.A_REVERSE) else: self.mainwin.addstr(i,0,str(obj)) else: self.mainwin.move(i,0) self.mainwin.clrtoeol() self.mainwin.addstr(i,0,str(obj), curses.A_REVERSE) self.curObj = obj.description() def eraseMenu(self): self.ctrlwin.move(0,0); self.ctrlwin.clrtoeol() self.ctrlwin.move(1,0); self.ctrlwin.clrtoeol() self.ctrlwin.move(2,0); self.ctrlwin.clrtoeol() self.ctrlwin.move(3,0); self.ctrlwin.clrtoeol() def defaultMenu(self): self.ctrlwin.addstr(0,0, time.strftime(" vxtop version 0.0.1 %H:%M:%S")) self.ctrlwin.move(2,0); self.ctrlwin.clrtoeol() if self.currentLine != 0: i = 0 for line in self.curObj: self.ctrlwin.move(2 + i,0); self.ctrlwin.clrtoeol() self.ctrlwin.addstr(2 + i, 0, line) i += 1 else: self.ctrlwin.move(3,0); self.ctrlwin.clrtoeol() self.ctrlwin.addstr(3,0, "Use the arrow keys to navigate. ? for help.") self.bottomwin.addstr(0,0,"press ? for help, press q to quit") def sortMenu(self): self.ctrlwin.addstr(0,0, 'Sort Entries On:') self.ctrlwin.addstr(1,0, '1) name 2) read operations 3) write operations') self.ctrlwin.addstr(2,0, '4) read blocks 5) write blocks') self.ctrlwin.addstr(3,0, '6) average read latency 7) average write latency') def helpMenu(self): self.ctrlwin.addstr(0,0, ' vxtop version 0.0.1') self.ctrlwin.addstr(1,0, 's - specify sort order | right arrow - zoom | - leave this menu') self.ctrlwin.addstr(2,0, 'v - show all volumes | left arrow - unzoom |') self.ctrlwin.addstr(3,0, 'd - show all disks | ? - this menu |') class PipeReader: def __init__(self,cmd): self.int = 1 self.pfd = popen2(cmd) # read off headers self.pfd.readline() self.fd.readline() self.fd.readline() def readable(self): return len(select.select([self.fd], [], [], 0)[0]) def read(self, vxinfo): while 1: if len(select.select([self.fd], [], [], 0)[0]) == 0: break line = self.fd.readline() if line[0:3] in ('Mon','Tue','Wed','Thu','Fri','Sat','Sun'): vxinfo.date = line.strip() elif line == "\n": break else: arr = line.strip().split() if len(arr) == 8: vxinfo.byName[arr[1]].updateStats(arr) def vxpoll(dg): global vxinfo global runthread global lock pfd = Popen("vxstat -spdv -i "+str(vxinfo.refreshInterval)+" -g "+dg) fd = pfd.fromchild fd.readline() fd.readline() fd.readline() while 1: rt = runthread if rt == 0: break line = fd.readline() if line[0:3] in ('Mon','Tue','Wed','Thu','Fri','Sat','Sun'): vxinfo.date = line.strip() elif line == "\n": time.sleep(vxinfo.refreshInterval) else: arr = line.strip().split() lock.acquire(1) try: vxinfo.byName[arr[1]].updateStats(arr) except IndexError: pass except KeyError: pass lock.release() pfd.fromchild.close() pfd.tochild.close() os.kill(pfd.pid, 9) def daemon(trgt): thread = threading.Thread(target=trgt) thread.start() def main(swin): global lock stdscr = swin.scr vxinfo = swin.vxinfo swin.defaultMenu() while 1: swin.scr.nodelay(1) c = swin.scr.getch() if 0