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:
parent
5b11b2638a
commit
9f768a7411
@ -1,4 +1,7 @@
|
||||
|
||||
// connect_network action
|
||||
action("connect_network")
|
||||
nova:network+(vm, network) :- connect_network(vm, network)
|
||||
|
||||
// disconnect_network action
|
||||
action("disconnect_network")
|
||||
|
@ -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.
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user