monitor.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  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 urllib2 as http, base64
  14. from threading import Thread, RLock
  15. import time
  16. import numpy as np
  17. from utils.ml import ML
  18. class Analysis:
  19. def __init__(self):
  20. self.logs = []
  21. pass
  22. def post(self,object):
  23. self.logs.append(object)
  24. def init(self):
  25. d = datetime.datetime.now()
  26. self.now = {"month":d.month,"year":d.year, "day":d.day,"hour":d.hour,"minute":d.minute}
  27. def getNow(self):
  28. d = datetime.datetime.now()
  29. return {"month":d.month,"year":d.year, "day":d.day,"hour":d.hour,"minute":d.minute}
  30. def getName(self):
  31. return self.__class__.__name__
  32. """
  33. This class is designed to analyze environment variables. Environment variables can either be folders, files or simple values
  34. The class returns a quantifiable assessment of the environment variables (expected 100%)
  35. """
  36. class Env(Analysis):
  37. def __init__(self):
  38. Analysis.__init__(self)
  39. def init(self,values):
  40. #Analysis.init(self)
  41. self.values = values
  42. """
  43. This function evaluate the validity of an environment variable by returning a 1 or 0 (computable)
  44. The function will use propositional logic (https://en.wikipedia.org/wiki/Propositional_calculus)
  45. """
  46. def evaluate(self,id):
  47. if id in os.environ :
  48. #
  49. # We can inspect to make sure the environment variable is not a path or filename.
  50. # Using propositional logic we proceed as follows:
  51. # - (p) We determine if the value is an folder or file name (using regex)
  52. # - (q) In case of a file or folder we check for existance
  53. # The final result is a conjuction of p and q
  54. #
  55. value = os.environ[id]
  56. expressions = [os.sep,'(\\.\w+)$']
  57. p = sum([ re.search(xchar,value) is not None for xchar in expressions])
  58. q = os.path.exists(value)
  59. return int(p and q)
  60. else:
  61. return 0
  62. def composite (self):
  63. #Analysis.init(self)
  64. r = [ self.evaluate(id) for id in self.values] ;
  65. N = len(r)
  66. n = sum(r)
  67. value = 100 * round(n/N,2)
  68. missing = [self.values[i] for i in range(0,N) if r[i] == 0]
  69. return dict(self.getNow(),**{"value":value,"missing":missing})
  70. """
  71. This class is designed to handle analaysis of the a python virtual environment i.e deltas between requirments file and a virtualenv
  72. @TODO: update the virtual environment
  73. """
  74. class Sandbox(Analysis):
  75. def __init__(self):
  76. Analysis.__init__(self)
  77. def init(self,conf):
  78. #Analysis.init(self)
  79. if os.path.exists(conf['sandbox']) :
  80. self.sandbox_path = conf['sandbox']
  81. else:
  82. self.sandbox_path = None
  83. if os.path.exists(conf['requirements']) :
  84. self.requirements_path = conf['requirements']
  85. else:
  86. self.requirements_path = None
  87. def get_requirements (self):
  88. f = open(self.requirements_path)
  89. return [ name.replace('-',' ').replace('_',' ') for name in f.read().split('\n') if name != '']
  90. """
  91. This function will return the modules installed in the sandbox (virtual environment)
  92. """
  93. def get_sandbox_requirements(self):
  94. cmd = ['freeze']
  95. xchar = ''.join([os.sep]*2)
  96. pip_vm = ''.join([self.sandbox_path,os.sep,'bin',os.sep,'pip']).replace(xchar,os.sep)
  97. cmd = [pip_vm]+cmd
  98. r = subprocess.check_output(cmd).split('\n')
  99. return [row.replace('-',' ').replace('_',' ') for row in r if row.strip() != '']
  100. def evaluate(self):
  101. pass
  102. """
  103. This function returns the ratio of existing modules relative to the ones expected
  104. """
  105. def composite(self):
  106. Analysis.init(self)
  107. if self.sandbox_path and self.requirements_path :
  108. required_modules= self.get_requirements()
  109. sandbox_modules = self.get_sandbox_requirements()
  110. N = len(required_modules)
  111. n = len(Set(required_modules) - Set(sandbox_modules))
  112. value = round(1 - (n/N),2)*100
  113. missing = list(Set(required_modules) - Set(sandbox_modules))
  114. return dict(self.getNow(),**{"value":value,"missing":missing})
  115. else:
  116. return None
  117. """
  118. This class performs the analysis of a list of processes and determines
  119. The class provides a quantifiable measure of how many processes it found over all
  120. """
  121. class ProcessCounter(Analysis):
  122. def __init__(self):
  123. Analysis.__init__(self)
  124. def init(self,names):
  125. #Analysis.init(self)
  126. self.names = names
  127. def evaluate(self,name):
  128. cmd = "".join(['ps -eo comm |grep ',name,' |wc -l'])
  129. handler = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE)
  130. return int(handler.communicate()[0].replace("\n","") )
  131. def composite(self):
  132. #Analysis.init(self)
  133. r = {}
  134. for name in self.names :
  135. r[name] = self.evaluate(name)
  136. #N = len(r)
  137. #n = sum(r)
  138. #return n/N
  139. return dict(self.getNow(),**r)
  140. """
  141. This class returns an application's both memory and cpu usage
  142. """
  143. class DetailProcess(Analysis):
  144. def __init__(self):
  145. Analysis.__init__(self)
  146. def init (self,names):
  147. #Analysis.init(self)
  148. self.names = names;
  149. def getName(self):
  150. return "apps"
  151. def split(self,name,stream):
  152. pattern = "(\d+.{0,1}\d*)\x20*(\d+.{0,1}\d*)\x20*(\d+.{0,1}\d*)".replace(":name",name).strip()
  153. g = re.match(pattern,stream.strip())
  154. if g :
  155. return list(g.groups())+['1']+[name]
  156. else:
  157. return ''
  158. def evaluate(self,name) :
  159. cmd = "ps -eo pmem,pcpu,vsize,command|grep -E \":app\""
  160. handler = subprocess.Popen(cmd.replace(":app",name),shell=True,stdout=subprocess.PIPE)
  161. ostream = handler.communicate()[0].split('\n')
  162. #xstr = ostream
  163. ostream = [ self.split(name,row) for row in ostream if row != '' and 'grep' not in row]
  164. if len(ostream) == 0 or len(ostream[0]) < 4 :
  165. ostream = [['0','0','0','0',name]]
  166. r = []
  167. for row in ostream :
  168. #
  169. # Though the comm should only return the name as specified,
  170. # On OSX it has been observed that the fully qualified path is sometimes returned (go figure)
  171. #
  172. row = [float(value) for value in row if value.strip() != '' and name not in value ] +[re.sub('\$|^','',name)]
  173. r.append(row)
  174. #
  175. # At this point we should aggregate results
  176. # The aggregation is intended for applications with several processes (e.g: apache2)
  177. #
  178. if len(r) > 1:
  179. m = None
  180. for row in r:
  181. if m is None:
  182. m = row
  183. else:
  184. m[3] += row[3]
  185. m[0] += row[0]
  186. m[1] += row[1]
  187. m[2] += row[2]
  188. m[0] = round((m[0] / m[3]),2)
  189. m[1] = round((m[1] / m[3]),2)
  190. m[2] = round((m[2] / m[3]),2)
  191. r = [m]
  192. return r
  193. def status(self,row):
  194. x = row['memory_usage']
  195. y = row['cpu_usage']
  196. z = row['memory_available']
  197. if z :
  198. if y :
  199. return "running"
  200. return "idle"
  201. else:
  202. return "crash"
  203. def format(self,row):
  204. r= {"memory_usage":row[0],"cpu_usage":row[1],"memory_available":row[2]/1000,"proc_count":row[3],"label":row[4]}
  205. status = self.status(r)
  206. r['status'] = status
  207. return r
  208. def composite(self):
  209. ma = []
  210. now = self.getNow()
  211. for name in self.names:
  212. matrix = self.evaluate(name)
  213. ma += [ dict(now, **self.format(row)) for row in matrix]
  214. return ma
  215. """
  216. This class evaluates a list of folders and provides detailed informaiton about age/size of each file
  217. Additionally the the details are summarized in terms of global size, and oldest file.
  218. """
  219. class FileWatch(Analysis):
  220. def __init__(self):
  221. pass
  222. def init(self,folders):
  223. self.folders = folders;
  224. def getName(self):
  225. return "folders"
  226. def split(self,row):
  227. x = row.split(' ')
  228. r = {}
  229. months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']
  230. if x:
  231. BYTES_TO_MB = 1000000
  232. size = int(x[0])/BYTES_TO_MB
  233. month = months.index(x[1]) + 1
  234. day = int(x[2])
  235. age = -1
  236. hour=minute = 0
  237. if ':' in x[3] :
  238. hour,minute = x[3].split(':')
  239. now = datetime.datetime.today()
  240. if month == now.month :
  241. year = now.year
  242. else:
  243. year = now.year - 1
  244. else:
  245. year = int(x[3])
  246. hour = 0
  247. minute = 0
  248. file_date = datetime.datetime(year,month,day,int(hour),int(minute))
  249. # size = round(size,2)
  250. #file_date = datetime.datetime(year,month,day,hour,minute)
  251. now = datetime.datetime.now()
  252. age = (now - file_date ).days
  253. return {"size":size,"age":age}
  254. return None
  255. def evaluate(self,path):
  256. cmd = "find :path -print0|xargs -0 ls -ls |awk '{print $6,$7,$8,$9,$10}'".replace(":path",path)
  257. handler = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE)
  258. ostream = handler.communicate()[0].split('\n')
  259. #return [self.split(stream) for stream in ostream if stream.strip() != '' and '.DS_Store' not in stream and 'total' not in stream]
  260. return [self.split(stream) for stream in ostream if path not in stream and not set(['','total','.DS_Store']) & set(stream.split(' '))]
  261. def composite(self):
  262. d = [] #-- vector of details (age,size)
  263. now = datetime.datetime.today()
  264. for folder in self.folders:
  265. if os.path.exists(folder):
  266. xo_raw = self.evaluate(folder)
  267. xo = np.array(ML.Extract(['size','age'],xo_raw))
  268. if len(xo) == 0:
  269. continue
  270. name = re.findall("([a-z,A-Z,0-9]+)",folder)
  271. name = folder.split(os.sep)
  272. if len(name) == 1:
  273. name = [folder]
  274. else:
  275. i = len(name) -1
  276. name = [name[i-1]+' '+name[i]]
  277. name = name[0]
  278. size = round(np.sum(xo[:,0]),2)
  279. if size > 1000 :
  280. size = round(size/1000,2)
  281. units = ' GB'
  282. elif size > 1000000:
  283. size = round(size/1000000,2)
  284. units = ' TB'
  285. else:
  286. size = size
  287. units = ' MB'
  288. size = str(size)+ units
  289. age = round(np.mean(xo[:,1]),2)
  290. if age > 30 and age <= 365 :
  291. age = round(age/30,2)
  292. units = ' Months'
  293. elif age > 365 :
  294. age = round(age/365,2)
  295. units = ' Years'
  296. else:
  297. age = age
  298. units = ' Days'
  299. age = str(age)+units
  300. N = len(xo[:,1])
  301. xo = {"label":folder} #,"details":xo_raw,"summary":{"size":size,"age":age,"count":len(xo[:,1])}}
  302. xo = dict(xo,**{"size":size,"age":age,"count":N})
  303. xo["name"] = name
  304. xo['day'] = now.day
  305. xo['month'] = now.month
  306. xo['year'] = now.year
  307. xo['date'] = time.mktime(now.timetuple())
  308. d.append(xo)
  309. return d
  310. # class Monitor (Thread):
  311. # def __init__(self,pConfig,pWriter,id='processes') :
  312. # Thread.__init__(self)
  313. # self.config = pConfig[id]
  314. # self.writer = pWriter;
  315. # self.logs = []
  316. # self.handler = self.config['class']
  317. # self.mconfig = self.config['config']
  318. # def stop(self):
  319. # self.keep_running = False
  320. # def run(self):
  321. # r = {}
  322. # self.keep_running = True
  323. # lock = RLock()
  324. # while self.keep_running:
  325. # lock.acquire()
  326. # for label in self.mconfig:
  327. # self.handler.init(self.mconfig[label])
  328. # r = self.handler.composite()
  329. # self.writer.write(label=label,row = r)
  330. # time.sleep(2)
  331. # lock.release()
  332. # self.prune()
  333. # TIME_LAPSE = 60*2
  334. # time.sleep(TIME_LAPSE)
  335. # print "Stopped ..."
  336. # def prune(self) :
  337. # MAX_ENTRIES = 100
  338. # if len(self.logs) > MAX_ENTRIES :
  339. # BEG = len(self.logs) - MAX_SIZE -1
  340. # self.logs = self.logs[BEG:]