# Copyright (c) 2015 VMware, Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # from __future__ import print_function from __future__ import division from __future__ import absolute_import import collections from oslo_log import log as logging import six from congress import exception LOG = logging.getLogger(__name__) DATABASE_POLICY_TYPE = 'database' NONRECURSIVE_POLICY_TYPE = 'nonrecursive' ACTION_POLICY_TYPE = 'action' MATERIALIZED_POLICY_TYPE = 'materialized' DELTA_POLICY_TYPE = 'delta' DATASOURCE_POLICY_TYPE = 'datasource' class Tracer(object): def __init__(self): self.expressions = [] self.funcs = [LOG.debug] # functions to call to trace def trace(self, table): self.expressions.append(table) def is_traced(self, table): return table in self.expressions or '*' in self.expressions def log(self, table, msg, *args, **kwargs): depth = kwargs.pop("depth", 0) if kwargs: raise TypeError("Unexpected keyword arguments: %s" % kwargs) if self.is_traced(table): for func in self.funcs: func(("| " * depth) + msg, *args) class StringTracer(Tracer): def __init__(self): super(StringTracer, self).__init__() self.stream = six.moves.StringIO() self.funcs.append(self.string_output) def string_output(self, msg, *args): self.stream.write((msg % args) + "\n") def get_value(self): return self.stream.getvalue() ############################################################################## # Logical Building Blocks ############################################################################## class Proof(object): """A single proof. Differs semantically from Database's Proof in that this version represents a proof that spans rules, instead of just a proof for a single rule. """ def __init__(self, root, children): self.root = root self.children = children def __str__(self): return self.str_tree(0) def str_tree(self, depth): s = " " * depth s += str(self.root) s += "\n" for child in self.children: s += child.str_tree(depth + 1) return s def leaves(self): if len(self.children) == 0: return [self.root] result = [] for child in self.children: result.extend(child.leaves()) return result ############################################################################## # Events ############################################################################## class EventQueue(object): def __init__(self): self.queue = collections.deque() def enqueue(self, event): self.queue.append(event) def dequeue(self): return self.queue.popleft() def __len__(self): return len(self.queue) def __str__(self): return "[" + ",".join([str(x) for x in self.queue]) + "]" ############################################################################## # Abstract Theories ############################################################################## class Theory(object): def __init__(self, name=None, abbr=None, schema=None, theories=None, id=None, desc=None, owner=None, kind=None): self.schema = schema self.theories = theories self.kind = kind self.id = id self.desc = desc self.owner = owner self.tracer = Tracer() if name is None: self.name = repr(self) else: self.name = name if abbr is None: self.abbr = "th" else: self.abbr = abbr maxlength = 6 if len(self.abbr) > maxlength: self.trace_prefix = self.abbr[0:maxlength] else: self.trace_prefix = self.abbr + " " * (maxlength - len(self.abbr)) def set_id(self, id): self.id = id def initialize_tables(self, tablenames, facts): """initialize_tables Event handler for (re)initializing a collection of tables. Clears tables befores assigning the new table content. @facts must be an iterable containing compile.Fact objects. """ raise NotImplementedError def actual_events(self, events): """Returns subset of EVENTS that are not noops.""" actual = [] for event in events: if event.insert: if event.formula not in self: actual.append(event) else: if event.formula in self: actual.append(event) return actual def debug_mode(self): tr = Tracer() tr.trace('*') self.set_tracer(tr) def set_tracer(self, tracer): self.tracer = tracer def get_tracer(self): return self.tracer def log(self, table, msg, *args, **kwargs): msg = self.trace_prefix + ": " + msg self.tracer.log(table, msg, *args, **kwargs) def policy(self): """Return a list of the policy statements in this theory.""" raise NotImplementedError() def content(self): """Return a list of the contents of this theory. Maybe rules and/or data. Note: do not change name to CONTENTS, as this is reserved for a dictionary of stuff used by TopDownTheory. """ raise NotImplementedError() def tablenames(self, body_only=False, include_builtin=False, include_modal=True): tablenames = set() for rule in self.policy(): tablenames |= rule.tablenames( body_only=body_only, include_builtin=include_builtin, include_modal=include_modal) return tablenames def __str__(self): return "Theory %s" % self.name def content_string(self): return '\n'.join([str(p) for p in self.content()]) + '\n' def get_rule(self, ident): for p in self.policy(): if hasattr(p, 'id') and str(p.id) == str(ident): return p raise exception.NotFound('rule_id %s is not found.' % ident) def arity(self, tablename, modal=None): """Return the number of columns for the given tablename. TABLENAME is of the form : or
. MODAL is the value of the modal operator. """ return NotImplementedError def get_attr_dict(self): '''return dict containing the basic attributes of this theory''' d = {'id': self.id, 'name': self.name, 'abbreviation': self.abbr, 'description': self.desc, 'owner_id': self.owner, 'kind': self.kind} return d