osmo_ctrl.py: add RateCounters
First user will be the upcoming handover_2G/handover.py test in I0b2671304165a1aaae2b386af46fbd8b098e3bd8. Change-Id: Id799b3bb81eb9c04d13c26ff611e40363920300echanges/22/21522/4
parent
012a17da6a
commit
5354058c75
|
@ -0,0 +1,16 @@
|
|||
import sys, os
|
||||
|
||||
script_dir = sys.path[0]
|
||||
top_dir = os.path.join(script_dir, '..', '..')
|
||||
src_dir = os.path.join(top_dir, 'src')
|
||||
|
||||
# to find the osmo_gsm_tester py module
|
||||
sys.path.append(src_dir)
|
||||
|
||||
from osmo_gsm_tester.core import log
|
||||
|
||||
log.TestsTarget()
|
||||
log.set_all_levels(log.L_DBG)
|
||||
|
||||
if '-v' in sys.argv:
|
||||
log.style_change(trace=True)
|
|
@ -0,0 +1,155 @@
|
|||
- empty RateCounters()
|
||||
|
|
||||
- initialized RateCounters, single var
|
||||
| rate_ctr.abs.inst.0.var = 0
|
||||
- incremented inst.var
|
||||
| rate_ctr.abs.inst.0.var = 1
|
||||
- incremented inst.var again
|
||||
| rate_ctr.abs.inst.0.var = 2
|
||||
- incremented inst.var by 5
|
||||
| rate_ctr.abs.inst.0.var = 7
|
||||
- initialized RateCounters, two vars
|
||||
| rate_ctr.abs.inst.0.foo = 0
|
||||
| rate_ctr.abs.inst.0.var = 0
|
||||
- incremented foo and var
|
||||
| rate_ctr.abs.inst.0.foo = 1
|
||||
| rate_ctr.abs.inst.0.var = 1
|
||||
- incremented var again
|
||||
| rate_ctr.abs.inst.0.foo = 1
|
||||
| rate_ctr.abs.inst.0.var = 2
|
||||
- incremented foo by 5
|
||||
| rate_ctr.abs.inst.0.foo = 6
|
||||
| rate_ctr.abs.inst.0.var = 2
|
||||
- initialized RateCounters, two vars, three instances
|
||||
| rate_ctr.abs.inst.0.foo = 0
|
||||
| rate_ctr.abs.inst.0.var = 0
|
||||
| rate_ctr.abs.inst.1.foo = 0
|
||||
| rate_ctr.abs.inst.1.var = 0
|
||||
| rate_ctr.abs.inst.2.foo = 0
|
||||
| rate_ctr.abs.inst.2.var = 0
|
||||
- incremented foo and var on separate instances
|
||||
| rate_ctr.abs.inst.0.foo = 1
|
||||
| rate_ctr.abs.inst.0.var = 0
|
||||
| rate_ctr.abs.inst.1.foo = 0
|
||||
| rate_ctr.abs.inst.1.var = 1
|
||||
| rate_ctr.abs.inst.2.foo = 0
|
||||
| rate_ctr.abs.inst.2.var = 0
|
||||
- incremented var on instance 2
|
||||
| rate_ctr.abs.inst.0.foo = 1
|
||||
| rate_ctr.abs.inst.0.var = 0
|
||||
| rate_ctr.abs.inst.1.foo = 0
|
||||
| rate_ctr.abs.inst.1.var = 1
|
||||
| rate_ctr.abs.inst.2.foo = 0
|
||||
| rate_ctr.abs.inst.2.var = 1
|
||||
- incremented foo by 5 on instances 1,2
|
||||
| rate_ctr.abs.inst.0.foo = 1
|
||||
| rate_ctr.abs.inst.0.var = 0
|
||||
| rate_ctr.abs.inst.1.foo = 5
|
||||
| rate_ctr.abs.inst.1.var = 1
|
||||
| rate_ctr.abs.inst.2.foo = 5
|
||||
| rate_ctr.abs.inst.2.var = 1
|
||||
- copy
|
||||
| rate_ctr.abs.inst.0.foo = 1
|
||||
| rate_ctr.abs.inst.0.var = 0
|
||||
| rate_ctr.abs.inst.1.foo = 5
|
||||
| rate_ctr.abs.inst.1.var = 1
|
||||
| rate_ctr.abs.inst.2.foo = 5
|
||||
| rate_ctr.abs.inst.2.var = 1
|
||||
- increment two vars by 100 on all three instances
|
||||
| rate_ctr.abs.inst.0.foo = 101
|
||||
| rate_ctr.abs.inst.0.var = 100
|
||||
| rate_ctr.abs.inst.1.foo = 105
|
||||
| rate_ctr.abs.inst.1.var = 101
|
||||
| rate_ctr.abs.inst.2.foo = 105
|
||||
| rate_ctr.abs.inst.2.var = 101
|
||||
- subtract original copy
|
||||
| rate_ctr.abs.inst.0.foo = 100
|
||||
| rate_ctr.abs.inst.0.var = 100
|
||||
| rate_ctr.abs.inst.1.foo = 100
|
||||
| rate_ctr.abs.inst.1.var = 100
|
||||
| rate_ctr.abs.inst.2.foo = 100
|
||||
| rate_ctr.abs.inst.2.var = 100
|
||||
- add original copy
|
||||
| rate_ctr.abs.inst.0.foo = 101
|
||||
| rate_ctr.abs.inst.0.var = 100
|
||||
| rate_ctr.abs.inst.1.foo = 105
|
||||
| rate_ctr.abs.inst.1.var = 101
|
||||
| rate_ctr.abs.inst.2.foo = 105
|
||||
| rate_ctr.abs.inst.2.var = 101
|
||||
- increment types per_hour, per_day by 23
|
||||
| rate_ctr.abs.inst.0.foo = 101
|
||||
| rate_ctr.abs.inst.0.var = 100
|
||||
| rate_ctr.abs.inst.1.foo = 105
|
||||
| rate_ctr.abs.inst.1.var = 101
|
||||
| rate_ctr.abs.inst.2.foo = 105
|
||||
| rate_ctr.abs.inst.2.var = 101
|
||||
| rate_ctr.per_day.inst.0.foo = 23
|
||||
| rate_ctr.per_day.inst.0.moo = 23
|
||||
| rate_ctr.per_day.inst.0.var = 23
|
||||
| rate_ctr.per_day.inst.1.foo = 23
|
||||
| rate_ctr.per_day.inst.1.moo = 23
|
||||
| rate_ctr.per_day.inst.1.var = 23
|
||||
| rate_ctr.per_day.inst.2.foo = 23
|
||||
| rate_ctr.per_day.inst.2.moo = 23
|
||||
| rate_ctr.per_day.inst.2.var = 23
|
||||
| rate_ctr.per_hour.inst.0.foo = 23
|
||||
| rate_ctr.per_hour.inst.0.moo = 23
|
||||
| rate_ctr.per_hour.inst.0.var = 23
|
||||
| rate_ctr.per_hour.inst.1.foo = 23
|
||||
| rate_ctr.per_hour.inst.1.moo = 23
|
||||
| rate_ctr.per_hour.inst.1.var = 23
|
||||
| rate_ctr.per_hour.inst.2.foo = 23
|
||||
| rate_ctr.per_hour.inst.2.moo = 23
|
||||
| rate_ctr.per_hour.inst.2.var = 23
|
||||
- copy
|
||||
| rate_ctr.abs.inst.0.foo = 101
|
||||
| rate_ctr.abs.inst.0.var = 100
|
||||
| rate_ctr.abs.inst.1.foo = 105
|
||||
| rate_ctr.abs.inst.1.var = 101
|
||||
| rate_ctr.abs.inst.2.foo = 105
|
||||
| rate_ctr.abs.inst.2.var = 101
|
||||
| rate_ctr.per_day.inst.0.foo = 23
|
||||
| rate_ctr.per_day.inst.0.moo = 23
|
||||
| rate_ctr.per_day.inst.0.var = 23
|
||||
| rate_ctr.per_day.inst.1.foo = 23
|
||||
| rate_ctr.per_day.inst.1.moo = 23
|
||||
| rate_ctr.per_day.inst.1.var = 23
|
||||
| rate_ctr.per_day.inst.2.foo = 23
|
||||
| rate_ctr.per_day.inst.2.moo = 23
|
||||
| rate_ctr.per_day.inst.2.var = 23
|
||||
| rate_ctr.per_hour.inst.0.foo = 23
|
||||
| rate_ctr.per_hour.inst.0.moo = 23
|
||||
| rate_ctr.per_hour.inst.0.var = 23
|
||||
| rate_ctr.per_hour.inst.1.foo = 23
|
||||
| rate_ctr.per_hour.inst.1.moo = 23
|
||||
| rate_ctr.per_hour.inst.1.var = 23
|
||||
| rate_ctr.per_hour.inst.2.foo = 23
|
||||
| rate_ctr.per_hour.inst.2.moo = 23
|
||||
| rate_ctr.per_hour.inst.2.var = 23
|
||||
- match? True
|
||||
- increment foo
|
||||
| rate_ctr.abs.inst.0.foo = 102
|
||||
| rate_ctr.abs.inst.0.var = 100
|
||||
| rate_ctr.abs.inst.1.foo = 105
|
||||
| rate_ctr.abs.inst.1.var = 101
|
||||
| rate_ctr.abs.inst.2.foo = 105
|
||||
| rate_ctr.abs.inst.2.var = 101
|
||||
| rate_ctr.per_day.inst.0.foo = 23
|
||||
| rate_ctr.per_day.inst.0.moo = 23
|
||||
| rate_ctr.per_day.inst.0.var = 23
|
||||
| rate_ctr.per_day.inst.1.foo = 23
|
||||
| rate_ctr.per_day.inst.1.moo = 23
|
||||
| rate_ctr.per_day.inst.1.var = 23
|
||||
| rate_ctr.per_day.inst.2.foo = 23
|
||||
| rate_ctr.per_day.inst.2.moo = 23
|
||||
| rate_ctr.per_day.inst.2.var = 23
|
||||
| rate_ctr.per_hour.inst.0.foo = 23
|
||||
| rate_ctr.per_hour.inst.0.moo = 23
|
||||
| rate_ctr.per_hour.inst.0.var = 23
|
||||
| rate_ctr.per_hour.inst.1.foo = 23
|
||||
| rate_ctr.per_hour.inst.1.moo = 23
|
||||
| rate_ctr.per_hour.inst.1.var = 23
|
||||
| rate_ctr.per_hour.inst.2.foo = 23
|
||||
| rate_ctr.per_hour.inst.2.moo = 23
|
||||
| rate_ctr.per_hour.inst.2.var = 23
|
||||
- match? False
|
|
@ -0,0 +1,56 @@
|
|||
#!/usr/bin/env python3
|
||||
import _prep
|
||||
|
||||
from osmo_gsm_tester.obj.osmo_ctrl import *
|
||||
|
||||
rc = RateCounters()
|
||||
print('- empty RateCounters()' + rc.str())
|
||||
|
||||
rc = RateCounters('inst', 'var')
|
||||
print('- initialized RateCounters, single var' + rc.str())
|
||||
rc.inc('inst', 'var')
|
||||
print('- incremented inst.var' + rc.str())
|
||||
rc.inc('inst', 'var')
|
||||
print('- incremented inst.var again' + rc.str())
|
||||
rc.inc('inst', 'var', 5)
|
||||
print('- incremented inst.var by 5' + rc.str())
|
||||
|
||||
rc = RateCounters('inst', ('foo', 'var'))
|
||||
print('- initialized RateCounters, two vars' + rc.str())
|
||||
rc.inc('inst', ('foo', 'var'))
|
||||
print('- incremented foo and var' + rc.str())
|
||||
rc.inc('inst', 'var')
|
||||
print('- incremented var again' + rc.str())
|
||||
rc.inc('inst', 'foo', 5)
|
||||
print('- incremented foo by 5' + rc.str())
|
||||
|
||||
rc = RateCounters('inst', ('foo', 'var'), instances=range(3))
|
||||
print('- initialized RateCounters, two vars, three instances' + rc.str())
|
||||
rc.inc('inst', 'foo', instances=0)
|
||||
rc.inc('inst', 'var', instances=1)
|
||||
print('- incremented foo and var on separate instances' + rc.str())
|
||||
rc.inc('inst', 'var', instances=2)
|
||||
print('- incremented var on instance 2' + rc.str())
|
||||
rc.inc('inst', 'foo', 5, instances=(1,2))
|
||||
print('- incremented foo by 5 on instances 1,2' + rc.str())
|
||||
|
||||
rc_rel = rc.copy()
|
||||
print('- copy' + rc_rel.str())
|
||||
rc.inc('inst', ('foo', 'var'), 100, instances=range(3))
|
||||
print('- increment two vars by 100 on all three instances' + rc.str())
|
||||
rc.subtract(rc_rel)
|
||||
print('- subtract original copy' + rc.str())
|
||||
rc.add(rc_rel)
|
||||
print('- add original copy' + rc.str())
|
||||
|
||||
rc.inc('inst', ('foo', 'var', 'moo'), 23, instances=range(3), kinds=('per_hour', 'per_day'))
|
||||
print('- increment types per_hour, per_day by 23' + rc.str())
|
||||
|
||||
rc2 = rc.copy()
|
||||
print('- copy' + rc2.str())
|
||||
print('- match? ', (rc == rc2))
|
||||
rc2.inc('inst', 'foo')
|
||||
print('- increment foo' + rc2.str())
|
||||
print('- match? ', (rc == rc2))
|
||||
|
||||
# vim: expandtab tabstop=4 shiftwidth=4
|
|
@ -238,4 +238,230 @@ class OsmoCtrl(log.Origin):
|
|||
def __exit__(self, *exc_info):
|
||||
self.disconnect()
|
||||
|
||||
class RateCountersExn(log.Error):
|
||||
pass
|
||||
|
||||
class RateCounters(dict):
|
||||
'''Usage example:
|
||||
counter_names = (
|
||||
'handover:completed',
|
||||
'handover:stopped',
|
||||
'handover:no_channel',
|
||||
'handover:timeout',
|
||||
'handover:failed',
|
||||
'handover:error',
|
||||
)
|
||||
|
||||
# initialize the listing of CTRL vars of the counters to watch.
|
||||
# First on the 'bsc' node:
|
||||
# rate_ctr.abs.bsc.0.handover:completed
|
||||
# rate_ctr.abs.bsc.0.handover:stopped
|
||||
# ...
|
||||
counters = RateCounters('bsc', counter_names, from_ctrl=bsc.ctrl)
|
||||
|
||||
# And also add counters for two 'bts' instances:
|
||||
# rate_ctr.abs.bts.0.handover:completed
|
||||
# rate_ctr.abs.bts.0.handover:stopped
|
||||
# ...
|
||||
# rate_ctr.abs.bts.1.handover:completed
|
||||
# ...
|
||||
counters.add(RateCounters('bts', counter_names, instances=(0, 1)))
|
||||
|
||||
# read initial counter values, from the bsc_ctrl, as set in
|
||||
# counters.from_ctrl in the RateCounters() constructor above.
|
||||
counters.read()
|
||||
|
||||
# Do some actions that should increment counters in the SUT
|
||||
do_a_handover()
|
||||
|
||||
if approach_without_wait:
|
||||
# increment the counters as expected
|
||||
counters.inc('bts', 'handover:completed')
|
||||
|
||||
# read counters from CTRL again, and fail if they differ
|
||||
counters.verify()
|
||||
|
||||
if approach_with_wait:
|
||||
# you can wait for counters to change. counters.changed() does not
|
||||
# modify counters' values, just reads values from CTRL and stores
|
||||
# the changes in counters.diff.
|
||||
wait(counters.changed, timeout=20)
|
||||
|
||||
# log which counters changed by how much, found in counters.diff
|
||||
# after each counters.changed() call:
|
||||
print(counters.diff.str(skip_zero_vals=True))
|
||||
|
||||
if check_all_vals:
|
||||
# Assert all values:
|
||||
expected_diff = counters.copy().clear()
|
||||
expected_diff.inc('bts', 'handover:completed', instances=(0, 1))
|
||||
counters.diff.expect(expected_diff)
|
||||
else:
|
||||
# Assert only some specific counters:
|
||||
expected_diff = RateCounters()
|
||||
expected_diff.inc('bts', 'handover:completed', instances=(0, 1))
|
||||
counters.diff.expect(expected_diff)
|
||||
|
||||
# update counters to the last read values if desired
|
||||
counters.add(counters.diff)
|
||||
'''
|
||||
|
||||
def __init__(self, instance_names=(), counter_names=(), instances=0, kinds='abs', init_val=0, from_ctrl=None):
|
||||
def init_cb(var):
|
||||
self[var] = init_val
|
||||
RateCounters.for_each(init_cb, instance_names, counter_names, instances, kinds, results=False)
|
||||
self.from_ctrl = from_ctrl
|
||||
self.diff = None
|
||||
|
||||
@staticmethod
|
||||
def for_each(callback_func, instance_names, counter_names, instances=0, kinds='abs', results=True):
|
||||
'''Call callback_func for a set of rate counter var names, mostly
|
||||
called by more convenient functions. See inc() for a comprehensive
|
||||
explanation.
|
||||
'''
|
||||
if type(instance_names) is str:
|
||||
instance_names = (instance_names, )
|
||||
if type(counter_names) is str:
|
||||
counter_names = (counter_names, )
|
||||
if type(kinds) is str:
|
||||
kinds = (kinds, )
|
||||
if type(instances) is int:
|
||||
instances = (instances, )
|
||||
if results is True:
|
||||
results = RateCounters()
|
||||
elif results is False:
|
||||
results = None
|
||||
for instance_name in instance_names:
|
||||
for instance_nr in instances:
|
||||
for counter_name in counter_names:
|
||||
for kind in kinds:
|
||||
var = 'rate_ctr.{kind}.{instance_name}.{instance_nr}.{counter_name}'.format(**locals())
|
||||
result = callback_func(var)
|
||||
if results is not None:
|
||||
results[var] = result
|
||||
return results
|
||||
|
||||
def __str__(self):
|
||||
return self.str(', ', '')
|
||||
|
||||
def str(self, sep='\n| ', prefix='\n| ', vals=None, skip_zero_vals=False):
|
||||
'''The 'vals' arg is useful to print a plain dict() of counter values like a RateCounters class.
|
||||
By default print self.'''
|
||||
if vals is None:
|
||||
vals = self
|
||||
return prefix + sep.join('%s = %d' % (var, val) for var, val in sorted(vals.items())
|
||||
if (not skip_zero_vals) or (val != 0))
|
||||
|
||||
def inc(self, instance_names, counter_names, inc=1, instances=0, kinds='abs'):
|
||||
'''Increment a set of counters.
|
||||
inc('xyz', 'val') --> rate_ctr.abs.xyz.0.val += 1
|
||||
|
||||
inc('xyz', ('foo', 'bar')) --> rate_ctr.abs.xyz.0.foo += 1
|
||||
rate_ctr.abs.xyz.0.bar += 1
|
||||
|
||||
inc(('xyz', 'pqr'), 'val') --> rate_ctr.abs.xyz.0.val += 1
|
||||
rate_ctr.abs.pqr.0.val += 1
|
||||
|
||||
inc('xyz', 'val', instances=range(3))
|
||||
--> rate_ctr.abs.xyz.0.val += 1
|
||||
rate_ctr.abs.xyz.1.val += 1
|
||||
rate_ctr.abs.xyz.2.val += 1
|
||||
'''
|
||||
def inc_cb(var):
|
||||
val = self.get(var, 0)
|
||||
val += inc
|
||||
self[var] = val
|
||||
return val
|
||||
RateCounters.for_each(inc_cb, instance_names, counter_names, instances, kinds, results=False)
|
||||
return self
|
||||
|
||||
def add(self, rate_counters):
|
||||
'''Add the given values up to the values in self.
|
||||
rate_counters can be a RateCounters instance or a plain dict of CTRL
|
||||
var as key and counter integer as value.
|
||||
'''
|
||||
for var, add_val in rate_counters.items():
|
||||
val = self.get(var, 0)
|
||||
val += add_val
|
||||
self[var] = val
|
||||
return self
|
||||
|
||||
def subtract(self, rate_counters):
|
||||
'''Same as add(), but subtract values from self instead.
|
||||
Useful to verify counters relative to an arbitrary reference.'''
|
||||
for var, subtract_val in rate_counters.items():
|
||||
val = self.get(var, 0)
|
||||
val -= subtract_val
|
||||
self[var] = val
|
||||
return self
|
||||
|
||||
|
||||
def clear(self, val=0):
|
||||
'''Set all counts to 0 (or a specific value)'''
|
||||
for var in self.keys():
|
||||
self[var] = val
|
||||
return self
|
||||
|
||||
def copy(self):
|
||||
'''Return a copy of all keys and values stored in self.'''
|
||||
cpy = RateCounters(from_ctrl = self.from_ctrl)
|
||||
cpy.update(self)
|
||||
return cpy
|
||||
|
||||
def read(self):
|
||||
'''Read all counters from the CTRL connection passed to RateCounters(from_ctrl=x).
|
||||
The CTRL must be connected, e.g.
|
||||
with bsc.ctrl() as ctrl:
|
||||
counters = RateCounters(ctrl)
|
||||
counters.read()
|
||||
'''
|
||||
for var in self.keys():
|
||||
self[var] = self.from_ctrl.get_int_var(var)
|
||||
self.from_ctrl.dbg('Read counters:', self.str())
|
||||
return self
|
||||
|
||||
def verify(self):
|
||||
'''Read counters from CTRL and assert that they match the current counts'''
|
||||
got_vals = self.copy()
|
||||
got_vals.read()
|
||||
got_vals.expect(self)
|
||||
|
||||
def changed(self):
|
||||
'''Read counters from CTRL, and return True if anyone is different now.
|
||||
Store the difference in counts in self.diff (replace self.diff for
|
||||
each changed() call). The counts in self are never modified.'''
|
||||
self.diff = None
|
||||
got_vals = self.copy()
|
||||
got_vals.read()
|
||||
if self != got_vals:
|
||||
self.diff = got_vals
|
||||
self.diff.subtract(self)
|
||||
self.from_ctrl.dbg('Changed counters:', self.diff.str(skip_zero_vals=True))
|
||||
return True
|
||||
return False
|
||||
|
||||
def expect(self, expect_vals):
|
||||
'''Iterate expect_vals and fail if any counter value differs from self.
|
||||
expect_vals can be a RateCounters instance or a plain dict of CTRL
|
||||
var as key and counter integer as value.
|
||||
'''
|
||||
ok = 0
|
||||
errs = []
|
||||
for var, expect_val in expect_vals.items():
|
||||
got_val = self.get(var)
|
||||
if got_val is None:
|
||||
errs.append('expected {var} == {expect_val}, but no such value found'.format(**locals()))
|
||||
continue
|
||||
if got_val != expect_val:
|
||||
errs.append('expected {var} == {expect_val}, but is {got_val}'.format(**locals()))
|
||||
continue
|
||||
ok += 1
|
||||
if errs:
|
||||
self.from_ctrl.dbg('Expected rate counters:', self.str(vals=expect_vals))
|
||||
self.from_ctrl.dbg('Got rate counters:', self.str())
|
||||
raise RateCountersExn('%d of %d rate counters mismatch:' % (len(errs), len(errs) + ok), '\n| ' + '\n| '.join(errs))
|
||||
else:
|
||||
self.from_ctrl.log('Verified %d rate counters' % ok)
|
||||
self.from_ctrl.dbg('Verified %d rate counters:' % ok, expect_vals)
|
||||
|
||||
# vim: expandtab tabstop=4 shiftwidth=4
|
||||
|
|
Loading…
Reference in New Issue