def test_module_schemas(self): """Test that rules are properly checked against module schemas.""" modules = compile.ModuleSchemas( {'mod1': compile.Schema({'p': (1, 2, 3), 'q': (1,)}), 'mod2': compile.Schema({'p': (1,), 'q': (1, 2)})}) def check_err(code_string, emsg, msg, f=compile.rule_errors): rule = compile.parse1(code_string) errs = f(rule, modules) 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, modules) self.assertEqual(len(errs), 0, "Should not have found any errors") # unknown module check_err('p(x) :- q(x), mod3:q(x), r(x)', 'unknown module', 'Unknown module for rule') # unknown table within module check_err('p(x) :- q(x), mod1:r(x), r(x)', 'unknown table', 'Unknown table for rule') # wrong number of arguments check_err('p(x) :- q(x), mod1:p(x,y,z,w), r(x)', 'only 3 arguments are permitted', 'Wrong number of arguments for rule') # same tests for an atom # no errors atom = compile.parse1('mod1:p(1, 2, 2)') errs = compile.fact_errors(atom, modules) self.assertEqual(len(errs), 0, "Should not have found any errors") # unknown module check_err('mod3:q(1)', 'unknown module', 'Unknown module for atom', f=compile.fact_errors) # unknown table within module check_err('mod1:r(1)', 'unknown table', 'Unknown table for atom', f=compile.fact_errors) # wrong number of arguments check_err('mod1:p(1, 2, 3, 4)', 'only 3 arguments are permitted', 'Wrong number of arguments for atom', f=compile.fact_errors)
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_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_rule_api_model(self): """Test the rule api model. Same as test_multiple except we use the api interface instead of the DSE interface. """ api = self.api cage = self.cage engine = self.engine # Insert formula (which creates neutron services) net_formula = create_networkXnetwork_group('p') LOG.debug("Sending formula: {}".format(str(net_formula))) engine.debug_mode() context = {'policy_id': engine.DEFAULT_THEORY} (id1, rule) = api['rule'].add_item( {'rule': str(net_formula)}, {}, context=context) # Poll neutron = cage.service_object('neutron') neutron2 = cage.service_object('neutron2') neutron.poll() neutron2.poll() helper.pause() # Insert a second formula other_formula = compile.parse1('q(x,y) :- p(x,y)') (id2, rule) = api['rule'].add_item( {'rule': str(other_formula)}, {}, context=context) helper.pause() # give time for messages/creation of services ans1 = ('p("240ff9df-df35-43ae-9df5-27fae87f2492", ' ' "240ff9df-df35-43ae-9df5-27fae87f2492") ') ans2 = ('q("240ff9df-df35-43ae-9df5-27fae87f2492", ' ' "240ff9df-df35-43ae-9df5-27fae87f2492") ') e = helper.db_equal(engine.select('p(x,y)'), ans1) self.assertTrue(e, "Insert rule-api 1") e = helper.db_equal(engine.select('q(x,y)'), ans2) self.assertTrue(e, "Insert rule-api 2") # Get formula ruleobj = api['rule'].get_item(id1, {}, context=context) self.assertTrue(e, net_formula == compile.parse1(ruleobj['rule'])) # Get all formulas ds = api['rule'].get_items({}, context=context)['results'] self.assertEqual(len(ds), 2) ids = set([x['id'] for x in ds]) rules = set([compile.parse1(x['rule']) for x in ds]) self.assertEqual(ids, set([id1, id2])) self.assertEqual(rules, set([net_formula, other_formula])) # Delete formula api['rule'].delete_item(id1, {}, context=context) # Get all formulas ds = api['rule'].get_items({}, context=context)['results'] self.assertEqual(len(ds), 1) ids = sorted([x['id'] for x in ds]) self.assertEqual(ids, sorted([id2]))
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_rule_api_model(self): """Test the rule api model. Same as test_multiple except we use the api interface instead of the DSE interface. """ api = self.api cage = self.cage engine = self.engine # Insert formula net_formula = create_networkXnetwork_group('p') LOG.debug("Sending formula: %s", net_formula) engine.debug_mode() context = {'policy_id': engine.DEFAULT_THEORY} (id1, rule) = api['rule'].add_item( {'rule': str(net_formula)}, {}, context=context) # Poll neutron = cage.service_object('neutron') neutron2 = cage.service_object('neutron2') neutron.poll() neutron2.poll() # Insert a second formula other_formula = compile.parse1('q(x,y) :- p(x,y)') (id2, rule) = api['rule'].add_item( {'rule': str(other_formula)}, {}, context=context) ans1 = ('p("240ff9df-df35-43ae-9df5-27fae87f2492", ' ' "240ff9df-df35-43ae-9df5-27fae87f2492") ') ans2 = ('q("240ff9df-df35-43ae-9df5-27fae87f2492", ' ' "240ff9df-df35-43ae-9df5-27fae87f2492") ') # Wait for first query so messages can be delivered. # But once the p table has its data, no need to wait anymore. helper.retry_check_db_equal(engine, 'p(x,y)', ans1) e = helper.db_equal(engine.select('q(x,y)'), ans2) self.assertTrue(e, "Insert rule-api 2") # Get formula ruleobj = api['rule'].get_item(id1, {}, context=context) self.assertTrue(e, net_formula == compile.parse1(ruleobj['rule'])) # Get all formulas ds = api['rule'].get_items({}, context=context)['results'] self.assertEqual(len(ds), 2) ids = set([x['id'] for x in ds]) rules = set([compile.parse1(x['rule']) for x in ds]) self.assertEqual(ids, set([id1, id2])) self.assertEqual(rules, set([net_formula, other_formula])) # Delete formula api['rule'].delete_item(id1, {}, context=context) # Get all formulas ds = api['rule'].get_items({}, context=context)['results'] self.assertEqual(len(ds), 1) ids = sorted([x['id'] for x in ds]) self.assertEqual(ids, sorted([id2]))
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 compile.CongressException 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 add_item(self, item, id_=None, context=None): """Add item to model. Args: item: The item to add to the model id_: The ID of the item, or None if an ID should be generated context: Key-values providing frame of reference of request Returns: Tuple of (ID, newly_created_item) Raises: KeyError: ID already exists. """ # TODO(thinrichs): add comment property to rule if id_ is not None: raise NotImplemented str_rule = item['rule'] rule = compile.parse1(str_rule) changes = self.change_rule(rule, context) for change in changes: if change.formula == rule: d = {'rule': str(rule), 'id': rule.id, 'comment': None} return (rule.id, d) # rule already existed policy_name = self.policy_name(context) for p in self.engine.theory[policy_name].policy(): if p == rule: d = {'rule': str(rule), 'id': rule.id, 'comment': 'None'} return (rule.id, d) raise Exception("add_item added a rule but then could not find it.")
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 compile.CongressException 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) # 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_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_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 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_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 test_policy_tables(self): """Test basic DSE functionality with policy engine and the API.""" cage = congress.dse.d6cage.d6Cage() # so that we exit once test finishes; all other threads are forced # to be daemons cage.daemon = True cage.start() cage.loadModule("TestDriver", helper.data_module_path("test_driver.py")) cage.loadModule("TestPolicy", helper.policy_module_path()) cage.createservice(name="data", moduleName="TestDriver", args=helper.datasource_openstack_args()) # using regular testdriver as API for now cage.createservice(name="api", moduleName="TestDriver", args=helper.datasource_openstack_args()) cage.createservice(name="policy", moduleName="TestPolicy", args={'d6cage': cage, 'rootdir': ''}) data = cage.services['data']['object'] api = cage.services['api']['object'] policy = cage.services['policy']['object'] policy.subscribe('api', 'policy-update', callback=policy.receive_policy_update) # simulate API call for insertion of policy statements formula = compile.parse1('p(x) :- data:q(x)') api.publish('policy-update', [runtime.Event(formula)]) helper.pause() # simulate data source publishing to q formula = compile.parse1('q(1)') data.publish('q', [runtime.Event(formula)]) helper.pause() # give other threads chance to run # check that policy did the right thing with data e = helper.db_equal(policy.select('data:q(x)'), 'data:q(1)') self.assertTrue(e, 'Policy insert 1') e = helper.db_equal(policy.select('p(x)'), 'p(1)') self.assertTrue(e, 'Policy insert 2') #check that publishing into 'p' does not work formula = compile.parse1('p(3)') data.publish('p', [runtime.Event(formula)]) helper.pause() e = helper.db_equal(policy.select('p(x)'), 'p(1)') self.assertTrue(e, 'Policy noninsert')
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)
def get_items(self, context=None): """Get items in model. Args: context: Key-values providing frame of reference of request Returns: A tuple (id, item) for all items in model. """ LOG.info("get_items(context=%s)", str(context)) if context['ds_id'] in self.engine.theory: tablename = context['table_id'] arity = self.engine.theory[context['ds_id']].get_arity(tablename) if arity is None: return [] args = ["x" + str(i) for i in xrange(0, arity)] query = compile.parse1(tablename + "(" + ",".join(args) + ")") LOG.info("query: " + str(query)) literals = self.engine.theory[context['ds_id']].select(query) else: tablename = context['ds_id'] + ":" + context['table_id'] arity = self.engine.theory[ self.engine.theory.DATABASE].get_arity(tablename) if arity is None: return [] args = ["x" + str(i) for i in xrange(0, arity)] query = compile.parse1(tablename + "(" + ",".join(args) + ")") LOG.info("query: " + str(query)) literals = self.engine.theory[ self.engine.theory.DATABASE].select(query) result = [] for lit in literals: d = {} d['data'] = [arg.name for arg in lit.arguments] # tuples don't have IDs for now. Could hash them I suppose. # But if you're trying to use an ID you're doing # something wrong. result.append((None, d)) return result
def test_policy_tables(self): """Test basic DSE functionality with policy engine and the API.""" cage = congress.dse.d6cage.d6Cage() cage.loadModule("TestDriver", helper.data_module_path("test_driver.py")) cage.loadModule("TestPolicy", helper.policy_module_path()) cage.createservice(name="data", moduleName="TestDriver", args=helper.datasource_openstack_args()) # using regular testdriver as API for now cage.createservice(name="api", moduleName="TestDriver", args=helper.datasource_openstack_args()) cage.createservice(name="policy", moduleName="TestPolicy", args={'d6cage': cage, 'rootdir': ''}) data = cage.services['data']['object'] api = cage.services['api']['object'] policy = cage.services['policy']['object'] policy.set_schema('data', compile.Schema({'q': (1,)})) policy.subscribe('api', 'policy-update', callback=policy.receive_policy_update) # simulate API call for insertion of policy statements formula = compile.parse1('p(x) :- data:q(x)') api.publish('policy-update', [runtime.Event(formula)]) helper.retry_check_nonempty_last_policy_change(policy) # simulate data source publishing to q formula = compile.parse1('q(1)') data.publish('q', [runtime.Event(formula)]) helper.retry_check_db_equal(policy, 'data:q(x)', 'data:q(1)') # check that policy did the right thing with data e = helper.db_equal(policy.select('p(x)'), 'p(1)') self.assertTrue(e, 'Policy insert') # check that publishing into 'p' does not work formula = compile.parse1('p(3)') data.publish('p', [runtime.Event(formula)]) # can't actually check that the update for p does not arrive # so instead wait a bit and check helper.pause() e = helper.db_equal(policy.select('p(x)'), 'p(1)') self.assertTrue(e, 'Policy non-insert')
def create_network_group(tablename): """Return rule of the form TABLENAME(x) :- neutron:network(..., x, ...) """ network_key_to_index = NeutronDriver.get_column_map( NeutronDriver.NETWORKS) network_id_index = network_key_to_index['id'] network_max_index = max(network_key_to_index.values()) net_args = ['x' + str(i) for i in xrange(0, network_max_index + 1)] formula = compile.parse1( '{}({}) :- neutron:networks({})'.format( tablename, 'x' + str(network_id_index), ",".join(net_args))) return formula
def create_network_group(tablename, full_neutron_tablename=None): if full_neutron_tablename is None: full_neutron_tablename = 'neutron:networks' network_key_to_index = NeutronDriver.network_key_position_map() network_id_index = network_key_to_index['id'] network_max_index = max(network_key_to_index.values()) network_args = ['x' + str(i) for i in xrange(0, network_max_index + 1)] formula = compile.parse1( '{}({}) :- {}({})'.format( tablename, 'x' + str(network_id_index), full_neutron_tablename, ",".join(network_args))) return formula
def create_network_group(tablename, full_neutron_tablename=None): driver = neutron_driver.NeutronDriver( args=helper.datasource_openstack_args()) if full_neutron_tablename is None: full_neutron_tablename = 'neutron:networks' network_key_to_index = driver.get_column_map( neutron_driver.NeutronDriver.NETWORKS) network_id_index = network_key_to_index['id'] network_max_index = max(network_key_to_index.values()) network_args = ['x' + str(i) for i in xrange(0, network_max_index + 1)] formula = compile.parse1('{}({}) :- {}({})'.format( tablename, 'x' + str(network_id_index), full_neutron_tablename, ",".join(network_args))) return formula
def create_networkXnetwork_group(tablename): network_key_to_index = NeutronDriver.get_column_map( NeutronDriver.NETWORKS) network_id_index = network_key_to_index['id'] network_max_index = max(network_key_to_index.values()) net1_args = ['x' + str(i) for i in xrange(0, network_max_index + 1)] net2_args = ['y' + str(i) for i in xrange(0, network_max_index + 1)] formula = compile.parse1( '{}({},{}) :- neutron:networks({}), neutron2:networks({})'.format( tablename, 'x' + str(network_id_index), 'y' + str(network_id_index), ",".join(net1_args), ",".join(net2_args))) return formula
def test_policy_subscriptions(self): """Test that policy engine subscriptions adjust to policy changes.""" engine = self.engine api = self.api cage = self.cage # Send formula helper.pause() formula = compile.parse1("p(y) :- neutron:networks(y)") LOG.debug("Sending formula: {}".format(str(formula))) api['rule'].publish('policy-update', [runtime.Event(formula)]) helper.pause() # give time for messages/creation of services # check we have the proper subscriptions self.assertTrue('neutron' in cage.services) neutron = cage.service_object('neutron') self.check_subscriptions(engine, [('neutron', 'networks')]) self.check_subscribers(neutron, [(engine.name, 'networks')])
def create_networkXnetwork_group(tablename): """Return rule of the form: TABLENAME(x,y) :- neutron:network(...,x,...),neutron:network(...,y,...) """ driver = neutron_driver.NeutronDriver( args=helper.datasource_openstack_args()) network_key_to_index = driver.get_column_map( neutron_driver.NeutronDriver.NETWORKS) network_id_index = network_key_to_index['id'] network_max_index = max(network_key_to_index.values()) net1_args = ['x' + str(i) for i in xrange(0, network_max_index + 1)] net2_args = ['y' + str(i) for i in xrange(0, network_max_index + 1)] formula = compile.parse1( '{}({},{}) :- neutron:networks({}), neutron2:networks({})'.format( tablename, 'x' + str(network_id_index), 'y' + str(network_id_index), ",".join(net1_args), ",".join(net2_args))) return formula
def delete_item(self, id_, context=None): """Remove item from model. Args: id_: The ID of the item to be removed context: Key-values providing frame of reference of request Returns: The removed item. Raises: KeyError: Item with specified id_ not present. """ item = self.get_item(id_, context) if item is None: raise KeyError('ID %s does not exist', id_) rule = compile.parse1(item['rule']) self.change_rule(rule, context, insert=False) return item
def test_policy_data(self): """Test policy properly inserts data and processes it normally.""" cage = congress.dse.d6cage.d6Cage() cage.loadModule("TestDriver", helper.data_module_path("test_driver.py")) cage.loadModule("TestPolicy", helper.policy_module_path()) cage.createservice(name="data", moduleName="TestDriver", args=helper.datasource_openstack_args()) cage.createservice(name="policy", moduleName="TestPolicy", args={'d6cage': cage, 'rootdir': ''}) data = cage.services['data']['object'] policy = cage.services['policy']['object'] # turn off module-schema syntax checking policy.set_schema('data', compile.Schema({'p': (1,)})) policy.subscribe('data', 'p', callback=policy.receive_data) formula = compile.parse1('p(1)') # sending a single Insert. (Default for Event is Insert.) data.publish('p', [runtime.Event(formula)]) helper.retry_check_db_equal(policy, 'data:p(x)', 'data:p(1)')
def test_policy_data(self): """Test policy properly inserts data and processes it normally.""" cage = congress.dse.d6cage.d6Cage() # so that we exit once test finishes; all other threads are forced # to be daemons cage.daemon = True cage.start() cage.loadModule("TestDriver", helper.data_module_path("test_driver.py")) cage.loadModule("TestPolicy", helper.policy_module_path()) cage.createservice(name="data", moduleName="TestDriver") cage.createservice(name="policy", moduleName="TestPolicy") data = cage.services['data']['object'] policy = cage.services['policy']['object'] policy.subscribe('data', 'p', callback=policy.receive_data) formula = compile.parse1('p(1)') # sending a single Insert. (Default for Event is Insert.) data.publish('p', [runtime.Event(formula)]) helper.pause() # give other threads chance to run e = helper.db_equal(policy.select('data:p(x)'), 'data:p(1)') self.assertTrue(e, 'Single insert')
def delete_item(self, id_, params, context=None): """Remove item from model. Args: id_: The ID of the item to be removed params: A dict-like object containing parameters from the request query string and body. context: Key-values providing frame of reference of request Returns: The removed item. Raises: KeyError: Item with specified id_ not present. """ item = self.get_item(id_, params, context) if item is None: raise KeyError('ID %s does not exist', id_) rule = compile.parse1(item['rule']) self.change_rule(rule, context, insert=False) db_policy_rules.delete_policy_rule(id_) return item
def benchmark_datasource_to_policy_update(self, size): """Benchmark small datsource update to policy propagation. Time the propagation of a datasource update from datasource.poll() to completion of a simple policy update. """ LOG.info("%s:: benchmarking datasource update of %d rows", size) self.datasource.datarows = size table_name = self.table_name # dummy policy only intended to produce a subscriber for the table key_to_index = self.datasource.get_column_map(table_name) id_index = 'x%d' % key_to_index.items()[0][1] max_index = max(key_to_index.values()) args = ['x%d' % i for i in xrange(max_index + 1)] formula = compile.parse1('p(%s) :- benchmark:%s(%s)' % (id_index, table_name, ','.join(args))) # publish the formula and verify we see a subscription LOG.debug('%s:: sending formula: %s', self.__class__.__name__, formula) self.api['rule'].publish('policy-update', [runtime.Event(formula)]) helper.retry_check_subscriptions( self.engine, [('benchmark', table_name)]) helper.retry_check_subscribers( self.datasource, [(self.engine.name, table_name)]) # intercept inbox.task_done() so we know when it's finished. Sadly, # eventlet doesn't have a condition-like object. fake_condition = eventlet.Queue() fake_notify = functools.partial(fake_condition.put_nowait, True) self.mox.StubOutWithMock(self.engine.inbox, "task_done") self.engine.inbox.task_done().WithSideEffects(fake_notify) self.mox.ReplayAll() LOG.info("%s:: polling datasource", self.__class__.__name__) self.datasource.poll() fake_condition.get(timeout=30) self.mox.VerifyAll()
def parse1(self, string): return compile.parse1(string, theories=self.theory)
def get_items(self, params, context=None): """Get items in model. Args: params: A dict-like object containing parameters from the request query string and body. context: Key-values providing frame of reference of request Returns: A dict containing at least a 'results' key whose value is a list of items in the model. Additional keys set in the dict will also be rendered for the user. """ LOG.info("get_items(context=%s)", context) gen_trace = False trace = "Not available" if 'trace' in params and params['trace'].lower() == 'true': gen_trace = True # table defined by data-source if 'ds_id' in context: service_name = context['ds_id'] service_obj = self.engine.d6cage.service_object(service_name) if service_obj is None: LOG.info("Unknown data-source name %s", service_name) return {"results": []} tablename = context['table_id'] if tablename not in service_obj.state: LOG.info("Unknown tablename %s for datasource %s", service_name, tablename) return {"results": []} results = [] for tup in service_obj.state[tablename]: d = {} d['data'] = tup results.append(d) # table defined by policy elif 'policy_id' in context: policy_name = context['policy_id'] if policy_name not in self.engine.theory: LOG.info("Unknown policy name %s", policy_name) return {"results": []} tablename = context['table_id'] if tablename not in self.engine.theory[policy_name].tablenames(): LOG.info("Unknown tablename %s for policy %s", tablename, policy_name) return {"results": []} arity = self.engine.theory[policy_name].get_arity(tablename) if arity is None: LOG.info("Unknown arity for table %s for policy %s", tablename, policy_name) return {"results": []} args = ["x" + str(i) for i in xrange(0, arity)] query = compile.parse1(tablename + "(" + ",".join(args) + ")") # LOG.debug("query: %s", query) result = self.engine.select(query, target=policy_name, trace=gen_trace) if gen_trace: literals = result[0] trace = result[1] else: literals = result # should NOT need to convert to set -- see bug 1344466 literals = frozenset(literals) # LOG.info("results: %s", '\n'.join(str(x) for x in literals)) results = [] for lit in literals: d = {} d['data'] = [arg.name for arg in lit.arguments] results.append(d) # unknown else: LOG.info("Unknown source for row data %s", context) results = {"results": []} if gen_trace: return {"results": results, "trace": trace} return {"results": results}
def str2form(formula_string, theories=None): return compile.parse1(formula_string, theories=theories)
def str2form(formula_string): return compile.parse1(formula_string)
def str2form(formula_string, module_schemas=None): return compile.parse1(formula_string, module_schemas=module_schemas)
def check_err(code_string, emsg, msg, f=compile.rule_errors): rule = compile.parse1(code_string) errs = f(rule, modules) 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_rule_dependency_graph(self): g = compile.RuleDependencyGraph() # first insertion g.formula_insert(compile.parse1('p(x), q(x) :- r(x), s(x)')) self.assertTrue(g.node_in('p')) self.assertTrue(g.node_in('q')) self.assertTrue(g.node_in('r')) self.assertTrue(g.node_in('s')) self.assertTrue(g.edge_in('p', 'r', False)) self.assertTrue(g.edge_in('p', 's', False)) self.assertTrue(g.edge_in('q', 'r', False)) self.assertTrue(g.edge_in('q', 's', False)) self.assertFalse(g.has_cycle()) # another insertion g.formula_insert(compile.parse1('r(x) :- t(x)')) self.assertTrue(g.node_in('p')) self.assertTrue(g.node_in('q')) self.assertTrue(g.node_in('r')) self.assertTrue(g.node_in('s')) self.assertTrue(g.edge_in('p', 'r', False)) self.assertTrue(g.edge_in('p', 's', False)) self.assertTrue(g.edge_in('q', 'r', False)) self.assertTrue(g.edge_in('q', 's', False)) self.assertTrue(g.node_in('t')) self.assertTrue(g.edge_in('r', 't', False)) self.assertFalse(g.has_cycle()) # 3rd insertion, creating a cycle g.formula_insert(compile.parse1('t(x) :- p(x)')) self.assertTrue(g.edge_in('t', 'p', False)) self.assertTrue(g.has_cycle()) # deletion g.formula_delete(compile.parse1('p(x), q(x) :- r(x), s(x)')) self.assertTrue(g.node_in('p')) self.assertTrue(g.node_in('r')) self.assertTrue(g.node_in('t')) self.assertTrue(g.edge_in('r', 't', False)) self.assertTrue(g.edge_in('t', 'p', False)) self.assertFalse(g.has_cycle()) # double-insertion g.formula_insert(compile.parse1('p(x) :- q(x), r(x)')) g.formula_insert(compile.parse1('p(1) :- r(1)')) self.assertTrue(g.has_cycle()) # deletion -- checking for bag semantics g.formula_delete(compile.parse1('p(1) :- r(1)')) self.assertTrue(g.has_cycle()) g.formula_delete(compile.parse1('p(x) :- q(x), r(x)')) self.assertFalse(g.has_cycle()) # update g.formula_update([ compile.Event(compile.parse1('a(x) :- b(x)')), compile.Event(compile.parse1('b(x) :- c(x)')), compile.Event(compile.parse1('c(x) :- a(x)')) ]) self.assertTrue(g.has_cycle()) g.formula_update( [compile.Event(compile.parse1('c(x) :- a(x)'), insert=False)]) self.assertFalse(g.has_cycle())
def test_module_schemas(self): """Test that rules are properly checked against module schemas.""" run = runtime.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_discard_nonexistent_rule(self): rule1 = compile.parse1('p(x,y) :- q(x), r(y)') self.assertFalse(self.ruleset.discard_rule('p', rule1)) self.assertFalse('p' in self.ruleset) self.assertEqual([], self.ruleset.keys())