CORD-1389: xproto extension to support policies

Change-Id: I5d6c11107d5bc9cd82e41c4a8e6a378d22d7fb61
diff --git a/lib/xos-genx/tests/policy_test.py b/lib/xos-genx/tests/policy_test.py
new file mode 100644
index 0000000..5f1dca0
--- /dev/null
+++ b/lib/xos-genx/tests/policy_test.py
@@ -0,0 +1,119 @@
+import unittest
+from xosgenx.generator import XOSGenerator
+from helpers import FakeArgs, XProtoTestHelpers
+import pdb
+
+"""
+The tests below convert the policy logic expression
+into Python, set up an appropriate environment and execute the Python.
+"""
+
+class XProtoPolicyTest(unittest.TestCase):
+    def test_constant(self):
+        xproto = \
+"""
+    policy true_policy < True >
+"""
+
+        target = XProtoTestHelpers.write_tmp_target("{{ proto.policies.true_policy }}")
+
+        args = FakeArgs()
+        args.inputs = xproto
+        args.target = target
+
+        output = XOSGenerator.generate(args).replace('t','T')
+        self.assertTrue(eval(output)) 
+
+    def test_equal(self):
+        xproto = \
+"""
+    policy slice_user < slice.user = obj.user >
+"""
+
+        target = XProtoTestHelpers.write_tmp_target("{{ proto.policies.slice_user }}")
+        args = FakeArgs()
+        args.inputs = xproto
+        args.target = target
+
+        output = XOSGenerator.generate(args)
+       
+        slice = FakeArgs()
+        slice.user = 'twin'
+        obj = FakeArgs()
+        obj.user = 'twin'
+
+        (op, operands), = eval(output).items()
+        expr = op.join(operands).replace('=','==')
+
+        self.assertTrue(eval(expr))
+
+    def test_bin(self):
+        xproto = \
+"""
+    policy slice_admin < slice.is_admin | obj.empty >
+"""
+        target = XProtoTestHelpers.write_tmp_target("{{ proto.policies.slice_admin }}")
+        args = FakeArgs()
+        args.inputs = xproto
+        args.target = target
+
+        output = XOSGenerator.generate(args)
+
+        slice = FakeArgs()
+        slice.is_admin = False
+        obj = FakeArgs()
+        obj.empty = []
+
+        (op, operands), = eval(output).items()
+        expr = op.join(operands).replace('|',' or ')
+
+        self.assertFalse(eval(expr))
+
+        
+    def test_exists(self):
+        xproto = \
+"""
+    policy privilege < exists Privilege: Privilege.object_id = obj.id >
+"""
+
+        target = XProtoTestHelpers.write_tmp_target("{{ proto.policies.privilege }} ")
+        args = FakeArgs()
+        args.inputs = xproto
+        args.target = target
+
+        output = XOSGenerator.generate(args)
+        
+        Privilege = FakeArgs()
+        Privilege.object_id = 1
+        obj = FakeArgs()
+        obj.id = 1
+
+        (op, operands), = eval(output).items()
+        (op2, operands2), = operands[1].items()
+        expr = op2.join(operands2).replace('=','==')
+
+        self.assertTrue(eval(expr))
+
+    def test_forall(self):
+        # This one we only parse
+        xproto = \
+"""
+    policy instance < forall Instance: exists Credential: Credential.obj_id = Instance.obj_id >
+"""
+
+        target = XProtoTestHelpers.write_tmp_target("{{ proto.policies.instance }}")
+
+        args = FakeArgs()
+        args.inputs = xproto
+        args.target = target
+
+        output = XOSGenerator.generate(args)
+        (op, operands), = eval(output).items()
+
+        self.assertEqual(op,'forall')
+
+
+if __name__ == '__main__':
+    unittest.main()
+
+
diff --git a/lib/xos-genx/xosgenx/generator.py b/lib/xos-genx/xosgenx/generator.py
index 668d351..8a4e1a8 100755
--- a/lib/xos-genx/xosgenx/generator.py
+++ b/lib/xos-genx/xosgenx/generator.py
@@ -191,11 +191,13 @@
                 models = {}
                 models[model] = v.models[model]
                 messages = [XOSGenerator._find_message_by_model_name(v.messages, model)]
+
                 rendered[model] = template.render(
                     {"proto":
                         {
                             'message_table': models,
                             'messages': messages,
+                            'policies': v.policies,
                             'message_names': [m['name'] for m in messages]
                         },
                         "context": context,
@@ -209,6 +211,7 @@
                     {
                         'message_table': v.models,
                         'messages': v.messages,
+                        'policies': v.policies,
                         'message_names': [m['name'] for m in v.messages]
                     },
                     "context": context,
diff --git a/lib/xos-genx/xosgenx/proto2xproto.py b/lib/xos-genx/xosgenx/proto2xproto.py
index f05c2bc..3723e7c 100644
--- a/lib/xos-genx/xosgenx/proto2xproto.py
+++ b/lib/xos-genx/xosgenx/proto2xproto.py
@@ -1,4 +1,5 @@
 import plyxproto.model as m
+from plyxproto.helpers import Visitor
 import pdb
 import argparse
 import plyxproto.parser as plyxproto
@@ -47,7 +48,7 @@
         except:
             return obj
 
-class Proto2XProto(m.Visitor):
+class Proto2XProto(Visitor):
     def __init__(self):
         super(Proto2XProto, self).__init__()
 
diff --git a/lib/xos-genx/xosgenx/xos2jinja.py b/lib/xos-genx/xosgenx/xos2jinja.py
index 929526d..c58260e 100644
--- a/lib/xos-genx/xosgenx/xos2jinja.py
+++ b/lib/xos-genx/xosgenx/xos2jinja.py
@@ -1,5 +1,5 @@
 import plyxproto.model as m
-import pdb
+from plyxproto.helpers import Visitor
 import argparse
 import plyxproto.parser as plyxproto
 import traceback
@@ -64,7 +64,6 @@
                 try:
                     rev_links[l['peer']['fqn']].append(rlink)
                 except TypeError:
-                    pdb.set_trace()
                     pass
             except KeyError:
                 rev_links[l['peer']['fqn']] = [rlink]
@@ -97,12 +96,7 @@
 ''' XOS2Jinja overrides the underlying visitor pattern to transform the tree
     in addition to traversing it '''
 
-
-class XOS2Jinja(m.Visitor):
-
-    def get_stack(self):
-        return self.stack
-
+class XOS2Jinja(Visitor):
     def __init__(self):
         super(XOS2Jinja, self).__init__()
 
@@ -112,6 +106,7 @@
         self.package = None
         self.message_options = {}
         self.count_stack = Stack()
+        self.policies = {}
         self.content = ""
         self.offset = 0
         self.current_message_name = None
@@ -119,6 +114,16 @@
         self.first_field = True
         self.first_method = True
 
+    def visit_PolicyDefinition(self, obj):
+        if self.package:
+            pname = '.'.join([self.package, obj.name.value.pval])
+        else:
+            pname = obj.name.value.pval
+
+        self.policies[pname] = obj.body
+
+        return True
+
     def visit_PackageStatement(self, obj):
         dotlist = obj.name.value
         dotlist2 = [f.pval for f in dotlist]
@@ -177,6 +182,10 @@
 
         s['src_port'] = obj.src_port.value.pval
         s['name'] = obj.src_port.value.pval
+        try:
+            s['policy'] = obj.policy.pval
+        except AttributeError:
+            s['policy'] = None
 
         try:
             s['dst_port'] = obj.dst_port.value.pval
@@ -218,6 +227,12 @@
             s['type'] = obj.ftype.name.pval
 
         s['name'] = obj.name.value.pval
+
+        try:
+            s['policy'] = obj.policy.pval
+        except AttributeError:
+            s['policy'] = None
+
         s['modifier'] = obj.field_modifier.pval
         s['id'] = obj.fieldId.pval
 
@@ -293,6 +308,11 @@
             model_name = obj.name.value.pval
 
         model_def = {'name':obj.name.value.pval,'fields':fields,'links':links, 'bases':obj.bases, 'options':self.message_options, 'package':self.package, 'fqn': model_name, 'rlinks': []}
+        try:
+            model_def['policy'] = obj.policy.pval
+        except AttributeError:
+            model_def['policy'] = None
+
         self.stack.push(model_def)
         
         self.models[model_name] = model_def
@@ -352,4 +372,4 @@
     def visit_LinkSpec(self, obj):
         count = self.count_stack.pop()
         self.count_stack.push(count + 1)
-        return True
\ No newline at end of file
+        return True
diff --git a/xos/coreapi/protos/Makefile b/xos/coreapi/protos/Makefile
index ad9ba1f..cfbaa52 100644
--- a/xos/coreapi/protos/Makefile
+++ b/xos/coreapi/protos/Makefile
@@ -102,4 +102,4 @@
 
 rebuild-protos: $(AUTOGENERATED)
 
-.PHONY: $(AUTOGENERATED) rebuild-protos
\ No newline at end of file
+.PHONY: $(AUTOGENERATED) rebuild-protos
diff --git a/xos/tools/corebuilder/corebuilder.py b/xos/tools/corebuilder/corebuilder.py
index be8702e..845e7c7 100644
--- a/xos/tools/corebuilder/corebuilder.py
+++ b/xos/tools/corebuilder/corebuilder.py
@@ -301,7 +301,6 @@
                     # Generate models
                     is_service = service_name != 'core'
 
-
                     args = Args()
                     args.output = build_dest_fn
                     args.attic = src_fn + '/attic'
@@ -328,8 +327,8 @@
                         XOSGenerator.generate(InitArgs())
 
                 except Exception, e:
-                    print e
-                    raise Exception('xproto build failed!')
+                    print 'xproto build failed.'
+                    raise e
 
 
         # Create the __init__.py files
@@ -386,10 +385,3 @@
 if __name__ == "__main__":
     main()
 
-
-
-
-
-
-
-