def test_event_facts(self): # insert event = compile.parse('insert[p(1) :- true]') self.assertEqual(len(event), 1) event = event[0] fact = compile.parse1('p(1) :- true') self.assertEqual(event.formula, fact) self.assertEqual(event.insert, True) self.assertIsNone(event.target) # delete event = compile.parse('delete[p(1) :- true]') self.assertEqual(len(event), 1) event = event[0] fact = compile.parse1('p(1) :- true') self.assertEqual(event.formula, fact) self.assertEqual(event.insert, False) self.assertIsNone(event.target) # insert with policy event = compile.parse('insert[p(1) :- true; "policy"]') self.assertEqual(len(event), 1) event = event[0] fact = compile.parse1('p(1) :- true') self.assertEqual(event.formula, fact) self.assertEqual(event.insert, True) self.assertEqual(event.target, "policy")
def check(self, atom1, atom2): atom1 = compile.parse1(atom1) atom2 = compile.parse1(atom2) unifier = unify.BiUnifier() changes = unify.match_atoms(atom1, unifier, atom2) self.assertIsNotNone(changes) self.assertEqual(atom1.plug(unifier), atom2)
def check(self, input_string, correct_string, msg): rule = compile.parse1(input_string) actual = compile.reorder_for_safety(rule) correct = compile.parse1(correct_string) if correct != actual: emsg = "Correct: " + str(correct) emsg += "; Actual: " + str(actual) self.fail(msg + " :: " + emsg)
def test_rule_hash(self): """Test whether rules are properly hashed.""" s = set() p = compile.parse1('p(x) :- q(x), s(x,y)') s.add(p) q = compile.parse1('p(x) :- q(x), s(x,y)') s.discard(q) self.assertEqual(s, set())
def test_hash(self): x = set() x.add(compile.parse1('p(x, y) :- nova:q(x, id=y)')) x.add(compile.parse1('p(x, y) :- nova:q(x, id=y)')) self.assertEqual(len(x), 1) self.assertIn(compile.parse1('p(x, y) :- nova:q(x, id=y)'), x) x.discard(compile.parse1('p(x, y) :- nova:q(x, id=y)')) self.assertEqual(len(x), 0)
def test_no_split_parsing(self): th = nonrecursive.NonrecursiveRuleTheory() th.insert(compile.parse1("nova:v(x, y) :- u(x, y)", use_modules=False)) self.assertEqual(th.arity("nova:v"), 2) self.assertIsNone(th.arity("nova:v", modal="insert")) th.insert(compile.parse1("insert[neutron:v(x, y, z)] :- u(x, y)", use_modules=False)) self.assertEqual(th.arity("nova:v"), 2) self.assertEqual(th.arity("neutron:v", modal="insert"), 3)
def test_module_schemas(self): """Test that rules are properly checked against module schemas.""" run = agnostic.Runtime() run.create_policy('mod1') run.create_policy('mod2') run.set_schema('mod1', compile.Schema({'p': (1, 2, 3), 'q': (1,)}), complete=True) run.set_schema('mod2', compile.Schema({'p': (1,), 'q': (1, 2)}), complete=True) def check_err(code_string, theory, emsg, msg, f=compile.rule_errors): rule = compile.parse1(code_string) errs = f(rule, run.theory, theory) self.assertTrue(any(emsg in str(err) for err in errs), msg + ":: Failed to find error message '" + emsg + "' in: " + ";".join(str(e) for e in errs)) # no errors rule = compile.parse1('p(x) :- q(x), mod1:p(x, y, z), mod2:q(x, y), ' 'mod1:q(t), mod2:p(t)') errs = compile.rule_errors(rule, run.theory) self.assertEqual(len(errs), 0, "Should not have found any errors") # unknown table within module check_err('p(x) :- q(x), mod1:r(x), r(x)', 'mod3', 'unknown table', 'Unknown table for rule') # wrong number of arguments check_err('p(x) :- q(x), mod1:p(x,y,z,w), r(x)', 'mod3', 'only 3 arguments are permitted', 'Wrong number of arguments for rule') # same tests for an atom # no errors atom = compile.parse1('p(1, 2, 2)') errs = compile.fact_errors(atom, run.theory, 'mod1') self.assertEqual(len(errs), 0, "Should not have found any errors") # unknown table within module check_err('r(1)', 'mod1', 'unknown table', 'Unknown table for atom', f=compile.fact_errors) # wrong number of arguments check_err('p(1, 2, 3, 4)', 'mod1', 'only 3 arguments are permitted', 'Wrong number of arguments for atom', f=compile.fact_errors)
def test_use_modules(self): literal = compile.parse1('nova:p(1)', use_modules=False) self.assertEqual(str(literal.table), 'nova:p') self.assertIsNone(literal.table.service) rule = compile.parse1('nova:q(x) :- neutron:p(x)', use_modules=False) self.assertEqual(rule.head.table.table, 'nova:q') self.assertEqual(rule.head.table.service, None) self.assertEqual(rule.body[0].table.table, 'neutron:p') self.assertEqual(rule.body[0].table.service, None)
def test_modals(self): g = compile.RuleDependencyGraph() g.formula_insert(compile.parse1('p(x) :- q(x)')) g.formula_insert(compile.parse1('q(x) :- r(x)')) g.formula_insert(compile.parse1('execute[p(x)] :- q(x)')) chgs = g.formula_insert(compile.parse1('execute[r(x)] :- q(x)')) g.formula_insert(compile.parse1('other[s(x)] :- q(x)')) self.assertTrue(set(g.tables_with_modal('execute')), set(['p', 'r'])) g.undo_changes(chgs) self.assertTrue(set(g.tables_with_modal('execute')), set(['p']))
def test_empty(self): # full empty th = nonrecursive.NonrecursiveRuleTheory() th.insert(compile.parse1('p(x) :- q(x)')) th.insert(compile.parse1('p(1)')) th.insert(compile.parse1('q(2)')) th.empty() self.assertEqual(len(th.content()), 0) # empty with tablenames th = nonrecursive.NonrecursiveRuleTheory() th.insert(compile.parse1('p(x) :- q(x)')) th.insert(compile.parse1('p(1)')) th.insert(compile.parse1('q(2)')) th.empty(['p']) e = helper.datalog_equal(th.content_string(), 'q(2)') self.assertTrue(e) # empty with invert th = nonrecursive.NonrecursiveRuleTheory() th.insert(compile.parse1('p(x) :- q(x)')) th.insert(compile.parse1('p(1)')) th.insert(compile.parse1('q(2)')) th.empty(['p'], invert=True) correct = ('p(x) :- q(x) p(1)') e = helper.datalog_equal(th.content_string(), correct) self.assertTrue(e)
def test_empty(self): # full empty th = NonrecursiveRuleTheory() th.insert(compile.parse1('p(x) :- q(x)')) th.insert(compile.parse1('p(1)')) th.insert(compile.parse1('q(2)')) th.empty() self.assertEqual(len(th.content()), 0) # empty with tablenames th = NonrecursiveRuleTheory() th.insert(compile.parse1('p(x) :- q(x)')) th.insert(compile.parse1('p(1)')) th.insert(compile.parse1('q(2)')) th.empty(['p']) e = helper.datalog_equal(th.content_string(), 'q(2)') self.assertTrue(e) # empty with invert th = NonrecursiveRuleTheory() th.insert(compile.parse1('p(x) :- q(x)')) th.insert(compile.parse1('p(1)')) th.insert(compile.parse1('q(2)')) th.empty(['p'], invert=True) correct = ('p(x) :- q(x) p(1)') e = helper.datalog_equal(th.content_string(), correct) self.assertTrue(e)
def test_empty(self): # full empty th = nonrecursive.NonrecursiveRuleTheory() th.insert(compile.parse1("p(x) :- q(x)")) th.insert(compile.parse1("p(1)")) th.insert(compile.parse1("q(2)")) th.empty() self.assertEqual(len(th.content()), 0) # empty with tablenames th = nonrecursive.NonrecursiveRuleTheory() th.insert(compile.parse1("p(x) :- q(x)")) th.insert(compile.parse1("p(1)")) th.insert(compile.parse1("q(2)")) th.empty(["p"]) e = helper.datalog_equal(th.content_string(), "q(2)") self.assertTrue(e) # empty with invert th = nonrecursive.NonrecursiveRuleTheory() th.insert(compile.parse1("p(x) :- q(x)")) th.insert(compile.parse1("p(1)")) th.insert(compile.parse1("q(2)")) th.empty(["p"], invert=True) correct = "p(x) :- q(x) p(1)" e = helper.datalog_equal(th.content_string(), correct) self.assertTrue(e)
def test_no_split_parsing(self): th = NonrecursiveRuleTheory() th.insert(compile.parse1('nova:v(x, y) :- u(x, y)', use_modules=False)) self.assertEqual(th.arity('nova:v'), 2) self.assertIsNone(th.arity('nova:v', modal='insert')) th.insert(compile.parse1('insert[neutron:v(x, y, z)] :- u(x, y)', use_modules=False)) self.assertEqual(th.arity('nova:v'), 2) self.assertEqual(th.arity('neutron:v', modal='insert'), 3)
def test_no_split_parsing(self): th = nonrecursive.NonrecursiveRuleTheory() th.insert(compile.parse1('nova:v(x, y) :- u(x, y)', use_modules=False)) self.assertEqual(th.arity('nova:v'), 2) self.assertIsNone(th.arity('nova:v', modal='insert')) th.insert( compile.parse1('insert[neutron:v(x, y, z)] :- u(x, y)', use_modules=False)) self.assertEqual(th.arity('nova:v'), 2) self.assertEqual(th.arity('neutron:v', modal='insert'), 3)
def test_dependency_batch(self): obj = self.MyObject() run = agnostic.Runtime() run.create_policy('test') run.insert('p(x) :- q(x)') run.register_trigger('p', lambda old, new: obj.increment()) rule = compile.parse1('q(x) :- r(x)') data = compile.parse1('r(1)') run.update([compile.Event(rule, target='test'), compile.Event(data, target='test')]) self.assertEqual(obj.value, 1)
def test_regular_parsing(self): th = nonrecursive.NonrecursiveRuleTheory() th.insert(compile.parse1('p(x) :- q(x, y)')) th.insert(compile.parse1('execute[r(x)] :- t(x, y)')) th.insert(compile.parse1('execute[nova:s(x, y)] :- u(x, y)')) th.insert(compile.parse1('execute[nova:noargs()] :- true')) self.assertEqual(th.arity('p'), 1) self.assertIsNone(th.arity('q')) self.assertIsNone(th.arity('r')) self.assertIsNone(th.arity('nova:s')) self.assertEqual(th.arity('r', modal='execute'), 1) self.assertEqual(th.arity('nova:s', modal='execute'), 2) self.assertEqual(th.arity('nova:noargs', modal='execute'), 0)
def test_regular_parsing(self): th = nonrecursive.NonrecursiveRuleTheory() th.insert(compile.parse1("p(x) :- q(x, y)")) th.insert(compile.parse1("execute[r(x)] :- t(x, y)")) th.insert(compile.parse1("execute[nova:s(x, y)] :- u(x, y)")) th.insert(compile.parse1("execute[nova:noargs()] :- true")) self.assertEqual(th.arity("p"), 1) self.assertIsNone(th.arity("q")) self.assertIsNone(th.arity("r")) self.assertIsNone(th.arity("nova:s")) self.assertEqual(th.arity("r", modal="execute"), 1) self.assertEqual(th.arity("nova:s", modal="execute"), 2) self.assertEqual(th.arity("nova:noargs", modal="execute"), 0)
def test_regular_parsing(self): th = NonrecursiveRuleTheory() th.insert(compile.parse1('p(x) :- q(x, y)')) th.insert(compile.parse1('execute[r(x)] :- t(x, y)')) th.insert(compile.parse1('execute[nova:s(x, y)] :- u(x, y)')) th.insert(compile.parse1('execute[nova:noargs()] :- true')) self.assertEqual(th.arity('p'), 1) self.assertIsNone(th.arity('q')) self.assertIsNone(th.arity('r')) self.assertIsNone(th.arity('nova:s')) self.assertEqual(th.arity('r', modal='execute'), 1) self.assertEqual(th.arity('nova:s', modal='execute'), 2) self.assertEqual(th.arity('nova:noargs', modal='execute'), 0)
def test_discard_rules_with_same_head(self): rule1 = compile.parse1('p(x,y) :- q(x), r(y)') rule2 = compile.parse1('p(x,y) :- s(x), t(y)') self.assertTrue(self.ruleset.add_rule('p', rule1)) self.assertTrue(self.ruleset.add_rule('p', rule2)) self.assertTrue('p' in self.ruleset) self.assertTrue(rule1 in self.ruleset.get_rules('p')) self.assertTrue(rule2 in self.ruleset.get_rules('p')) self.assertTrue(self.ruleset.discard_rule('p', rule1)) self.assertTrue(self.ruleset.discard_rule('p', rule2)) self.assertFalse('p' in self.ruleset) self.assertEqual([], self.ruleset.keys())
def test_dependency_batch(self): obj = self.MyObject() run = agnostic.Runtime() run.create_policy('test') run.insert('p(x) :- q(x)') run.register_trigger('p', lambda old, new: obj.increment()) rule = compile.parse1('q(x) :- r(x)') data = compile.parse1('r(1)') run.update([ compile.Event(rule, target='test'), compile.Event(data, target='test') ]) self.assertEqual(obj.value, 1)
def test_add_rules_with_same_head(self): rule1 = compile.parse1('p(x,y) :- q(x), r(y)') rule2 = compile.parse1('p(x,y) :- s(x), t(y)') self.assertTrue(self.ruleset.add_rule('p', rule1)) self.assertIn('p', self.ruleset) self.assertEqual([rule1], self.ruleset.get_rules('p')) self.assertEqual(['p'], self.ruleset.keys()) self.assertTrue(self.ruleset.add_rule('p', rule2)) self.assertIn('p', self.ruleset) self.assertIn(rule1, self.ruleset.get_rules('p')) self.assertIn(rule2, self.ruleset.get_rules('p')) self.assertEqual(['p'], self.ruleset.keys())
def test_add_rules_with_different_head(self): rule1 = compile.parse1('p1(x,y) :- q(x), r(y)') rule2 = compile.parse1('p2(x,y) :- s(x), t(y)') self.assertTrue(self.ruleset.add_rule('p1', rule1)) self.assertTrue(self.ruleset.add_rule('p2', rule2)) self.assertTrue('p1' in self.ruleset) self.assertEqual([rule1], self.ruleset.get_rules('p1')) self.assertTrue('p1' in self.ruleset.keys()) self.assertTrue('p2' in self.ruleset) self.assertEqual([rule2], self.ruleset.get_rules('p2')) self.assertTrue('p2' in self.ruleset.keys())
def test_eliminate_column_references_body_order(self): """Test eliminate_column_references preserves order insensitivity.""" run = agnostic.Runtime() run.create_policy('nova') schema = compile.Schema({'q': ('id', 'name', 'status'), 'r': ('id', 'age', 'weight')}) theories = {'nova': self.SchemaWrapper(schema)} rule1 = compile.parse1("p(x) :- nova:q(id=x, 2=y), nova:r(id=x)" ).eliminate_column_references(theories) rule2 = compile.parse1("p(x) :- nova:r(id=x), nova:q(id=x, 2=y)" ).eliminate_column_references(theories) self.assertEqual(rule1, rule2, 'eliminate_column_references failed to ' 'preserve order insensitivity')
def check_err(self, input_string, unsafe_lit_strings, msg): rule = compile.parse1(input_string) try: compile.reorder_for_safety(rule) self.fail("Failed to raise exception for " + input_string) except PolicyException as e: errmsg = str(e) # parse then print to string so string rep same in err msg unsafe_lits = [str(compile.parse1(x)) for x in unsafe_lit_strings] missing_lits = [m for m in unsafe_lits if m + " (vars" not in errmsg] if len(missing_lits) > 0: self.fail( "Unsafe literals {} not reported in error: {}".format( ";".join(missing_lits), errmsg))
def test_old_new_correctness(self): obj = self.MyObject() run = agnostic.Runtime() run.create_policy('test') run.insert('p(x) :- q(x)') run.insert('q(x) :- r(x), not s(x)') run.insert('r(1) r(2) r(3)') run.insert('s(2)') oldp = set(compile.parse('p(1) p(3)')) newp = set(compile.parse('p(1) p(2)')) run.register_trigger('p', lambda old, new: obj.equal(oldp, newp, old, new)) run.update([compile.Event(compile.parse1('s(3)')), compile.Event(compile.parse1('s(2)'), insert=False)]) self.assertEqual(obj.equals, True)
def check_err(self, input_string, unsafe_lit_strings, msg): rule = compile.parse1(input_string) try: compile.reorder_for_safety(rule) self.fail("Failed to raise exception for " + input_string) except exception.PolicyException as e: errmsg = str(e) # parse then print to string so string rep same in err msg unsafe_lits = [str(compile.parse1(x)) for x in unsafe_lit_strings] missing_lits = [m for m in unsafe_lits if m + " (vars" not in errmsg] if len(missing_lits) > 0: self.fail( "Unsafe literals {} not reported in error: {}".format( ";".join(missing_lits), errmsg))
def test_error_checking(self): """Test error-checking on insertion of rules.""" code = ("p(x) :- q(x)") run = self.prep_runtime(code) result = run.get_target(MAT_THEORY).policy() self.assertTrue(len(result) == 1) self.assertTrue(compile.parse1("p(x) :- q(x)") in result) # safety 1 code = ("p(x) :- not q(x)") run = self.prep_runtime("", "** Safety 1 **") permitted, changes = run.insert(code, MAT_THEORY) self.assertFalse(permitted) # safety 2 code = ("p(x) :- q(y)") run = self.prep_runtime("", "** Safety 2 **") permitted, changes = run.insert(code, MAT_THEORY) self.assertFalse(permitted) # TODO(thinrichs): weaken cross-policy recursion restriction # so that we can include recursion within a single theory. # recursion into classification theory # code = ("p(x) :- p(x)") # run = self.prep_runtime("", "** Classification Recursion **") # permitted, changes = run.insert(code, MAT_THEORY) # self.assertTrue(permitted) # stratification into classification theory code = ("p(x) :- q(x), not p(x)") run = self.prep_runtime("", "** Classification Stratification **") permitted, changes = run.insert(code, MAT_THEORY) self.assertFalse(permitted)
def test_initialize_tables_dse(self): """Test performance of initializing data with DSE and Engine. This test populates the tables exported by a datasource driver, and then invokes the poll() method to send that data to the policy engine. It tests the amount of time to send tables across the DSE and load them into the policy engine. """ MAX_TUPLES = 700 # install datasource driver we can control self.cage.loadModule( "TestDriver", helper.data_module_path("../tests/datasources/test_driver.py")) self.cage.createservice(name="data", moduleName="TestDriver", args=helper.datasource_openstack_args()) driver = self.cage.service_object('data') driver.poll_time = 0 self.engine.create_policy('data') # set return value for datasource driver facts = [(1, 2.3, 'foo', 'bar', i, 'a' * 100 + str(i)) for i in range(MAX_TUPLES)] driver.state = {'p': facts} # Send formula to engine (so engine subscribes to data:p) policy = self.engine.DEFAULT_THEORY formula = compile.parse1( 'q(1) :- data:p(1, 2.3, "foo", "bar", 1, %s)' % ('a' * 100 + '1')) self.api['rule'].publish('policy-update', [agnostic.Event(formula, target=policy)]) # Poll data and wait til it arrives at engine driver.poll() self.wait_til_query_nonempty('q(1)', policy)
def test_clear_ruleset(self): rule1 = compile.parse1('p(x,y) :- q(x), r(y)') self.ruleset.add_rule('p', rule1) self.ruleset.clear() self.assertFalse('p' in self.ruleset) self.assertEqual([], self.ruleset.keys())
def test_tablename(self): """Test correct parsing of tablenames.""" p = compile.parse1('p(1)') self.assertEqual(p.table.table, 'p') self.assertIsNone(p.table.modal) self.assertIsNone(p.table.service) p = compile.parse1('nova:p(1)') self.assertEqual(p.table.table, 'p') self.assertIsNone(p.table.modal) self.assertEqual(p.table.service, 'nova') p = compile.parse1('execute[nova:p(1)]') self.assertEqual(p.table.table, 'p') self.assertEqual(p.table.modal, 'execute') self.assertEqual(p.table.service, 'nova')
def test_initialize_tables_dse(self): """Test performance of initializing data with DSE and Engine. This test populates the tables exported by a datasource driver, and then invokes the poll() method to send that data to the policy engine. It tests the amount of time to send tables across the DSE and load them into the policy engine. """ MAX_TUPLES = 700 # install datasource driver we can control self.cage.loadModule("TestDriver", helper.data_module_path("../tests/datasources/test_driver.py")) self.cage.createservice(name="data", moduleName="TestDriver", args=helper.datasource_openstack_args()) driver = self.cage.service_object("data") driver.poll_time = 0 self.engine.create_policy("data") # set return value for datasource driver facts = [(1, 2.3, "foo", "bar", i, "a" * 100 + str(i)) for i in range(MAX_TUPLES)] driver.state = {"p": facts} # Send formula to engine (so engine subscribes to data:p) policy = self.engine.DEFAULT_THEORY formula = compile.parse1('q(1) :- data:p(1, 2.3, "foo", "bar", 1, %s)' % ("a" * 100 + "1")) self.api["rule"].publish("policy-update", [compile.Event(formula, target=policy)]) # Poll data and wait til it arrives at engine driver.poll() self.wait_til_query_nonempty("q(1)", policy)
def check_err(code_string, theory, emsg, msg, f=compile.rule_errors): rule = compile.parse1(code_string) errs = f(rule, run.theory, theory) self.assertTrue( any(emsg in str(err) for err in errs), msg + ":: Failed to find error message '" + emsg + "' in: " + ";".join(str(e) for e in errs))
def test_initialize_tables_dse(self): """Test performance of initializing data with DSE and Engine. This test populates the tables exported by a datasource driver, and then invokes the poll() method to send that data to the policy engine. It tests the amount of time to send tables across the DSE and load them into the policy engine. """ MAX_TUPLES = 700 # install datasource driver we can control kwds = {} kwds['name'] = 'data' kwds['args'] = helper.datasource_openstack_args() kwds['driver'] = 'performance' driver = self.cage.create_datasource_service(kwds) self.cage.register_service(driver) driver.poll_time = 0 self.engine.create_policy('data') # set return value for datasource driver facts = [(1, 2.3, 'foo', 'bar', i, 'a'*100 + str(i)) for i in range(MAX_TUPLES)] driver.state = {'p': facts} # Send formula to engine (so engine subscribes to data:p) policy = self.engine.DEFAULT_THEORY formula = compile.parse1( 'q(1) :- data:p(1, 2.3, "foo", "bar", 1, %s)' % ('a'*100 + '1')) self.engine.process_policy_update( [compile.Event(formula, target=policy)]) # Poll data and wait til it arrives at engine driver.poll() self.wait_til_query_nonempty('q(1)', policy)
def test_error_checking(self): """Test error-checking on insertion of rules.""" code = ("p(x) :- q(x)") run = self.prep_runtime(code) result = run.get_target(MAT_THEORY).policy() self.assertEqual(1, len(result)) self.assertIn(compile.parse1("p(x) :- q(x)"), result) # safety 1 code = ("p(x) :- not q(x)") run = self.prep_runtime("", "** Safety 1 **") permitted, changes = run.insert(code, MAT_THEORY) self.assertFalse(permitted) # safety 2 code = ("p(x) :- q(y)") run = self.prep_runtime("", "** Safety 2 **") permitted, changes = run.insert(code, MAT_THEORY) self.assertFalse(permitted) # TODO(thinrichs): weaken cross-policy recursion restriction # so that we can include recursion within a single theory. # recursion into classification theory # code = ("p(x) :- p(x)") # run = self.prep_runtime("", "** Classification Recursion **") # permitted, changes = run.insert(code, MAT_THEORY) # self.assertTrue(permitted) # stratification into classification theory code = ("p(x) :- q(x), not p(x)") run = self.prep_runtime("", "** Classification Stratification **") permitted, changes = run.insert(code, MAT_THEORY) self.assertFalse(permitted)
def test_eliminate_column_references_body_order(self): """Test eliminate_column_references preserves order insensitivity.""" run = agnostic.Runtime() run.create_policy('nova') schema = compile.Schema({'q': ('id', 'name', 'status'), 'r': ('id', 'age', 'weight')}) theories = {'nova': self.SchemaWrapper(schema)} rule1 = compile.parse1( "p(x) :- nova:q(id=x, 2=y), nova:r(id=x)" ).eliminate_column_references_and_pad_positional(theories) rule2 = compile.parse1( "p(x) :- nova:r(id=x), nova:q(id=x, 2=y)" ).eliminate_column_references_and_pad_positional(theories) self.assertEqual(rule1, rule2, 'eliminate_column_references failed to ' 'preserve order insensitivity')
def insert_rule(self, engine, statement, target=None): statement = compile.parse1(statement) if target is None: e = compile.Event(statement) else: e = compile.Event(statement, target=target) engine.process_policy_update([e])
def test_initialize_tables_dse(self): """Test performance of initializing data with DSE and Engine. This test populates the tables exported by a datasource driver, and then invokes the poll() method to send that data to the policy engine. It tests the amount of time to send tables across the DSE and load them into the policy engine. """ MAX_TUPLES = 700 # install datasource driver we can control kwds = {} kwds['name'] = 'data' kwds['args'] = helper.datasource_openstack_args() kwds['driver'] = 'performance' driver = self.cage.create_datasource_service(kwds) self.cage.register_service(driver) driver.poll_time = 0 self.engine.create_policy('data') # set return value for datasource driver facts = [(1, 2.3, 'foo', 'bar', i, 'a' * 100 + str(i)) for i in range(MAX_TUPLES)] driver.state = {'p': facts} # Send formula to engine (so engine subscribes to data:p) policy = self.engine.DEFAULT_THEORY formula = compile.parse1( 'q(1) :- data:p(1, 2.3, "foo", "bar", 1, %s)' % ('a' * 100 + '1')) self.engine.process_policy_update( [compile.Event(formula, target=policy)]) # Poll data and wait til it arrives at engine driver.poll() self.wait_til_query_nonempty('q(1)', policy)
def test_add_fact(self): fact1 = Fact('p', (1, 2, 3)) equivalent_rule = compile.Rule(compile.parse1('p(1,2,3)'), ()) self.assertTrue(self.ruleset.add_rule('p', fact1)) self.assertTrue('p' in self.ruleset) self.assertEqual([equivalent_rule], self.ruleset.get_rules('p')) self.assertEqual(['p'], self.ruleset.keys())
def test_add_fact(self): fact1 = compile.Fact('p', (1, 2, 3)) equivalent_rule = compile.Rule(compile.parse1('p(1,2,3)'), ()) self.assertTrue(self.ruleset.add_rule('p', fact1)) self.assertTrue('p' in self.ruleset) self.assertEqual([equivalent_rule], self.ruleset.get_rules('p')) self.assertEqual(['p'], self.ruleset.keys())
def test_old_new_correctness(self): obj = self.MyObject() run = agnostic.Runtime() run.create_policy('test') run.insert('p(x) :- q(x)') run.insert('q(x) :- r(x), not s(x)') run.insert('r(1) r(2) r(3)') run.insert('s(2)') oldp = set(compile.parse('p(1) p(3)')) newp = set(compile.parse('p(1) p(2)')) run.register_trigger('p', lambda old, new: obj.equal(oldp, newp, old, new)) run.update([ compile.Event(compile.parse1('s(3)')), compile.Event(compile.parse1('s(2)'), insert=False) ]) self.assertEqual(obj.equals, True)
def test_dependencies(self): g = compile.RuleDependencyGraph() g.formula_insert(compile.parse1('p(x) :- q(x), r(x)')) g.formula_insert(compile.parse1('q(x) :- t(x), not s(x)')) self.assertEqual(g.dependencies('p'), set(['p', 'q', 'r', 't', 's'])) self.assertEqual(g.dependencies('q'), set(['q', 't', 's'])) self.assertEqual(g.dependencies('r'), set(['r'])) self.assertEqual(g.dependencies('t'), set(['t'])) self.assertEqual(g.dependencies('s'), set(['s'])) # cyclic case g = compile.RuleDependencyGraph() g.formula_insert(compile.parse1('p(x) :- q(x), r(x)')) g.formula_insert(compile.parse1('q(x) :- t(x), not s(x)')) g.formula_insert(compile.parse1('t(x) :- t(x), p(x), q(x)')) self.assertEqual(g.dependencies('p'), set(['p', 'q', 'r', 't', 's'])) self.assertEqual(g.dependencies('q'), set(['p', 'q', 'r', 't', 's'])) self.assertEqual(g.dependencies('r'), set(['r'])) self.assertEqual(g.dependencies('t'), set(['p', 'q', 'r', 't', 's'])) self.assertEqual(g.dependencies('s'), set(['s'])) g = compile.RuleDependencyGraph(head_to_body=False) g.formula_insert(compile.parse1('p(x) :- q(x), r(x)')) g.formula_insert(compile.parse1('q(x) :- t(x), not s(x)')) self.assertEqual(g.dependencies('p'), set(['p'])) self.assertEqual(g.dependencies('q'), set(['q', 'p'])) self.assertEqual(g.dependencies('r'), set(['r', 'p'])) self.assertEqual(g.dependencies('t'), set(['t', 'q', 'p'])) self.assertEqual(g.dependencies('s'), set(['s', 'q', 'p']))
def test_add_equivalent_rule(self): # equivalent_rule could be a fact because it has no body, and is # ground. equivalent_rule = compile.Rule(compile.parse1('p(1,2,3)'), ()) self.assertTrue(self.ruleset.add_rule('p', equivalent_rule)) self.assertTrue('p' in self.ruleset) self.assertEqual([equivalent_rule], self.ruleset.get_rules('p')) self.assertEqual(['p'], self.ruleset.keys())
def test_discard_rule(self): rule1 = compile.parse1('p(x,y) :- q(x), r(y)') self.assertTrue(self.ruleset.add_rule('p', rule1)) self.assertTrue('p' in self.ruleset) self.assertEqual([rule1], self.ruleset.get_rules('p')) self.assertTrue(self.ruleset.discard_rule('p', rule1)) self.assertFalse('p' in self.ruleset) self.assertEqual([], self.ruleset.keys())
def adjust_for_comparison(rules): # compile rule string into rule object # replace dict with tuple for sorting # 'id' field implicitly dropped if present rules = [(compile.parse1(rule['rule']), rule['name'], rule['comment']) for rule in rules] # sort lists for comparison return sorted(rules)
def test_batch_change(self): obj = self.MyObject() run = agnostic.Runtime() run.create_policy('test') run.register_trigger('p', lambda old, new: obj.increment()) p1 = compile.parse1('p(1)') result = run.update([compile.Event(p1, target='test')]) self.assertTrue(result[0], ("Update failed with errors: " + ";".join(str(x) for x in result[1]))) self.assertEqual(obj.value, 1)
def check_err(rule, errmsg, msg): rule = compile.parse1(rule) try: rule.eliminate_column_references(theories) self.fail("Failed to throw error {}".format(errmsg)) except (exception.PolicyException, exception.IncompleteSchemaException) as e: emsg = "Err messages '{}' should include '{}'".format( str(e), errmsg) self.assertTrue(errmsg in str(e), msg + ": " + emsg)
def test_rule_validation(self): """Test that rules are properly validated.""" # unsafe var in head rule = compile.parse1('p(x) :- q(y)') errs = compile.rule_errors(rule) self.assertEqual(len(errs), 1) # multiple unsafe vars in head rule = compile.parse1('p(x,y,z) :- q(w)') errs = compile.rule_errors(rule) self.assertEqual(len(set([str(x) for x in errs])), 3) # unsafe var in negtative literal: rule = compile.parse1('p(x) :- q(x), not r(y)') errs = compile.rule_errors(rule) self.assertEqual(len(set([str(x) for x in errs])), 1) # unsafe var in negative literal: ensure head doesn't make safe rule = compile.parse1('p(x) :- not q(x)') errs = compile.rule_errors(rule) self.assertEqual(len(set([str(x) for x in errs])), 1) # unsafe var in negative literal: # ensure partial safety not total safety rule = compile.parse1('p(x) :- q(x), not r(x,y)') errs = compile.rule_errors(rule) self.assertEqual(len(set([str(x) for x in errs])), 1) # unsafe var in negative literal: ensure double negs doesn't make safe rule = compile.parse1('p(x) :- q(x), not r(x,y), not s(x, y)') errs = compile.rule_errors(rule) self.assertEqual(len(set([str(x) for x in errs])), 1) # multiple heads with modal rule = compile.parse1('execute[p(x)], r(x) :- q(x)') errs = compile.rule_errors(rule) self.assertEqual(len(set([str(x) for x in errs])), 1) # modal in body rule = compile.parse1('p(x) :- execute[q(x)]') errs = compile.rule_errors(rule) self.assertEqual(len(set([str(x) for x in errs])), 1) # keywords rule = compile.parse1('equal(x) :- q(x)') errs = compile.rule_errors(rule) self.assertEqual(len(set([str(x) for x in errs])), 1)