Ejemplo n.º 1
0
 def get_target(self, name):
     if name is None:
         if len(self.theory) == 1:
             name = self.theory.keys()[0]
         elif len(self.theory) == 0:
             raise compile.CongressException("No policies exist.")
         else:
             raise compile.CongressException(
                 "Must choose a policy to operate on")
     if name not in self.theory:
         raise compile.CongressException("Unknown policy " + str(name))
     return self.theory[name]
Ejemplo n.º 2
0
    def update_would_cause_errors(self, events):
        """Return a list of compile.CongressException.

        Return a list of compile.CongressException if we were
        to apply the insert/deletes of policy statements dictated by
        EVENTS to the current policy.
        """
        self.log(None, "update_would_cause_errors %s", iterstr(events))
        errors = []
        for event in events:
            if not compile.is_datalog(event.formula):
                errors.append(
                    compile.CongressException("Non-formula found: {}".format(
                        str(event.formula))))
            else:
                if event.formula.is_atom():
                    errors.extend(
                        compile.fact_errors(event.formula, self.theories,
                                            self.name))
                else:
                    errors.extend(
                        compile.rule_errors(event.formula, self.theories,
                                            self.name))
        # Would also check that rules are non-recursive, but that
        #   is currently being handled by Runtime.  The current implementation
        #   disallows recursion in all theories.
        return errors
Ejemplo n.º 3
0
    def update_would_cause_errors(self, events):
        """Return a list of compile.CongressException.

        Return a list of compile.CongressException if we were
        to apply the events EVENTS to the current policy.
        """
        self.log(None, "update_would_cause_errors %s", iterstr(events))
        errors = []
        for event in events:
            if not compile.is_datalog(event.formula):
                errors.append(
                    compile.CongressException("Non-formula found: {}".format(
                        str(event.formula))))
            else:
                if event.formula.is_atom():
                    errors.extend(
                        compile.fact_errors(event.formula, self.theories,
                                            self.name))
                else:
                    errors.extend(
                        compile.rule_head_has_no_theory(
                            event.formula,
                            permit_head=lambda lit: lit.is_update()))
                    # Should put this back in place, but there are some
                    # exceptions that we don't handle right now.
                    # Would like to mark some tables as only being defined
                    #   for certain bound/free arguments and take that into
                    #   account when doing error checking.
                    # errors.extend(compile.rule_negation_safety(event.formula))
        return errors
Ejemplo n.º 4
0
    def update_obj(self, events):
        """Do the updating.

        Checks if applying EVENTS is permitted and if not
        returns a list of errors.  If it is permitted, it
        applies it and then returns a list of changes.
        In both cases, the return is a 2-tuple (if-permitted, list).
        """
        self.table_log(None, "Updating with %s", iterstr(events))
        by_theory = self.group_events_by_target(events)
        # check that the updates would not cause an error
        errors = []
        actual_events = []
        for th, th_events in by_theory.items():
            th_obj = self.get_target(th)
            errors.extend(th_obj.update_would_cause_errors(th_events))
            actual_events.extend(th_obj.actual_events(th_events))
        # update dependency graph (and undo it if errors)
        changes = self.global_dependency_graph.formula_update(events)
        if changes:
            if self.global_dependency_graph.has_cycle():
                # TODO(thinrichs): include path
                errors.append(compile.CongressException(
                    "Rules are recursive"))
                self.global_dependency_graph.undo_changes(changes)
        if len(errors) > 0:
            return (False, errors)
        # actually apply the updates
        changes = []
        for th, th_events in by_theory.items():
            changes.extend(self.get_target(th).update(events))
        return (True, changes)
Ejemplo n.º 5
0
    def create_policy(self, name, abbr=None, kind=None):
        """Create a new policy and add it to the runtime.

        ABBR is a shortened version of NAME that appears in
        traces.  KIND is the name of the datastructure used to
        represent a policy.
        """
        if not isinstance(name, basestring):
            raise KeyError("Policy name %s must be a string" % name)
        if name in self.theory:
            raise KeyError("Policy with name %s already exists" % name)
        if not isinstance(abbr, basestring):
            abbr = name[0:5]
        LOG.debug("Creating policy <%s> with abbr <%s> and kind <%s>",
                  name, abbr, kind)
        if kind is None:
            kind = NONRECURSIVE_POLICY_TYPE
        else:
            kind = kind.lower()
        if kind is None or kind == NONRECURSIVE_POLICY_TYPE:
            PolicyClass = NonrecursiveRuleTheory
        elif kind == ACTION_POLICY_TYPE:
            PolicyClass = ActionTheory
        elif kind == DATABASE_POLICY_TYPE:
            PolicyClass = Database
        elif kind == MATERIALIZED_POLICY_TYPE:
            PolicyClass = MaterializedViewTheory
        else:
            raise compile.CongressException(
                "Unknown kind of policy: %s" % kind)
        policy_obj = PolicyClass(name=name, abbr=abbr, theories=self.theory)
        policy_obj.set_tracer(self.tracer)
        self.theory[name] = policy_obj
        return policy_obj
Ejemplo n.º 6
0
 def policy_type(self, name):
     """Return type of policy NAME.  Throws KeyError if does not exist."""
     policy = self.policy_object(name)
     if isinstance(policy, NonrecursiveRuleTheory):
         return NONRECURSIVE_POLICY_TYPE
     if isinstance(policy, MaterializedViewTheory):
         return MATERIALIZED_POLICY_TYPE
     if isinstance(policy, ActionTheory):
         return ACTION_POLICY_TYPE
     if isinstance(policy, Database):
         return DATABASE_POLICY_TYPE
     raise compile.CongressException("Policy %s has unknown type" % name)
Ejemplo n.º 7
0
 def change_rule(self, parsed_rule, context, insert=True):
     policy_name = self.policy_name(context)
     if policy_name not in self.engine.theory:
         raise KeyError("Policy with ID '%s' does not exist", policy_name)
     event = runtime.Event(formula=parsed_rule,
                           insert=insert,
                           target=policy_name)
     (permitted, changes) = self.engine.process_policy_update([event])
     if not permitted:
         raise compile.CongressException("Errors: " +
                                         ";".join((str(x)
                                                   for x in changes)))
     return changes
Ejemplo n.º 8
0
    def update_would_cause_errors(self, events):
        """Return a list of compile.CongressException.

        Return a list of compile.CongressException if we were
        to apply the events EVENTS to the current policy.
        """
        self.log(None, "update_would_cause_errors %s", iterstr(events))
        errors = []
        for event in events:
            if not compile.is_atom(event.formula):
                errors.append(compile.CongressException(
                    "Non-atomic formula is not permitted: {}".format(
                        str(event.formula))))
            else:
                errors.extend(compile.fact_errors(
                    event.formula, self.theories, self.name))
        return errors
Ejemplo n.º 9
0
    def project(self, sequence, policy_theory, action_theory):
        """Apply the list of updates SEQUENCE.

        Apply the list of updates SEQUENCE, where actions are described
        in ACTION_THEORY. Return an update sequence that will undo the
        projection.

        SEQUENCE can include atom insert/deletes, rule insert/deletes,
        and action invocations.  Projecting an action only
        simulates that action's invocation using the action's description;
        the results are therefore only an approximation of executing
        actions directly.  Elements of SEQUENCE are just formulas
        applied to the given THEORY.  They are NOT Event()s.

        SEQUENCE is really a program in a mini-programming
        language--enabling results of one action to be passed to another.
        Hence, even ignoring actions, this functionality cannot be achieved
        by simply inserting/deleting.
        """
        actth = self.theory[action_theory]
        policyth = self.theory[policy_theory]
        # apply changes to the state
        newth = NonrecursiveRuleTheory(abbr="Temp")
        newth.tracer.trace('*')
        actth.includes.append(newth)
        # TODO(thinrichs): turn 'includes' into an object that guarantees
        #   there are no cycles through inclusion.  Otherwise we get
        #   infinite loops
        if actth is not policyth:
            actth.includes.append(policyth)
        actions = self.get_action_names(action_theory)
        self.table_log(None, "Actions: %s", iterstr(actions))
        undos = []         # a list of updates that will undo SEQUENCE
        self.table_log(None, "Project: %s", sequence)
        last_results = []
        for formula in sequence:
            self.table_log(None, "** Updating with %s", formula)
            self.table_log(None, "Actions: %s", iterstr(actions))
            self.table_log(None, "Last_results: %s", iterstr(last_results))
            tablename = formula.tablename()
            if tablename not in actions:
                if not formula.is_update():
                    raise compile.CongressException(
                        "Sequence contained non-action, non-update: " +
                        str(formula))
                updates = [formula]
            else:
                self.table_log(tablename, "Projecting %s", formula)
                # define extension of current Actions theory
                if formula.is_atom():
                    assert formula.is_ground(), (
                        "Projection atomic updates must be ground")
                    assert not formula.is_negated(), (
                        "Projection atomic updates must be positive")
                    newth.define([formula])
                else:
                    # instantiate action using prior results
                    newth.define(last_results)
                    self.table_log(tablename, "newth (with prior results) %s",
                                   iterstr(newth.content()))
                    bindings = actth.top_down_evaluation(
                        formula.variables(), formula.body, find_all=False)
                    if len(bindings) == 0:
                        continue
                    grounds = formula.plug_heads(bindings[0])
                    grounds = [act for act in grounds if act.is_ground()]
                    assert all(not lit.is_negated() for lit in grounds)
                    newth.define(grounds)
                self.table_log(tablename,
                               "newth contents (after action insertion): %s",
                               iterstr(newth.content()))
                # self.table_log(tablename, "action contents: %s",
                #     iterstr(actth.content()))
                # self.table_log(tablename, "action.includes[1] contents: %s",
                #     iterstr(actth.includes[1].content()))
                # self.table_log(tablename, "newth contents: %s",
                #     iterstr(newth.content()))
                # compute updates caused by action
                updates = actth.consequences(compile.is_update)
                updates = self.resolve_conflicts(updates)
                updates = unify.skolemize(updates)
                self.table_log(tablename, "Computed updates: %s",
                               iterstr(updates))
                # compute results for next time
                for update in updates:
                    newth.insert(update)
                last_results = actth.consequences(compile.is_result)
                last_results = set([atom for atom in last_results
                                    if atom.is_ground()])
            # apply updates
            for update in updates:
                undo = self.project_updates(update, policy_theory)
                if undo is not None:
                    undos.append(undo)
        undos.reverse()
        if actth is not policyth:
            actth.includes.remove(policyth)
        actth.includes.remove(newth)
        return undos