Esempio n. 1
0
    def test_positional_args_padding_multiple_atoms(self):
        """Test positional args padding on a single atom."""
        def check(code, correct, msg, no_theory=False):
            actual = compile.parse1(
                code).eliminate_column_references_and_pad_positional(
                    {} if no_theory else theories)
            eq = helper.datalog_same(str(actual), correct)
            self.assertTrue(eq, msg)

        run = agnostic.Runtime()
        run.create_policy('nova')
        schema = compile.Schema({'q': ('id', 'name', 'status'),
                                 'r': ('id', 'age', 'weight')})
        theories = {'nova': self.SchemaWrapper(schema)}

        # Multiple atoms, no shared variable
        code = ("p(x) :- nova:q(x, y), nova:r(w)")
        correct = "p(x) :- nova:q(x, y, z0), nova:r(w, y0, y1)"
        check(code, correct, 'Multiple atoms')

        # Multiple atoms, some shared variable
        code = ("p(x) :- nova:q(x, y), nova:r(x)")
        correct = "p(x) :- nova:q(x, y, z0), nova:r(x, y0, y1)"
        check(code, correct, 'Multiple atoms')

        # Multiple atoms, same table
        code = ("p(x) :- nova:q(x, y), nova:q(x)")
        correct = "p(x) :- nova:q(x, y, z0), nova:q(x, w0, w1)"
        check(code, correct, 'Multiple atoms, same table')
Esempio n. 2
0
    def test_column_references_multiple_atoms(self):
        """Test column references occurring in multiple atoms in a rule."""
        def check(code, correct, msg):
            actual = compile.parse1(code).eliminate_column_references(theories)
            eq = helper.datalog_same(str(actual), correct)
            self.assertTrue(eq, msg)

        run = agnostic.Runtime()
        run.create_policy('nova')
        schema = compile.Schema({
            'q': ('id', 'name', 'status'),
            'r': ('id', 'age', 'weight')
        })
        theories = {'nova': self.SchemaWrapper(schema)}

        # Multiple atoms
        code = ("p(x) :- nova:q(id=x, 2=y), nova:r(id=x)")
        correct = "p(x) :- nova:q(x, x0, y), nova:r(x, y0, y1)"
        check(code, correct, 'Multiple atoms')

        # Multiple atoms sharing column name but different variables
        code = ("p(x) :- nova:q(id=x), nova:r(id=y)")
        correct = "p(x) :- nova:q(x, x0, x1), nova:r(y, y0, y1)"
        check(code, correct, 'Multiple atoms shared column name')

        # Multiple atoms, same table
        code = ("p(x) :- nova:q(id=x, 2=y), nova:q(id=x)")
        correct = "p(x) :- nova:q(x, x0, y), nova:q(x, y0, y1)"
        check(code, correct, 'Multiple atoms, same table')
Esempio n. 3
0
 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("../tests/datasources/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.create_policy('data')
     policy.set_schema('data', compile.Schema({'p': (1, )}))
     policy.subscribe('data', 'p', callback=policy.receive_data)
     formula = policy.parse1('p(1)')
     # sending a single Insert.  (Default for Event is Insert.)
     data.publish('p', [agnostic.Event(formula)])
     helper.retry_check_db_equal(policy, 'data:p(x)', 'data:p(1)')
Esempio n. 4
0
 def test_inject(self):
     theory = mock.MagicMock(spec=topdown.TopDownTheory)
     world = {'t': theory}
     theory.name = 't'
     theory.schema = ast.Schema()
     theory.schema.map['l'] = [mkc('Int'), mkc('Int')]
     theory.select.return_value = ast.parse('l(1,2). l(3,4). l(5,6)')
     context = z3theory.Z3Context()
     # An external theory world
     context.theories = world
     # inject the declaration of external relation without rules
     param_types = [
         context.type_registry.get_type(typ)
         for typ in ['Int', 'Int', 'Bool']
     ]
     relation = z3.Function('t:l', *param_types)
     context.context.register_relation(relation)
     context.relations['t:l'] = relation
     # the test
     context.inject('t', 'l')
     rules = context.context._rules  # pylint: disable=E1101
     self.assertIs(True, all(r.decl().name() == 't:l' for r in rules))
     self.assertEqual([[1, 2], [3, 4], [5, 6]],
                      sorted([[c.as_long() for c in r.children()]
                              for r in rules]))
Esempio n. 5
0
    def test_policy_table_publish(self):
        """Policy table result publish

        Test basic DSE functionality with policy engine and table result
        publish.
        """
        node = helper.make_dsenode_new_partition('testnode')
        data = fake_datasource.FakeDataSource('data')
        policy = agnostic.DseRuntime('policy')
        policy2 = agnostic.DseRuntime('policy2')
        node.register_service(data)
        node.register_service(policy)
        node.register_service(policy2)
        policy.synchronizer = mock.MagicMock()
        policy2.synchronizer = mock.MagicMock()

        policy.create_policy('data', kind=datalog_base.DATASOURCE_POLICY_TYPE)
        policy.create_policy('classification')
        policy.set_schema('data', compile.Schema({'q': (1, )}))
        policy.insert('p(x):-data:q(x),gt(x,2)', target='classification')

        policy.insert('q(3)', target='data')
        # TODO(ekcs): test that no publish triggered (because no subscribers)

        policy2.create_policy('policy')
        policy2.subscribe('policy', 'classification:p')
        helper.retry_check_function_return_value(
            lambda: 'classification:p' in policy.
            _published_tables_with_subscriber, True)
        self.assertEqual(list(policy.policySubData.keys()),
                         [('p', 'classification', None)])

        helper.retry_check_db_equal(policy2, 'policy:classification:p(x)',
                                    'policy:classification:p(3)')

        policy.insert('q(4)', target='data')
        helper.retry_check_db_equal(policy2, 'policy:classification:p(x)',
                                    ('policy:classification:p(3)'
                                     ' policy:classification:p(4)'))

        # test that no change to p means no publish triggered
        policy.insert('q(2)', target='data')
        # TODO(ekcs): test no publish triggered

        policy.delete('q(4)', target='data')
        helper.retry_check_db_equal(policy2, 'policy:classification:p(x)',
                                    'policy:classification:p(3)')

        policy2.unsubscribe('policy', 'classification:p')
        # trigger removed
        helper.retry_check_function_return_value(
            lambda: len(policy._published_tables_with_subscriber) == 0, True)
        self.assertEqual(list(policy.policySubData.keys()), [])

        policy.insert('q(4)', target='data')
        # TODO(ekcs): test that no publish triggered (because no subscribers)
        node.stop()
Esempio n. 6
0
    def test_communication(self):
        """Test for communication.

        Test the module's ability to be loaded into the DSE
        by checking its ability to communicate on the message bus.
        """
        cage = d6cage.d6Cage()

        # Create modules.
        # Turn off polling so we don't need to deal with real data.
        args = helper.datasource_openstack_args()
        args['poll_time'] = 0
        cage.loadModule("NovaDriver",
                        helper.data_module_path("nova_driver.py"))
        cage.loadModule("PolicyDriver", helper.policy_module_path())
        cage.createservice(name="policy",
                           moduleName="PolicyDriver",
                           args={
                               'd6cage': cage,
                               'rootdir': helper.data_module_path('')
                           })
        cage.createservice(name="nova", moduleName="NovaDriver", args=args)

        # Check that data gets sent from nova to policy as expected
        nova = cage.service_object('nova')
        policy = cage.service_object('policy')
        policy.debug_mode()
        policy.create_policy('nova')
        policy.set_schema('nova', compile.Schema({'server': (1, )}))
        policy.subscribe('nova', 'server', callback=policy.receive_data)

        # publishing is slightly convoluted b/c deltas are computed
        #  automatically.  (Not just convenient--useful so that DSE
        #  properly handles the initial state problem.)
        # Need to set nova.state and nova.prior_state and then publish
        #  anything.

        # publish server(1), server(2), server(3)
        helper.retry_check_subscribers(nova, [(policy.name, 'server')])
        nova.prior_state = {}
        nova.state['server'] = set([(1, ), (2, ), (3, )])
        nova.publish('server', None)
        helper.retry_check_db_equal(
            policy, 'nova:server(x)',
            'nova:server(1) nova:server(2) nova:server(3)')

        # publish server(1), server(4), server(5)
        nova.prior_state['server'] = nova.state['server']
        nova.state['server'] = set([(1, ), (4, ), (5, )])
        nova.publish('server', None)
        helper.retry_check_db_equal(
            policy, 'nova:server(x)',
            'nova:server(1) nova:server(4) nova:server(5)')
Esempio n. 7
0
    def test_rule_api_model_extended(self):
        """Test extended rule syntax."""
        api = self.api
        engine = self.engine
        engine.set_schema('nova',
                          compile.Schema({'q': ("name", "status", "year")}))

        # insert/retrieve rule with column references
        # just testing that no errors are thrown--correctness tested elsewhere
        # Assuming that api-models are pass-throughs to functionality
        context = {'policy_id': engine.DEFAULT_THEORY}
        (id1, rule) = api['rule'].add_item({'rule': 'p(x) :- nova:q(name=x)'},
                                           {},
                                           context=context)
        api['rule'].get_item(id1, {}, context=context)
Esempio n. 8
0
 def test_schema_columns(self):
     test_schema = compile.Schema({
         'p': (1, 2, 3),
         'q': ({'name': 'a', 'type': 'Str'},
               {'name': 'b', 'nullable': False})},
         complete=True)
     self.assertEqual(test_schema.columns('p'),
                      [1, 2, 3])
     self.assertEqual(test_schema.columns('q'),
                      ['a', 'b'])
     self.assertEqual([(data_types.Scalar, True), (data_types.Scalar, True),
                       (data_types.Scalar, True)],
                      test_schema.types('p'))
     self.assertEqual([(data_types.Str, True), (data_types.Scalar, False)],
                      test_schema.types('q'))
Esempio n. 9
0
    def test_column_references_validation_errors(self):
        """Test invalid column references occurring in a single atom."""
        schema = compile.Schema({'q': ('id', 'name', 'status'),
                                 'r': ('id', 'age', 'weight')},
                                complete=True)
        theories = {'nova': self.SchemaWrapper(schema)}

        def check_err(rule, errmsg, msg):
            rule = compile.parse1(rule)
            try:
                rule.eliminate_column_references_and_pad_positional(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.assertIn(errmsg, str(e), msg + ": " + emsg)

        check_err(
            'p(x) :- nova:missing(id=x)',
            'uses unknown table missing',
            'Unknown table')

        check_err(
            'p(x) :- nova:q(id=x, birthday=y)',
            'column name birthday does not exist',
            'Unknown column name')

        check_err(
            'p(x) :- nova:q(4=y)',
            'column index 4 is too large',
            'Large column number')

        check_err(
            'p(x) :- nova:q(id=x, 0=y)',
            'index 0 references column id, which is also referenced by name',
            'Conflict between name and number references')

        check_err(
            'p(x) :- nova:q(x, y, id=z)',
            'already provided by position',
            'Conflict between name and position')

        theories = {}
        check_err(
            'p(x) :- nova:missing(id=x)',
            'schema is unknown',
            'Missing schema')
Esempio n. 10
0
    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')
Esempio n. 11
0
 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("../tests/datasources/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.create_policy('data')
     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 = policy.parse1('p(x) :- data:q(x)')
     api.publish('policy-update', [agnostic.Event(formula)])
     helper.retry_check_nonempty_last_policy_change(policy)
     # simulate data source publishing to q
     formula = policy.parse1('q(1)')
     data.publish('q', [agnostic.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 = policy.parse1('p(3)')
     data.publish('p', [agnostic.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')
Esempio n. 12
0
    def test_positional_args_padding_atom(self):
        """Test positional args padding on a single atom."""
        def check_err(rule, errmsg, msg):
            rule = compile.parse1(rule)
            try:
                rule.eliminate_column_references_and_pad_positional(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.assertIn(errmsg, str(e), msg + ": " + emsg)

        def check(code, correct, msg, no_theory=False):
            actual = compile.parse1(
                code).eliminate_column_references_and_pad_positional(
                    {} if no_theory else theories)
            eq = helper.datalog_same(str(actual), correct)
            self.assertTrue(eq, msg)

        run = agnostic.Runtime()
        run.create_policy('nova')
        schema = compile.Schema({'q': ('id', 'name', 'status')})
        theories = {'nova': self.SchemaWrapper(schema)}

        # Too few positional args
        code = ("p(x) :- nova:q(w, y)")
        correct = "p(x) :- nova:q(w, y, x3)"
        check(code, correct, 'Too few positional args')

        code = ("p(x) :- nova:q(w)")
        correct = "p(x) :- nova:q(w, y, x3)"
        check(code, correct, 'Too few positional args')

        code = ("p(x) :- nova:q()")
        correct = "p(x) :- nova:q(w, y, x3)"
        check(code, correct, 'Too few (no) positional args')

        # No schema provided, no change
        code = ("p(x) :- nova:q(w, y)")
        correct = "p(x) :- nova:q(w, y)"
        check(code, correct, 'No schema provided', True)

        code = ("p(x) :- nova:q(w, x, y, z)")
        correct = "p(x) :- nova:q(w, x, y, z)"
        check(code, correct, 'No schema provided', True)
Esempio n. 13
0
 def __init__(self,
              name=None,
              abbr=None,
              schema=None,
              theories=None,
              desc=None,
              owner=None):
     super(NonrecursiveRuleTheory, self).__init__(name=name,
                                                  abbr=abbr,
                                                  theories=theories,
                                                  schema=schema,
                                                  desc=desc,
                                                  owner=owner)
     # dictionary from table name to list of rules with that table in head
     self.rules = ruleset.RuleSet()
     self.kind = base.NONRECURSIVE_POLICY_TYPE
     if schema is None:
         self.schema = compile.Schema()
Esempio n. 14
0
 def __init__(self,
              name=None,
              abbr=None,
              schema=None,
              theories=None,
              desc=None,
              owner=None):
     super(Z3Theory,
           self).__init__(name=name,
                          abbr=abbr,
                          theories=theories,
                          schema=ast.Schema() if schema is None else schema,
                          desc=desc,
                          owner=owner)
     LOG.info('z3theory: create %s', name)
     self.kind = base.Z3_POLICY_TYPE
     self.rules = ruleset.RuleSet()
     self.dirty = False
     self.z3context = None
     Z3Context.get_context().register(self)
Esempio n. 15
0
 def __init__(self,
              name=None,
              abbr=None,
              schema=None,
              theories=None,
              desc=None,
              owner=None):
     super(NonrecursiveRuleTheory, self).__init__(name=name,
                                                  abbr=abbr,
                                                  theories=theories,
                                                  schema=schema,
                                                  desc=desc,
                                                  owner=owner)
     # dictionary from table name to list of rules with that table in head
     self.rules = ruleset.RuleSet()
     self.kind = base.NONRECURSIVE_POLICY_TYPE
     if schema is None:
         self.schema = compile.Schema()
     # Indicates that a rule was added/removed
     # Used by the compiler to know if a theory should be recompiled.
     self.dirty = False
Esempio n. 16
0
    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)

        # schema update
        schema = compile.Schema()
        rule1 = compile.parse1('p(x) :- q(x, y)')
        change1 = schema.update(rule1.head, True)
        rule2 = compile.parse1('p(x) :- r(x, y)')
        change2 = schema.update(rule2.head, True)
        self.assertEqual(schema.count['p'], 2)
        schema.revert(change2)
        self.assertEqual(schema.count['p'], 1)
        schema.revert(change1)
        self.assertEqual('p' in schema.count, False)

        schema.update(rule1.head, True)
        schema.update(rule2.head, True)
        change1 = schema.update(rule1.head, False)
        change2 = schema.update(rule2.head, False)
        self.assertEqual('p' in schema.count, False)
        schema.revert(change2)
        self.assertEqual(schema.count['p'], 1)
        schema.revert(change1)
        self.assertEqual(schema.count['p'], 2)
Esempio n. 17
0
    def test_column_references_atom(self):
        """Test column references occurring in a single atom in a rule."""
        def check(code, correct, msg):
            actual = compile.parse1(code).eliminate_column_references(theories)
            eq = helper.datalog_same(str(actual), correct)
            self.assertTrue(eq, msg)

        run = agnostic.Runtime()
        run.create_policy('nova')
        schema = compile.Schema({'q': ('id', 'name', 'status')})
        theories = {'nova': self.SchemaWrapper(schema)}

        # Multiple column names
        code = ("p(x) :- nova:q(id=x, status=y)")
        correct = "p(x) :- nova:q(x, w, y)"
        check(code, correct, 'Multiple column names')

        # Multiple column numbers
        code = ("p(x) :- nova:q(0=x, 1=y, 2=z)")
        correct = "p(x) :- nova:q(x, y, z)"
        check(code, correct, 'Multiple column numbers')

        # Mix column names and numbers
        code = ("p(x) :- nova:q(id=x, 2=y)")
        correct = "p(x) :- nova:q(x, w, y)"
        check(code, correct, 'Mix names and numbers')

        # Object constants
        code = ("p(x) :- nova:q(id=3, 2=2)")
        correct = "p(x) :- nova:q(3, w, 2)"
        check(code, correct, 'Object constants')

        # Out of order
        code = ("p(x, y) :- nova:q(status=y, id=x)")
        correct = "p(x, y) :- nova:q(x, z, y)"
        check(code, correct, 'Out of order')

        # Out of order with numbers
        code = ("p(x, y) :- nova:q(1=y, 0=x)")
        correct = "p(x, y) :- nova:q(x, y, z)"
        check(code, correct, 'Out of order with numbers')

        # Positional plus named
        code = ("p(x, y) :- nova:q(x, status=y)")
        correct = "p(x, y) :- nova:q(x, z, y)"
        check(code, correct, 'Positional plus named')

        # Positional plus named 2
        code = ("p(x, y, z) :- nova:q(x, y, 2=z)")
        correct = "p(x, y, z) :- nova:q(x, y, z)"
        check(code, correct, 'Positional plus named 2')

        # Pure positional (different since we are providing schema)
        code = ("p(x, y, z) :- nova:q(x, y, z)")
        correct = "p(x, y, z) :- nova:q(x, y, z)"
        check(code, correct, 'Pure positional')

        # Pure positional (without schema)
        code = ("p(x) :- nova:q(x, y, z)")
        run.delete_policy('nova')
        correct = "p(x) :- nova:q(x, y, z)"
        check(code, correct, 'Pure positional without schema')
Esempio n. 18
0
 def __init__(self, name, theories):
     super(MinTheory, self).__init__(name=name, theories=theories)
     self.rules = ruleset.RuleSet()
     self.schema = ast.Schema()
Esempio n. 19
0
 def test_schema(self):
     th = nonrecursive.NonrecursiveRuleTheory(name='alice')
     th.schema = compile.Schema({'p': ('id', 'status', 'name')})
     self.assertEqual(th.arity('p'), 3)
     self.assertEqual(th.arity('alice:p'), 3)