Commit 8f45534e91a9bba445077cc48559befefe4eb201

Authored by John Snowdon
1 parent 6c28a4d8e4
Exists in master

added matrix orbital compatible lcd output class, can now toggle between terminal or lcd mode.

data.py
... ... @@ -122,7 +122,7 @@ class data():
122 122 self.stats_now["net_bytes_write"] = n.bytes_sent
123 123 self.stats_now["net_bytes_read"] = n.bytes_recv
124 124  
125   - # Get addresses currently in use
  125 + # Get address currently in use by primary interface
126 126 e = psutil.net_if_addrs()
127 127 try:
128 128 for v in e[settings.ETH_DEVICE]:
... ... @@ -132,35 +132,41 @@ class data():
132 132 except Exception as e:
133 133 self.network_address = "0.0.0.0"
134 134  
135   - # Get interface up/down status
136   - try:
137   - e = psutil.net_if_stats()
138   - if e[settings.ETH_DEVICE].isup == True:
139   - self.network_status = "Up"
140   - else:
  135 + # Check connections of all devices
  136 + for dev in settings.ETH_DEVICES:
  137 + # Get interface up/down status
  138 + try:
  139 + e = psutil.net_if_stats()
  140 + if e[dev].isup == True:
  141 + self.network_status = "Up"
  142 + else:
  143 + self.network_status = False
  144 + err = settings.ERR_NETWORK_STATUS.replace('ETH', dev)
  145 + if err not in self.errors:
  146 + self.errors.append(err)
  147 + except Exception as e:
141 148 self.network_status = False
142   - if settings.ERR_NETWORK_STATUS not in self.errors:
143   - self.errors.append(settings.ERR_NETWORK_STATUS)
144   - except Exception as e:
145   - self.network_status = False
146   - if settings.ERR_NETWORK_STATUS not in self.errors:
147   - self.errors.append(settings.ERR_NETWORK_STATUS)
148   -
149   - # Get media connection status
150   - try:
151   - f = open("/sys/class/net/" + settings.ETH_DEVICE + "/carrier", "ro")
152   - fdata = f.read().strip('\n')
153   - f.close()
154   - if fdata == '1':
155   - self.network_media = "Ok"
156   - else:
  149 + err = settings.ERR_NETWORK_STATUS.replace('ETH', dev)
  150 + if err not in self.errors:
  151 + self.errors.append(err)
  152 +
  153 + # Get media connection status
  154 + try:
  155 + f = open("/sys/class/net/" + dev + "/carrier", "ro")
  156 + fdata = f.read().strip('\n')
  157 + f.close()
  158 + if fdata == '1':
  159 + self.network_media = "Ok"
  160 + else:
  161 + self.network_media = False
  162 + err = settings.ERR_NETWORK_MEDIA.replace('ETH', dev)
  163 + if err not in self.errors:
  164 + self.errors.append(err)
  165 + except Exception as e:
157 166 self.network_media = False
158   - if settings.ERR_NETWORK_MEDIA not in self.errors:
159   - self.errors.append(settings.ERR_NETWORK_MEDIA)
160   - except Exception as e:
161   - self.network_media = False
162   - if settings.ERR_NETWORK_MEDIA not in self.errors:
163   - self.errors.append(settings.ERR_NETWORK_MEDIA)
  167 + err = settings.ERR_NETWORK_MEDIA.replace('ETH', dev)
  168 + if err not in self.errors:
  169 + self.errors.append(err)
164 170  
165 171 def disk(self):
166 172 """ Disk information """
... ...
matrix.py
... ... @@ -0,0 +1,123 @@
  1 +'''
  2 +Test python sketch for Adafruit USB+Serial LCD backpack
  3 +---> http://www.adafruit.com/category/63_96
  4 +
  5 +Adafruit invests time and resources providing this open source code,
  6 +please support Adafruit and open-source hardware by purchasing
  7 +products from Adafruit!
  8 +
  9 +Written by Limor Fried/Ladyada for Adafruit Industries.
  10 +BSD license, check license.txt for more information
  11 +All text above must be included in any redistribution
  12 +
  13 +*** Changed to a class by John Snowdon (john.snowdon@ncl.ac.uk, January 2016)
  14 +
  15 +'''
  16 +
  17 +
  18 +import serial
  19 +import sys
  20 +import time
  21 +
  22 +# 20x4 LCD
  23 +#ROWS = 4
  24 +#COLS = 20
  25 +
  26 +# 16x2 LCD:
  27 +ROWS = 2
  28 +COLS = 16
  29 +
  30 +class Matrix():
  31 +
  32 + def __init__(self, device = "/dev/ttyACM0"):
  33 + # initialise the class and attempt to open the serial device
  34 + s = serial.Serial(device, 57600, timeout=1)
  35 + self.device = s
  36 + self.scroll = False
  37 + self.cols = COLS
  38 + self.rows = ROWS
  39 +
  40 + def matrixwritecommand(self, commandlist):
  41 + # write a command to the lcd
  42 + commandlist.insert(0, 0xFE)
  43 + for i in range(0, len(commandlist)):
  44 + self.device.write(chr(commandlist[i]))
  45 +
  46 + def matrixNewline(self):
  47 + self.device.write(chr(0x0A))
  48 +
  49 + def matrixWrite(self, text):
  50 + # If we're not autoscrolling, then
  51 + # pad the text to the length of the line.
  52 + if self.scroll == False:
  53 + if len(text) < self.cols:
  54 + text = text.ljust(self.cols)
  55 + self.device.write(text)
  56 +
  57 + def matrixSize(self, cols = COLS, rows = ROWS):
  58 + # set size
  59 + self.cols = cols
  60 + self.rows = rows
  61 + self.matrixwritecommand([0xD1, cols, rows]);
  62 + self.matrixwritecommand([0x58])
  63 +
  64 + def matrixAutoscrollOn(self):
  65 + self.matrixwritecommand([0xFE, 0x51])
  66 +
  67 + def matrixAutoscrollOff(self):
  68 + self.matrixwritecommand([0xFE, 0x52])
  69 +
  70 + def matrixContrast(self, contrast = 0x220):
  71 + # set contrast
  72 + self.matrixwritecommand([0x50, contrast])
  73 +
  74 + def matrixOff(self):
  75 + # turn off display
  76 + self.matrixwritecommand([0x46])
  77 +
  78 + def matrixOn(self):
  79 + # turn on display
  80 + self.matrixwritecommand([0x42, 0])
  81 +
  82 + def matrixBrightness(self, brightness = 0x200):
  83 + # set brightness
  84 + self.matrixwritecommand([0x99, brightness])
  85 +
  86 + def matrixClear(self):
  87 + # clear screen
  88 + self.matrixwritecommand([0x58])
  89 +
  90 + def matrixRGB(self, r = 0x200, g = 0x0, b = 0x0):
  91 + self.matrixwritecommand([0xD0, r, g, b])
  92 +
  93 + def matrixScrollOn(self):
  94 + # Turn on scrolling of lines longer than COLS
  95 + self.matrixwritecommand([0x58])
  96 + self.matrixwritecommand([0x51])
  97 + self.scroll = True
  98 +
  99 + def matrixScrollOff(self):
  100 + # Turn off scrolling of lines longer than COLS
  101 + self.matrixwritecommand([0x58])
  102 + self.matrixwritecommand([0x52])
  103 + self.scroll = False
  104 +
  105 + def matrixCursorOn(self):
  106 + #underline cursor on
  107 + self.matrixwritecommand([0x4A])
  108 +
  109 + def matrixCursorOff(self):
  110 + # cursor off
  111 + self.matrixwritecommand([0x4B])
  112 +
  113 + def matrixBlockCursorOn(self):
  114 + # block cursor on
  115 + self.matrixwritecommand([0x53])
  116 +
  117 + def matrixBlockCursorOff(self):
  118 + self.matrixwritecommand([0x54])
  119 +
  120 + def matrixSetSplash(self, splash = "Hello"):
  121 + # Splashscreen change
  122 + # loop over string, get the ordinal values, append to 0x40...
  123 + self.matrixwritecommand([0x40, ord('H'),ord('e'),ord('l'),ord('l'),ord('o'),ord(' '),ord('W'),ord('o'),ord('r'),ord('l'),ord('d'),ord('!'),ord(' '),ord(' '),ord(' '),ord(' '),ord('T'),ord('e'),ord('s'),ord('t'),ord('i'),ord('n'),ord('g'),ord(' '),ord('1'),ord('6'),ord('x'),ord('2'),ord(' '),ord('L'),ord('C'),ord('D')])
... ...
output.py
1 1 #!/usr/bin/env python
2 2  
3 3 import time
4   -
5 4 import settings
  5 +from matrix import Matrix
6 6 from data import data
7 7  
8 8 class Output():
9 9  
10 10 def __init__(self, mode = "terminal"):
11 11 self.mode = mode
  12 + self.errors = False
  13 + if mode == "lcd":
  14 + self.matrix = Matrix(device="/dev/ttyACM0")
  15 + self.matrix.matrixOn()
  16 + self.matrix.matrixSize(16, 2)
  17 + self.matrix.matrixClear()
  18 + self.matrix.matrixAutoscrollOff()
  19 + self.matrix.matrixBrightness(250)
  20 + self.matrix.matrixRGB(0,255,0)
  21 + else:
  22 + self.matrix = False
12 23  
13 24 def sizeof_fmt(self, num, suffix='B'):
14 25 """ Handy convertor for unit suffixes """
... ... @@ -18,9 +29,115 @@ class Output():
18 29 num /= 1024.0
19 30 return "%.1f%s%s" % (num, 'Yi', suffix)
20 31  
21   - def lcd_overviwe(self, data, interval):
  32 + def lcd_overview(self, data, interval):
22 33 """ Show the information on the lcd serial screen """
23   - pass
  34 + self.matrix.matrixClear()
  35 + # Display error conditions first and foremost
  36 + error = False
  37 + if len(data.errors) > 0:
  38 + # On detection of errors, screen goes red
  39 + self.matrix.matrixRGB(255,0,0)
  40 + # Loop over and scroll each error - ignore the normal cycle process in the outer call
  41 + e_num = 1
  42 + for e in data.errors:
  43 +
  44 + display_text = "%s: %s" % (e_num, e)
  45 +
  46 + if len(display_text) <= settings.DISPLAY_WIDTH:
  47 + # Display normal errors that fit in the width
  48 + self.matrix.matrixClear()
  49 + self.matrix.matrixWrite("%s Errors!" % len(data.errors))
  50 + self.matrix.matrixNewline()
  51 + self.matrix.matrixWrite(display_text)
  52 +
  53 + else:
  54 + # Display longer errors that need to be scrolled
  55 + display_text = ""
  56 + e = "%s: %s" % (e_num, e)
  57 + for i in range (0, len(e)):
  58 + self.matrix.matrixClear()
  59 + self.matrix.matrixWrite("%s Errors!" % len(data.errors))
  60 + self.matrix.matrixNewline()
  61 + display_text = e[i:(i+settings.DISPLAY_WIDTH)]
  62 + self.matrix.matrixWrite(display_text)
  63 + if i == 0:
  64 + # Pause the first time we show the beginning of the error.
  65 + time.sleep(1)
  66 + else:
  67 + # Slight gap between each character scrolling by.
  68 + time.sleep(settings.SCROLL_SPEED)
  69 +
  70 + # Sleep for next error
  71 + time.sleep(1)
  72 + e_num += 1
  73 + error = True
  74 +
  75 + # Don't bother printing metrics if we have errors to show!
  76 + if error:
  77 + self.errors = True
  78 + return
  79 + else:
  80 + self.errors = False
  81 +
  82 + # If no errors, screen defaults back to green
  83 + self.matrix.matrixRGB(0,255,0)
  84 +
  85 + # Are we showing alternate screen 1?
  86 + # Show CPU
  87 + # Show IP address
  88 + if (interval >= settings.DETAIL_1_INTERVAL) and (interval <= (settings.DETAIL_1_INTERVAL + settings.DETAIL_DURATION)):
  89 + #print "CPU %2d%% %2d%% %2d%% %2d%%" % (data.cpu_percent[0], data.cpu_percent[1], data.cpu_percent[2], data.cpu_percent[3])
  90 + self.matrix.matrixWrite("CPU %2d%% %sMHz" % (data.cpu_percent_avg, data.cpu_mhz))
  91 + self.matrix.matrixWrite("IP %s" % (data.network_address))
  92 + return
  93 +
  94 + # Are we showing alternate screen 2?
  95 + # Show CPU
  96 + # Show CPU load
  97 + if (interval >= settings.DETAIL_2_INTERVAL) and (interval <= (settings.DETAIL_2_INTERVAL + settings.DETAIL_DURATION)):
  98 + #print "CPU %2d%% %2d%% %2d%% %2d%%" % (data.cpu_percent[0], data.cpu_percent[1], data.cpu_percent[2], data.cpu_percent[3])
  99 + self.matrix.matrixWrite("CPU %2d%% %sMHz" % (data.cpu_percent_avg, data.cpu_mhz))
  100 + self.matrix.matrixWrite("%s %s %s" % (data.load_average[0], data.load_average[1], data.load_average[2]))
  101 + return
  102 +
  103 + # Are we showing alternate screen 3?
  104 + # Show CPU
  105 + # Show Disk space
  106 + if (interval >= settings.DETAIL_3_INTERVAL) and (interval <= (settings.DETAIL_3_INTERVAL + settings.DETAIL_DURATION)):
  107 + #print "CPU %2d%% %2d%% %2d%% %2d%%" % (data.cpu_percent[0], data.cpu_percent[1], data.cpu_percent[2], data.cpu_percent[3])
  108 + self.matrix.matrixWrite("CPU %2d%% %sMHz" % (data.cpu_percent_avg, data.cpu_mhz))
  109 + self.matrix.matrixWrite("DSK %s/%s" % (self.sizeof_fmt(num = data.disk_space), self.sizeof_fmt(num = data.disk_size)))
  110 + return
  111 +
  112 + # Are we showing alternate screen 4?
  113 + # Show CPU
  114 + # Show RAM use
  115 + if (interval == settings.DETAIL_4_INTERVAL):
  116 + #print "CPU %2d%% %2d%% %2d%% %2d%%" % (data.cpu_percent[0], data.cpu_percent[1], data.cpu_percent[2], data.cpu_percent[3])
  117 + self.matrix.matrixWrite("CPU %2d%% %sMHz" % (data.cpu_percent_avg, data.cpu_mhz))
  118 + display_text = ""
  119 + t = "RAM %s of %s used, %s buffers." % (self.sizeof_fmt(num = data.ram_avail), self.sizeof_fmt(num = data.ram_total), (self.sizeof_fmt(num = data.ram_buffer)))
  120 + t = ' '.join(t.split())
  121 + for i in range (0, len(t)):
  122 + self.matrix.matrixClear()
  123 + self.matrix.matrixWrite("CPU %2d%% %sMHz" % (data.cpu_percent_avg, data.cpu_mhz))
  124 + display_text = t[i:(i+settings.DISPLAY_WIDTH)]
  125 + self.matrix.matrixWrite(display_text)
  126 + if i == 0:
  127 + # Pause the first time we show the beginning of the error.
  128 + time.sleep(1)
  129 + else:
  130 + # Slight gap between each character scrolling by.
  131 + time.sleep(settings.SCROLL_SPEED)
  132 + return
  133 +
  134 + # Show the normal screen
  135 + # Show CPU
  136 + # Show total network and disk IO
  137 + #print "CPU %2d%% %2d%% %2d%% %2d%%" % (data.cpu_percent[0], data.cpu_percent[1], data.cpu_percent[2], data.cpu_percent[3])
  138 + self.matrix.matrixWrite("CPU %2d%% %sMHz" % (data.cpu_percent_avg, data.cpu_mhz))
  139 + self.matrix.matrixWrite("IO %ss %ss" % (self.sizeof_fmt(num = data.stats_bandwidth["net_bytes_rw_bw"]), self.sizeof_fmt(num = data.stats_bandwidth["disk_bytes_rw_bw"])))
  140 + return
24 141  
25 142 def terminal_overview(self, data, interval):
26 143 """ SHow the main overview screen on a unix console """
... ... @@ -45,7 +162,7 @@ class Output():
45 162 e = "%s: %s" % (e_num, e)
46 163 for i in range (0, len(e)):
47 164 print "%s Errors detected!" % len(data.errors)
48   - display_text = e[i:(i+settings.DISPLAY_WIDTH-1)]
  165 + display_text = e[i:(i+settings.DISPLAY_WIDTH)]
49 166 print display_text
50 167 if i == 0:
51 168 # Pause the first time we show the beginning of the error.
... ... @@ -61,7 +178,10 @@ class Output():
61 178  
62 179 # Don't bother printing metrics if we have errors to show!
63 180 if error:
  181 + self.errors = True
64 182 return
  183 + else:
  184 + self.errors = False
65 185  
66 186 # Are we showing alternate screen 1?
67 187 # Show CPU
... ... @@ -100,7 +220,7 @@ class Output():
100 220 t = "RAM %s of %s used, %s buffers." % (self.sizeof_fmt(num = data.ram_avail), self.sizeof_fmt(num = data.ram_total), (self.sizeof_fmt(num = data.ram_buffer)))
101 221 for i in range (0, len(t)):
102 222 print "CPU %2d%% %sMHz" % (data.cpu_percent_avg, data.cpu_mhz)
103   - display_text = t[i:(i+settings.DISPLAY_WIDTH-1)]
  223 + display_text = t[i:(i+settings.DISPLAY_WIDTH)]
104 224 print display_text
105 225 if i == 0:
106 226 # Pause the first time we show the beginning of the error.
... ... @@ -138,7 +258,12 @@ class Output():
138 258 d.update(services = True)
139 259 else:
140 260 # Grab all the normal system metrics
141   - d.update()
  261 + # unless we had errors last time, so
  262 + # check for errors again.
  263 + if self.errors:
  264 + d.update(services = True)
  265 + else:
  266 + d.update()
142 267  
143 268 # Call the correct output module
144 269 if self.mode == "terminal":
... ...
run.py
... ... @@ -2,5 +2,5 @@
2 2  
3 3 from output import Output
4 4  
5   -t = Output(mode="wibble")
  5 +t = Output(mode="lcd")
6 6 t.run()
... ...
settings.py
... ... @@ -29,11 +29,13 @@ DISK_PATH = &quot;/var&quot;
29 29 # An NFS mounted directory that we should check. Set to '/' to bypass NFS check.
30 30 NFS_PATH = "/"
31 31  
32   -# Name of the primary network adapter
  32 +# Name of the primary network adapter (to determine our IP address)
33 33 ETH_DEVICE = "eth1"
  34 +# List all of the network devices we should check are up and connected
  35 +ETH_DEVICES = ["eth1"]
34 36  
35 37 # Serial display width - how many characters we can display before scrolling right to left
36   -DISPLAY_WIDTH = 19
  38 +DISPLAY_WIDTH = 16
37 39  
38 40 # Time to pause between scroll updates
39 41 SCROLL_SPEED = 0.25
... ... @@ -48,8 +50,8 @@ SERVICE_CHECKS = [&quot;ssh&quot;]
48 50 #SERVICE_CHECKS = ["ssh", "mysql", "httpd", "keystone", "nova", "cinder", "glance", "neutron"]
49 51  
50 52 # Error messages
51   -ERR_NETWORK_STATUS = "Network error, interface unconfigured."
52   -ERR_NETWORK_MEDIA = "Network error, Ethernet cable disconnected."
  53 +ERR_NETWORK_STATUS = "Network error, ETH interface unconfigured."
  54 +ERR_NETWORK_MEDIA = "Network error, ETH cable disconnected."
53 55 ERR_DISK_STATUS = "Disk error, %s is not mounted." % DISK_PATH
54 56 ERR_NFS_STATUS = "NFS error, %s is not mounted or unavailable." % NFS_PATH
55 57 ERR_SERVICE_SSH = "OpenSSH error or service not running."
... ...