Expanded private_network script

Renamed 'projection' to 'simulation' and expanded private_public_network
demo script to include simulation.

Issue: #
Change-Id: I3cae526d37425bb53e91034d1af75199c1d83050
This commit is contained in:
Tim Hinrichs 2013-10-18 11:27:04 -07:00
parent 5b11b2638a
commit 9f768a7411
4 changed files with 132 additions and 63 deletions

View File

@ -1,4 +1,7 @@
// connect_network action
action("connect_network")
nova:network+(vm, network) :- connect_network(vm, network)
// disconnect_network action
action("disconnect_network")

View File

@ -1,13 +1,16 @@
Script for a demo.
0) Example policy
*********************************************************************
** Monitoring: Classification and Data sources
*********************************************************************
1) Classification Policy
Informally:
"all vms must be attached to public networks or to private networks owned by someone in the same group as the vm owner"
1) Draws on disparate data sources
Schema:
Cloud services at our disposal:
nova:virtual_machine(vm)
nova:network(vm, network)
nova:owner(vm, owner)
@ -16,8 +19,7 @@ neutron:owner(network, owner)
cms:group(user, group)
2) Policy
Formal policy:
error(vm) :- nova:virtual_machine(vm), nova:network(vm, network),
not neutron:public_network(network),
neutron:owner(network, netowner), nova:owner(vm, vmowner), not same_group(netowner, vmowner)
@ -34,7 +36,7 @@ python
-------------------------------------------------
3) Are there any violations? Not yet.
2) Are there any violations? Not yet.
--- Commands ------------------------------------
>>> print r.select("error(x)")
@ -42,36 +44,36 @@ python
-------------------------------------------------
4) Change some data to create an error: remove "tim" from group "congress"
3) Change some data to create an error: remove "tim" from group "congress"
OR make this change in ActiveDirectory.
--- Commands ------------------------------------
>>> r.delete('cms:group("tim", "congress")')
-------------------------------------------------
5) Check for violations
4) Check for violations
--- Commands ------------------------------------
>>> print r.select("error(x)")
error(vm1)
error("vm1")
-------------------------------------------------
6) Explain the violation:
5) Explain the violation:
--- Commands ------------------------------------
>>> print r.explain('error("vm1")')
error(vm1)
nova:virtual_machine(vm1)
nova:network(vm1, net_private)
not neutron:public_network(net_private)
neutron:owner(net_private, martin)
nova:owner(vm1, tim)
not same_group(martin, tim)
error("vm1")
nova:virtual_machine("vm1")
nova:network("vm1", "net_private")
not neutron:public_network("net_private")
neutron:owner("net_private", "martin")
nova:owner("vm1", "tim")
not same_group("martin", "tim")
-------------------------------------------------
7) Insert new rules: "Error if vm without a network"
6) Insert new policy fragment: "Error if vm without a network"
--- Commands ------------------------------------
>>> r.insert('error(vm) :- nova:virtual_machine(vm), not is_some_network(vm)'
@ -79,25 +81,43 @@ error(vm1)
-------------------------------------------------
8) Check for violations
7) Check for violations
--- Commands ------------------------------------
>>> print r.select("error(x)")
error(vm1) error(vm3)
error("vm1") error("vm3")
-------------------------------------------------
9) Explain the new violation
8) Explain the new violation
--- Commands ------------------------------------
>>> print r.explain('error("vm3")')
error(vm3)
nova:virtual_machine(vm3)
not is_some_network(vm3)
error("vm3")
nova:virtual_machine("vm3")
not is_some_network("vm3")
-------------------------------------------------
10) Install Action theory
*********************************************************************
** Enforcement: Action Policy and Operations Policy
*********************************************************************
9) To help with enforcement, Congress needs to know what actions are available to it. Codify these in the Action policy.
Informal policy:
connect_network(vm, network)
disconnect_network(vm, network)
delete_vm(vm)
make_public(network)
Formal policy:
// connect_network action
action("connect_network")
nova:network+(vm, network) :- connect_network(vm, network)
// disconnect_network action
action("disconnect_network")
@ -118,13 +138,61 @@ neutron:public_network+(network) :- make_public(network)
-------------------------------------------------
11) Ask for remediations: actions that when executed will fix a violation
10) Simulate actions and query resulting state (without actually changing state)
--- 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)
>>> print r.simulate('error(x)', 'connect_network("vm3", "net_public")')
error("vm1")
-------------------------------------------------
11) Ask for remediations: action sequence that when executed will fix a violation. (Caveat: multiple reasons for a violation--fixing a single reason.)
--- 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")
-------------------------------------------------
*********************************************************************
** Future Enforcement: Operations Policy
*********************************************************************
12) Execute an action-sequence: disconnect offending private network
r.execute('action1 action2 action3')
13) Dictate conditions under which actions should automatically be executed: Operations policy.
Informal policy:
every time a VM has an error and that VM is connected to a private network not owned by someone in the same group as the VM owner, then execute 'disconnect_network'.
Formal policy:
disconnect_network(vm1, net_private) :-
error(vm1)
nova:virtual_machine(vm1)
nova:network(vm1, net_private)
not neutron:public_network(net_private)
neutron:owner(net_private, martin)
nova:owner(vm1, tim)
not same_group(martin, tim)
r.load_file("../../examples/private_public_network.operations", target=r.OPERATION_POLICY)
14) Create an error that the operations policy should automatically correct.
r.execute('connect_network("vm1", "net_private")')
print r.select("error(x)")
error("vm3")
15) Show log for auditing to see that we actually did connect the network and then disconnect it. Log should include explanation for why action was executed.

View File

@ -1336,16 +1336,16 @@ class Runtime (object):
else:
return self.remediate_obj(formula)
def project(self, query, sequence):
""" Event handler for projection: the computation of a query given an
def simulate(self, query, sequence):
""" Event handler for simulation: the computation of a query given an
action sequence. [Eventually, we will want to support a sequence of
{action, rule insert/delete, atom insert/delete}. Holding
off only because no syntactic way of differentiating those
within the language. May need modals/function constants.] """
if isinstance(query, basestring) and isinstance(sequence, basestring):
return self.project_string(query, sequence)
return self.simulate_string(query, sequence)
else:
return self.project_obj(query, sequence)
return self.simulate_obj(query, sequence)
# Maybe implement one day
# def select_if(self, query, temporary_data):
@ -1470,30 +1470,42 @@ class Runtime (object):
results.append(abduction)
return results
# project
def project_string(self, query, sequence):
# simulate
def simulate_string(self, query, sequence):
query = compile.parse1(query)
sequence = compile.parse(sequence)
result = self.project_obj(query, sequence)
result = self.simulate_obj(query, sequence)
return compile.formulas_to_string(result)
def project_obj(self, query, sequence):
def simulate_obj(self, query, sequence):
assert (isinstance(query, compile.Rule) or
isinstance(query, compile.Atom)), "Query must be formula"
# Each action is represented as a rule with the actual action
# in the head and its supporting data (e.g. options) in the body
assert all(isinstance(x, compile.Rule) or isinstance(x, compile.Atom)
for x in sequence), "Sequence must be an iterable of Rules"
# apply SEQUENCE
undo = self.project(sequence)
# query the resulting state
result = self.theory[self.CLASSIFY_THEORY].select(query)
self.log(query.tablename(), "Result of {} is {}".format(
str(query), iterstr(result)))
# rollback the changes
self.project(undo)
return result
def project(self, sequence):
""" Apply the list of updates SEQUENCE to the classification theory.
Return an update sequence that will undo the projection. """
actth = self.theory[self.ACTION_THEORY]
clsth = self.theory[self.CLASSIFY_THEORY]
# apply changes to the state
newth = NonrecursiveRuleTheory()
newth.tracer.trace('*')
actth.includes.append(newth)
actions = self.get_actions()
self.log(query.tablename(), "Actions: " + str(actions))
change_sequence = [] # a list of lists of updates
self.log(None, "Actions: " + str(actions))
undos = [] # a list of updates that will undo SEQUENCE
for formula in sequence:
if formula.is_atom():
tablename = formula.table
@ -1511,25 +1523,11 @@ class Runtime (object):
updates = self.resolve_conflicts(updates)
else:
updates = [formula]
# apply and remember each update-set
changes = []
for update in updates:
undo = self.update_classifier(update)
if undo is not None:
changes.append(undo)
change_sequence.append(changes)
# query the resulting state
result = clsth.select(query)
self.log(query.tablename(), "Result of {} is {}".format(
str(query), iterstr(result)))
# rollback the changes: in the reverse order we applied them in
self.log(query.tablename(), "* Rolling back")
actth.includes.remove(newth)
for changes in reversed(change_sequence):
for undo in reversed(changes):
self.update_classifier(undo)
return result
undos.append(undo)
return reversed(undos)
def update_classifier(self, delta):
""" Takes an atom/rule DELTA with update head table

View File

@ -993,8 +993,8 @@ class TestRuntime(unittest.TestCase):
'p-(1) :- a(1) q-(1) :- b(1)',
'Monadic, two conditions, two actions')
def test_projection(self):
""" Test projection: the computation of a query given a sequence of
def test_simulate(self):
""" Test simulate: the computation of a query given a sequence of
actions. """
def create(action_code, class_code):
run = self.prep_runtime()
@ -1004,7 +1004,7 @@ class TestRuntime(unittest.TestCase):
run.insert(class_code, target=clsth)
return run
def check(run, action_sequence, query, correct, original_db, msg):
actual = run.project(query, action_sequence)
actual = run.simulate(query, action_sequence)
self.check_equal(actual, correct, msg)
self.check(run, original_db, msg)