osmo-gsm-tester/selftest/resource_test/resource_test.py

155 lines
5.0 KiB
Python
Raw Permalink Normal View History

#!/usr/bin/env python3
import tempfile
import os
import sys
import pprint
import shutil
import atexit
import time
import threading
import _prep
from osmo_gsm_tester.core import config, log, util, resource
from osmo_gsm_tester.core.schema import generate_schemas
workdir = util.get_tempdir()
# override config locations to make sure we use only the test conf
config.override_conf = os.path.join(os.path.dirname(sys.argv[0]), 'conf', 'paths.conf')
log.get_process_id = lambda: '123-1490837279'
# Generate supported schemas dynamically from objects:
generate_schemas()
print('- expect solutions:')
pprint.pprint(
resource.solve([ [0, 1, 2],
[0, 1, 2],
[0, 1, 2] ]) )
pprint.pprint(
resource.solve([ [0, 1, 2],
[0, 1],
[0, 2] ]) ) # == [0, 1, 2]
pprint.pprint(
resource.solve([ [0, 1, 2],
[0],
[0, 2] ]) ) # == [1, 0, 2]
pprint.pprint(
resource.solve([ [0, 1, 2],
[2],
[0, 2] ]) ) # == [1, 2, 0]
print('- expect failure to solve:')
try:
resource.solve([ [0, 2],
[2],
[0, 2] ])
assert False
except resource.NotSolvable as e:
print(e)
print('- test removing a Resources list from itself')
try:
r = resource.Resources({ 'k': [ {'a': 1, 'b': 2}, {'a': 3, 'b': 4}, ],
'i': [ {'c': 1, 'd': 2}, {'c': 3, 'd': 4}, ] })
r.drop(r)
assert False
except RuntimeError as e:
print('ok, caused exception RuntimeError: %s' % str(e))
print('- test removing a Resources list from one with the same list in it')
r = resource.Resources({ 'k': [ {'a': 1, 'b': 2}, {'a': 3, 'b': 4}, ],
'i': [ {'c': 1, 'd': 2}, {'c': 3, 'd': 4}, ] })
r.drop({ 'k': r.get('k'), 'i': r.get('i') })
assert not r
print('- test resources config and state dir:')
resources_conf = os.path.join(_prep.script_dir, 'resource_test', 'etc',
'resources.conf')
state_dir = config.get_state_dir()
rrfile = state_dir.child(resource.RESERVED_RESOURCES_FILE)
pool = resource.ResourcesPool()
print('*** all resources:')
pprint.pprint(pool.all_resources)
print('*** end: all resources\n')
print('- request some resources')
want = {
'ip_address': [ { 'times': 1 } ],
'bts': [ { 'type': 'osmo-bts-sysmo', 'times': 1 , 'ciphers': ['a5_1']}, { 'type': 'osmo-bts-trx', 'times': 1 } ],
'modem': [ { 'times': 2 , 'ciphers': ['a5_0', 'a5_1']} ],
}
modifiers = {
'bts': [ {}, {'num_trx': 2 }],
}
fix and refactor logging: drop 'with', simplify With the recent fix of the junit report related issues, another issue arose: the 'with log.Origin' was changed to disallow __enter__ing an object twice to fix problems, now still code would fail because it tries to do 'with' on the same object twice. The only reason is to ensure that logging is associated with a given object. Instead of complicating even more, implement differently. Refactor logging to simplify use: drop the 'with Origin' style completely, and instead use the python stack to determine which objects are created by which, and which object to associate a log statement with. The new way: we rely on the convention that each class instance has a local 'self' referencing the object instance. If we need to find an origin as a new object's parent, or to associate a log message with, we traverse each stack frame, fetching the first local 'self' object that is a log.Origin class instance. How to use: Simply call log.log() anywhere, and it finds an Origin object to log for, from the stack. Alternatively call self.log() for any Origin() object to skip the lookup. Create classes as child class of log.Origin and make sure to call super().__init__(category, name). This constructor will magically find a parent Origin on the stack. When an exception happens, we first escalate the exception up through call scopes to where ever it is handled by log.log_exn(). This then finds an Origin object in the traceback's stack frames, no need to nest in 'with' scopes. Hence the 'with log.Origin' now "happens implicitly", we can write pure natural python code, no more hassles with scope ordering. Furthermore, any frame can place additional logging information in a frame by calling log.ctx(). This is automatically inserted in the ancestry associated with a log statement / exception. Change-Id: I5f9b53150f2bb6fa9d63ce27f0806f0ca6a45e90
2017-06-09 23:18:27 +00:00
origin = log.Origin(None, 'testowner')
resources = pool.reserve(origin, config.replicate_times(want), config.replicate_times(modifiers))
print('~~~ currently reserved:')
with open(rrfile, 'r') as f:
print(f.read())
print('~~~ end: currently reserved\n')
print('~~~ with modifiers:')
print(repr(resources))
print('~~~ end: with modifiers:')
resources.free()
print('~~~ currently reserved:')
with open(rrfile, 'r') as f:
print(f.read())
print('~~~ end: currently reserved\n')
print('- item_matches:')
superset = { 'hello': 'world', 'foo': 'bar', 'ordered_list': [{'xkey': 'xvalue'},{'ykey': 'yvalue'}], 'unordered_list_set': [1, 2, 3]}
subset = { 'foo': 'bar', 'ordered_list': [{'xkey': 'xvalue'},{'ykey': 'yvalue'}], 'unordered_list_set': [2, 1] }
if resource.item_matches(superset, subset):
print('1st subset matches correctly, pass')
subset = { 'ordered_list': [{},{'ykey': 'yvalue'}], 'unordered_list_set': [] }
if resource.item_matches(superset, subset):
print('2nd subset matches correctly, pass')
subset = { 'ordered_list': [{'ykey': 'yvalue'}, {'xkey': 'xvalue'}] }
if not resource.item_matches(superset, subset):
print('3rd subset should not match, pass')
subset = { 'ordered_list': [{'xkey': 'xvalue'}, {'ykey': 'yvalue'}, {'zkey': 'zvalue'}] }
if not resource.item_matches(superset, subset):
print('3rd subset should not match, pass')
subset = { 'unordered_list_set': [4] }
if not resource.item_matches(superset, subset):
print('4th subset should not match, pass')
print('*** concurrent allocation:')
origin1 = log.Origin(None, 'testowner1')
origin2 = log.Origin(None, 'testowner2')
# We disable dbg() for second thread since FileWatch output result is
# non-deterministic, since sometimes 1 Modiffied event is triggered, sometimes 2.
origin1.dbg = origin2.dbg = lambda obj, *messages, _src=3, **named_items: None
resources2 = None
def second_ogt_instance():
# should block here until "resources" are freed.
print('- 2nd instance reserve() start')
resources2 = pool.reserve(origin2, config.replicate_times(want), config.replicate_times(modifiers))
print('- 2nd instance reserve() done')
resources2.free()
resources = pool.reserve(origin1, config.replicate_times(want), config.replicate_times(modifiers))
th = threading.Thread(target=second_ogt_instance)
th.start()
time.sleep(1.0)
print('- 1st instance free()')
resources.free()
th.join()
print('*** end: concurrent allocation')
# vim: expandtab tabstop=4 shiftwidth=4