Extended example to include remediation

The private/public network example now includes
a step that asks for remediations.

All tests pass

Issue: #
Change-Id: I6f341c6cc74040288b4ef67e776bf079f3346624
This commit is contained in:
Tim Hinrichs 2013-10-15 12:39:30 -07:00
parent ef84b421b5
commit 1734809fe7
5 changed files with 80 additions and 32 deletions

View File

@ -1,17 +1,17 @@
// disconnect_network action
action(disconnect_network)
action("disconnect_network")
nova:network-(vm, network) :- disconnect_network(vm, network)
// delete_vm action
action(delete_vm)
action("delete_vm")
nova:virtual_machine-(vm) :- delete_vm(vm)
nova:network-(vm, network) :- delete_vm(vm), nova:network(vm, network)
nova:owner-(vm, owner) :- delete_vm(vm), nova:owner(vm, owner)
// make_public action
action(make_public)
action("make_public")
neutron:public_network+(network) :- make_public(network)

View File

@ -1,4 +1,4 @@
. script for a demo.
Script for a demo.
0) Example policy
@ -97,3 +97,34 @@ error(vm3)
-------------------------------------------------
10) Install Action theory
// disconnect_network action
action("disconnect_network")
nova:network-(vm, network) :- disconnect_network(vm, network)
// delete_vm action
action("delete_vm")
nova:virtual_machine-(vm) :- delete_vm(vm)
nova:network-(vm, network) :- delete_vm(vm), nova:network(vm, network)
nova:owner-(vm, owner) :- delete_vm(vm), nova:owner(vm, owner)
// make_public action
action("make_public")
neutron:public_network+(network) :- make_public(network)
--- Commands ------------------------------------
>>> r.load_file("../../examples/private_public_network.action", target=r.ACTION_THEORY)
-------------------------------------------------
11) Ask for remediations: actions that when executed will fix a violation
--- Commands ------------------------------------
print r.remediate('error("vm1")')
nova:virtual_machine-(vm1) :- delete_vm(vm1)
nova:network-(vm1, net_private) :- disconnect_network(vm1, net_private)
neutron:public_network+(net_private) :- make_public(net_private)
nova:owner-(vm1, tim) :- delete_vm(vm1)
-------------------------------------------------

View File

@ -95,11 +95,11 @@ class Variable (Term):
return not self == other
def __repr__(self):
return "Variable(name={}, location={})".format(
repr(self.name), repr(self.location))
# Use repr to hash rule--can't include location
return "Variable(name={})".format(repr(self.name))
def __hash__(self):
return hash("Variable(name={})".format(repr(self.name)))
return hash(repr(self))
def is_variable(self):
return True
@ -123,12 +123,12 @@ class ObjectConstant (Term):
return str(self.name)
def __repr__(self):
return "ObjectConstant(name={}, type={}, location={})".format(
repr(self.name), repr(self.type), repr(self.location))
# Use repr to hash rule--can't include location
return "ObjectConstant(name={}, type={})".format(
repr(self.name), repr(self.type))
def __hash__(self):
return hash("ObjectConstant(name={}, type={})".format(
repr(self.name), repr(self.type)))
return hash(repr(self))
def __eq__(self, other):
return (isinstance(other, ObjectConstant) and
@ -181,15 +181,13 @@ class Atom (object):
return not self == other
def __repr__(self):
return "Atom(table={}, arguments={}, location={})".format(
# Use repr to hash rule--can't include location
return "Atom(table={}, arguments={})".format(
repr(self.table),
"[" + ",".join(repr(arg) for arg in self.arguments) + "]",
repr(self.location))
"[" + ",".join(repr(arg) for arg in self.arguments) + "]")
def __hash__(self):
return hash("Atom(table={}, arguments={})".format(
repr(self.table),
"[" + ",".join(repr(arg) for arg in self.arguments) + "]"))
return hash(repr(self))
def is_atom(self):
return True
@ -252,10 +250,10 @@ class Literal(Atom):
return (self.negated == other.negated and Atom.__eq__(self, other))
def __repr__(self):
return "Literal(table={}, arguments={}, location={}, negated={})".format(
# Use repr to hash rule--can't include location
return "Literal(table={}, arguments={}, negated={})".format(
repr(self.table),
"[" + ",".join(repr(arg) for arg in self.arguments) + "]",
repr(self.location),
repr(self.negated))
def __hash__(self):
@ -310,6 +308,7 @@ class Rule (object):
repr(self.location))
def __hash__(self):
# won't properly treat a positive literal and an atom as the same
return hash("Rule(head={}, body={})".format(
repr(self.head),
"[" + ",".join(repr(arg) for arg in self.body) + "]"))
@ -342,6 +341,8 @@ def formulas_to_string(formulas):
""" Takes an iterable of compiler sentence objects and returns a
string representing that iterable, which the compiler will parse
into the original iterable. """
if formulas is None:
return "None"
return " ".join([str(formula) for formula in formulas])
##############################################################################

View File

@ -871,7 +871,10 @@ class MaterializedRuleTheory(TopDownTheory):
# ignoring TABLENAMES and FIND_ALL
# except that we return the proper type.
proof = self.explain_aux(query, 0)
return [proof]
if proof is None:
return None
else:
return [proof]
############### Interface implementation ###############
@ -886,6 +889,8 @@ class MaterializedRuleTheory(TopDownTheory):
return Proof(query, [])
# grab first local proof, since they're all equally good
localproofs = self.database.explain(query)
if localproofs is None:
return None
if len(localproofs) == 0: # base fact
return Proof(query, [])
localproof = localproofs[0]
@ -1107,6 +1112,23 @@ class Runtime (object):
def log(self, table, msg, depth=0):
self.tracer.log(table, "RT: " + msg, depth)
def debug_mode(self):
tracer = Tracer()
tracer.trace('*')
self.tracer = tracer
self.theory[self.CLASSIFY_THEORY].tracer = tracer
self.theory[self.SERVICE_THEORY].tracer = tracer
self.theory[self.ACTION_THEORY].tracer = tracer
self.theory[self.CLASSIFY_THEORY].database.tracer = tracer
def production_mode(self):
tracer = Tracer()
self.tracer = tracer
self.theory[self.CLASSIFY_THEORY].tracer = tracer
self.theory[self.SERVICE_THEORY].tracer = tracer
self.theory[self.ACTION_THEORY].tracer = tracer
self.theory[self.CLASSIFY_THEORY].database.tracer = tracer
############### External interface ###############
def load_file(self, filename, target=None):
""" Compile the given FILENAME and insert each of the statements
@ -1272,9 +1294,9 @@ class Runtime (object):
else:
goal.table = goal.table + "-"
# return is a list of goal :- act1, act2, ...
# Turn it into a list of output :- act1, act2, ...
# This is more informative than query :- act1, act2, ...
for abduction in actionth.abduce(goal, actions, False):
results.append(compile.Rule(output, abduction.body))
results.append(abduction)
return results
def remediate_string(self, policy_string, theory):

View File

@ -21,13 +21,7 @@ class TestRuntime(unittest.TestCase):
code = ""
run = runtime.Runtime()
run.insert(code, target=target)
tracer = runtime.Tracer()
tracer.trace('*')
run.tracer = tracer
run.theory[run.CLASSIFY_THEORY].tracer = tracer
run.theory[run.SERVICE_THEORY].tracer = tracer
run.theory[run.ACTION_THEORY].tracer = tracer
run.theory[run.CLASSIFY_THEORY].database.tracer = tracer
run.debug_mode()
return run
def insert(self, run, alist):
@ -947,7 +941,7 @@ class TestRuntime(unittest.TestCase):
'p-(x) :- a(x)')
class_code = ('err(x) :- p(x)'
'p(1)')
check(action_code, class_code, 'err(1)', 'err(1) :- a(1)', 'Monadic')
check(action_code, class_code, 'err(1)', 'p-(1) :- a(1)', 'Monadic')
# rules in action theory
action_code = ('action("a")'
@ -955,7 +949,7 @@ class TestRuntime(unittest.TestCase):
'q(x) :- a(x)')
class_code = ('err(x) :- p(x)'
'p(1)')
check(action_code, class_code, 'err(1)', 'err(1) :- a(1)',
check(action_code, class_code, 'err(1)', 'p-(1) :- a(1)',
'Monadic, indirect')
# multiple conditions in error
@ -967,7 +961,7 @@ class TestRuntime(unittest.TestCase):
'p(1)'
'q(1)')
check(action_code, class_code, 'err(1)',
'err(1) :- a(1) err(1) :- b(1)',
'p-(1) :- a(1) q-(1) :- b(1)',
'Monadic, two conditions, two actions')