The Assimilation Project  based on Assimilation version 1.1.7.1474836767
querytest.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 # vim: smartindent tabstop=4 shiftwidth=4 expandtab number
3 #
4 # This file is part of the Assimilation Project.
5 #
6 # Author: Alan Robertson <alanr@unix.sh>
7 # Copyright (C) 2014 - Assimilation Systems Limited
8 #
9 # Free support is available from the Assimilation Project community - http://assimproj.org
10 # Paid support is available from Assimilation Systems Limited - http://assimilationsystems.com
11 #
12 # The Assimilation software is free software: you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation, either version 3 of the License, or
15 # (at your option) any later version.
16 #
17 # The Assimilation software is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
21 #
22 # You should have received a copy of the GNU General Public License
23 # along with the Assimilation Project software. If not, see http://www.gnu.org/licenses/
24 #
25 #
26 '''
27 The querytest module is component of our system test tools architecture.
28 We can perform various kinds of queries and then validate the results against the
29 criteria we've been given.
30 
31 It allows us to (for example) verify that after a nanoprobe goes down that
32 we updated the database accordingly.
33 
34 At this point in time, I'm not sure if we should allow parameters to the queries, or just
35 reparse and recreate the query objects each time. Since efficiency is not a huge criteria,
36 it might make sense to just compile them from text each time and not fool with parameters.
37 
38 So that's what I'm going to do.
39 '''
40 import sys, time
41 from py2neo import neo4j
42 
43 # pylint -- too few public methods
44 # pylint: disable=R0903
45 class QueryTest(object):
46  '''
47  This class performs queries and validates that the results
48  meet some specific criteria.
49  This class is a component of our system test tools - not part of
50  the operational software.
51 
52  The general idea is that after a test completes, we can see if it updated
53  the database correctly by running some particular query whose results
54  can be evaluated for correctness.
55  '''
56  def __init__(self, store, querystring, classfactory, debug=False):
57  '''Init function for the QueryTest class
58  The querystring we're given will be given to the string <i>format</i> method
59  along with a set of objects passed in to runandcheck() to create the final query.
60  '''
61  self.store = store
62  self.db = store.db
63  self.querystring = querystring
64  self.debug = debug
65  self.classfactory = classfactory
66 
67 
68  def check(self, objectlist, validator=None, minrows=None, maxrows=None
69  , maxtries=300, delay=1):
70  '''
71  We run a query using _checkone_ repeatedly until we like the results or give up...
72  It's hard to know when everything is done like it ought to be...
73  Maxtries is the maximum of attempts to make for getting good query results
74  Delay is the how long to wait between query calls
75  '''
76  j=0
77  while j < maxtries:
78  if self.checkone(objectlist, validator, minrows, maxrows):
79  return True
80  time.sleep(delay)
81  j += 1
82 
83  print ('Rerunning failed query with debug=True')
84  self.checkone(objectlist, validator, minrows, maxrows, debug=True)
85  return False
86 
87  def checkone(self, objectlist, validator=None, minrows=None, maxrows=None, debug=None):
88  '''
89  We run a query and see if we like the results:
90  - use the format() method to format the query using the objects in <i>objectlist</i>
91  - run the query
92  - validate the results of the query by calling <i>validator</i> on each row retrieved
93 
94  We return False if the validator dislikes any query row, or if the wrong number of
95  rows was returned, and True if everything looks good.
96  '''
97  if debug is None:
98  debug = self.debug
99  finalquerystring = self.querystring.format(*objectlist)
100  if debug:
101  print >> sys.stderr, 'Final query string [%s]' % finalquerystring
102  self.store.clean_store()
103  rowcount = 0
104  for row in self.store.load_cypher_nodes(finalquerystring, self.classfactory, debug=debug):
105  rowcount += 1
106  if debug:
107  print >> sys.stderr, ("DEBUG: row [%d] is %s" % (rowcount, str(row)))
108  if validator is not None and not validator(row):
109  if debug:
110  print >> sys.stderr, ("VALIDATOR [%s] doesn't like row [%d] %s"
111  % (validator, rowcount, str(row)))
112  return False
113  if minrows is not None and rowcount < minrows:
114  if debug:
115  print >> sys.stderr, "Too few rows in result [%d]" % rowcount
116  return False
117  if maxrows is not None and rowcount > maxrows:
118  if debug:
119  print >> sys.stderr, "Too many rows in result [%d]" % rowcount
120  return False
121  return True
122 
123 # A little test code...
124 if __name__ == "__main__":
125  # pylint: disable=C0411,C0413,E0401
126  import os
127  from docker import SystemTestEnvironment
128  sys.path.append('..')
129  import graphnodes as GN
130  from store import Store
131  from cmainit import CMAinit
132  from logwatcher import LogWatcher
133 
134 
135  def downbyshutdown(drone):
136  'Return TRUE if this node is down by reason of HBSHUTDOWN'
137  #print >> sys.stderr, 'VALIDATOR: status [%s] reason [%s]' % (drone.status, drone.reason)
138  return drone.status == 'dead' and drone.reason == 'HBSHUTDOWN'
139 
140  def testmain(logname, maxdrones=25, debug=False):
141  'A simple test main program'
142  regexes = []
143  #pylint says: [W0612:testmain] Unused variable 'j'
144  #pylint: disable=W0612
145  for j in range(0,maxdrones+1):
146  regexes.append('Stored packages JSON data from *([^ ]*) ')
147  logwatch = LogWatcher(logname, regexes, timeout=90, returnonlymatch=True)
148  logwatch.setwatch()
149  sysenv = SystemTestEnvironment(maxdrones)
150  print >> sys.stderr, 'Systems all up and running.'
151  url = ('http://%s:%d/db/data/' % (sysenv.cma.ipaddr, 7474))
152  CMAinit(None)
153  store = Store(neo4j.Graph(url), readonly=True)
154  for classname in GN.GraphNode.classmap:
155  GN.GraphNode.initclasstypeobj(store, classname)
156  results = logwatch.lookforall()
157  if debug:
158  print >> sys.stderr, 'WATCH RESULTS:', results
159  tq = QueryTest(store
160  , "START drone=node:Drone('*:*') RETURN drone"
161  , GN.nodeconstructor, debug=debug)
162  print >> sys.stderr, 'Running Query'
163  if tq.check([None,], minrows=maxdrones+1, maxrows=maxdrones+1):
164  print 'WOOT! Systems passed query check after initial startup!'
165  else:
166  print 'Systems FAILED initial startup query check'
167  print 'Do you have a second CMA running??'
168  print 'Rerunning query with debug=True'
169  tq.debug = True
170  tq.check([None,], minrows=maxdrones+1, maxrows=maxdrones+1)
171  return 1
172  cma = sysenv.cma
173  nano = sysenv.nanoprobes[0]
174  regex = (r'%s cma INFO: System %s at \[::ffff:%s]:1984 reports graceful shutdown'
175  % (cma.hostname, nano.hostname, nano.ipaddr))
176  #print >> sys.stderr, 'REGEX IS: [%s]' % regex
177  logwatch = LogWatcher(logname, [regex,], timeout=30, returnonlymatch=False)
178  logwatch.setwatch()
179  nano.stopservice(SystemTestEnvironment.NANOSERVICE)
180  logwatch.look()
181  time.sleep(30)
182  tq = QueryTest(store
183  , ('''START drone=node:Drone('*:*') '''
184  '''WHERE drone.designation = "{0.hostname}" RETURN drone''')
185  , GN.nodeconstructor, debug=debug)
186  if tq.check([nano,], downbyshutdown, maxrows=1):
187  print 'WOOT! Systems passed query check after nano shutdown!'
188  else:
189  print 'Systems FAILED query check after nano shutdown'
190 
191  if os.access('/var/log/syslog', os.R_OK):
192  sys.exit(testmain('/var/log/syslog'))
193  elif os.access('/var/log/messages', os.R_OK):
194  sys.exit(testmain('/var/log/messages'))
def testmain(logname, maxdrones=25, debug=False)
Definition: querytest.py:140
def check(self, objectlist, validator=None, minrows=None, maxrows=None, maxtries=300, delay=1)
Definition: querytest.py:69
def checkone(self, objectlist, validator=None, minrows=None, maxrows=None, debug=None)
Definition: querytest.py:87
def __init__(self, store, querystring, classfactory, debug=False)
Definition: querytest.py:56
def downbyshutdown(drone)
Definition: querytest.py:135