The Assimilation Project  based on Assimilation version 1.1.7.1474836767
cma_test.py
Go to the documentation of this file.
1 # vim: smartindent tabstop=4 shiftwidth=4 expandtab number
2 #
3 #
4 # This file is part of the Assimilation Project.
5 #
6 # Copyright (C) 2011, 2012 - Alan Robertson <alanr@unix.sh>
7 #
8 # The Assimilation software is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # The Assimilation software is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the GNU General Public License
19 # along with the Assimilation Project software. If not, see http://www.gnu.org/licenses/
20 #
21 #
22 _suites = ['all', 'cma']
23 import sys
24 sys.path.insert(0, "../cma")
25 sys.path.insert(0, "..")
26 sys.path.append("/usr/local/lib/python2.7/dist-packages")
27 from py2neo import neo4j
28 
29 from frameinfo import *
30 from AssimCclasses import *
31 import gc, sys, time, collections, os, subprocess, re
32 from graphnodes import nodeconstructor, ProcessNode
33 from cmainit import CMAinit
34 from cmadb import CMAdb
35 from packetlistener import PacketListener
36 from messagedispatcher import MessageDispatcher
37 from dispatchtarget import DispatchSTARTUP, DispatchHBDEAD, DispatchJSDISCOVERY, DispatchSWDISCOVER, DispatchHBSHUTDOWN
38 from hbring import HbRing
39 from droneinfo import Drone
40 import optparse
41 from graphnodes import GraphNode
42 from monitoring import MonitorAction, LSBMonitoringRule, MonitoringRule, OCFMonitoringRule
43 from transaction import Transaction
44 from assimevent import AssimEvent
45 from cmaconfig import ConfigFile
46 from graphnodeexpression import ExpressionContext
47 import assimglib as glib # This is now our glib bindings...
48 import discoverylistener
49 from store import Store
50 
51 
52 os.environ['G_MESSAGES_DEBUG'] = 'all'
53 WorstDanglingCount = 0
54 
55 CheckForDanglingClasses = False
56 AssertOnDanglingClasses = False
57 CheckForDanglingClasses = True
58 AssertOnDanglingClasses = True
59 
60 DEBUG=True
61 DEBUG=False
62 DoAudit=True
63 doHBDEAD=True
64 BuildListOnly = False
65 SavePackets=True
66 MaxDrone=5
67 
68 
69 if BuildListOnly:
70  doHBDEAD=False
71  SavePackets=False
72  DoAudit=False
73  CheckForDanglingClasses=False
74  DEBUG=False
75 
76 
77 t1 = MaxDrone
78 if t1 < 1000: t1 = 1000
79 t2 = MaxDrone/100
80 if t2 < 10: t2 = 10
81 t3 = t2
82 
83 if not DoAudit:
84  print >> sys.stderr, 'WARNING: Audits suppressed.'
85 if not doHBDEAD:
86  print >> sys.stderr, 'WARNING: Server death tests disabled.'
87 if not CheckForDanglingClasses:
88  print >> sys.stderr, 'WARNING: Memory Leak Detection disabled.'
89 elif not AssertOnDanglingClasses:
90  print >> sys.stderr, 'WARNING: Memory Leak assertions disabled (detection still enabled).'
91 
92 
93 #gc.set_threshold(t1, t2, t3)
94 
95 AssimEvent.disable_all_observers()
96 
97 def assert_no_dangling_Cclasses(doassert=None):
98  global CheckForDanglingClasses
99  global WorstDanglingCount
100  if not CheckForDanglingClasses:
101  return
102  sys._clear_type_cache()
103  if doassert is None:
104  doassert = AssertOnDanglingClasses
105  print 'ASSERTNODANGLING SHUTDOWN CALL'
106  IOTestIO.shutdown()
107  print 'DANGLING UNINIT CALL'
108  CMAinit.uninit()
109  gc.collect() # For good measure...
111  #print >>sys.stderr, "CHECKING FOR DANGLING CLASSES (%d)..." % count
112  # Avoid cluttering the output up with redundant messages...
113  if count > WorstDanglingCount and CheckForDanglingClasses:
114  WorstDanglingCount = count
115  if doassert:
116  print >> sys.stderr, 'STARTING OBJECT DUMP'
118  print >> sys.stderr, 'OBJECT DUMP COMPLETE'
119  print 'stdout OBJECT DUMP COMPLETE'
120  raise AssertionError("Dangling C-class objects - %d still around" % count)
121  else:
122  print >> sys.stderr, ("*****ERROR: Dangling C-class objects - %d still around" % count)
123 
124 
125 class TestCase(object):
126  def assertEqual(self, a, b):
127  assert a == b
128 
129  def assertNotEqual(self, a, b):
130  assert a != b
131 
132  def assertTrue(self, a):
133  assert a is True
134 
135  def assertFalse(self, a):
136  assert a is False
137 
138  def assertRaises(self, exception, function, *args):
139  try:
140  function(*args)
141  raise Exception('Did not raise exception %s: %s(%s)', exception, function, str(args))
142  except exception as e:
143  return True
144 
145  def teardown_method(self, method):
146  print 'teardown_method CALL for %s' % str(method)
148 
149 
150 # Values to substitute into this string via '%' operator:
151 # dronedesignation (%s) MAC address byte (%02x), MAC address byte (%02x), IP address (%s)
152 netdiscoveryformat='''
153 {
154  "discovertype": "netconfig",
155  "description": "IP Network Configuration",
156  "instance": "netconfig",
157  "source": "netconfig",
158  "host": "%s",
159  "data": {
160  "eth0": {
161  "address": "00:1b:fc:1b:%02x:%02x",
162  "carrier": 1,
163  "duplex": "full",
164  "mtu": 1500,
165  "operstate": "up",
166  "speed": 1000,
167  "default_gw": true,
168  "ipaddrs": { "%s/16": {"brd":"10.20.255.255", "scope":"global", "name":"eth0"}}
169  },
170  "lo": {
171  "address": "00:00:00:00:00:00",
172  "carrier": 1,
173  "mtu": 16436,
174  "operstate": "unknown",
175  "ipaddrs": { "127.0.0.1/8": {"scope":"host"}, "::1/128": {"scope":"host"}}
176  }
177  }
178 }
179 '''
180 
181 
182 byte1 = 10
183 byte2 = 20
184 
185 def droneipaddress(hostnumber):
186  byte2 = int(hostnumber / 65536)
187  byte3 = int((hostnumber / 256) % 256)
188  byte4 = hostnumber % 256
189  return pyNetAddr([byte1,byte2,byte3,byte4],1984)
190 
191 def dronedesignation(hostnumber):
192  return 'drone%06d' % hostnumber
193 
194 def hostdiscoveryinfo(hostnumber):
195  byte3 = int(hostnumber / 256)
196  byte4 = hostnumber % 256
197  ip =droneipaddress(hostnumber)
198  ip.setport(0)
199  s = str(ip)
200  return netdiscoveryformat % (dronedesignation(hostnumber), byte3, byte4, s)
201 
202 def geninitconfig(ouraddr):
203  configinfo = ConfigFile()
204  for j in ('cmainit', 'cmaaddr', 'cmadisc', 'cmafail'):
205  configinfo[j] = ouraddr
206  configinfo['outsig'] = pySignFrame(1)
207  return configinfo.complete_config()
208 
210  def auditadrone(self, droneid):
211  designation = dronedesignation(droneid)
212  droneip = droneipaddress(droneid)
213  droneipstr = str(droneip)
214  # Did the drone get put in the Drone table?
215  drone=Drone.find(designation)
216  self.assertTrue(drone is not None)
217  # Did the drone's list of addresses get updated?
218  ipnodes = drone.get_owned_ips()
219  ipnodes = [ip for ip in ipnodes]
220  self.assertEqual(len(ipnodes), 1)
221  ipnode = ipnodes[0]
222  ipnodeaddr = pyNetAddr(ipnode.ipaddr)
223  json = drone['netconfig']
224  jsobj = pyConfigContext(init=json)
225  jsdata = jsobj['data']
226  eth0obj = jsdata['eth0']
227  eth0addrcidr = eth0obj['ipaddrs'].keys()[0]
228  eth0addrstr, cidrmask = eth0addrcidr.split('/')
229  eth0addr = pyNetAddr(eth0addrstr)
230  self.assertTrue(eth0addr == ipnodeaddr)
231 
232  # Do we know that eth0 is the default gateway?
233  self.assertEqual(eth0obj['default_gw'], True)
234 
235  # the JSON should have exactly 6 top-level keys
236  self.assertEqual(len(jsobj.keys()), 6)
237  # Was the JSON host name saved away correctly?
238  self.assertEqual(jsobj['host'], designation)
239  assert drone.get_active_nic_count() == 1
240 
241  def auditSETCONFIG(self, packetreturn, droneid, configinit):
242  toaddr = packetreturn[0]
243  sentfs = packetreturn[1]
244  droneip = droneipaddress(droneid)
245 
246  # Was it a SETCONFIG packet?
247  self.assertEqual(sentfs.get_framesettype(), FrameSetTypes.SETCONFIG)
248  # Was the SETCONFIG sent back to the drone?
249  self.assertEqual(toaddr, droneip)
250  # Lets check the number of Frames in the SETCONFIG Frameset
251  self.assertEqual(1, len(sentfs)) # Was it the right size?
252 
253  def auditaRing(self, ring):
254  'Verify that each ring has its neighbor pairs set up properly'
255  # Check that each element of the ring is connected to its neighbors...
256  print "Ring %s" % (str(ring))
257  listmembers = {}
258 
259  ringmembers = {}
260  for drone in ring.members():
261  ringmembers[drone.designation] = None
262  for drone in ring.membersfromlist():
263  listmembers[drone.designation] = None
264  for drone in listmembers.keys():
265  self.assertTrue(drone in ringmembers)
266  for drone in ringmembers.keys():
267  print >> sys.stderr, 'RINGMEMBERS: %s: members:%s' % (str(drone), listmembers)
268  self.assertTrue(drone in listmembers)
269 
270 
272  print 'DRONE1: CMADB', CMAdb
273  print 'DRONE1: CMADB.IO:', CMAdb.io
274  print 'DRONE1: CMADB.store', CMAdb.store
275  audit = AUDITS()
276  qtext = "MATCH (drone) WHERE drone.nodetype = 'Drone' RETURN drone"
277  droneobjs = CMAdb.store.load_cypher_nodes(qtext, Drone)
278  droneobjs = [drone for drone in droneobjs]
279  print 'DRONE2: CMADB', CMAdb
280  print 'DRONE2: CMADB.IO:', CMAdb.io
281  print 'DRONE2: CMADB.store', CMAdb.store
282  numdrones = len(droneobjs)
283  for droneid in range(0, numdrones):
284  droneid = int(droneobjs[droneid].designation[6:])
285  audit.auditadrone(droneid)
286  queryobjs = CMAdb.store.load_cypher_nodes('''START n=node:Drone('*:*') RETURN n''', Drone)
287  queryobjs = [drone for drone in queryobjs]
288  dronetbl = {}
289  for drone in droneobjs:
290  dronetbl[drone.designation] = drone
291  querytbl = {}
292  for drone in queryobjs:
293  querytbl[drone.designation] = drone
294  # Now compare them
295  for drone in dronetbl:
296  assert(querytbl[drone] is dronetbl[drone])
297  for drone in querytbl:
298  assert(querytbl[drone] is dronetbl[drone])
299  print 'DRONE3: CMADB', CMAdb
300  print 'DRONE3: CMADB.IO:', CMAdb.io
301  print 'DRONE3: CMADB.store', CMAdb.store
302 
303 
305  print 'AUDIT: CMADB', CMAdb
306  print 'AUDIT: CMADB.IO:', CMAdb.io
307  print 'AUDIT: CMADB.store', CMAdb.store
308 
309  if CMAdb.store is None:
310  print 'SKIPPING RING AUDIT'
311  raise ValueError('STORE IS NONE')
312  return
313  print 'PERFORMING RING AUDIT'
314  audit = AUDITS()
315  for ring in CMAdb.store.load_cypher_nodes("START n=node:HbRing('*:*') RETURN n", HbRing):
316  ring.AUDIT()
317 
318 ASSIMCLI='assimcli'
319 inityet = False
320 def assimcli_check(command, expectedcount=None):
321  'This code only works if you have assimcli installed'
322  cmd='%s %s' % (ASSIMCLI, command)
323  #print >> sys.stderr, 'RUNNING COMMAND: %s' % str(command)
324  if expectedcount is None:
325  subprocess.check_call(('sh', '-c', cmd))
326  else:
327  linecount = 0
328  fd = os.popen(cmd)
329  while True:
330  if not fd.readline():
331  break
332  linecount += 1
333  rc = fd.close()
334  if expectedcount != linecount:
335  print >> sys.stderr, 'Rerunning query [%s]:' % cmd
336  subprocess.check_call(('sh', '-c', cmd))
337  raise RuntimeError('%s command produced %s lines instead of %s'
338  % (cmd, linecount, expectedcount))
339  assert rc is None or rc == 0
340 
341 class IOTestIO:
342  '''A pyNetIOudp replacement for testing. It is given a list of packets to be 'read'
343  and in turn saves all the packets it 'writes' for us to inspect.
344  '''
345  mainloop = None
346  @staticmethod
347  def shutdown():
348  if IOTestIO.singleinstance is not None:
349  print 'CLEANING OUT SINGLEINSTANCE IO OBJECT'
350  IOTestIO.singleinstance.cleanio()
351  IOTestIO.singleinstance = None
352 
353  def __init__(self, addrframesetpairs, sleepatend=0):
354  IOTestIO.singleinstance = self
355  if isinstance(addrframesetpairs, tuple):
356  addrframesetpairs = addrframesetpairs
357  self.inframes = addrframesetpairs
359  self.packetsread=0
360  self.sleepatend=sleepatend
361  self.index=0
362  self.writecount=0
363  self.config = ConfigFile().complete_config()
364  (self.pipe_read, self.pipe_write) = os.pipe()
365  os.write(self.pipe_write, ' ')
366  os.close(self.pipe_write)
367  self.pipe_write = -1
368  self.atend = False
369  self.readfails = 0
370  self.initpackets = len(self.inframes)
371  print >> sys.stderr, 'INITPACKETS: self.initpackets'
372 
373  @staticmethod
375  if IOTestIO.mainloop is not None:
376  IOTestIO.mainloop.quit()
377  return False
378 
379  def recvframesets(self):
380  print 'RECV: CMADB', CMAdb
381  print 'RECV: CMADB.IO:', CMAdb.io
382  print 'RECV: CMADB.store', CMAdb.store
383  # Audit after each packet is processed - and once before the first packet.
384  assert CMAdb.io.config is not None
385  if DoAudit:
386  if self.packetsread < 200 or (self.packetsread % 500) == 0:
387  print 'RECV2: CMADB', CMAdb
388  print 'RECV2: CMADB.IO:', CMAdb.io
389  print 'RECV2: CMADB.store', CMAdb.store
390  CMAdb.store.commit()
391  print 'RECV3: CMADB', CMAdb
392  print 'RECV3: CMADB.IO:', CMAdb.io
393  print 'RECV3: CMADB.store', CMAdb.store
395  print 'RECV4: CMADB', CMAdb
396  print 'RECV4: CMADB.IO:', CMAdb.io
397  print 'RECV4: CMADB.store', CMAdb.store
398  auditallrings()
399  print 'RECV5: CMADB', CMAdb
400  print 'RECV5: CMADB.IO:', CMAdb.io
401  print 'RECV5: CMADB.store', CMAdb.store
402  if self.index >= len(self.inframes):
403  if not self.atend:
404  self.timeout = glib.GMainTimeout(int(self.sleepatend*1000), IOTestIO.shutdown_on_timeout, self)
405  self.atend = True
406  #self.config = None
407  else:
408  #self.mainloop.quit()
409  if self.pipe_read >= 0:
410  os.close(self.pipe_read)
411  self.pipe_read = -1
412  self.readfails += 1
413  if self.readfails > self.initpackets+4:
414  self.mainloop.quit()
415 
416  return (None, None)
417  ret = self.inframes[self.index]
418  self.index += 1
419  self.packetsread += len(ret[1])
420  print >> sys.stderr, "RET[0]: %s" % ret[0]
421  print >> sys.stderr, "RET[1][0]: %s" % ret[1][0]
422  return ret
423 
424  def sendframesets(self, dest, fslist):
425  if not isinstance(fslist, collections.Sequence):
426  return self._sendaframeset(dest, fslist)
427  for fs in fslist:
428  self._sendaframeset(dest, fs)
429 
430  def sendreliablefs(self, dest, fslist):
431  self.sendframesets(dest, fslist)
432 
433  def ackmessage(self, dest, fs):
434  pass
435 
436  def connactive(self, ioaddr, qid=0):
437  ioaddr = ioaddr
438  return True
439 
440  def closeconn(self, qid, dest):
441  pass
442 
443  def _sendaframeset(self, dest, fs):
444  self.writecount += 1
445  if SavePackets:
446  self.packetswritten.append((dest,fs))
447 
448  def cleanio(self):
449  print 'CLEANING OUT IO OBJECT'
450  if IOTestIO.mainloop is not None:
451  IOTestIO.mainloop.quit()
452  if self.pipe_read >= 0:
453  os.close(self.pipe_read)
454  self.pipe_read = -1
455  IOTestIO.mainloop = None
456  # Note that this having to do this implies that our I/O object persists
457  # longer than I would have expected...
458  # Is this because uninit needs to be done as part of the test instead of
459  # as part of the cleanup action?
460  self.inframes = []
461  self.packetswritten = 0
462  self.config = None
463  self.timeout = None
464  if CMAdb.store:
465  CMAdb.store.abort()
466  CMAdb.store.weaknoderefs = {}
467  CMAdb.store = None
468  CMAinit.uninit()
469 
470  def getmaxpktsize(self): return 60000
471  def fileno(self): return self.pipe_read
472  def bindaddr(self, addr): return True
473  def mcastjoin(self, addr): return True
474  def setblockio(self, tf): return
475 
476  def dumppackets(self):
477  print >>sys.stderr, 'Sent %d packets' % len(self.packetswritten)
478  for packet in self.packetswritten:
479  print 'PACKET: %s (%s)' % (packet[0], packet[1])
480 
481 class FakeDrone(dict):
482  def __init__(self, json):
483  self['_init_monitoringagents'] = json
484 
485  def get(self, name, ret):
486  return ret
487 
488 
489 
491  def test_eof(self):
492  'Get EOF with empty input'
493  if BuildListOnly: return
494  if DEBUG:
495  print >> sys.stderr, 'Running test_test_eof()'
496  AssimEvent.disable_all_observers()
497  framesets=[]
498  io = IOTestIO(framesets, 0)
499  CMAinit(io, cleanoutdb=True, debug=DEBUG)
500  print 'IO:', io
501  print 'CMADB', CMAdb
502  print 'CMADB.store', CMAdb.store
503  # just make sure it seems to do the right thing
504  (foo, bar) = io.recvframesets()
505  assert foo is None
506  #assert_no_dangling_Cclasses()
507 
508  def test_get1pkt(self):
509  'Read a single packet'
510  if BuildListOnly: return
511  if DEBUG:
512  print >> sys.stderr, 'Running test_test_eof()'
513  AssimEvent.disable_all_observers()
514  otherguy = pyNetAddr([1,2,3,4],)
515  strframe1=pyCstringFrame(FrameTypes.CSTRINGVAL, "Hello, world.")
516  fs = pyFrameSet(42)
517  fs.append(strframe1)
518  framesets=((otherguy, (strframe1,)),)
519  io = IOTestIO(framesets, 0)
520  CMAinit(io, cleanoutdb=True, debug=DEBUG)
521  gottenfs = io.recvframesets()
522  self.assertEqual(len(gottenfs), 2)
523  self.assertEqual(gottenfs, framesets[0])
524  gottenfs = io.recvframesets()
525  self.assertEqual(len(gottenfs), 2)
526  assert gottenfs[0] is None
527  #assert_no_dangling_Cclasses()
528 
529  def test_echo1pkt(self):
530  'Read a packet and write it back out'
531  if BuildListOnly: return
532  if DEBUG:
533  print >> sys.stderr, 'Running test_echo1pkt()'
534  AssimEvent.disable_all_observers()
535  strframe1=pyCstringFrame(FrameTypes.CSTRINGVAL, "Hello, world.")
536  fs = pyFrameSet(42)
537  fs.append(strframe1)
538  otherguy = pyNetAddr([1,2,3,4],)
539  framesets=((otherguy, (strframe1,)),)
540  io = IOTestIO(framesets, 0)
541  CMAinit(io, cleanoutdb=True, debug=DEBUG)
542  fslist = io.recvframesets() # read in a packet
543  self.assertEqual(len(fslist), 2)
544  self.assertEqual(fslist, framesets[0])
545  io.sendframesets(fslist[0], fslist[1]) # echo it back out
546  self.assertEqual(len(io.packetswritten), len(framesets))
547  gottenfs = io.recvframesets()
548  self.assertEqual(len(gottenfs), 2)
549  assert gottenfs[0] is None
550  #assert_no_dangling_Cclasses()
551 
553  OS_DISCOVERY = '''{
554  "discovertype": "os",
555  "description": "OS information",
556  "host": "drone000001",
557  "instance": "os",
558  "source": "../discovery_agents/os",
559  "proxy": "local/local",
560  "data": {
561  "nodename": "drone000001",
562  "operating-system": "GNU/Linux",
563  "machine": "x86_64",
564  "processor": "x86_64",
565  "hardware-platform": "x86_64",
566  "kernel-name": "Linux",
567  "kernel-release": "3.19.0-31-generic",
568  "kernel-version": "#36-Ubuntu SMP Wed Oct 7 15:04:02 UTC 2015",
569  "Distributor ID": "Ubuntu",
570  "Description": "Ubuntu 15.04",
571  "Release": "15.04",
572  "Codename": "vivid"
573  }
574 }'''
575  ULIMIT_DISCOVERY= '''{
576  "discovertype": "ulimit",
577  "description": "ulimit values for root",
578  "host": "drone000001",
579  "instance": "ulimit",
580  "source": "../discovery_agents/ulimit",
581  "proxy": "local/local",
582  "data": {
583  "hard": {"c":null,"d":null,"f":null,"l":null,"m":null,"n":65536,"p":63557,"s":null,"t":null,"v":null},
584  "soft": {"c":0,"d":null,"f":null,"l":null,"m":null,"n":1024,"p":63557,"s":8192,"t":null,"v":null}
585  }
586 }'''
587  DRAWING_PRODUCER = 'drawwithdot'
588 
589  def check_discovery(self, drone, expectedjson):
590  'We check to see if the discovery JSON object thingy is working...'
591  disctypes = []
592 
593  for json in expectedjson:
594  jsobj = pyConfigContext(json)
595  dtype = jsobj['instance']
596  # Compare hash sums - without retrieving the big string from Neo4j
597  self.assertTrue(drone.json_eq(dtype, json))
598  # Fetch string from the database and compare for string equality
599  self.assertEqual(str(pyConfigContext(json)), str(drone[dtype]))
600  disctypes.append(dtype)
601 
602  disctypes.sort()
603  dronekeys = drone.keys()
604  dronekeys.sort()
605  self.assertEqual(dronekeys, disctypes)
606 
607 
608 
609  def test_startup(self):
610  '''A semi-interesting test: We send a STARTUP message and get back a
611  SETCONFIG message with lots of good stuff in it.
612  and for good measure, we also send along some discovery packets.
613  '''
614  if BuildListOnly: return
615  if DEBUG:
616  print >> sys.stderr, 'Running test_startup()'
617  CMAdb.debug = True
618  AssimEvent.disable_all_observers()
619  droneid = 1
620  droneip = droneipaddress(droneid)
621  designation = dronedesignation(droneid)
622  designationframe=pyCstringFrame(FrameTypes.HOSTNAME, designation)
623  dronediscovery=hostdiscoveryinfo(droneid)
624  discoveryframe=pyCstringFrame(FrameTypes.JSDISCOVER, dronediscovery)
625  fs = pyFrameSet(FrameSetTypes.STARTUP)
626  fs.append(designationframe)
627  fs.append(discoveryframe)
628 
629  fs2 = pyFrameSet(FrameSetTypes.JSDISCOVERY)
630  osdiscovery=pyCstringFrame(FrameTypes.JSDISCOVER, self.OS_DISCOVERY)
631  fs2.append(osdiscovery)
632  fs3 = pyFrameSet(FrameSetTypes.JSDISCOVERY)
633  ulimitdiscovery=pyCstringFrame(FrameTypes.JSDISCOVER, self.ULIMIT_DISCOVERY)
634  fs3.append(ulimitdiscovery)
635  fsin = ((droneip, (fs,)), (droneip, (fs2,)), (droneip, (fs3,)))
636  io = IOTestIO(fsin,0)
637  #print >> sys.stderr, 'CMAinit: %s' % str(CMAinit)
638  #print >> sys.stderr, 'CMAinit.__init__: %s' % str(CMAinit.__init__)
639  OurAddr = pyNetAddr((127,0,0,1),1984)
640  configinit = geninitconfig(OurAddr)
641  config = pyConfigContext(init=configinit)
642  io.config = config
643  CMAinit(io, cleanoutdb=True, debug=DEBUG)
644  CMAdb.io.config = config
645  assimcli_check('loadqueries')
646  from dispatchtarget import DispatchTarget
647  disp = MessageDispatcher(DispatchTarget.dispatchtable, encryption_required=False)
648  listener = PacketListener(config, disp, io=io, encryption_required=False)
649  io.mainloop = listener.mainloop
650  IOTestIO.mainloop = listener.mainloop
651  # We send the CMA an intial STARTUP packet
652  listener.listen()
653  # Let's see what happened...
654  print >> sys.stderr, ('READ: %s' % io.packetsread)
655  print >> sys.stderr, ('WRITTEN: %s' % len(io.packetswritten))
656  print >> sys.stderr, ('PACKETS WRITTEN: %s' % str(io.packetswritten))
657 
658  self.assertEqual(len(io.packetswritten), 2) # Did we send out two packets?
659  # Note that this change over time
660  # As we change discovery...
661  self.assertEqual(io.packetsread, 3) # Did we read 3 packets?
662  AUDITS().auditSETCONFIG(io.packetswritten[0], droneid, configinit)
663  assimcli_check("query allips", 1)
664  assimcli_check("query allservers", 1)
665  assimcli_check("query findip %s" % str(droneip), 1)
666  assimcli_check("query shutdown", 0)
667  assimcli_check("query crashed", 0)
668  assimcli_check("query unknownips", 0)
669  CMAdb.io.config = config
670  Drones = CMAdb.store.load_cypher_nodes("START n=node:Drone('*:*') RETURN n", Drone)
671  Drones = [drone for drone in Drones]
672  for drone in Drones:
673  self.check_discovery(drone, (dronediscovery, self.OS_DISCOVERY, self.ULIMIT_DISCOVERY))
674  self.assertEqual(len(Drones), 1) # Should only be one drone
675  io.config = None
676  del ulimitdiscovery, osdiscovery, Drones, disp, listener
677  DispatchTarget.dispatchtable = {}
678  del DispatchTarget
679  #assert_no_dangling_Cclasses()
680 
681  def check_live_counts(self, expectedlivecount, expectedpartnercount, expectedringmembercount):
682  Drones = CMAdb.store.load_cypher_nodes(query, Drone)
683  Drones = [drone for drone in Drones]
684  partnercount = 0
685  livecount = 0
686  ringcount = 0
687  for drone1 in Drones:
688  if drone1.status != 'dead': livecount += 1
689  for partner in CMAdb.store.load_related(drone1, CMAdb.TheOneRing.ournexttype, Drone):
690  partnercount += 1
691  for partner in CMAdb.store.load_in_related(drone1, CMAdb.TheOneRing.ournexttype, Drone):
692  partnercount += 1
693  for ring in CMAdb.store.load_in_related(drone1, CMAdb.TheOneRing.ourreltype, HbRing):
694  ringcount += 1
695  print >> sys.stderr, 'PARTNERCOUNT: (%s, %s)' % (partnercount, expectedpartnercount)
696  print >> sys.stderr, 'LIVECOUNT: (%s, %s)' % (livecount, expectedlivecount)
697  print >> sys.stderr, 'RINGCOUNT: (%s, %s)' % (ringcount, expectedringmembercount)
698  self.assertEqual(partnercount, expectedpartnercount)
699  self.assertEqual(livecount, expectedlivecount)
700  self.assertEqual(ringcount, expectedringmembercount)
701 
702  def construct_and_verify_diagram(self, diagramtype, patterncounts):
703  'Construct a "drawithdot" diagram and then validate it'
704  #print >> sys.stderr, 'PROCESSING DIAGRAM TYPE: %s' % diagramtype
705  dot = subprocess.Popen((self.DRAWING_PRODUCER, diagramtype), stdout=subprocess.PIPE, shell=True)
706  foundcounts = {}
707  for line in dot.stdout.readlines():
708  #print >> sys.stderr, 'GOT: %s' % line.strip()
709  for pattern in patterncounts:
710  if re.search(pattern, line) is not None:
711  if pattern not in foundcounts:
712  foundcounts[pattern] = 0
713  foundcounts[pattern] += 1
714  dot.stdout.close()
715 
716  errcount = 0
717  for pattern in patterncounts:
718  found = foundcounts[pattern] if pattern in foundcounts else 0
719  if found != patterncounts[pattern]:
720  print >> sys.stderr, ('Expecting %d matches of %s. Found %d instead.'
721  % (patterncounts[pattern], pattern, found))
722  errcount += 1
723  self.assertEqual(errcount, 0)
724 
725  def diagram_patterns(self, diagramtype, nodecount):
726  upnode='shape=house color=green penwidth=3.*label="%s'
727  downnode='shape=house style="filled,dashed" fillcolor=gray90 .*label="%s'
728  anynode='shape=house'
729  ringnext='node_.*->node_.* \[label=RingNext_The_One_Ring\]'
730 
731  pats = {
732  'node_.*->node_.* label=nicowner': nodecount*2,
733  '\[shape=octagon color=navy label="00-00-00-00-00-00': 1,
734  # Is having only one loopback NIC a bug?? I think maybe so!
735  # Some Python versions have this wrong...
736  #'\[shape=octagon color=navy label="00-1b-fc-.*ASUSTek COMPUTER INC\.': nodecount,
737  '\[shape=octagon color=navy label="00-1b-fc-': nodecount,
738 
739  'node_.*->node_.* label=ipowner': nodecount,
740  }
741  pats[upnode % dronedesignation(1)] = 1
742  if doHBDEAD:
743  for node in range(2, nodecount+1):
744  pats[downnode % dronedesignation(node)] = 1
745  pats[ringnext] = 0
746  else:
747  for node in range(2, nodecount+1):
748  pats[upnode % dronedesignation(node)] = 1
749  pats[ringnext] = nodecount if nodecount > 2 else 1
750  return pats
751 
752 
753  # Drone and Ring tables are automatically audited after each packet
755  '''A very interesting test: We send a STARTUP message and get back a
756  SETCONFIG message and then send back a bunch of discovery requests.'''
757  #if Store.debug:
758  # raise ValueError('Debug enabled')
759  if DEBUG:
760  print >> sys.stderr, 'Running test_several_startups()'
761  AssimEvent.disable_all_observers()
762  OurAddr = pyNetAddr((10,10,10,5), 1984)
763  configinit = geninitconfig(OurAddr)
764  # Create the STARTUP FrameSets that our fake Drones should appear to send
765  fsin = []
766  droneid=0
767  for droneid in range(1,MaxDrone+1):
768  droneip = droneipaddress(droneid)
769  designation = dronedesignation(droneid)
770  designationframe=pyCstringFrame(FrameTypes.HOSTNAME, designation)
771  dronediscovery=hostdiscoveryinfo(droneid)
772  discoveryframe=pyCstringFrame(FrameTypes.JSDISCOVER, dronediscovery)
773  fs = pyFrameSet(FrameSetTypes.STARTUP)
774  fs.append(designationframe)
775  fs.append(discoveryframe)
776  fsin.append((droneip, (fs,)))
777  addrone = droneipaddress(1)
778  maxdrones = droneid
779  if doHBDEAD:
780  # Create the HBDEAD FrameSets that our first fake Drone should appear to send
781  # concerning the death of its dearly departed peers
782  #print >> sys.stderr, 'KILLING THEM ALL!!!'
783  for droneid in range(2,maxdrones+1):
784  droneip = droneipaddress(droneid)
785  designation = dronedesignation(droneid)
786  #deadframe=pyIpPortFrame(FrameTypes.IPPORT, addrstring=droneip)
787  fs = pyFrameSet(FrameSetTypes.HBSHUTDOWN)
788  #fs.append(deadframe)
789  hostframe=pyCstringFrame(FrameTypes.HOSTNAME, designation)
790  fs.append(hostframe)
791  fsin.append((droneip, (fs,)))
792  io = IOTestIO(fsin)
793  CMAinit(io, cleanoutdb=True, debug=DEBUG)
794  assert CMAdb.io.config is not None
795  assimcli_check('loadqueries')
796  disp = MessageDispatcher( {
797  FrameSetTypes.STARTUP: DispatchSTARTUP(),
798  FrameSetTypes.HBDEAD: DispatchHBDEAD(),
799  FrameSetTypes.HBSHUTDOWN: DispatchHBSHUTDOWN(),
800  }, encryption_required=False)
801  config = pyConfigContext(init=configinit)
802  listener = PacketListener(config, disp, io=io, encryption_required=False)
803  io.mainloop = listener.mainloop
804  IOTestIO.mainloop = listener.mainloop
805  # We send the CMA a BUNCH of intial STARTUP packets
806  # and (optionally) a bunch of HBDEAD packets
807  assert CMAdb.io.config is not None
808  listener.listen()
809  # We audit after each packet is processed
810  # The auditing code will make sure all is well...
811  # But it doesn't know how many drones we just registered
812  Drones = CMAdb.store.load_cypher_nodes("START n=node:Drone('*:*') RETURN n", Drone)
813  Drones = [drone for drone in Drones]
814  #print >> sys.stderr, 'WE NOW HAVE THESE DRONES:', Drones
815  self.assertEqual(len(Drones), maxdrones)
816  if doHBDEAD:
817  # Verify that all drones except one are dead
818  #livecount, partnercount, ringmemberships
819  #self.check_live_counts(1, 0, 1)
820  assimcli_check("query allservers", maxdrones)
821  assimcli_check("query down", maxdrones-1)
822  assimcli_check("query crashed", 0)
823  assimcli_check("query shutdown", maxdrones-1)
824  else:
825  if maxdrones == 1:
826  partnercount=0
827  elif maxdrones == 2:
828  partnercount = 2
829  else:
830  partnercount=2*maxdrones
831  # livecount partnercount ringmemberships
832  #self.check_live_counts(maxdrones, partnercount, maxdrones)
833  assimcli_check("query allservers", maxdrones)
834  assimcli_check("query down", 0)
835  assimcli_check("query shutdown", 0)
836  assimcli_check("query unknownips", 0)
837  for droneid in range(1,MaxDrone+1):
838  droneip = droneipaddress(droneid)
839  assimcli_check("query findip %s" % str(droneip), 1)
840  if DoAudit:
842  auditallrings()
843  for dtype in ('monitoring', 'network', 'service', 'monring', 'everything'):
844  self.construct_and_verify_diagram(dtype, self.diagram_patterns(dtype, MaxDrone))
845 
846  if DEBUG:
847  print "The CMA read %d packets." % io.packetsread
848  print "The CMA wrote %d packets." % io.writecount
849  #io.dumppackets()
850  #assert_no_dangling_Cclasses()
851 
852 
854  def test_activate(self):
855  AssimEvent.disable_all_observers()
856  io = IOTestIO([],0)
857  CMAinit(io, cleanoutdb=True, debug=DEBUG)
858  dummy = CMAdb.store.load_or_create(MonitorAction, domain='global', monitorname='DummyName'
859  , monitorclass='OCF', monitortype='Dummy', interval=1, timeout=120, provider='heartbeat')
860 
861  self.assertEqual(len(CMAdb.transaction.tree['packets']), 0)
862  CMAdb.store.commit()
863  CMAdb.transaction.commit_trans(io)
864  self.assertEqual(len(io.packetswritten), 0) # Shouldn't have sent out any pkts yet...
865  CMAdb.transaction = Transaction(encryption_required=False)
866 
867  droneid = 1
868  droneip = droneipaddress(droneid)
869  designation = dronedesignation(droneid)
870  droneAddr = pyNetAddr((127,0,0,1),1984)
871  droneone = CMAdb.store.load_or_create(Drone, designation=designation, port=1984
872  , startaddr=droneip, primary_ip_addr=droneip)
873  self.assertTrue(not dummy.isactive)
874  dummy.activate(droneone)
875  CMAdb.store.commit()
876  count=0
877  for obj in CMAdb.store.load_related(droneone, CMAconsts.REL_hosting, MonitorAction):
878  self.assertTrue(obj is dummy)
879  count += 1
880  self.assertEqual(count, 1)
881  self.assertTrue(dummy.isactive)
882  count=0
883  for obj in CMAdb.store.load_related(dummy, CMAconsts.REL_monitoring, Drone):
884  self.assertTrue(obj is droneone)
885  count += 1
886  self.assertEqual(count, 1)
887 
888 #worked if we returned at or before here
889  CMAdb.transaction.commit_trans(io)
890 #failed if we return here or later
891  self.assertEqual(len(io.packetswritten), 1) # Did we send out exactly one packet?
892  if SavePackets:
893  #io.dumppackets()
894  for fstuple in io.packetswritten:
895  (dest, frameset) = fstuple
896  self.assertEqual(frameset.get_framesettype(), FrameSetTypes.DORSCOP)
897  for frame in frameset.iter():
898  self.assertEqual(frame.frametype(), FrameTypes.RSCJSON)
899  table = pyConfigContext(init=frame.getstr())
900  for field in ('class', 'type', 'instance', 'repeat'):
901  self.assertTrue(field in table)
902  if field == 'monitorclass' and table['monitorclass'] == 'OCF':
903  self.assertTrue('provider' in table)
904  for tup in (('class', str), ('type', str), ('resourcename', str)
905  , ('monitorclass', str), ('provider', str)
906  , ('repeat_interval', (int, long))
907  , ('timeout', (int,long))):
908  (n, t) = tup
909  if n in table:
910  self.assertTrue(isinstance(table[n], t))
911 
912  # TODO: Add test for deactivating the resource(s)
913  #assert_no_dangling_Cclasses()
914 
916  AssimEvent.disable_all_observers()
917  drone = FakeDrone({
918  'data': {
919  'lsb': {
920  'ssh',
921  'neo4j-service',
922  }
923  }
924  })
925  neoargs = (
926  ('$argv[0]', r'.*/[^/]*java[^/]*$'), # Might be overkill
927  ('$argv[3]', r'-server$'), # Probably overkill
928  ('$argv[-1]', r'org\.neo4j\.server\.Bootstrapper$'),
929  )
930  neorule = LSBMonitoringRule('neo4j-service', neoargs)
931 
932  sshnode = ProcessNode('global', 'foofred', 'fred', '/usr/bin/sshd', ['/usr/bin/sshd', '-D' ]
933  #ProcessNode:
934  # (domain, host, nodename, pathname, argv, uid, gid, cwd, roles=None):
935  , 'root', 'root', '/', roles=(CMAconsts.ROLE_server,))
936 
937  sshargs = (
938  # This means one of our nodes should have a value called
939  # pathname, and it should end in '/sshd'
940  ('@basename()', 'sshd$'),
941  )
942  sshrule = LSBMonitoringRule('ssh', sshargs)
943 
944  udevnode = ProcessNode('global', 'foofred', 'fred', '/usr/bin/udevd', ['/usr/bin/udevd']
945  , 'root', 'root', '/', roles=(CMAconsts.ROLE_server,))
946 
947 
948  neoprocargs = ("/usr/bin/java", "-cp"
949  , "/var/lib/neo4j/lib/concurrentlinkedhashmap-lru-1.3.1.jar:"
950  "AND SO ON:"
951  "/var/lib/neo4j/system/lib/slf4j-api-1.6.2.jar:"
952  "/var/lib/neo4j/conf/", "-server", "-XX:"
953  "+DisableExplicitGC"
954  , "-Dorg.neo4j.server.properties=conf/neo4j-server.properties"
955  , "-Djava.util.logging.config.file=conf/logging.properties"
956  , "-Dlog4j.configuration=file:conf/log4j.properties"
957  , "-XX:+UseConcMarkSweepGC"
958  , "-XX:+CMSClassUnloadingEnabled"
959  , "-Dneo4j.home=/var/lib/neo4j"
960  , "-Dneo4j.instance=/var/lib/neo4j"
961  , "-Dfile.encoding=UTF-8"
962  , "org.neo4j.server.Bootstrapper")
963 
964  neonode = ProcessNode('global', 'foofred', 'fred', '/usr/bin/java', neoprocargs
965  , 'root', 'root', '/', roles=(CMAconsts.ROLE_server,))
966 
967  for tup in (sshrule.specmatch(ExpressionContext((udevnode, drone)))
968  , sshrule.specmatch(ExpressionContext((neonode, drone)))
969  , neorule.specmatch(ExpressionContext((sshnode, drone)))):
970  (prio, table) = tup
971  self.assertEqual(prio, MonitoringRule.NOMATCH)
972  self.assertTrue(table is None)
973 
974  (prio, table) = sshrule.specmatch(ExpressionContext((sshnode, drone)))
975  self.assertEqual(prio, MonitoringRule.LOWPRIOMATCH)
976  self.assertEqual(table['monitorclass'], 'lsb')
977  self.assertEqual(table['monitortype'], 'ssh')
978 
979  (prio, table) = neorule.specmatch(ExpressionContext((neonode, drone)))
980  self.assertEqual(prio, MonitoringRule.LOWPRIOMATCH)
981  self.assertEqual(table['monitorclass'], 'lsb')
982  self.assertEqual(table['monitortype'], 'neo4j-service')
983 
985  AssimEvent.disable_all_observers()
986  self.assertRaises(ValueError, LSBMonitoringRule, 'neo4j-service', [])
987  self.assertRaises(ValueError, LSBMonitoringRule, 'neo4j-service',
988  (('a.b.c', ')'),))
989  self.assertRaises(ValueError, LSBMonitoringRule, 'neo4j-service',
990  ((1,2,3,4,5),))
991  self.assertRaises(ValueError, LSBMonitoringRule, 'neo4j-service',
992  ((1,),))
993  self.assertRaises(ValueError, LSBMonitoringRule, 'neo4j-service',
994  ((),))
995  #assert_no_dangling_Cclasses()
996 
997 
999  # @TODO What I have in mind for this test is that it
1000  # actually construct an auto-generated LSB monitoring node and activate it
1001  # It will have to add name, timeout and repeat intervals before activating it.
1002  pass
1003 
1005  AssimEvent.disable_all_observers()
1006  self.assertRaises(ValueError, OCFMonitoringRule, 'assimilation', 'neo4j',
1007  ((1,2,3,4,5),))
1008  self.assertRaises(ValueError, OCFMonitoringRule, 'assimilation', 'neo4j',
1009  ((),))
1010  #assert_no_dangling_Cclasses()
1011 
1013  AssimEvent.disable_all_observers()
1014  drone = FakeDrone({
1015  'data': {
1016  'ocf': {
1017  'assimilation/neo4j',
1018  }
1019  }
1020  })
1021  kitchensink = OCFMonitoringRule('assimilation', 'neo4j',
1022  ( ('cantguess',) # length 1 - name
1023  , ('port', '$port') # length 2 - name, expression
1024  , (None, '$port') # length 2 - name, expression
1025  , ('-', '$pathname') # length 2 - name, expression
1026  , ('port', '$port', '[0-9]+$') # length 3 - name, expression, regex
1027  , (None, '$pathname', '.*/java$') # length 3 - name, expression, regex
1028  , (None, '@basename()', 'java$') # length 3 - name, expression, regex
1029  , ('-', '$argv[-1]', r'org\.neo4j\.server\.Bootstrapper$')
1030  # length 3 - name, expression, regex
1031  , ('port', '@serviceport()', '[0-9]+$', re.I) # length 4 - name, expression, regex, flags
1032  ))
1033  keys = kitchensink.nvpairs.keys()
1034  keys.sort()
1035  self.assertEqual(str(keys), "['cantguess', 'port']")
1036  values = []
1037  for key in keys:
1038  values.append(kitchensink.nvpairs[key])
1039  self.assertEqual(str(values), "[None, '@serviceport()']")
1040  regex = re.compile('xxx')
1041  regextype = type(regex)
1042  exprlist = []
1043  for tup in kitchensink._tuplespec:
1044  self.assertEqual(type(tup[1]), regextype)
1045  exprlist.append(tup[0])
1046  self.assertEqual(str(exprlist)
1047  , "['$port', '$pathname', '@basename()', '$argv[-1]', '@serviceport()']")
1048  #
1049  # That was a pain...
1050  #
1051  # Now, let's test the basics in a little more depth by creating what should be a working
1052  # set of arguments to a (hypothetical) OCF resource agent
1053  #
1054  neo4j = OCFMonitoringRule('assimilation', 'neo4j',
1055  ( ('port', '$port')
1056  , (None, '$pathname', '.*/java$')
1057  , ('-', '$argv[-1]', r'org\.neo4j\.server\.Bootstrapper$')
1058  , ('home', '@argequals(-Dneo4j.home)', '/.*')
1059  , ('neo4j', '@basename(@argequals(-Dneo4j.home))', '.')
1060  )
1061  )
1062  neoprocargs = ("/usr/bin/java", "-cp"
1063  , "/var/lib/neo4j/lib/concurrentlinkedhashmap-lru-1.3.1.jar:"
1064  "AND SO ON:"
1065  "/var/lib/neo4j/system/lib/slf4j-api-1.6.2.jar:"
1066  "/var/lib/neo4j/conf/", "-server", "-XX:"
1067  "+DisableExplicitGC"
1068  , "-Dorg.neo4j.server.properties=conf/neo4j-server.properties"
1069  , "-Djava.util.logging.config.file=conf/logging.properties"
1070  , "-Dlog4j.configuration=file:conf/log4j.properties"
1071  , "-XX:+UseConcMarkSweepGC"
1072  , "-XX:+CMSClassUnloadingEnabled"
1073  , "-Dneo4j.home=/var/lib/neo4j"
1074  , "-Dneo4j.instance=/var/lib/neo4j"
1075  , "-Dfile.encoding=UTF-8"
1076  , "org.neo4j.server.Bootstrapper")
1077 
1078  neonode = ProcessNode('global', 'foofred', 'fred', '/usr/bin/java', neoprocargs
1079  , 'root', 'root', '/', roles=(CMAconsts.ROLE_server,))
1080  # We'll be missing the value of 'port'
1081  neocontext = ExpressionContext((neonode, drone))
1082  match = neo4j.specmatch(neocontext)
1083  (prio, table, missing) = neo4j.specmatch(neocontext)
1084  self.assertEqual(prio, MonitoringRule.PARTMATCH)
1085  self.assertEqual(missing, ['port'])
1086  # Now fill in the port value
1087  neonode.port=7474
1088  (prio, table) = neo4j.specmatch(neocontext)
1089  self.assertEqual(prio, MonitoringRule.HIGHPRIOMATCH)
1090  self.assertEqual(table['monitortype'], 'neo4j')
1091  self.assertEqual(table['monitorclass'], 'ocf')
1092  self.assertEqual(table['provider'], 'assimilation')
1093  keys = table.keys()
1094  keys.sort()
1095  self.assertEqual(str(keys), "['arglist', 'monitorclass', 'monitortype', 'provider']")
1096  arglist = table['arglist']
1097  keys = arglist.keys()
1098  keys.sort()
1099  self.assertEqual(keys, ['home', 'neo4j', 'port'])
1100  self.assertEqual(arglist['port'], '7474')
1101  self.assertEqual(arglist['home'], '/var/lib/neo4j')
1102  self.assertEqual(arglist['neo4j'], 'neo4j')
1103  #assert_no_dangling_Cclasses()
1104 
1106  # Clean things out so we only see what we want to see...
1107  AssimEvent.disable_all_observers()
1108  ocf_string = '''{
1109 # comment
1110  "class": "ocf",
1111  "type": "neo4j",
1112  "provider": "assimilation",
1113  "classconfig": [
1114  [null, "@basename()", "java$"],
1115  [null, "$argv[-1]", "org\\.neo4j\\.server\\.Bootstrapper$"],
1116  ["PORT", "serviceport", "[0-9]+$"],
1117  ["NEOHOME", "@argequals(-Dneo4j.home)", "/.*"]
1118  ]
1119 }'''
1120  ocf = MonitoringRule.ConstructFromString(ocf_string)
1121  self.assertTrue(isinstance(ocf, OCFMonitoringRule))
1122  lsb_string = '''{
1123 # comment
1124  "class": "lsb",
1125  "type": "neo4j",
1126  "classconfig": [
1127  ["@basename()", "java$"],
1128  ["$argv[-1]", "org\\.neo4j\\.server\\.Bootstrapper$"],
1129  ]
1130 }'''
1131  lsb = MonitoringRule.ConstructFromString(lsb_string)
1132  self.assertTrue(isinstance(lsb, LSBMonitoringRule))
1133  #assert_no_dangling_Cclasses()
1134 
1136  AssimEvent.disable_all_observers()
1137  drone = FakeDrone({
1138  'data': {
1139  'ocf': {
1140  'assimilation/neo4j',
1141  },
1142  'lsb': {
1143  'neo4j-service',
1144  }
1145  }
1146  })
1147  MonitoringRule.monitor_objects = {'service': {}, 'host':{}}
1148  ocf_string = '''{
1149  "class": "ocf", "type": "neo4j", "provider": "assimilation",
1150  "classconfig": [
1151  [null, "@basename()", "java$"],
1152  [null, "$argv[-1]", "org\\.neo4j\\.server\\.Bootstrapper$"],
1153  ["PORT", "$serviceport"],
1154  ["NEOHOME", "@argequals(-Dneo4j.home)", "/.*"]
1155  ]
1156  }'''
1157  MonitoringRule.ConstructFromString(ocf_string)
1158  lsb_string = '''{
1159  "class": "lsb", "type": "neo4j-service",
1160  "classconfig": [
1161  ["@basename()", "java$"],
1162  ["$argv[-1]", "org\\.neo4j\\.server\\.Bootstrapper$"],
1163  ]
1164  }'''
1165  MonitoringRule.ConstructFromString(lsb_string)
1166  neoprocargs = ("/usr/bin/java", "-cp"
1167  , "/var/lib/neo4j/lib/concurrentlinkedhashmap-lru-1.3.1.jar:"
1168  "AND SO ON:"
1169  "/var/lib/neo4j/system/lib/slf4j-api-1.6.2.jar:"
1170  "/var/lib/neo4j/conf/", "-server", "-XX:"
1171  "+DisableExplicitGC"
1172  , "-Dneo4j.home=/var/lib/neo4j"
1173  , "-Dneo4j.instance=/var/lib/neo4j"
1174  , "-Dfile.encoding=UTF-8"
1175  , "org.neo4j.server.Bootstrapper")
1176 
1177  neonode = ProcessNode('global', 'foofred', 'fred', '/usr/bin/java', neoprocargs
1178  , 'root', 'root', '/', roles=(CMAconsts.ROLE_server,))
1179  #neonode.serviceport=7474
1180  context = ExpressionContext((neonode, drone))
1181  first = MonitoringRule.findbestmatch(context)
1182  second = MonitoringRule.findbestmatch(context, False)
1183  list1 = MonitoringRule.findallmatches(context)
1184  neonode.serviceport=7474
1185  third = MonitoringRule.findbestmatch(context)
1186  list2 = MonitoringRule.findallmatches(context)
1187 
1188  # first should be the LSB instance
1189  self.assertEqual(first[1]['monitorclass'], 'lsb')
1190  self.assertEqual(first[0], MonitoringRule.LOWPRIOMATCH)
1191  # second should be the incomplete OCF instance
1192  self.assertEqual(second[1]['monitorclass'], 'ocf')
1193  self.assertEqual(second[0], MonitoringRule.PARTMATCH)
1194  # third should be the high priority OCF instance
1195  self.assertEqual(third[1]['monitorclass'], 'ocf')
1196  self.assertEqual(third[0], MonitoringRule.HIGHPRIOMATCH)
1197  # list1 should be the incomplete OCF and the complete LSB - in that order
1198  self.assertEqual(len(list1), 2)
1199  # They should come out sorted by monitorclass
1200  self.assertEqual(list1[0][0], MonitoringRule.LOWPRIOMATCH)
1201  self.assertEqual(list1[0][1]['monitorclass'], 'lsb')
1202  self.assertEqual(list1[1][0], MonitoringRule.PARTMATCH)
1203  self.assertEqual(list1[1][1]['monitorclass'], 'ocf')
1204  # third should be a complete OCF match
1205  # list2 should be the complete OCF and the complete OCF - in that order
1206  self.assertEqual(len(list2), 2)
1207  self.assertEqual(list2[0][0], MonitoringRule.LOWPRIOMATCH)
1208  self.assertEqual(list2[0][1]['monitorclass'], 'lsb')
1209  self.assertEqual(list2[1][0], MonitoringRule.HIGHPRIOMATCH)
1210  self.assertEqual(list2[1][1]['monitorclass'], 'ocf')
1211  #assert_no_dangling_Cclasses()
1212 
1214  AssimEvent.disable_all_observers()
1215  MonitoringRule.monitor_objects = {'service': {}, 'host':{}}
1216  drone = FakeDrone({
1217  'data': {
1218  'ocf': {
1219  'assimilation/neo4j',
1220  },
1221  'lsb': {
1222  'bacula',
1223  },
1224  'nagios': {
1225  'check_ssh',
1226  },
1227  }
1228  })
1229  ocf_string = '''{
1230  "class": "ocf", "type": "neo4j", "provider": "assimilation",
1231  "classconfig": [
1232  [null, "@basename()", "java"],
1233  ["classpath", "@flagvalue(-cp)", "..."],
1234  ["ipaddr", "@serviceip($procinfo.listenaddrs)", "..."],
1235  ["port", "@serviceport()", "[0-9]+$"]
1236  ]
1237  }'''
1238  nagios_string = '''{
1239  "class": "nagios",
1240  "type": "check_ssh",
1241  "prio": "med",
1242  "objclass": "service", # possible values are "service" or "host"
1243  "initargs": ["-t", "3600"], # Nanoprobes have their own timeouts...
1244  "classconfig": [
1245  [null, "@basename()", "sshd$"],
1246  ["-p", "@serviceport()", "[0-9]+"],
1247  ["__ARGV__", "@serviceip()", "..."]
1248  ]
1249 }'''
1250  ssh_json = '''{
1251  "exe": "/usr/sbin/sshd",
1252  "argv": [ "/usr/sbin/sshd", "-D" ],
1253  "uid": "root",
1254  "gid": "root",
1255  "cwd": "/",
1256  "listenaddrs": {
1257  "127.0.0.1:22": {
1258  "proto": "tcp",
1259  "addr": "127.0.0.1",
1260  "port": 22
1261  },
1262  ":::22": {
1263  "proto": "tcp6",
1264  "addr": "::",
1265  "port": 22
1266  }
1267  }
1268  }'''
1269  neo4j_json = '''{
1270  "exe": "/usr/lib/jvm/java-7-openjdk-amd64/jre/bin/java",
1271  "argv": [ "/usr/bin/java", "-cp", "/var/lib/neo4j/lib/concurrentlinkedhashmap-lru-1.3.1.jar: ...", "-server", "-XX:+DisableExplicitGC", "-Dorg.neo4j.server.properties=conf/neo4
1272  j-server.properties", "-Djava.util.logging.config.file=conf/logging.properties", "-Dlog4j.configuration=file:conf/log4j.properties", "-XX:
1273  +UseConcMarkSweepGC", "-XX:+CMSClassUnloadingEnabled", "-Dneo4j.home=/var/lib/neo4j", "-Dneo4j.instance=/var/lib/neo4j", "-Dfile.encoding=
1274  UTF-8", "org.neo4j.server.Bootstrapper" ],
1275  "uid": "neo4j",
1276  "gid": "neo4j",
1277  "cwd": "/var/lib/neo4j",
1278  "listenaddrs": {
1279  "::1:1337": {
1280  "proto": "tcp6",
1281  "addr": "::1",
1282  "port": 1337
1283  },
1284  ":::39185": {
1285  "proto": "tcp6",
1286  "addr": "::",
1287  "port": 39185
1288  }
1289  }
1290  }'''
1291  bacula_json = '''{
1292  "exe": "/usr/sbin/bacula-dir",
1293  "argv": [ "/usr/sbin/bacula-dir", "-c", "/etc/bacula/bacula-dir.conf", "-u", "bacula", "-g", "bacula" ],
1294  "uid": "bacula",
1295  "gid": "bacula",
1296  "cwd": "/",
1297  "listenaddrs": {
1298  "10.10.10.5:9101": {
1299  "proto": "tcp",
1300  "addr": "10.10.10.5",
1301  "port": 9101
1302  }
1303  }
1304  }'''
1305  MonitoringRule.ConstructFromString(ocf_string)
1306  MonitoringRule.ConstructFromString(nagios_string)
1307  neoargs = pyConfigContext(neo4j_json)['argv']
1308  testnode = ProcessNode('global', 'foofred', 'fred', '/usr/bin/java', neoargs
1309  , 'root', 'root', '/', roles=(CMAconsts.ROLE_server,))
1310 
1311  testnode.procinfo = neo4j_json
1312  context = ExpressionContext((testnode, drone))
1313  (prio, match) = MonitoringRule.findbestmatch(context)
1314  self.assertEqual(prio, MonitoringRule.HIGHPRIOMATCH)
1315  self.assertEqual(match['arglist']['ipaddr'], '::1')
1316  self.assertEqual(match['arglist']['port'], '1337')
1317 
1318  sshargs = pyConfigContext(ssh_json)['argv']
1319  testnode = ProcessNode('global', 'foofred', 'fred', '/usr/bin/sshd', sshargs
1320  , 'root', 'root', '/', roles=(CMAconsts.ROLE_server,))
1321  testnode.procinfo = ssh_json
1322  context = ExpressionContext((testnode, drone))
1323  (prio, match) = MonitoringRule.findbestmatch(context)
1324  #print >> sys.stderr, 'MATCH:', match
1325  self.assertEqual(prio, MonitoringRule.MEDPRIOMATCH)
1326  self.assertEqual(match['argv'], ['-t', '3600', '-p', '22', '127.0.0.1'])
1327  #assert_no_dangling_Cclasses()
1328 
1329 
1331  # @TODO What I have in mind for this test is that it
1332  # actually construct an auto-generated OCF monitoring node and activate it
1333  # It will have to add name, timeout and repeat intervals before activating it.
1334  pass
1335 
1336 
1337 if __name__ == "__main__":
1338  run()
def geninitconfig(ouraddr)
Definition: cma_test.py:202
def dronedesignation(hostnumber)
Definition: cma_test.py:191
def droneipaddress(hostnumber)
Definition: cma_test.py:185
def connactive(self, ioaddr, qid=0)
Definition: cma_test.py:436
def get(self, name, ret)
Definition: cma_test.py:485
def assertFalse(self, a)
Definition: cma_test.py:135
def construct_and_verify_diagram(self, diagramtype, patterncounts)
Definition: cma_test.py:702
def _sendaframeset(self, dest, fs)
Definition: cma_test.py:443
def check_live_counts(self, expectedlivecount, expectedpartnercount, expectedringmembercount)
Definition: cma_test.py:681
def auditalldrones()
Definition: cma_test.py:271
def closeconn(self, qid, dest)
Definition: cma_test.py:440
def bindaddr(self, addr)
Definition: cma_test.py:472
def assertNotEqual(self, a, b)
Definition: cma_test.py:129
guint32 proj_class_live_object_count(void)
Return the count of live C class objects.
Definition: proj_classes.c:406
def __init__(self, addrframesetpairs, sleepatend=0)
Definition: cma_test.py:353
def setblockio(self, tf)
Definition: cma_test.py:474
def assert_no_dangling_Cclasses(doassert=None)
Definition: cma_test.py:97
def assertRaises(self, exception, function, args)
Definition: cma_test.py:138
def auditadrone(self, droneid)
Definition: cma_test.py:210
def auditSETCONFIG(self, packetreturn, droneid, configinit)
Definition: cma_test.py:241
def auditaRing(self, ring)
Definition: cma_test.py:253
def hostdiscoveryinfo(hostnumber)
Definition: cma_test.py:194
def assertEqual(self, a, b)
Definition: cma_test.py:126
def __init__(self, json)
Definition: cma_test.py:482
def sendreliablefs(self, dest, fslist)
Definition: cma_test.py:430
def assimcli_check(command, expectedcount=None)
Definition: cma_test.py:320
def check_discovery(self, drone, expectedjson)
Definition: cma_test.py:589
def sendframesets(self, dest, fslist)
Definition: cma_test.py:424
def teardown_method(self, method)
Definition: cma_test.py:145
def diagram_patterns(self, diagramtype, nodecount)
Definition: cma_test.py:725
def ackmessage(self, dest, fs)
Definition: cma_test.py:433