monitor.py 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. """
  2. This program is designed to inspect an application environment
  3. This program should only be run on unix friendly systems
  4. We enable the engines to be able to run a several configurations
  5. Similarly to what a visitor design-pattern would do
  6. """
  7. from __future__ import division
  8. import os
  9. import subprocess
  10. from sets import Set
  11. import re
  12. import datetime
  13. import Queue
  14. from threading import Thread, RLock
  15. import time
  16. class Analysis:
  17. def __init__(self):
  18. self.logs = []
  19. pass
  20. def post(self,object):
  21. self.logs.append(object)
  22. def init(self):
  23. d = datetime.datetime.now()
  24. self.now = {"month":d.month,"year":d.year, "day":d.day,"hour":d.hour,"minute":d.minute}
  25. def getNow(self):
  26. d = datetime.datetime.now()
  27. return {"month":d.month,"year":d.year, "day":d.day,"hour":d.hour,"minute":d.minute}
  28. """
  29. This class is designed to analyze environment variables. Environment variables can either be folders, files or simple values
  30. The class returns a quantifiable assessment of the environment variables (expected 100%)
  31. """
  32. class Env(Analysis):
  33. def __init__(self):
  34. Analysis.__init__(self)
  35. def init(self,values):
  36. #Analysis.init(self)
  37. self.values = values
  38. """
  39. This function evaluate the validity of an environment variable by returning a 1 or 0 (computable)
  40. The function will use propositional logic (https://en.wikipedia.org/wiki/Propositional_calculus)
  41. """
  42. def evaluate(self,id):
  43. if id in os.environ :
  44. #
  45. # We can inspect to make sure the environment variable is not a path or filename.
  46. # Using propositional logic we proceed as follows:
  47. # - (p) We determine if the value is an folder or file name (using regex)
  48. # - (q) In case of a file or folder we check for existance
  49. # The final result is a conjuction of p and q
  50. #
  51. value = os.environ[id]
  52. expressions = [os.sep,'(\\.\w+)$']
  53. p = sum([ re.search(xchar,value) is not None for xchar in expressions])
  54. q = os.path.exists(value)
  55. return int(p and q)
  56. else:
  57. return 0
  58. def composite (self):
  59. #Analysis.init(self)
  60. r = [ self.evaluate(id) for id in self.values] ;
  61. N = len(r)
  62. n = sum(r)
  63. value = 100 * round(n/N,2)
  64. print '*** ',value
  65. missing = [self.values[i] for i in range(0,N) if r[i] == 0]
  66. return dict(self.getNow(),**{"value":value,"missing":missing})
  67. class Sandbox(Analysis):
  68. def __init__(self):
  69. Analysis.__init__(self)
  70. def init(self,conf):
  71. #Analysis.init(self)
  72. self.sandbox_path = conf['sandbox']
  73. self.requirements_path = conf['requirements']
  74. def get_requirements (self):
  75. f = open(self.requirements_path)
  76. return [ name.replace('-',' ').replace('_',' ') for name in f.read().split('\n') if name != '']
  77. """
  78. This function will return the modules installed in the sandbox (virtual environment)
  79. """
  80. def get_sandbox_requirements(self):
  81. cmd = ['freeze']
  82. xchar = ''.join([os.sep]*2)
  83. pip_vm = ''.join([self.sandbox_path,os.sep,'bin',os.sep,'pip']).replace(xchar,os.sep)
  84. cmd = [pip_vm]+cmd
  85. r = subprocess.check_output(cmd).split('\n')
  86. return [row.replace('-',' ').replace('_',' ') for row in r if row.strip() != '']
  87. def evaluate(self):
  88. pass
  89. """
  90. This function returns the ratio of existing modules relative to the ones expected
  91. """
  92. def composite(self):
  93. Analysis.init(self)
  94. required_modules= self.get_requirements()
  95. sandbox_modules = self.get_sandbox_requirements()
  96. N = len(required_modules)
  97. n = len(Set(required_modules) - Set(sandbox_modules))
  98. value = round(1 - (n/N),2)*100
  99. missing = list(Set(required_modules) - Set(sandbox_modules))
  100. return dict(self.getNow(),**{"value":value,"missing":missing})
  101. """
  102. This class performs the analysis of a list of processes and determines
  103. The class provides a quantifiable measure of how many processes it found over all
  104. """
  105. class ProcessCounter(Analysis):
  106. def __init__(self):
  107. Analysis.__init__(self)
  108. def init(self,names):
  109. #Analysis.init(self)
  110. self.names = names
  111. def evaluate(self,name):
  112. cmd = "".join(['ps -eo comm |grep ',name,' |wc -l'])
  113. handler = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE)
  114. return int(handler.communicate()[0].replace("\n","") )
  115. def composite(self):
  116. #Analysis.init(self)
  117. r = {}
  118. for name in self.names :
  119. r[name] = self.evaluate(name)
  120. #N = len(r)
  121. #n = sum(r)
  122. #return n/N
  123. return dict(self.getNow(),**r)
  124. """
  125. This class returns an application's both memory and cpu usage
  126. """
  127. class DetailProcess(Analysis):
  128. def __init__(self):
  129. Analysis.__init__(self)
  130. def init (self,names):
  131. #Analysis.init(self)
  132. self.names = names;
  133. def split(self,name,stream):
  134. pattern = "(\d+.{0,1}\d*)\x20*(\d+.{0,1}\d*)\x20*(\d+.{0,1}\d*)".replace(":name",name).strip()
  135. g = re.match(pattern,stream.strip())
  136. if g :
  137. return list(g.groups())+['1']+[name]
  138. else:
  139. return ''
  140. def evaluate(self,name) :
  141. cmd = "ps -eo pmem,pcpu,vsize,command|grep -E \":app\""
  142. handler = subprocess.Popen(cmd.replace(":app",name),shell=True,stdout=subprocess.PIPE)
  143. ostream = handler.communicate()[0].split('\n')
  144. #xstr = ostream
  145. ostream = [ self.split(name,row) for row in ostream if row != '' and 'grep' not in row]
  146. if len(ostream) == 0 or len(ostream[0]) < 4 :
  147. ostream = [['0','0','0','0',name]]
  148. r = []
  149. for row in ostream :
  150. #
  151. # Though the comm should only return the name as specified,
  152. # On OSX it has been observed that the fully qualified path is sometimes returned (go figure)
  153. #
  154. row = [float(value) for value in row if value.strip() != '' and name not in value ] +[re.sub('\$|^','',name)]
  155. r.append(row)
  156. #
  157. # At this point we should aggregate results
  158. # The aggregation is intended for applications with several processes (e.g: apache2)
  159. #
  160. if len(r) > 1:
  161. m = None
  162. for row in r:
  163. if m is None:
  164. m = row
  165. else:
  166. m[3] += row[3]
  167. m[0] += row[0]
  168. m[1] += row[1]
  169. m[2] += row[2]
  170. m[0] = round((m[0] / m[3]),2)
  171. m[1] = round((m[1] / m[3]),2)
  172. m[2] = round((m[2] / m[3]),2)
  173. r = [m]
  174. return r
  175. def status(self,row):
  176. x = row['memory_usage']
  177. y = row['cpu_usage']
  178. z = row['memory_available']
  179. if z :
  180. if y :
  181. return "running"
  182. return "idle"
  183. else:
  184. return "crash"
  185. def format(self,row):
  186. r= {"memory_usage":row[0],"cpu_usage":row[1],"memory_available":row[2]/1000,"proc_count":row[3],"label":row[4]}
  187. status = self.status(r)
  188. r['status'] = status
  189. return r
  190. #return dict(self.getNow(),**r)
  191. def composite(self):
  192. #Analysis.init(self)
  193. #value = self.evaluate(self.name)
  194. #row= {"memory_usage":value[0],"cpu_usage":value[1]}
  195. #return row
  196. #ma = [self.evaluate(name) for name in self.names]
  197. ma = []
  198. now = self.getNow()
  199. for name in self.names:
  200. matrix = self.evaluate(name)
  201. ma += [ dict(now, **self.format(row)) for row in matrix]
  202. #return [{"memory_usage":row[0],"cpu_usage":row[1],"memory_available":row[2]/1000,"label":row[3]} for row in ma]
  203. return ma
  204. class FileWatch(Analysis):
  205. def __init__(self,conf):
  206. pass
  207. def split(self,row):
  208. x = row.split(' ')
  209. r = {}
  210. months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']
  211. if 'K' in x[0]:
  212. size = x[0].replace('K','').replace('KB','') / 1000
  213. elif 'MB' in x[0] :
  214. size = x[0].replace('MB','')
  215. elif 'GB' in x[0] :
  216. size = x[0].replace('GB','') * 1000
  217. month = months.index(m[1]) + 1
  218. day = x[2]
  219. hour,minute = x[3].split(':')
  220. year = x[4]
  221. return {"size":size,"age":age}
  222. def evaluate(self,path):
  223. cmd = "find :path|xargs ls -lh |awk '{print $5,$6,$7,$8,$9}'".replace(":path",path)
  224. handler = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE)
  225. ostream = handler.communicate()[0].split('\n')
  226. [self.split(stream) for stream in ostream if stream.strip() != '']
  227. pass
  228. class Monitor (Thread):
  229. def __init__(self,pConfig,pWriter,id='processes') :
  230. Thread.__init__(self)
  231. self.config = pConfig[id]
  232. self.writer = pWriter;
  233. self.logs = []
  234. self.handler = self.config['class']
  235. self.mconfig = self.config['config']
  236. def stop(self):
  237. self.keep_running = False
  238. def run(self):
  239. r = {}
  240. self.keep_running = True
  241. lock = RLock()
  242. while self.keep_running:
  243. lock.acquire()
  244. for label in self.mconfig:
  245. self.handler.init(self.mconfig[label])
  246. r = self.handler.composite()
  247. self.writer.write(label=label,row = r)
  248. time.sleep(2)
  249. lock.release()
  250. self.prune()
  251. TIME_LAPSE = 60*2
  252. time.sleep(TIME_LAPSE)
  253. print "Stopped ..."
  254. def prune(self) :
  255. MAX_ENTRIES = 100
  256. if len(self.logs) > MAX_ENTRIES :
  257. BEG = len(self.logs) - MAX_SIZE -1
  258. self.logs = self.logs[BEG:]