CORD-1939: support for nose2
modify test cases to be compliant with automation
Cherry-pick from cord-4.1 b641f9060eb912470a284bfd04dd955b031af708
Change-Id: I01914ccfb57979994534c6128bc5ff72ae10f64b
diff --git a/lib/xos-genx/xos-genx-tests/__init__.py b/lib/xos-genx/xos-genx-tests/__init__.py
new file mode 100644
index 0000000..d4e8062
--- /dev/null
+++ b/lib/xos-genx/xos-genx-tests/__init__.py
@@ -0,0 +1,16 @@
+
+# Copyright 2017-present Open Networking Foundation
+#
+# 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.
+
+
diff --git a/lib/xos-genx/xos-genx-tests/attics/xosmodel_bottom.py b/lib/xos-genx/xos-genx-tests/attics/xosmodel_bottom.py
new file mode 100644
index 0000000..8e28718
--- /dev/null
+++ b/lib/xos-genx/xos-genx-tests/attics/xosmodel_bottom.py
@@ -0,0 +1,18 @@
+
+# Copyright 2017-present Open Networking Foundation
+#
+# 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.
+
+
+def bottom():
+ return 'bottom'
\ No newline at end of file
diff --git a/lib/xos-genx/xos-genx-tests/attics/xosmodel_header.py b/lib/xos-genx/xos-genx-tests/attics/xosmodel_header.py
new file mode 100644
index 0000000..044eb4e
--- /dev/null
+++ b/lib/xos-genx/xos-genx-tests/attics/xosmodel_header.py
@@ -0,0 +1,18 @@
+
+# Copyright 2017-present Open Networking Foundation
+#
+# 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.
+
+
+def header():
+ return 'header'
\ No newline at end of file
diff --git a/lib/xos-genx/xos-genx-tests/attics/xosmodel_model.py b/lib/xos-genx/xos-genx-tests/attics/xosmodel_model.py
new file mode 100644
index 0000000..ca48b14
--- /dev/null
+++ b/lib/xos-genx/xos-genx-tests/attics/xosmodel_model.py
@@ -0,0 +1,18 @@
+
+# Copyright 2017-present Open Networking Foundation
+#
+# 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.
+
+
+def model():
+ return 'model'
\ No newline at end of file
diff --git a/lib/xos-genx/xos-genx-tests/attics/xosmodel_top.py b/lib/xos-genx/xos-genx-tests/attics/xosmodel_top.py
new file mode 100644
index 0000000..2d0adcb
--- /dev/null
+++ b/lib/xos-genx/xos-genx-tests/attics/xosmodel_top.py
@@ -0,0 +1,18 @@
+
+# Copyright 2017-present Open Networking Foundation
+#
+# 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.
+
+
+def top():
+ return 'top'
\ No newline at end of file
diff --git a/lib/xos-genx/xos-genx-tests/counts b/lib/xos-genx/xos-genx-tests/counts
new file mode 100755
index 0000000..8ab44d4
--- /dev/null
+++ b/lib/xos-genx/xos-genx-tests/counts
@@ -0,0 +1,34 @@
+#!/usr/bin/python
+
+import core.models
+import inspect
+from core.models import XOSBase, PlModelMixIn
+import pdb
+
+def count(lst):
+ c = 0
+ for l in lst[0]:
+ ll = l.lstrip()
+ if (ll and not ll.startswith('#') and ll.rstrip()!='pass' and 'ModelLink' not in ll and 'CHOICES' not in ll):
+ c+=1
+ return c
+
+def is_model_class(model):
+ """ Return True if 'model' is something that we're interested in """
+ if not inspect.isclass(model):
+ return False
+ if model.__name__ in ["PlModelMixIn"]:
+ return False
+ bases = inspect.getmro(model)
+ bases = [x.__name__ for x in bases]
+ if ("XOSBase" in bases) or ("PlModelMixIn" in bases):
+ return True
+
+ return False
+
+for a in dir(core.models):
+ x = getattr(core.models,a)
+ if (is_model_class(x)):
+ lines = inspect.getsourcelines(x)
+ print x.__name__,":",count(lines)
+
diff --git a/lib/xos-genx/xos-genx-tests/django_generator_test.py b/lib/xos-genx/xos-genx-tests/django_generator_test.py
new file mode 100644
index 0000000..914c4a3
--- /dev/null
+++ b/lib/xos-genx/xos-genx-tests/django_generator_test.py
@@ -0,0 +1,43 @@
+
+# Copyright 2017-present Open Networking Foundation
+#
+# 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.
+
+
+import unittest
+import os
+from xosgenx.generator import XOSGenerator
+from helpers import FakeArgs
+
+VROUTER_XPROTO = os.path.abspath(os.path.dirname(os.path.realpath(__file__)) + "/xproto/vrouterport.xproto")
+
+# Generate Protobuf from Xproto and then parse the resulting Protobuf
+class XProtoProtobufGeneratorTest(unittest.TestCase):
+ def test_proto_generator(self):
+ """
+ [XOS-GenX] Generate DJANGO models, verify Fields and Foreign Keys
+ """
+ args = FakeArgs()
+ args.files = [VROUTER_XPROTO]
+ args.target = 'django.xtarget'
+ output = XOSGenerator.generate(args)
+
+ fields = filter(lambda s:'Field(' in s, output.splitlines())
+ self.assertEqual(len(fields), 2)
+ links = filter(lambda s:'Key(' in s, output.splitlines())
+ self.assertEqual(len(links), 2)
+
+if __name__ == '__main__':
+ unittest.main()
+
+
diff --git a/lib/xos-genx/xos-genx-tests/field_graph_test.py b/lib/xos-genx/xos-genx-tests/field_graph_test.py
new file mode 100644
index 0000000..f7f004c
--- /dev/null
+++ b/lib/xos-genx/xos-genx-tests/field_graph_test.py
@@ -0,0 +1,92 @@
+
+# Copyright 2017-present Open Networking Foundation
+#
+# 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.
+
+
+import unittest
+from xosgenx.jinja2_extensions import FieldNotFound
+from helpers import FakeArgs, OUTPUT_DIR, XProtoTestHelpers
+from xosgenx.generator import XOSGenerator
+
+class XProtoFieldGraphTest(unittest.TestCase):
+ def test_field_graph(self):
+ xproto = \
+"""
+message VRouterDevice (PlCoreBase){
+ optional string name = 1 [help_text = "device friendly name", max_length = 20, null = True, db_index = False, blank = True, unique_with="openflow_id"];
+ required string openflow_id = 2 [help_text = "device identifier in ONOS", max_length = 20, null = False, db_index = False, blank = False, unique_with="name"];
+ required string config_key = 3 [default = "basic", max_length = 32, blank = False, help_text = "configuration key", null = False, db_index = False, unique_with="driver"];
+ required string driver = 4 [help_text = "driver type", max_length = 32, null = False, db_index = False, blank = False, unique_with="vrouter_service"];
+ required manytoone vrouter_service->VRouterService:devices = 5 [db_index = True, null = False, blank = False];
+ required string A = 6 [unique_with="B"];
+ required string B = 7 [unique_with="C"];
+ required string C = 8 [unique_with="A"];
+ required string D = 9;
+ required string E = 10 [unique_with="F,G"];
+ required string F = 11;
+ required string G = 12;
+}
+"""
+ target = XProtoTestHelpers.write_tmp_target(
+"""
+{{ xproto_field_graph_components(proto.messages.0.fields) }}
+""")
+
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = target
+ output = XOSGenerator.generate(args)
+ output = eval(output)
+ self.assertIn({'A','B','C'}, output)
+ self.assertIn({'openflow_id','name'}, output)
+ self.assertIn({'config_key','vrouter_service','driver'}, output)
+ self.assertIn({'E','F','G'}, output)
+
+ union = reduce(lambda acc,x: acc | x, output)
+ self.assertNotIn('D', union)
+
+ def test_missing_field(self):
+ xproto = \
+"""
+message VRouterDevice (PlCoreBase){
+ optional string name = 1 [help_text = "device friendly name", max_length = 20, null = True, db_index = False, blank = True, unique_with="hamburger"];
+ required string openflow_id = 2 [help_text = "device identifier in ONOS", max_length = 20, null = False, db_index = False, blank = False, unique_with="name"];
+ required string config_key = 3 [default = "basic", max_length = 32, blank = False, help_text = "configuration key", null = False, db_index = False, unique_with="driver"];
+ required string driver = 4 [help_text = "driver type", max_length = 32, null = False, db_index = False, blank = False, unique_with="vrouter_service"];
+ required manytoone vrouter_service->VRouterService:devices = 5 [db_index = True, null = False, blank = False];
+ required string A = 6 [unique_with="B"];
+ required string B = 7 [unique_with="C"];
+ required string C = 8 [unique_with="A"];
+ required string D = 9;
+}
+"""
+ target = XProtoTestHelpers.write_tmp_target(
+"""
+{{ xproto_field_graph_components(proto.messages.0.fields) }}
+""")
+
+ def generate():
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = target
+ output = XOSGenerator.generate(args)
+
+ # The following call generates some output, which should disappear
+ # when Matteo merges his refactoring of XOSGenerator.
+ self.assertRaises(FieldNotFound, generate)
+
+if __name__ == '__main__':
+ unittest.main()
+
+
diff --git a/lib/xos-genx/xos-genx-tests/general_security_test.py b/lib/xos-genx/xos-genx-tests/general_security_test.py
new file mode 100644
index 0000000..dbf8538
--- /dev/null
+++ b/lib/xos-genx/xos-genx-tests/general_security_test.py
@@ -0,0 +1,258 @@
+
+# Copyright 2017-present Open Networking Foundation
+#
+# 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.
+
+
+import unittest
+from xosgenx.generator import XOSGenerator
+from helpers import FakeArgs, XProtoTestHelpers
+
+"""The function below is for eliminating warnings arising due to the missing output_security_check,
+which is generated and loaded dynamically.
+"""
+def output_security_check(x, y):
+ raise Exception("Security enforcer not generated. Test failed.")
+ return False
+
+"""
+The tests below use the Python code target to generate
+Python security policies, set up an appropriate environment and execute the Python.
+"""
+class XProtoSecurityTest(unittest.TestCase):
+ def setUp(self):
+ self.target = XProtoTestHelpers.write_tmp_target("""
+{% for name, policy in proto.policies.items() %}
+{{ xproto_fol_to_python_test(name, policy, None, '0') }}
+{% endfor %}
+""")
+
+ def test_constant(self):
+ xproto = \
+"""
+ policy output < True >
+"""
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = self.target
+
+ output = XOSGenerator.generate(args)
+
+ exec(output) # This loads the generated function, which should look like this:
+
+ """
+ def output_security_check(obj, ctx):
+ i1 = True
+ return i1
+ """
+
+ verdict = output_security_check({}, {})
+ self.assertTrue(verdict)
+
+ def test_equal(self):
+ xproto = \
+"""
+ policy output < ctx.user = obj.user >
+"""
+
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = self.target
+
+ output = XOSGenerator.generate(args)
+
+ exec(output) # This loads the generated function, which should look like this:
+
+ """
+ def output_security_check(obj, ctx):
+ i1 = (ctx.user == obj.user)
+ return i1
+ """
+
+ obj = FakeArgs()
+ obj.user = 1
+ ctx = FakeArgs()
+ ctx.user = 1
+
+ verdict = output_security_check(obj, ctx)
+
+ def test_call_policy(self):
+ xproto = \
+"""
+ policy sub_policy < ctx.user = obj.user >
+ policy output < *sub_policy(child) >
+"""
+
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = self.target
+
+ output = XOSGenerator.generate(args)
+
+ exec(output,globals()) # This loads the generated function, which should look like this:
+
+ """
+ def sub_policy_security_check(obj, ctx):
+ i1 = (ctx.user == obj.user)
+ return i1
+
+ def output_security_check(obj, ctx):
+ if obj.child:
+ i1 = sub_policy_security_check(obj.child, ctx)
+ else:
+ i1 = True
+ return i1
+ """
+
+ obj = FakeArgs()
+ obj.child = FakeArgs()
+ obj.child.user = 1
+
+ ctx = FakeArgs()
+ ctx.user = 1
+
+ verdict = output_security_check(obj, ctx)
+ self.assertTrue(verdict)
+
+ def test_call_policy_child_none(self):
+ xproto = \
+"""
+ policy sub_policy < ctx.user = obj.user >
+ policy output < *sub_policy(child) >
+"""
+
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = self.target
+
+ output = XOSGenerator.generate(args)
+
+ exec(output,globals()) # This loads the generated function, which should look like this:
+
+ """
+ def sub_policy_security_check(obj, ctx):
+ i1 = (ctx.user == obj.user)
+ return i1
+
+ def output_security_check(obj, ctx):
+ if obj.child:
+ i1 = sub_policy_security_check(obj.child, ctx)
+ else:
+ i1 = True
+ return i1
+ """
+
+ obj = FakeArgs()
+ obj.child = None
+
+ ctx = FakeArgs()
+ ctx.user = 1
+
+ verdict = output_security_check(obj, ctx)
+ self.assertTrue(verdict)
+
+
+ def test_bin(self):
+ xproto = \
+"""
+ policy output < ctx.is_admin = True | obj.empty = True>
+"""
+
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = self.target
+
+ output = XOSGenerator.generate(args)
+ exec(output) # This loads the generated function, which should look like this:
+
+ """
+ def output_security_check(obj, ctx):
+ i2 = (ctx.is_admin == True)
+ i3 = (obj.empty == True)
+ i1 = (i2 or i3)
+ return i1
+ """
+
+ obj = FakeArgs()
+ obj.empty = True
+
+ ctx = FakeArgs()
+ ctx.is_admin = True
+
+ verdict = output_security_check(obj, ctx)
+
+ self.assertTrue(verdict)
+
+
+ def test_exists(self):
+ xproto = \
+"""
+ policy output < exists Privilege: Privilege.object_id = obj.id >
+"""
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = self.target
+
+ output = XOSGenerator.generate(args)
+ exec(output) # This loads the generated function, which should look like this:
+
+ """
+ def output_security_check(obj, ctx):
+ i1 = Privilege.objects.filter(object_id=obj.id)
+ return i1
+ """
+
+ self.assertTrue(output_security_check is not None)
+
+ def test_python(self):
+ xproto = \
+"""
+ policy output < {{ "jack" in ["the", "box"] }} = False >
+"""
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = self.target
+ output = XOSGenerator.generate(args)
+ exec(output) # This loads the generated function, which should look like this:
+
+ """
+ def output_security_check(obj, ctx):
+ i2 = ('jack' in ['the', 'box'])
+ i1 = (i2 == False)
+ return i1
+ """
+
+ self.assertTrue(output_security_check({}, {}) is True)
+
+ def test_forall(self):
+ # This one we only parse
+ xproto = \
+"""
+ policy output < forall Credential: Credential.obj_id = obj_id >
+"""
+
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = self.target
+
+ output = XOSGenerator.generate(args)
+ """
+ def output_security_check(obj, ctx):
+ i2 = Credential.objects.filter((~ Q(obj_id=obj_id)))[0]
+ i1 = (not i2)
+ return i1
+ """
+ exec(output)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/lib/xos-genx/xos-genx-tests/general_validation_test.py b/lib/xos-genx/xos-genx-tests/general_validation_test.py
new file mode 100644
index 0000000..fbed567
--- /dev/null
+++ b/lib/xos-genx/xos-genx-tests/general_validation_test.py
@@ -0,0 +1,259 @@
+
+# Copyright 2017-present Open Networking Foundation
+#
+# 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.
+
+
+import unittest
+from xosgenx.generator import XOSGenerator
+from helpers import FakeArgs, XProtoTestHelpers
+import pdb
+
+"""The function below is for eliminating warnings arising due to the missing policy_output_validator,
+which is generated and loaded dynamically.
+"""
+def policy_output_validator(x, y):
+ raise Exception("Validator not generated. Test failed.")
+ return False
+
+"""
+The tests below use the Python code target to generate
+Python validation policies, set up an appropriate environment and execute the Python.
+"""
+class XProtoGeneralValidationTest(unittest.TestCase):
+ def setUp(self):
+ self.target = XProtoTestHelpers.write_tmp_target("""
+{% for name, policy in proto.policies.items() %}
+{{ xproto_fol_to_python_validator(name, policy, None, 'Necessary Failure') }}
+{% endfor %}
+""")
+
+ def test_constant(self):
+ xproto = \
+"""
+ policy output < False >
+"""
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = self.target
+
+ output = XOSGenerator.generate(args)
+
+ exec(output) # This loads the generated function, which should look like this:
+
+ """
+ def policy_output_validator(obj, ctx):
+ i1 = False
+ if (not i1):
+ raise Exception('Necessary Failure')
+ """
+
+ with self.assertRaises(Exception):
+ policy_output_validator({}, {})
+
+ def test_equal(self):
+ xproto = \
+"""
+ policy output < not (ctx.user = obj.user) >
+"""
+
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = self.target
+
+ output = XOSGenerator.generate(args)
+
+ exec(output) # This loads the generated function, which should look like this:
+
+ """
+ def policy_output_validator(obj, ctx):
+ i2 = (ctx.user == obj.user)
+ i1 = (not i2)
+ if (not i1):
+ raise Exception('Necessary Failure')
+ """
+
+ obj = FakeArgs()
+ obj.user = 1
+ ctx = FakeArgs()
+ ctx.user = 1
+
+ with self.assertRaises(Exception):
+ policy_output_validator(obj, ctx)
+
+ def test_equal(self):
+ xproto = \
+"""
+ policy output < not (ctx.user = obj.user) >
+"""
+
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = self.target
+
+ output = XOSGenerator.generate(args)
+
+ exec(output) # This loads the generated function, which should look like this:
+
+ """
+ def policy_output_validator(obj, ctx):
+ i2 = (ctx.user == obj.user)
+ i1 = (not i2)
+ if (not i1):
+ raise Exception('Necessary Failure')
+ """
+
+ obj = FakeArgs()
+ obj.user = 1
+ ctx = FakeArgs()
+ ctx.user = 1
+
+ with self.assertRaises(Exception):
+ policy_output_validator(obj, ctx)
+
+ def test_bin(self):
+ xproto = \
+"""
+ policy output < (ctx.is_admin = True | obj.empty = True) | False>
+"""
+
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = self.target
+
+ output = XOSGenerator.generate(args)
+ exec(output) # This loads the generated function, which should look like this:
+
+ """
+ def policy_output_validator(obj, ctx):
+ i2 = (ctx.is_admin == True)
+ i3 = (obj.empty == True)
+ i1 = (i2 or i3)
+ if (not i1):
+ raise Exception('Necessary Failure')
+ """
+
+ obj = FakeArgs()
+ obj.empty = False
+
+ ctx = FakeArgs()
+ ctx.is_admin = False
+
+ with self.assertRaises(Exception):
+ verdict = policy_output_validator(obj, ctx)
+
+
+ def test_exists(self):
+ xproto = \
+"""
+ policy output < exists Privilege: Privilege.object_id = obj.id >
+"""
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = self.target
+
+ output = XOSGenerator.generate(args)
+ exec(output) # This loads the generated function, which should look like this:
+
+ """
+ def policy_output_validator(obj, ctx):
+ i1 = Privilege.objects.filter(Q(object_id=obj.id))[0]
+ if (not i1):
+ raise Exception('Necessary Failure')
+ """
+
+ self.assertTrue(policy_output_validator is not None)
+
+ def test_python(self):
+ xproto = \
+"""
+ policy output < {{ "jack" in ["the", "box"] }} = True >
+"""
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = self.target
+ output = XOSGenerator.generate(args)
+ exec(output) # This loads the generated function, which should look like this:
+
+ """
+ def policy_output_validator(obj, ctx):
+ i2 = ('jack' in ['the', 'box'])
+ i1 = (i2 == True)
+ if (not i1):
+ raise Exception('Necessary Failure')
+ """
+
+ with self.assertRaises(Exception):
+ self.assertTrue(policy_output_validator({}, {}) is True)
+
+ def test_call_policy(self):
+ xproto = \
+"""
+ policy sub_policy < ctx.user = obj.user >
+ policy output < *sub_policy(child) >
+"""
+
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = self.target
+
+ output = XOSGenerator.generate(args)
+
+ exec(output,globals()) # This loads the generated function, which should look like this:
+
+ """
+ def policy_sub_policy_validator(obj, ctx):
+ i1 = (ctx.user == obj.user)
+ if (not i1):
+ raise ValidationError('Necessary Failure')
+
+ def policy_output_validator(obj, ctx):
+ i1 = policy_sub_policy_validator(obj.child, ctx)
+ if (not i1):
+ raise ValidationError('Necessary Failure')
+ """
+
+ obj = FakeArgs()
+ obj.child = FakeArgs()
+ obj.child.user = 1
+
+ ctx = FakeArgs()
+ ctx.user = 1
+
+ with self.assertRaises(Exception):
+ verdict = policy_output_enforcer(obj, ctx)
+
+ def test_forall(self):
+ # This one we only parse
+ xproto = \
+"""
+ policy output < forall Credential: Credential.obj_id = obj_id >
+"""
+
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = self.target
+
+ output = XOSGenerator.generate(args)
+
+ """
+ def policy_output_enforcer(obj, ctx):
+ i2 = Credential.objects.filter((~ Q(obj_id=obj_id)))[0]
+ i1 = (not i2)
+ return i1
+ """
+
+ self.assertIn('policy_output_validator', output)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/lib/xos-genx/xos-genx-tests/graph_test.py b/lib/xos-genx/xos-genx-tests/graph_test.py
new file mode 100644
index 0000000..48978e9
--- /dev/null
+++ b/lib/xos-genx/xos-genx-tests/graph_test.py
@@ -0,0 +1,317 @@
+
+# Copyright 2017-present Open Networking Foundation
+#
+# 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.
+
+
+import unittest
+from xosgenx.generator import XOSGenerator
+from helpers import FakeArgs, XProtoTestHelpers
+
+class XProtoGraphTests(unittest.TestCase):
+ def test_cross_model(self):
+ target = XProtoTestHelpers.write_tmp_target(
+"""
+ {% for m in proto.messages %}
+ {{ m.name }} {
+ {%- for l in m.links %}
+ {%- if proto.message_table[l.peer.fqn] -%}
+ {%- set model = proto.message_table[l.peer.fqn] %}
+ {% for f in model.fields %}
+ {{ f.type }} {{ f.name }};
+ {% endfor %}
+ {%- endif -%}
+ {% endfor %}
+ }
+ {% endfor %}
+""")
+
+ proto = \
+"""
+message Port (PlCoreBase,ParameterMixin){
+ required manytoone network->Network:links = 1 [db_index = True, null = False, blank = False];
+ optional manytoone instance->Instance:ports = 2 [db_index = True, null = True, blank = True];
+ optional string ip = 3 [max_length = 39, content_type = "ip", blank = True, help_text = "Instance ip address", null = True, db_index = False];
+ optional string port_id = 4 [help_text = "Neutron port id", max_length = 256, null = True, db_index = False, blank = True];
+ optional string mac = 5 [help_text = "MAC address associated with this port", max_length = 256, null = True, db_index = False, blank = True];
+ required bool xos_created = 6 [default = False, null = False, db_index = False, blank = True];
+}
+
+message Instance (PlCoreBase){
+ optional string instance_id = 1 [max_length = 200, content_type = "stripped", blank = True, help_text = "Nova instance id", null = True, db_index = False];
+ optional string instance_uuid = 2 [max_length = 200, content_type = "stripped", blank = True, help_text = "Nova instance uuid", null = True, db_index = False];
+ required string name = 3 [max_length = 200, content_type = "stripped", blank = False, help_text = "Instance name", null = False, db_index = False];
+ optional string instance_name = 4 [max_length = 200, content_type = "stripped", blank = True, help_text = "OpenStack generated name", null = True, db_index = False];
+ optional string ip = 5 [max_length = 39, content_type = "ip", blank = True, help_text = "Instance ip address", null = True, db_index = False];
+ required manytoone image->Image:instances = 6 [db_index = True, null = False, blank = False];
+ optional manytoone creator->User:instances = 7 [db_index = True, null = True, blank = True];
+ required manytoone slice->Slice:instances = 8 [db_index = True, null = False, blank = False];
+ required manytoone deployment->Deployment:instance_deployment = 9 [db_index = True, null = False, blank = False];
+ required manytoone node->Node:instances = 10 [db_index = True, null = False, blank = False];
+ required int32 numberCores = 11 [help_text = "Number of cores for instance", default = 0, null = False, db_index = False, blank = False];
+ required manytoone flavor->Flavor:instance = 12 [help_text = "Flavor of this instance", default = "get_default_flavor()", null = False, db_index = True, blank = False];
+ optional string userData = 13 [help_text = "user_data passed to instance during creation", null = True, db_index = False, blank = True];
+ required string isolation = 14 [default = "vm", choices = "(('vm', 'Virtual Machine'), ('container', 'Container'), ('container_vm', 'Container In VM'))", max_length = 30, blank = False, null = False, db_index = False];
+ optional string volumes = 15 [help_text = "Comma-separated list of directories to expose to parent context", null = True, db_index = False, blank = True];
+ optional manytoone parent->Instance:instance = 16 [help_text = "Parent Instance for containers nested inside of VMs", null = True, db_index = True, blank = True];
+ required manytomany tags->Tag = 17 [db_index = False, null = False, blank = True];
+}
+
+message Network (PlCoreBase,ParameterMixin) {
+ required string name = 1 [db_index = False, max_length = 32, null = False, blank = False];
+ required manytoone template->NetworkTemplate:network = 2 [db_index = True, null = False, blank = False];
+ required string subnet = 3 [db_index = False, max_length = 32, null = False, blank = True];
+ required string start_ip = 4 [db_index = False, max_length = 32, null = False, blank = True];
+ required string end_ip = 5 [db_index = False, max_length = 32, null = False, blank = True];
+ optional string ports = 6 [db_index = False, max_length = 1024, null = True, blank = True];
+ optional string labels = 7 [db_index = False, max_length = 1024, null = True, blank = True];
+ required manytoone owner->Slice:ownedNetworks = 8 [help_text = "Slice that owns control of this Network", null = False, db_index = True, blank = False];
+ required int32 guaranteed_bandwidth = 9 [default = 0, null = False, db_index = False, blank = False];
+ required bool permit_all_slices = 10 [default = False, null = False, db_index = False, blank = True];
+ optional string topology_parameters = 11 [db_index = False, null = True, blank = True];
+ optional string controller_url = 12 [db_index = False, max_length = 1024, null = True, blank = True];
+ optional string controller_parameters = 13 [db_index = False, null = True, blank = True];
+ optional string network_id = 14 [help_text = "Quantum network", max_length = 256, null = True, db_index = False, blank = True];
+ optional string router_id = 15 [help_text = "Quantum router id", max_length = 256, null = True, db_index = False, blank = True];
+ optional string subnet_id = 16 [help_text = "Quantum subnet id", max_length = 256, null = True, db_index = False, blank = True];
+ required bool autoconnect = 17 [help_text = "This network can be autoconnected to the slice that owns it", default = True, null = False, db_index = False, blank = True];
+ required manytomany permitted_slices->Slice/Network_permitted_slices:availableNetworks = 18 [db_index = False, null = False, blank = True];
+ required manytomany slices->Slice/NetworkSlice:networks = 19 [db_index = False, null = False, blank = True];
+ required manytomany instances->Instance/Port:networks = 20 [db_index = False, null = False, blank = True];
+}
+
+message Slice (PlCoreBase){
+ required string name = 1 [max_length = 80, content_type = "stripped", blank = False, help_text = "The Name of the Slice", null = False, db_index = False];
+ required bool enabled = 2 [help_text = "Status for this Slice", default = True, null = False, db_index = False, blank = True];
+ required bool omf_friendly = 3 [default = False, null = False, db_index = False, blank = True];
+ required string description = 4 [help_text = "High level description of the slice and expected activities", max_length = 1024, null = False, db_index = False, blank = True];
+ required string slice_url = 5 [db_index = False, max_length = 512, null = False, content_type = "url", blank = True];
+ required manytoone site->Site:slices = 6 [help_text = "The Site this Slice belongs to", null = False, db_index = True, blank = False];
+ required int32 max_instances = 7 [default = 10, null = False, db_index = False, blank = False];
+ optional manytoone service->Service:slices = 8 [db_index = True, null = True, blank = True];
+ optional string network = 9 [blank = True, max_length = 256, null = True, db_index = False, choices = "((None, 'Default'), ('host', 'Host'), ('bridged', 'Bridged'), ('noauto', 'No Automatic Networks'))"];
+ optional string exposed_ports = 10 [db_index = False, max_length = 256, null = True, blank = True];
+ optional manytoone serviceClass->ServiceClass:slices = 11 [db_index = True, null = True, blank = True];
+ optional manytoone creator->User:slices = 12 [db_index = True, null = True, blank = True];
+ optional manytoone default_flavor->Flavor:slices = 13 [db_index = True, null = True, blank = True];
+ optional manytoone default_image->Image:slices = 14 [db_index = True, null = True, blank = True];
+ optional manytoone default_node->Node:slices = 15 [db_index = True, null = True, blank = True];
+ optional string mount_data_sets = 16 [default = "GenBank", max_length = 256, content_type = "stripped", blank = True, null = True, db_index = False];
+ required string default_isolation = 17 [default = "vm", choices = "(('vm', 'Virtual Machine'), ('container', 'Container'), ('container_vm', 'Container In VM'))", max_length = 30, blank = False, null = False, db_index = False];
+ required manytomany tags->Tag = 18 [db_index = False, null = False, blank = True];
+}
+"""
+
+ args = FakeArgs()
+ args.inputs = proto
+ args.target = target
+ output = XOSGenerator.generate(args)
+ num_semis = output.count(';')
+ self.assertGreater(num_semis, 3) # 3 is the number of links, each of which contains at least one field
+
+ def test_base_class_fields(self):
+ target = \
+"""
+ {% for m in proto.messages %}
+ {{ m.name }} {
+ {%- for l in m.links %}
+ {%- if proto.message_table[l.peer.fqn] -%}
+ {%- set model = proto.message_table[l.peer.fqn] %}
+ {% for f in model.fields %}
+ {{ f.type }} {{ f.name }};
+ {% endfor %}
+ {%- endif -%}
+ {% endfor %}
+ }
+ {% endfor %}
+"""
+ xtarget = XProtoTestHelpers.write_tmp_target(target)
+
+ proto = \
+"""
+message Port (PlCoreBase,ParameterMixin){
+ required manytoone network->Network:links = 1 [db_index = True, null = False, blank = False];
+ optional manytoone instance->Instance:ports = 2 [db_index = True, null = True, blank = True];
+ optional string ip = 3 [max_length = 39, content_type = "ip", blank = True, help_text = "Instance ip address", null = True, db_index = False];
+ optional string port_id = 4 [help_text = "Neutron port id", max_length = 256, null = True, db_index = False, blank = True];
+ optional string mac = 5 [help_text = "MAC address associated with this port", max_length = 256, null = True, db_index = False, blank = True];
+ required bool xos_created = 6 [default = False, null = False, db_index = False, blank = True];
+}
+
+message Instance (PlCoreBase){
+ optional string instance_id = 1 [max_length = 200, content_type = "stripped", blank = True, help_text = "Nova instance id", null = True, db_index = False];
+ optional string instance_uuid = 2 [max_length = 200, content_type = "stripped", blank = True, help_text = "Nova instance uuid", null = True, db_index = False];
+ required string name = 3 [max_length = 200, content_type = "stripped", blank = False, help_text = "Instance name", null = False, db_index = False];
+ optional string instance_name = 4 [max_length = 200, content_type = "stripped", blank = True, help_text = "OpenStack generated name", null = True, db_index = False];
+ optional string ip = 5 [max_length = 39, content_type = "ip", blank = True, help_text = "Instance ip address", null = True, db_index = False];
+ required manytoone image->Image:instances = 6 [db_index = True, null = False, blank = False];
+ optional manytoone creator->User:instances = 7 [db_index = True, null = True, blank = True];
+ required manytoone slice->Slice:instances = 8 [db_index = True, null = False, blank = False];
+ required manytoone deployment->Deployment:instance_deployment = 9 [db_index = True, null = False, blank = False];
+ required manytoone node->Node:instances = 10 [db_index = True, null = False, blank = False];
+ required int32 numberCores = 11 [help_text = "Number of cores for instance", default = 0, null = False, db_index = False, blank = False];
+ required manytoone flavor->Flavor:instance = 12 [help_text = "Flavor of this instance", default = "get_default_flavor()", null = False, db_index = True, blank = False];
+ optional string userData = 13 [help_text = "user_data passed to instance during creation", null = True, db_index = False, blank = True];
+ required string isolation = 14 [default = "vm", choices = "(('vm', 'Virtual Machine'), ('container', 'Container'), ('container_vm', 'Container In VM'))", max_length = 30, blank = False, null = False, db_index = False];
+ optional string volumes = 15 [help_text = "Comma-separated list of directories to expose to parent context", null = True, db_index = False, blank = True];
+ optional manytoone parent->Instance:instance = 16 [help_text = "Parent Instance for containers nested inside of VMs", null = True, db_index = True, blank = True];
+ required manytomany tags->Tag = 17 [db_index = False, null = False, blank = True];
+}
+
+message Network (PlCoreBase,ParameterMixin) {
+ required string name = 1 [db_index = False, max_length = 32, null = False, blank = False];
+ required manytoone template->NetworkTemplate:network = 2 [db_index = True, null = False, blank = False];
+ required string subnet = 3 [db_index = False, max_length = 32, null = False, blank = True];
+ required string start_ip = 4 [db_index = False, max_length = 32, null = False, blank = True];
+ required string end_ip = 5 [db_index = False, max_length = 32, null = False, blank = True];
+ optional string ports = 6 [db_index = False, max_length = 1024, null = True, blank = True];
+ optional string labels = 7 [db_index = False, max_length = 1024, null = True, blank = True];
+ required manytoone owner->Slice:ownedNetworks = 8 [help_text = "Slice that owns control of this Network", null = False, db_index = True, blank = False];
+ required int32 guaranteed_bandwidth = 9 [default = 0, null = False, db_index = False, blank = False];
+ required bool permit_all_slices = 10 [default = False, null = False, db_index = False, blank = True];
+ optional string topology_parameters = 11 [db_index = False, null = True, blank = True];
+ optional string controller_url = 12 [db_index = False, max_length = 1024, null = True, blank = True];
+ optional string controller_parameters = 13 [db_index = False, null = True, blank = True];
+ optional string network_id = 14 [help_text = "Quantum network", max_length = 256, null = True, db_index = False, blank = True];
+ optional string router_id = 15 [help_text = "Quantum router id", max_length = 256, null = True, db_index = False, blank = True];
+ optional string subnet_id = 16 [help_text = "Quantum subnet id", max_length = 256, null = True, db_index = False, blank = True];
+ required bool autoconnect = 17 [help_text = "This network can be autoconnected to the slice that owns it", default = True, null = False, db_index = False, blank = True];
+ required manytomany permitted_slices->Slice/Network_permitted_slices:availableNetworks = 18 [db_index = False, null = False, blank = True];
+ required manytomany slices->Slice/NetworkSlice:networks = 19 [db_index = False, null = False, blank = True];
+ required manytomany instances->Instance/Port:networks = 20 [db_index = False, null = False, blank = True];
+}
+
+message Slice (PlCoreBase){
+ required string name = 1 [max_length = 80, content_type = "stripped", blank = False, help_text = "The Name of the Slice", null = False, db_index = False];
+ required bool enabled = 2 [help_text = "Status for this Slice", default = True, null = False, db_index = False, blank = True];
+ required bool omf_friendly = 3 [default = False, null = False, db_index = False, blank = True];
+ required string description = 4 [help_text = "High level description of the slice and expected activities", max_length = 1024, null = False, db_index = False, blank = True];
+ required string slice_url = 5 [db_index = False, max_length = 512, null = False, content_type = "url", blank = True];
+ required manytoone site->Site:slices = 6 [help_text = "The Site this Slice belongs to", null = False, db_index = True, blank = False];
+ required int32 max_instances = 7 [default = 10, null = False, db_index = False, blank = False];
+ optional manytoone service->Service:slices = 8 [db_index = True, null = True, blank = True];
+ optional string network = 9 [blank = True, max_length = 256, null = True, db_index = False, choices = "((None, 'Default'), ('host', 'Host'), ('bridged', 'Bridged'), ('noauto', 'No Automatic Networks'))"];
+ optional string exposed_ports = 10 [db_index = False, max_length = 256, null = True, blank = True];
+ optional manytoone serviceClass->ServiceClass:slices = 11 [db_index = True, null = True, blank = True];
+ optional manytoone creator->User:slices = 12 [db_index = True, null = True, blank = True];
+ optional manytoone default_flavor->Flavor:slices = 13 [db_index = True, null = True, blank = True];
+ optional manytoone default_image->Image:slices = 14 [db_index = True, null = True, blank = True];
+ optional manytoone default_node->Node:slices = 15 [db_index = True, null = True, blank = True];
+ optional string mount_data_sets = 16 [default = "GenBank", max_length = 256, content_type = "stripped", blank = True, null = True, db_index = False];
+ required string default_isolation = 17 [default = "vm", choices = "(('vm', 'Virtual Machine'), ('container', 'Container'), ('container_vm', 'Container In VM'))", max_length = 30, blank = False, null = False, db_index = False];
+ required manytomany tags->Tag = 18 [db_index = False, null = False, blank = True];
+}
+"""
+
+ args = FakeArgs()
+ args.inputs = proto
+ args.target = xtarget
+ output = XOSGenerator.generate(args)
+
+ num_semis = output.count(';')
+ self.assertGreater(num_semis, 3)
+
+ def test_from_base(self):
+ target = \
+"""
+ {% for f in xproto_base_fields(proto.messages.3, proto.message_table) %}
+ {{ f.type }} {{ f.name }};
+ {% endfor %}
+"""
+ xtarget = XProtoTestHelpers.write_tmp_target(target)
+ proto = \
+"""
+message Port (PlCoreBase,ParameterMixin){
+ required string easter_egg = 1;
+ required manytoone network->Network:links = 1 [db_index = True, null = False, blank = False];
+ optional manytoone instance->Instance:ports = 2 [db_index = True, null = True, blank = True];
+ optional string ip = 3 [max_length = 39, content_type = "ip", blank = True, help_text = "Instance ip address", null = True, db_index = False];
+ optional string port_id = 4 [help_text = "Neutron port id", max_length = 256, null = True, db_index = False, blank = True];
+ optional string mac = 5 [help_text = "MAC address associated with this port", max_length = 256, null = True, db_index = False, blank = True];
+ required bool xos_created = 6 [default = False, null = False, db_index = False, blank = True];
+}
+
+message Instance (Port){
+ optional string instance_id = 1 [max_length = 200, content_type = "stripped", blank = True, help_text = "Nova instance id", null = True, db_index = False];
+ optional string instance_uuid = 2 [max_length = 200, content_type = "stripped", blank = True, help_text = "Nova instance uuid", null = True, db_index = False];
+ required string name = 3 [max_length = 200, content_type = "stripped", blank = False, help_text = "Instance name", null = False, db_index = False];
+ optional string instance_name = 4 [max_length = 200, content_type = "stripped", blank = True, help_text = "OpenStack generated name", null = True, db_index = False];
+ optional string ip = 5 [max_length = 39, content_type = "ip", blank = True, help_text = "Instance ip address", null = True, db_index = False];
+ required manytoone image->Image:instances = 6 [db_index = True, null = False, blank = False];
+ optional manytoone creator->User:instances = 7 [db_index = True, null = True, blank = True];
+ required manytoone slice->Slice:instances = 8 [db_index = True, null = False, blank = False];
+ required manytoone deployment->Deployment:instance_deployment = 9 [db_index = True, null = False, blank = False];
+ required manytoone node->Node:instances = 10 [db_index = True, null = False, blank = False];
+ required int32 numberCores = 11 [help_text = "Number of cores for instance", default = 0, null = False, db_index = False, blank = False];
+ required manytoone flavor->Flavor:instance = 12 [help_text = "Flavor of this instance", default = "get_default_flavor()", null = False, db_index = True, blank = False];
+ optional string userData = 13 [help_text = "user_data passed to instance during creation", null = True, db_index = False, blank = True];
+ required string isolation = 14 [default = "vm", choices = "(('vm', 'Virtual Machine'), ('container', 'Container'), ('container_vm', 'Container In VM'))", max_length = 30, blank = False, null = False, db_index = False];
+ optional string volumes = 15 [help_text = "Comma-separated list of directories to expose to parent context", null = True, db_index = False, blank = True];
+ optional manytoone parent->Instance:instance = 16 [help_text = "Parent Instance for containers nested inside of VMs", null = True, db_index = True, blank = True];
+ required manytomany tags->Tag = 17 [db_index = False, null = False, blank = True];
+}
+
+message Network (Instance) {
+ required string name = 1 [db_index = False, max_length = 32, null = False, blank = False];
+ required manytoone template->NetworkTemplate:network = 2 [db_index = True, null = False, blank = False];
+ required string subnet = 3 [db_index = False, max_length = 32, null = False, blank = True];
+ required string start_ip = 4 [db_index = False, max_length = 32, null = False, blank = True];
+ required string end_ip = 5 [db_index = False, max_length = 32, null = False, blank = True];
+ optional string ports = 6 [db_index = False, max_length = 1024, null = True, blank = True];
+ optional string labels = 7 [db_index = False, max_length = 1024, null = True, blank = True];
+ required manytoone owner->Slice:ownedNetworks = 8 [help_text = "Slice that owns control of this Network", null = False, db_index = True, blank = False];
+ required int32 guaranteed_bandwidth = 9 [default = 0, null = False, db_index = False, blank = False];
+ required bool permit_all_slices = 10 [default = False, null = False, db_index = False, blank = True];
+ optional string topology_parameters = 11 [db_index = False, null = True, blank = True];
+ optional string controller_url = 12 [db_index = False, max_length = 1024, null = True, blank = True];
+ optional string controller_parameters = 13 [db_index = False, null = True, blank = True];
+ optional string network_id = 14 [help_text = "Quantum network", max_length = 256, null = True, db_index = False, blank = True];
+ optional string router_id = 15 [help_text = "Quantum router id", max_length = 256, null = True, db_index = False, blank = True];
+ optional string subnet_id = 16 [help_text = "Quantum subnet id", max_length = 256, null = True, db_index = False, blank = True];
+ required bool autoconnect = 17 [help_text = "This network can be autoconnected to the slice that owns it", default = True, null = False, db_index = False, blank = True];
+ required manytomany permitted_slices->Slice/Network_permitted_slices:availableNetworks = 18 [db_index = False, null = False, blank = True];
+ required manytomany slices->Slice/NetworkSlice:networks = 19 [db_index = False, null = False, blank = True];
+ required manytomany instances->Instance/Port:networks = 20 [db_index = False, null = False, blank = True];
+}
+
+message Slice (Network){
+ required string name = 1 [max_length = 80, content_type = "stripped", blank = False, help_text = "The Name of the Slice", null = False, db_index = False];
+ required bool enabled = 2 [help_text = "Status for this Slice", default = True, null = False, db_index = False, blank = True];
+ required bool omf_friendly = 3 [default = False, null = False, db_index = False, blank = True];
+ required string description = 4 [help_text = "High level description of the slice and expected activities", max_length = 1024, null = False, db_index = False, blank = True];
+ required string slice_url = 5 [db_index = False, max_length = 512, null = False, content_type = "url", blank = True];
+ required manytoone site->Site:slices = 6 [help_text = "The Site this Slice belongs to", null = False, db_index = True, blank = False];
+ required int32 max_instances = 7 [default = 10, null = False, db_index = False, blank = False];
+ optional manytoone service->Service:slices = 8 [db_index = True, null = True, blank = True];
+ optional string network = 9 [blank = True, max_length = 256, null = True, db_index = False, choices = "((None, 'Default'), ('host', 'Host'), ('bridged', 'Bridged'), ('noauto', 'No Automatic Networks'))"];
+ optional string exposed_ports = 10 [db_index = False, max_length = 256, null = True, blank = True];
+ optional manytoone serviceClass->ServiceClass:slices = 11 [db_index = True, null = True, blank = True];
+ optional manytoone creator->User:slices = 12 [db_index = True, null = True, blank = True];
+ optional manytoone default_flavor->Flavor:slices = 13 [db_index = True, null = True, blank = True];
+ optional manytoone default_image->Image:slices = 14 [db_index = True, null = True, blank = True];
+ optional manytoone default_node->Node:slices = 15 [db_index = True, null = True, blank = True];
+ optional string mount_data_sets = 16 [default = "GenBank", max_length = 256, content_type = "stripped", blank = True, null = True, db_index = False];
+ required string default_isolation = 17 [default = "vm", choices = "(('vm', 'Virtual Machine'), ('container', 'Container'), ('container_vm', 'Container In VM'))", max_length = 30, blank = False, null = False, db_index = False];
+ required manytomany tags->Tag = 18 [db_index = False, null = False, blank = True];
+}
+"""
+ args = FakeArgs()
+ args.inputs = proto
+ args.target = xtarget
+ output = XOSGenerator.generate(args)
+ self.assertIn('easter_egg', output)
+
+if __name__ == '__main__':
+ unittest.main()
+
+
diff --git a/lib/xos-genx/xos-genx-tests/helpers.py b/lib/xos-genx/xos-genx-tests/helpers.py
new file mode 100644
index 0000000..232cae1
--- /dev/null
+++ b/lib/xos-genx/xos-genx-tests/helpers.py
@@ -0,0 +1,36 @@
+
+# Copyright 2017-present Open Networking Foundation
+#
+# 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.
+
+
+import os
+
+# Constants
+OUTPUT_DIR = os.path.abspath(os.path.dirname(os.path.realpath(__file__)) + "/out/")
+
+TMP_TARGET_PATH = os.path.join(OUTPUT_DIR, 'tmp.xtarget')
+
+# Store in this class the args to pass at the generator
+class FakeArgs:
+ pass
+
+class XProtoTestHelpers:
+
+ @staticmethod
+ def write_tmp_target(target):
+ tmp_file = open(TMP_TARGET_PATH, 'w')
+ tmp_file.write(target)
+ tmp_file.close()
+ return TMP_TARGET_PATH
+
diff --git a/lib/xos-genx/xos-genx-tests/optimize_test.py b/lib/xos-genx/xos-genx-tests/optimize_test.py
new file mode 100644
index 0000000..e31deb8
--- /dev/null
+++ b/lib/xos-genx/xos-genx-tests/optimize_test.py
@@ -0,0 +1,97 @@
+
+# Copyright 2017-present Open Networking Foundation
+#
+# 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.
+
+
+import unittest
+from xosgenx.jinja2_extensions.fol2 import FOL2Python
+
+class XProtoOptimizeTest(unittest.TestCase):
+ def setUp(self):
+ self.f2p = FOL2Python()
+ self.maxDiff=None
+
+ def test_constant(self):
+ input = 'True'
+ output = self.f2p.hoist_outer(input)
+ self.assertEqual(output, input)
+
+ def test_exists(self):
+ input = {'exists': ['X',{'|':['X.foo','y']}]}
+
+ output = self.f2p.hoist_outer(input)
+ expected = {'|': ['y', {'&': [{'not': 'y'}, {'exists': ['X', 'X.foo']}]}]}
+ self.assertEqual(output, expected)
+
+ def test_exists_implies(self):
+ input = {'exists': ['Foo', {'&': [{'=': ('Foo.a', '1')}, {'->': ['write_access', {'=': ('Foo.b', '1')}]}]}]}
+
+ output = self.f2p.hoist_outer(input)
+ expected = {'|': [{'&': ['write_access', {'exists': ['Foo', {'&': [{'=': ['Foo.a', '1']}, {'=': ['Foo.b', '1']}]}]}]}, {'&': [{'not': 'write_access'}, {'exists': ['Foo', {'=': ['Foo.a', '1']}]}]}]}
+ self.assertEqual(output, expected)
+
+ def test_forall(self):
+ input = {'forall': ['X',{'|':['X.foo','y']}]}
+
+ output = self.f2p.hoist_outer(input)
+ expected = {'|': ['y', {'&': [{'not': 'y'}, {'forall': ['X', 'X.foo']}]}]}
+ self.assertEqual(output, expected)
+
+ def test_exists_embedded(self):
+ input = {'&':['True',{'exists': ['X',{'|':['X.foo','y']}]}]}
+
+ output = self.f2p.hoist_outer(input)
+ expected = {'|': ['y', {'&': [{'not': 'y'}, {'exists': ['X', 'X.foo']}]}]}
+ self.assertEqual(output, expected)
+
+ def test_exists_equals(self):
+ input = {'&':['True',{'exists': ['X',{'|':['X.foo',{'=':['y','z']}]}]}]}
+
+ output = self.f2p.hoist_outer(input)
+ expected = {'|': [{'=': ['y', 'z']}, {'&': [{'not': {'=': ['y', 'z']}}, {'exists': ['X', 'X.foo']}]}]}
+ self.assertEqual(output, expected)
+
+ def test_exists_nested_constant(self):
+ input = {'&':['True',{'exists': ['X',{'|':['y',{'=':['y','X.foo']}]}]}]}
+
+ output = self.f2p.hoist_outer(input)
+ expected = {'|': ['y', {'&': [{'not': 'y'}, {'exists': ['X', {'=': ['False', 'X.foo']}]}]}]}
+ self.assertEqual(output, expected)
+
+ def test_exists_nested(self):
+ input = {'exists': ['X',{'exists':['Y',{'=':['Y.foo','X.foo']}]}]}
+
+ output = self.f2p.hoist_outer(input)
+ expected = input
+ self.assertEqual(input, output)
+
+ def test_exists_nested2(self):
+ input = {'exists': ['X',{'exists':['Y',{'=':['Z','Y']}]}]}
+
+ output = self.f2p.hoist_outer(input)
+ expected = {'exists': ['Y', {'=': ['Z', 'Y']}]}
+ self.assertEqual(output, expected)
+
+ def test_exists_nested3(self):
+ input = {'exists': ['X',{'exists':['Y',{'=':['Z','X']}]}]}
+
+ output = self.f2p.hoist_outer(input)
+ expected = {'exists': ['X', {'=': ['Z', 'X']}]}
+ self.assertEqual(output, expected)
+
+
+if __name__ == '__main__':
+ unittest.main()
+
+
diff --git a/lib/xos-genx/xos-genx-tests/out/.gitignore b/lib/xos-genx/xos-genx-tests/out/.gitignore
new file mode 100644
index 0000000..d6b7ef3
--- /dev/null
+++ b/lib/xos-genx/xos-genx-tests/out/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
diff --git a/lib/xos-genx/xos-genx-tests/package_test.py b/lib/xos-genx/xos-genx-tests/package_test.py
new file mode 100644
index 0000000..127fc07
--- /dev/null
+++ b/lib/xos-genx/xos-genx-tests/package_test.py
@@ -0,0 +1,442 @@
+
+# Copyright 2017-present Open Networking Foundation
+#
+# 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.
+
+
+import unittest
+import os
+from xosgenx.generator import XOSGenerator
+from helpers import FakeArgs, XProtoTestHelpers
+
+class XProtoPackageTest(unittest.TestCase):
+ def test_package_fqn(self):
+ args = FakeArgs()
+ target = XProtoTestHelpers.write_tmp_target(
+"""
+ {% for m in proto.messages %}
+ {{ m.name }},{{ m.package }},{{ m.fqn }}
+ {% endfor %}
+""")
+
+ xproto =\
+"""
+package xos.core;
+
+message Port (PlCoreBase,ParameterMixin) {
+ required manytoone network->Network:links = 1 [db_index = True, null = False, blank = False];
+ optional manytoone instance->Instance:ports = 2 [db_index = True, null = True, blank = True];
+ optional string ip = 3 [max_length = 39, content_type = "ip", blank = True, help_text = "Instance ip address", null = True, db_index = False];
+ optional string port_id = 4 [help_text = "Neutron port id", max_length = 256, null = True, db_index = False, blank = True];
+ optional string mac = 5 [help_text = "MAC address associated with this port", max_length = 256, null = True, db_index = False, blank = True];
+ required bool xos_created = 6 [default = False, null = False, db_index = False, blank = True];
+}
+"""
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = target
+
+ output = XOSGenerator.generate(args)
+
+ self.assertIn('Port,xos.core,xos.core.Port', output)
+
+ def test_cross_model(self):
+ target = XProtoTestHelpers.write_tmp_target( \
+"""
+ {% for m in proto.messages %}
+ {{ m.fqn }} {
+ {%- for l in m.links %}
+ {%- if proto.message_table[l.peer.fqn] %}
+ {{ l.peer.name }} {
+ {%- set model = proto.message_table[l.peer.fqn] %}
+ {% for f in model.fields %}
+ {{ f.type }} {{ f.name }};
+ {% endfor %}
+ }
+ {%- endif -%}
+ {%- if proto.message_table[m.package + '.' + l.peer.name] %}
+ {{ l.peer.name }} {
+ {%- set model = proto.message_table[m.package + '.' + l.peer.name] %}
+ {% for f in model.fields %}
+ {{ f.type }} {{ f.name }};
+ {% endfor %}
+ }
+ {%- endif -%}
+ {% endfor %}
+ }
+ {% endfor %}
+""")
+
+ xproto = \
+"""
+package xos.network;
+
+message Port (PlCoreBase,ParameterMixin){
+ required manytoone network->Network:links = 1 [db_index = True, null = False, blank = False];
+ optional manytoone instance->xos.core.Instance:ports = 2 [db_index = True, null = True, blank = True];
+ optional string ip = 3 [max_length = 39, content_type = "ip", blank = True, help_text = "Instance ip address", null = True, db_index = False];
+ optional string port_id = 4 [help_text = "Neutron port id", max_length = 256, null = True, db_index = False, blank = True];
+ optional string mac = 5 [help_text = "MAC address associated with this port", max_length = 256, null = True, db_index = False, blank = True];
+ required bool xos_created = 6 [default = False, null = False, db_index = False, blank = True];
+}
+
+
+package xos.core;
+
+message Instance (PlCoreBase){
+ optional string instance_id = 1 [max_length = 200, content_type = "stripped", blank = True, help_text = "Nova instance id", null = True, db_index = False];
+ optional string instance_uuid = 2 [max_length = 200, content_type = "stripped", blank = True, help_text = "Nova instance uuid", null = True, db_index = False];
+ required string name = 3 [max_length = 200, content_type = "stripped", blank = False, help_text = "Instance name", null = False, db_index = False];
+ optional string instance_name = 4 [max_length = 200, content_type = "stripped", blank = True, help_text = "OpenStack generated name", null = True, db_index = False];
+ optional string ip = 5 [max_length = 39, content_type = "ip", blank = True, help_text = "Instance ip address", null = True, db_index = False];
+ required manytoone image->Image:instances = 6 [db_index = True, null = False, blank = False];
+ optional manytoone creator->User:instances = 7 [db_index = True, null = True, blank = True];
+ required manytoone slice->Slice:instances = 8 [db_index = True, null = False, blank = False];
+ required manytoone deployment->Deployment:instance_deployment = 9 [db_index = True, null = False, blank = False];
+ required manytoone node->Node:instances = 10 [db_index = True, null = False, blank = False];
+ required int32 numberCores = 11 [help_text = "Number of cores for instance", default = 0, null = False, db_index = False, blank = False];
+ required manytoone flavor->Flavor:instance = 12 [help_text = "Flavor of this instance", default = "get_default_flavor()", null = False, db_index = True, blank = False];
+ optional string userData = 13 [help_text = "user_data passed to instance during creation", null = True, db_index = False, blank = True];
+ required string isolation = 14 [default = "vm", choices = "(('vm', 'Virtual Machine'), ('container', 'Container'), ('container_vm', 'Container In VM'))", max_length = 30, blank = False, null = False, db_index = False];
+ optional string volumes = 15 [help_text = "Comma-separated list of directories to expose to parent context", null = True, db_index = False, blank = True];
+ optional manytoone parent->Instance:instance = 16 [help_text = "Parent Instance for containers nested inside of VMs", null = True, db_index = True, blank = True];
+ required manytomany tags->Tag = 17 [db_index = False, null = False, blank = True];
+}
+
+package xos.network;
+
+message Network (PlCoreBase,ParameterMixin) {
+ required string name = 1 [db_index = False, max_length = 32, null = False, blank = False];
+ required manytoone template->NetworkTemplate:network = 2 [db_index = True, null = False, blank = False];
+ required string subnet = 3 [db_index = False, max_length = 32, null = False, blank = True];
+ required string start_ip = 4 [db_index = False, max_length = 32, null = False, blank = True];
+ required string end_ip = 5 [db_index = False, max_length = 32, null = False, blank = True];
+ optional string ports = 6 [db_index = False, max_length = 1024, null = True, blank = True];
+ optional string labels = 7 [db_index = False, max_length = 1024, null = True, blank = True];
+ required manytoone owner->Slice:ownedNetworks = 8 [help_text = "Slice that owns control of this Network", null = False, db_index = True, blank = False];
+ required int32 guaranteed_bandwidth = 9 [default = 0, null = False, db_index = False, blank = False];
+ required bool permit_all_slices = 10 [default = False, null = False, db_index = False, blank = True];
+ optional string topology_parameters = 11 [db_index = False, null = True, blank = True];
+ optional string controller_url = 12 [db_index = False, max_length = 1024, null = True, blank = True];
+ optional string controller_parameters = 13 [db_index = False, null = True, blank = True];
+ optional string network_id = 14 [help_text = "Quantum network", max_length = 256, null = True, db_index = False, blank = True];
+ optional string router_id = 15 [help_text = "Quantum router id", max_length = 256, null = True, db_index = False, blank = True];
+ optional string subnet_id = 16 [help_text = "Quantum subnet id", max_length = 256, null = True, db_index = False, blank = True];
+ required bool autoconnect = 17 [help_text = "This network can be autoconnected to the slice that owns it", default = True, null = False, db_index = False, blank = True];
+ required manytomany permitted_slices->Slice/Network_permitted_slices:availableNetworks = 18 [db_index = False, null = False, blank = True];
+ required manytomany slices->Slice/NetworkSlice:networks = 19 [db_index = False, null = False, blank = True];
+ required manytomany instances->xos.core.Instance/xos.network.Port:networks = 20 [db_index = False, null = False, blank = True];
+}
+
+message Slice (PlCoreBase){
+ required string name = 1 [max_length = 80, content_type = "stripped", blank = False, help_text = "The Name of the Slice", null = False, db_index = False];
+ required bool enabled = 2 [help_text = "Status for this Slice", default = True, null = False, db_index = False, blank = True];
+ required bool omf_friendly = 3 [default = False, null = False, db_index = False, blank = True];
+ required string description = 4 [help_text = "High level description of the slice and expected activities", max_length = 1024, null = False, db_index = False, blank = True];
+ required string slice_url = 5 [db_index = False, max_length = 512, null = False, content_type = "url", blank = True];
+ required manytoone site->Site:slices = 6 [help_text = "The Site this Slice belongs to", null = False, db_index = True, blank = False];
+ required int32 max_instances = 7 [default = 10, null = False, db_index = False, blank = False];
+ optional manytoone service->Service:slices = 8 [db_index = True, null = True, blank = True];
+ optional string network = 9 [blank = True, max_length = 256, null = True, db_index = False, choices = "((None, 'Default'), ('host', 'Host'), ('bridged', 'Bridged'), ('noauto', 'No Automatic Networks'))"];
+ optional string exposed_ports = 10 [db_index = False, max_length = 256, null = True, blank = True];
+ optional manytoone serviceClass->ServiceClass:slices = 11 [db_index = True, null = True, blank = True];
+ optional manytoone creator->User:slices = 12 [db_index = True, null = True, blank = True];
+ optional manytoone default_flavor->Flavor:slices = 13 [db_index = True, null = True, blank = True];
+ optional manytoone default_image->Image:slices = 14 [db_index = True, null = True, blank = True];
+ optional manytoone default_node->Node:slices = 15 [db_index = True, null = True, blank = True];
+ optional string mount_data_sets = 16 [default = "GenBank", max_length = 256, content_type = "stripped", blank = True, null = True, db_index = False];
+ required string default_isolation = 17 [default = "vm", choices = "(('vm', 'Virtual Machine'), ('container', 'Container'), ('container_vm', 'Container In VM'))", max_length = 30, blank = False, null = False, db_index = False];
+ required manytomany tags->Tag = 18 [db_index = False, null = False, blank = True];
+}
+"""
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = target
+ output = XOSGenerator.generate(args)
+
+ self.assertIn('numberCores', output) # Instance showed up via cross-package call
+ self.assertIn('ip;', output) # Network showed up via cross-package call
+ self.assertIn('max_instances', output) # Slice showed up via implicit in-package call
+
+ def test_base_class_fields(self):
+ target = XProtoTestHelpers.write_tmp_target(
+"""
+ {% for m in proto.messages %}
+ {{ m.name }} {
+ {%- for b in m.bases %}
+ {%- if proto.message_table[b.fqn] -%}
+ {%- set model = proto.message_table[b.fqn] %}
+ {% for f in model.fields %}
+ {{ f.type }} {{ f.name }};
+ {% endfor %}
+ {%- endif -%}
+ {% endfor %}
+ }
+ {% endfor %}
+""")
+
+ xproto =\
+"""
+package xos.network;
+
+message Port (PlCoreBase,ParameterMixin){
+ required manytoone network->Network:links = 1 [db_index = True, null = False, blank = False];
+ optional manytoone instance->Instance:ports = 2 [db_index = True, null = True, blank = True];
+ optional string ip = 3 [max_length = 39, content_type = "ip", blank = True, help_text = "Instance ip address", null = True, db_index = False];
+ optional string port_id = 4 [help_text = "Neutron port id", max_length = 256, null = True, db_index = False, blank = True];
+ optional string mac = 5 [help_text = "MAC address associated with this port", max_length = 256, null = True, db_index = False, blank = True];
+ required bool xos_created = 6 [default = False, null = False, db_index = False, blank = True];
+}
+
+package xos.someotherpackage;
+
+message Instance (xos.network.Port){
+ optional string instance_id = 1 [max_length = 200, content_type = "stripped", blank = True, help_text = "Nova instance id", null = True, db_index = False];
+ optional string instance_uuid = 2 [max_length = 200, content_type = "stripped", blank = True, help_text = "Nova instance uuid", null = True, db_index = False];
+ required string name = 3 [max_length = 200, content_type = "stripped", blank = False, help_text = "Instance name", null = False, db_index = False];
+ optional string instance_name = 4 [max_length = 200, content_type = "stripped", blank = True, help_text = "OpenStack generated name", null = True, db_index = False];
+ optional string ip = 5 [max_length = 39, content_type = "ip", blank = True, help_text = "Instance ip address", null = True, db_index = False];
+ required manytoone image->Image:instances = 6 [db_index = True, null = False, blank = False];
+ optional manytoone creator->User:instances = 7 [db_index = True, null = True, blank = True];
+ required manytoone slice->Slice:instances = 8 [db_index = True, null = False, blank = False];
+ required manytoone deployment->Deployment:instance_deployment = 9 [db_index = True, null = False, blank = False];
+ required manytoone node->Node:instances = 10 [db_index = True, null = False, blank = False];
+ required int32 numberCores = 11 [help_text = "Number of cores for instance", default = 0, null = False, db_index = False, blank = False];
+ required manytoone flavor->Flavor:instance = 12 [help_text = "Flavor of this instance", default = "get_default_flavor()", null = False, db_index = True, blank = False];
+ optional string userData = 13 [help_text = "user_data passed to instance during creation", null = True, db_index = False, blank = True];
+ required string isolation = 14 [default = "vm", choices = "(('vm', 'Virtual Machine'), ('container', 'Container'), ('container_vm', 'Container In VM'))", max_length = 30, blank = False, null = False, db_index = False];
+ optional string volumes = 15 [help_text = "Comma-separated list of directories to expose to parent context", null = True, db_index = False, blank = True];
+ optional manytoone parent->Instance:instance = 16 [help_text = "Parent Instance for containers nested inside of VMs", null = True, db_index = True, blank = True];
+ required manytomany tags->Tag = 17 [db_index = False, null = False, blank = True];
+}
+"""
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = target
+ output = XOSGenerator.generate(args)
+
+ self.assertIn('xos_created', output)
+
+ def test_from_base(self):
+ target = XProtoTestHelpers.write_tmp_target( \
+"""
+ {% for f in xproto_base_fields(proto.messages.3, proto.message_table) %}
+ {{ f.type }} {{ f.name }};
+ {% endfor %}
+""")
+
+ xproto =\
+"""
+option app_name = "firstapp";
+
+message Port (PlCoreBase,ParameterMixin){
+ required string easter_egg = 1;
+ required manytoone network->Network:links = 1 [db_index = True, null = False, blank = False];
+ optional manytoone instance->Instance:ports = 2 [db_index = True, null = True, blank = True];
+ optional string ip = 3 [max_length = 39, content_type = "ip", blank = True, help_text = "Instance ip address", null = True, db_index = False];
+ optional string port_id = 4 [help_text = "Neutron port id", max_length = 256, null = True, db_index = False, blank = True];
+ optional string mac = 5 [help_text = "MAC address associated with this port", max_length = 256, null = True, db_index = False, blank = True];
+ required bool xos_created = 6 [default = False, null = False, db_index = False, blank = True];
+}
+
+package A;
+
+message Instance (Port){
+ optional string instance_id = 1 [max_length = 200, content_type = "stripped", blank = True, help_text = "Nova instance id", null = True, db_index = False];
+ optional string instance_uuid = 2 [max_length = 200, content_type = "stripped", blank = True, help_text = "Nova instance uuid", null = True, db_index = False];
+ required string name = 3 [max_length = 200, content_type = "stripped", blank = False, help_text = "Instance name", null = False, db_index = False];
+ optional string instance_name = 4 [max_length = 200, content_type = "stripped", blank = True, help_text = "OpenStack generated name", null = True, db_index = False];
+ optional string ip = 5 [max_length = 39, content_type = "ip", blank = True, help_text = "Instance ip address", null = True, db_index = False];
+ required manytoone image->Image:instances = 6 [db_index = True, null = False, blank = False];
+ optional manytoone creator->User:instances = 7 [db_index = True, null = True, blank = True];
+ required manytoone slice->Slice:instances = 8 [db_index = True, null = False, blank = False];
+ required manytoone deployment->Deployment:instance_deployment = 9 [db_index = True, null = False, blank = False];
+ required manytoone node->Node:instances = 10 [db_index = True, null = False, blank = False];
+ required int32 numberCores = 11 [help_text = "Number of cores for instance", default = 0, null = False, db_index = False, blank = False];
+ required manytoone flavor->Flavor:instance = 12 [help_text = "Flavor of this instance", default = "get_default_flavor()", null = False, db_index = True, blank = False];
+ optional string userData = 13 [help_text = "user_data passed to instance during creation", null = True, db_index = False, blank = True];
+ required string isolation = 14 [default = "vm", choices = "(('vm', 'Virtual Machine'), ('container', 'Container'), ('container_vm', 'Container In VM'))", max_length = 30, blank = False, null = False, db_index = False];
+ optional string volumes = 15 [help_text = "Comma-separated list of directories to expose to parent context", null = True, db_index = False, blank = True];
+ optional manytoone parent->Instance:instance = 16 [help_text = "Parent Instance for containers nested inside of VMs", null = True, db_index = True, blank = True];
+ required manytomany tags->Tag = 17 [db_index = False, null = False, blank = True];
+}
+
+package B;
+
+option app_name="networkapp";
+
+message Network (A.Instance) {
+ required string name = 1 [db_index = False, max_length = 32, null = False, blank = False];
+ required manytoone template->NetworkTemplate:network = 2 [db_index = True, null = False, blank = False];
+ required string subnet = 3 [db_index = False, max_length = 32, null = False, blank = True];
+ required string start_ip = 4 [db_index = False, max_length = 32, null = False, blank = True];
+ required string end_ip = 5 [db_index = False, max_length = 32, null = False, blank = True];
+ optional string ports = 6 [db_index = False, max_length = 1024, null = True, blank = True];
+ optional string labels = 7 [db_index = False, max_length = 1024, null = True, blank = True];
+ required manytoone owner->Slice:ownedNetworks = 8 [help_text = "Slice that owns control of this Network", null = False, db_index = True, blank = False];
+ required int32 guaranteed_bandwidth = 9 [default = 0, null = False, db_index = False, blank = False];
+ required bool permit_all_slices = 10 [default = False, null = False, db_index = False, blank = True];
+ optional string topology_parameters = 11 [db_index = False, null = True, blank = True];
+ optional string controller_url = 12 [db_index = False, max_length = 1024, null = True, blank = True];
+ optional string controller_parameters = 13 [db_index = False, null = True, blank = True];
+ optional string network_id = 14 [help_text = "Quantum network", max_length = 256, null = True, db_index = False, blank = True];
+ optional string router_id = 15 [help_text = "Quantum router id", max_length = 256, null = True, db_index = False, blank = True];
+ optional string subnet_id = 16 [help_text = "Quantum subnet id", max_length = 256, null = True, db_index = False, blank = True];
+ required bool autoconnect = 17 [help_text = "This network can be autoconnected to the slice that owns it", default = True, null = False, db_index = False, blank = True];
+ required manytomany permitted_slices->Slice/Network_permitted_slices:availableNetworks = 18 [db_index = False, null = False, blank = True];
+ required manytomany slices->Slice/NetworkSlice:networks = 19 [db_index = False, null = False, blank = True];
+ required manytomany instances->Instance/Port:networks = 20 [db_index = False, null = False, blank = True];
+}
+
+option app_name="sliceapp";
+
+message Slice (Network){
+ required string name = 1 [max_length = 80, content_type = "stripped", blank = False, help_text = "The Name of the Slice", null = False, db_index = False];
+ required bool enabled = 2 [help_text = "Status for this Slice", default = True, null = False, db_index = False, blank = True];
+ required bool omf_friendly = 3 [default = False, null = False, db_index = False, blank = True];
+ required string description = 4 [help_text = "High level description of the slice and expected activities", max_length = 1024, null = False, db_index = False, blank = True];
+ required string slice_url = 5 [db_index = False, max_length = 512, null = False, content_type = "url", blank = True];
+ required manytoone site->Site:slices = 6 [help_text = "The Site this Slice belongs to", null = False, db_index = True, blank = False];
+ required int32 max_instances = 7 [default = 10, null = False, db_index = False, blank = False];
+ optional manytoone service->Service:slices = 8 [db_index = True, null = True, blank = True];
+ optional string network = 9 [blank = True, max_length = 256, null = True, db_index = False, choices = "((None, 'Default'), ('host', 'Host'), ('bridged', 'Bridged'), ('noauto', 'No Automatic Networks'))"];
+ optional string exposed_ports = 10 [db_index = False, max_length = 256, null = True, blank = True];
+ optional manytoone serviceClass->ServiceClass:slices = 11 [db_index = True, null = True, blank = True];
+ optional manytoone creator->User:slices = 12 [db_index = True, null = True, blank = True];
+ optional manytoone default_flavor->Flavor:slices = 13 [db_index = True, null = True, blank = True];
+ optional manytoone default_image->Image:slices = 14 [db_index = True, null = True, blank = True];
+ optional manytoone default_node->Node:slices = 15 [db_index = True, null = True, blank = True];
+ optional string mount_data_sets = 16 [default = "GenBank", max_length = 256, content_type = "stripped", blank = True, null = True, db_index = False];
+ required string default_isolation = 17 [default = "vm", choices = "(('vm', 'Virtual Machine'), ('container', 'Container'), ('container_vm', 'Container In VM'))", max_length = 30, blank = False, null = False, db_index = False];
+ required manytomany tags->Tag = 18 [db_index = False, null = False, blank = True];
+}
+"""
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = target
+ output = XOSGenerator.generate(args)
+
+ self.assertIn('easter_egg', output)
+
+ def test_model_options(self):
+ target = XProtoTestHelpers.write_tmp_target(
+"""
+ Options:
+
+ {{ proto.options }}
+ {% for m in proto.messages %}
+ {{ m.options.app_name }}
+ {% endfor %}
+""")
+
+ xproto =\
+"""
+option app_name = "firstapp";
+
+message Port (PlCoreBase,ParameterMixin){
+ required string easter_egg = 1;
+ required manytoone network->Network:links = 1 [db_index = True, null = False, blank = False];
+ optional manytoone instance->Instance:ports = 2 [db_index = True, null = True, blank = True];
+ optional string ip = 3 [max_length = 39, content_type = "ip", blank = True, help_text = "Instance ip address", null = True, db_index = False];
+ optional string port_id = 4 [help_text = "Neutron port id", max_length = 256, null = True, db_index = False, blank = True];
+ optional string mac = 5 [help_text = "MAC address associated with this port", max_length = 256, null = True, db_index = False, blank = True];
+ required bool xos_created = 6 [default = False, null = False, db_index = False, blank = True];
+}
+
+package A;
+
+message Instance (Port){
+ optional string instance_id = 1 [max_length = 200, content_type = "stripped", blank = True, help_text = "Nova instance id", null = True, db_index = False];
+ optional string instance_uuid = 2 [max_length = 200, content_type = "stripped", blank = True, help_text = "Nova instance uuid", null = True, db_index = False];
+ required string name = 3 [max_length = 200, content_type = "stripped", blank = False, help_text = "Instance name", null = False, db_index = False];
+ optional string instance_name = 4 [max_length = 200, content_type = "stripped", blank = True, help_text = "OpenStack generated name", null = True, db_index = False];
+ optional string ip = 5 [max_length = 39, content_type = "ip", blank = True, help_text = "Instance ip address", null = True, db_index = False];
+ required manytoone image->Image:instances = 6 [db_index = True, null = False, blank = False];
+ optional manytoone creator->User:instances = 7 [db_index = True, null = True, blank = True];
+ required manytoone slice->Slice:instances = 8 [db_index = True, null = False, blank = False];
+ required manytoone deployment->Deployment:instance_deployment = 9 [db_index = True, null = False, blank = False];
+ required manytoone node->Node:instances = 10 [db_index = True, null = False, blank = False];
+ required int32 numberCores = 11 [help_text = "Number of cores for instance", default = 0, null = False, db_index = False, blank = False];
+ required manytoone flavor->Flavor:instance = 12 [help_text = "Flavor of this instance", default = "get_default_flavor()", null = False, db_index = True, blank = False];
+ optional string userData = 13 [help_text = "user_data passed to instance during creation", null = True, db_index = False, blank = True];
+ required string isolation = 14 [default = "vm", choices = "(('vm', 'Virtual Machine'), ('container', 'Container'), ('container_vm', 'Container In VM'))", max_length = 30, blank = False, null = False, db_index = False];
+ optional string volumes = 15 [help_text = "Comma-separated list of directories to expose to parent context", null = True, db_index = False, blank = True];
+ optional manytoone parent->Instance:instance = 16 [help_text = "Parent Instance for containers nested inside of VMs", null = True, db_index = True, blank = True];
+ required manytomany tags->Tag = 17 [db_index = False, null = False, blank = True];
+}
+
+package B;
+
+option app_name = "networkapp";
+
+message Network (A.Instance) {
+ required string name = 1 [db_index = False, max_length = 32, null = False, blank = False];
+ required manytoone template->NetworkTemplate:network = 2 [db_index = True, null = False, blank = False];
+ required string subnet = 3 [db_index = False, max_length = 32, null = False, blank = True];
+ required string start_ip = 4 [db_index = False, max_length = 32, null = False, blank = True];
+ required string end_ip = 5 [db_index = False, max_length = 32, null = False, blank = True];
+ optional string ports = 6 [db_index = False, max_length = 1024, null = True, blank = True];
+ optional string labels = 7 [db_index = False, max_length = 1024, null = True, blank = True];
+ required manytoone owner->Slice:ownedNetworks = 8 [help_text = "Slice that owns control of this Network", null = False, db_index = True, blank = False];
+ required int32 guaranteed_bandwidth = 9 [default = 0, null = False, db_index = False, blank = False];
+ required bool permit_all_slices = 10 [default = False, null = False, db_index = False, blank = True];
+ optional string topology_parameters = 11 [db_index = False, null = True, blank = True];
+ optional string controller_url = 12 [db_index = False, max_length = 1024, null = True, blank = True];
+ optional string controller_parameters = 13 [db_index = False, null = True, blank = True];
+ optional string network_id = 14 [help_text = "Quantum network", max_length = 256, null = True, db_index = False, blank = True];
+ optional string router_id = 15 [help_text = "Quantum router id", max_length = 256, null = True, db_index = False, blank = True];
+ optional string subnet_id = 16 [help_text = "Quantum subnet id", max_length = 256, null = True, db_index = False, blank = True];
+ required bool autoconnect = 17 [help_text = "This network can be autoconnected to the slice that owns it", default = True, null = False, db_index = False, blank = True];
+ required manytomany permitted_slices->Slice/Network_permitted_slices:availableNetworks = 18 [db_index = False, null = False, blank = True];
+ required manytomany slices->Slice/NetworkSlice:networks = 19 [db_index = False, null = False, blank = True];
+ required manytomany instances->Instance/Port:networks = 20 [db_index = False, null = False, blank = True];
+}
+
+
+option app_name = "networkapp";
+
+message Slice (Network){
+ required string name = 1 [max_length = 80, content_type = "stripped", blank = False, help_text = "The Name of the Slice", null = False, db_index = False];
+ required bool enabled = 2 [help_text = "Status for this Slice", default = True, null = False, db_index = False, blank = True];
+ required bool omf_friendly = 3 [default = False, null = False, db_index = False, blank = True];
+ required string description = 4 [help_text = "High level description of the slice and expected activities", max_length = 1024, null = False, db_index = False, blank = True];
+ required string slice_url = 5 [db_index = False, max_length = 512, null = False, content_type = "url", blank = True];
+ required manytoone site->Site:slices = 6 [help_text = "The Site this Slice belongs to", null = False, db_index = True, blank = False];
+ required int32 max_instances = 7 [default = 10, null = False, db_index = False, blank = False];
+ optional manytoone service->Service:slices = 8 [db_index = True, null = True, blank = True];
+ optional string network = 9 [blank = True, max_length = 256, null = True, db_index = False, choices = "((None, 'Default'), ('host', 'Host'), ('bridged', 'Bridged'), ('noauto', 'No Automatic Networks'))"];
+ optional string exposed_ports = 10 [db_index = False, max_length = 256, null = True, blank = True];
+ optional manytoone serviceClass->ServiceClass:slices = 11 [db_index = True, null = True, blank = True];
+ optional manytoone creator->User:slices = 12 [db_index = True, null = True, blank = True];
+ optional manytoone default_flavor->Flavor:slices = 13 [db_index = True, null = True, blank = True];
+ optional manytoone default_image->Image:slices = 14 [db_index = True, null = True, blank = True];
+ optional manytoone default_node->Node:slices = 15 [db_index = True, null = True, blank = True];
+ optional string mount_data_sets = 16 [default = "GenBank", max_length = 256, content_type = "stripped", blank = True, null = True, db_index = False];
+ required string default_isolation = 17 [default = "vm", choices = "(('vm', 'Virtual Machine'), ('container', 'Container'), ('container_vm', 'Container In VM'))", max_length = 30, blank = False, null = False, db_index = False];
+ required manytomany tags->Tag = 18 [db_index = False, null = False, blank = True];
+}
+"""
+
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = target
+ output = XOSGenerator.generate(args)
+
+ self.assertEqual(output.count('firstapp'), 2)
+ self.assertEqual(output.count('networkapp'), 2)
+
+
+if __name__ == '__main__':
+ unittest.main()
+
+
diff --git a/lib/xos-genx/xos-genx-tests/parse_test.py b/lib/xos-genx/xos-genx-tests/parse_test.py
new file mode 100644
index 0000000..88c7372
--- /dev/null
+++ b/lib/xos-genx/xos-genx-tests/parse_test.py
@@ -0,0 +1,130 @@
+
+# Copyright 2017-present Open Networking Foundation
+#
+# 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.
+
+
+import unittest
+from xosgenx.generator import XOSGenerator
+from helpers import FakeArgs, XProtoTestHelpers
+
+class XProtoParseTests(unittest.TestCase):
+ def test_global_options(self):
+
+ xtarget = XProtoTestHelpers.write_tmp_target("{{ options }}")
+
+ xproto = \
+"""
+ option kind = "vsg";
+ option verbose_name = "vSG Service";
+"""
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = xtarget
+ output = XOSGenerator.generate(args)
+ self.assertIn("vsg", output)
+ self.assertIn("vSG Service", output)
+
+ def test_basic_proto(self):
+ xtarget = XProtoTestHelpers.write_tmp_target("{{ proto }}")
+
+ xproto = \
+"""
+message Person {
+ required string name = 1;
+ required int32 id = 2; // Unique ID number for this person.
+ optional string email = 3 [symphony = "da da da dum"];
+
+ enum PhoneType {
+ MOBILE = 0;
+ HOME = 1;
+ WORK = 2;
+ }
+
+ required string number = 1;
+ optional PhoneType type = 2;
+
+ repeated PhoneNumber phones = 4;
+}
+"""
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = xtarget
+ output = XOSGenerator.generate(args)
+ self.assertIn("PhoneNumber", output)
+
+ def test_link_extensions(self):
+
+ xtarget = XProtoTestHelpers.write_tmp_target("{{ proto.messages.0.links }}")
+ xproto = \
+"""
+message links {
+ required manytoone vrouter_service->VRouterService:device_ports = 4 [db_index = True, null = False, blank = False];
+}
+"""
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = xtarget
+ output = XOSGenerator.generate(args)
+ self.assertIn("VRouterService", output)
+
+ pass
+
+ def test_through_extensions(self):
+ xtarget = XProtoTestHelpers.write_tmp_target("{{ proto.messages.0.links.0.through }}")
+ xproto = \
+"""
+message links {
+ required manytomany vrouter_service->VRouterService/ServiceProxy:device_ports = 4 [db_index = True, null = False, blank = False];
+}
+"""
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = xtarget
+ output = XOSGenerator.generate(args)
+ self.assertIn("ServiceProxy", output)
+
+ def test_message_options(self):
+ xtarget = XProtoTestHelpers.write_tmp_target("{{ proto.messages.0.options.type }}")
+ xproto = \
+"""
+message link {
+ option type = "e1000";
+}
+"""
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = xtarget
+ output = XOSGenerator.generate(args)
+ self.assertIn("e1000", output)
+
+ pass
+
+ def test_message_base(self):
+ xtarget = XProtoTestHelpers.write_tmp_target("{{ proto.messages.0.bases }}")
+ xproto = \
+"""
+message base(Base) {
+}
+"""
+
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = xtarget
+ output = XOSGenerator.generate(args)
+ self.assertIn("Base", output)
+
+if __name__ == '__main__':
+ unittest.main()
+
+
diff --git a/lib/xos-genx/xos-genx-tests/policy_test.py b/lib/xos-genx/xos-genx-tests/policy_test.py
new file mode 100644
index 0000000..3ff700b
--- /dev/null
+++ b/lib/xos-genx/xos-genx-tests/policy_test.py
@@ -0,0 +1,291 @@
+
+# Copyright 2017-present Open Networking Foundation
+#
+# 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.
+
+
+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_annotation(self):
+ xproto = \
+"""
+ policy true_policy < True >
+
+ message always::true_policy {
+ required int still = 9;
+ }
+"""
+
+ target = XProtoTestHelpers.write_tmp_target("{{ proto.messages.0 }}")
+
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = target
+
+ output = XOSGenerator.generate(args)
+ self.assertIn("true_policy", output)
+
+ 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_function_term(self):
+ xproto = \
+"""
+ policy slice_user < slice.user.compute_is_admin() >
+"""
+
+ 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 = FakeArgs()
+ slice.user.compute_is_admin = lambda: True
+
+ expr = eval(output)
+ self.assertTrue(expr)
+
+ def test_term(self):
+ xproto = \
+"""
+ policy slice_user < slice.user.is_admin >
+"""
+
+ 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 = FakeArgs()
+ slice.user.is_admin = True
+
+ expr = eval(output)
+ self.assertTrue(expr)
+
+ def test_num_constant(self):
+ xproto = \
+"""
+ policy slice_user < slice.user.age = 57 >
+"""
+
+ 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 = FakeArgs()
+ slice.user.is_admin = True
+
+ expr = eval(output)
+ self.assertTrue(expr)
+
+ def test_string_constant(self):
+ xproto = \
+"""
+ policy slice_user < slice.user.email = "admin@opencord.org" >
+"""
+
+ 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 = FakeArgs()
+ slice.user.is_admin = True
+
+ expr = eval(output)
+ self.assertTrue(expr)
+
+ 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_implies(self):
+ xproto = \
+"""
+ policy implies < obj.name -> obj.creator >
+"""
+ target = XProtoTestHelpers.write_tmp_target("{{ proto.policies.implies }}")
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = target
+
+ output = XOSGenerator.generate(args)
+
+ slice = FakeArgs()
+ slice.is_admin = False
+ obj = FakeArgs()
+ obj.name = 'Thing 1'
+ obj.creator = None
+
+ (op, operands), = eval(output).items()
+ expr = 'not ' + 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_policy_function(self):
+ xproto = \
+"""
+ policy slice_policy < exists Privilege: Privilege.object_id = obj.id >
+ policy network_slice_policy < *slice_policy(slice) >
+"""
+
+ target = XProtoTestHelpers.write_tmp_target("{{ proto.policies.network_slice_policy }} ")
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = target
+
+ output = XOSGenerator.generate(args)
+
+ (op, operands), = eval(output).items()
+
+ self.assertIn('slice_policy', operands)
+ self.assertIn('slice', operands)
+
+ def test_policy_missing_function(self):
+ xproto = \
+"""
+ policy slice_policy < exists Privilege: Privilege.object_id = obj.id >
+ policy network_slice_policy < *slice_policyX(slice) >
+"""
+
+ target = XProtoTestHelpers.write_tmp_target("{{ proto.policies.network_slice_policy }} ")
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = target
+
+ with self.assertRaises(Exception):
+ output = XOSGenerator.generate(args)
+
+
+ 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/xos-genx-tests/pure_proto_test.py b/lib/xos-genx/xos-genx-tests/pure_proto_test.py
new file mode 100644
index 0000000..511682e
--- /dev/null
+++ b/lib/xos-genx/xos-genx-tests/pure_proto_test.py
@@ -0,0 +1,133 @@
+
+# Copyright 2017-present Open Networking Foundation
+#
+# 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.
+
+
+
+import unittest
+from xosgenx.generator import XOSGenerator
+from helpers import FakeArgs, XProtoTestHelpers
+
+# Generate from xproto, then generate from equivalent proto
+class XPureProtobufGenerator(unittest.TestCase):
+ def test_pure_proto(self):
+ xproto = \
+"""
+message VRouterPort (XOSBase){
+ optional string name = 1 [help_text = "port friendly name", max_length = 20, null = True, db_index = False, blank = True];
+ required string openflow_id = 2 [help_text = "port identifier in ONOS", max_length = 21, null = False, db_index = False, blank = False];
+ required manytoone vrouter_device->VRouterDevice:ports = 3 [db_index = True, null = False, blank = False];
+ required manytoone vrouter_service->VRouterService:device_ports = 4 [db_index = True, null = False, blank = False];
+}
+"""
+
+ proto = \
+"""
+message VRouterPort {
+ option bases = "XOSBase";
+ optional string name = 1 [ null = "True", max_length = "20", blank = "True", help_text = "port friendly name", modifier = "optional", db_index = "False" ];
+ required string openflow_id = 2 [ null = "False", max_length = "21", blank = "False", help_text = "port identifier in ONOS", modifier = "required", db_index = "False" ];
+ required int32 vrouter_device = 3 [ null = "False", blank = "False", model = "VRouterDevice", modifier = "required", type = "link", port = "ports", db_index = "True", link = "manytoone"];
+ required int32 vrouter_service = 4 [ null = "False", blank = "False", model = "VRouterService", modifier = "required", type = "link", port = "device_ports", db_index = "True", link = "manytoone"];
+}
+"""
+ target = XProtoTestHelpers.write_tmp_target(
+"""
+from header import *
+{% for m in proto.messages %}
+{% if file_exists(xproto_base_name(m.name)|lower+'_header.py') -%}from {{xproto_base_name(m.name)|lower }}_header import *{% endif %}
+{% if file_exists(xproto_base_name(m.name)|lower+'_top.py') -%}{{ include_file(xproto_base_name(m.name)|lower+'_top.py') }} {% endif %}
+
+{%- for l in m.links %}
+
+{% if l.peer.name != m.name %}
+from core.models.{{ l.peer.name | lower }} import {{ l.peer.name }}
+{% endif %}
+
+{%- endfor %}
+{% for b in m.bases %}
+{% if b!='XOSBase' and 'Mixin' not in b%}
+from core.models.{{b.name | lower}} import {{ b.name }}
+{% endif %}
+{% endfor %}
+
+
+class {{ m.name }}{{ xproto_base_def(m, m.bases) }}:
+ # Primitive Fields (Not Relations)
+ {% for f in m.fields %}
+ {%- if not f.link -%}
+ {{ f.name }} = {{ xproto_django_type(f.type, f.options) }}( {{ xproto_django_options_str(f) }} )
+ {% endif %}
+ {%- endfor %}
+
+ # Relations
+ {% for l in m.links %}
+ {{ l.src_port }} = {{ xproto_django_link_type(l) }}( {%- if l.peer.name==m.name -%}'self'{%- else -%}{{ l.peer.name }} {%- endif -%}, {{ xproto_django_link_options_str(l, l.dst_port ) }} )
+ {%- endfor %}
+
+ {% if file_exists(m.name|lower + '_model.py') -%}{{ include_file(m.name|lower + '_model.py') | indent(width=2)}}{%- endif %}
+ pass
+
+{% if file_exists(xproto_base_name(m.name)|lower+'_bottom.py') -%}{{ include_file(xproto_base_name(m.name)|lower+'_bottom.py') }}{% endif %}
+{% endfor %}
+""")
+
+ args_xproto = FakeArgs()
+ args_xproto.inputs = xproto
+ args_xproto.target = target
+ xproto_gen = XOSGenerator.generate(args_xproto)
+
+ count1 = len(xproto_gen.split('\n'))
+
+ args_proto = FakeArgs()
+ args_proto.inputs = proto
+ args_proto.target = target
+ args_proto.rev = True
+ proto_gen = XOSGenerator.generate(args_proto)
+ count2 = len(proto_gen.split('\n'))
+
+ self.assertEqual(count1, count2)
+
+ def test_pure_policies(self):
+ xproto = \
+"""
+policy my_policy < exists x:a=b >
+"""
+
+ proto = \
+"""
+option my_policy = "policy:< exists x:a=b >";
+"""
+ target = XProtoTestHelpers.write_tmp_target(
+"""
+{{ policies }}
+""")
+
+ args_xproto = FakeArgs()
+ args_xproto.inputs = xproto
+ args_xproto.target = target
+ xproto_gen = XOSGenerator.generate(args_xproto)
+
+ args_proto = FakeArgs()
+ args_proto.inputs = proto
+ args_proto.target = target
+ args_proto.rev = True
+ proto_gen = XOSGenerator.generate(args_proto)
+
+ self.assertEqual(proto_gen, xproto_gen)
+
+if __name__ == '__main__':
+ unittest.main()
+
+
diff --git a/lib/xos-genx/xos-genx-tests/rlinks_test.py b/lib/xos-genx/xos-genx-tests/rlinks_test.py
new file mode 100644
index 0000000..b195249
--- /dev/null
+++ b/lib/xos-genx/xos-genx-tests/rlinks_test.py
@@ -0,0 +1,66 @@
+
+# Copyright 2017-present Open Networking Foundation
+#
+# 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.
+
+
+import unittest
+from xosgenx.generator import XOSGenerator
+from helpers import FakeArgs, XProtoTestHelpers
+
+class XProtoRlinkTests(unittest.TestCase):
+ def test_proto_generator(self):
+ target = XProtoTestHelpers.write_tmp_target("""
+{% for m in proto.messages %}
+ {% for r in m.rlinks %}
+ {{ r }}
+ {% endfor %}
+{% endfor %}
+""")
+
+ xproto = \
+"""
+message VRouterPort (PlCoreBase){
+ optional string name = 1 [help_text = "port friendly name", max_length = 20, null = True, db_index = False, blank = True];
+ required string openflow_id = 2 [help_text = "port identifier in ONOS", max_length = 21, null = False, db_index = False, blank = False];
+ required manytoone vrouter_device->VRouterDevice:ports = 3 [db_index = True, null = False, blank = False];
+ required manytoone vrouter_service->VRouterService:device_ports = 4 [db_index = True, null = False, blank = False];
+}
+
+message VRouterService (Service) {
+ optional string rest_hostname = 1 [db_index = False, max_length = 255, null = True, content_type = "stripped", blank = True];
+ required int32 rest_port = 2 [default = 8181, null = False, db_index = False, blank = False];
+ required string rest_user = 3 [default = "onos", max_length = 255, content_type = "stripped", blank = False, null = False, db_index = False];
+ required string rest_pass = 4 [default = "rocks", max_length = 255, content_type = "stripped", blank = False, null = False, db_index = False];
+}
+
+message VRouterDevice (PlCoreBase){
+ optional string name = 1 [help_text = "device friendly name", max_length = 20, null = True, db_index = False, blank = True];
+ required string openflow_id = 2 [help_text = "device identifier in ONOS", max_length = 20, null = False, db_index = False, blank = False];
+ required string config_key = 3 [default = "basic", max_length = 32, blank = False, help_text = "configuration key", null = False, db_index = False];
+ required string driver = 4 [help_text = "driver type", max_length = 32, null = False, db_index = False, blank = False];
+ required manytoone vrouter_service->VRouterService:devices = 5 [db_index = True, null = False, blank = False];
+}
+"""
+
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = target
+ output = XOSGenerator.generate(args)
+ self.assertIn("'src_port': 'device_ports'", output)
+ self.assertIn("'src_port': 'ports'", output)
+
+if __name__ == '__main__':
+ unittest.main()
+
+
diff --git a/lib/xos-genx/xos-genx-tests/swagger_test.py b/lib/xos-genx/xos-genx-tests/swagger_test.py
new file mode 100644
index 0000000..f87f47a
--- /dev/null
+++ b/lib/xos-genx/xos-genx-tests/swagger_test.py
@@ -0,0 +1,72 @@
+
+# Copyright 2017-present Open Networking Foundation
+#
+# 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.
+
+
+import unittest
+
+import yaml
+from xosgenx.generator import XOSGenerator
+from helpers import FakeArgs, OUTPUT_DIR
+
+class Args:
+ pass
+
+class XOSGeneratorTest(unittest.TestCase):
+
+ def test_swagger_target(self):
+ """
+ [XOS-GenX] The swagger xtarget should generate the appropriate json
+ """
+
+ # xosgenx --output . --target xosgenx/targets/swagger.xtarget --write-to-file single --dest-file swagger.yaml ../../xos/core/models/core.xproto
+ # http-server --cors Users/teone/Sites/opencord/orchestration/xos/lib/xos-genx/
+ xproto = \
+ """
+ option app_label = "core";
+
+ message Instance::instance_policy (XOSBase) {
+ option validators = "instance_creator:Instance has no creator, instance_isolation: Container instance {obj.name} must use container image, instance_isolation_container_vm_parent:Container-vm instance {obj.name} must have a parent, instance_parent_isolation_container_vm:Parent field can only be set on Container-vm instances ({obj.name}), instance_isolation_vm: VM Instance {obj.name} must use VM image, instance_creator_privilege: instance creator has no privileges on slice";
+ optional string instance_id = 1 [max_length = 200, content_type = "stripped", blank = True, help_text = "Nova instance id", null = True, db_index = False];
+ optional string instance_uuid = 2 [max_length = 200, content_type = "stripped", blank = True, help_text = "Nova instance uuid", null = True, db_index = False];
+ required string name = 3 [max_length = 200, content_type = "stripped", blank = False, help_text = "Instance name", null = False, db_index = False];
+ optional string instance_name = 4 [max_length = 200, content_type = "stripped", blank = True, help_text = "OpenStack generated name", null = True, db_index = False];
+ optional string ip = 5 [max_length = 39, content_type = "ip", blank = True, help_text = "Instance ip address", null = True, db_index = False, gui_hidden = True];
+ required manytoone image->Image:instances = 6 [db_index = True, null = False, blank = False];
+ optional manytoone creator->User:instances = 7 [db_index = True, null = True, blank = True];
+ required manytoone slice->Slice:instances = 8 [db_index = True, null = False, blank = False];
+ required manytoone deployment->Deployment:instance_deployment = 9 [db_index = True, null = False, blank = False];
+ required manytoone node->Node:instances = 10 [db_index = True, null = False, blank = False];
+ required int32 numberCores = 11 [help_text = "Number of cores for instance", default = 0, null = False, db_index = False, blank = False];
+ required manytoone flavor->Flavor:instance = 12 [help_text = "Flavor of this instance", null = False, db_index = True, blank = False];
+ optional string userData = 13 [help_text = "user_data passed to instance during creation", null = True, db_index = False, blank = True, varchar = True];
+ required string isolation = 14 [default = "vm", choices = "(('vm', 'Virtual Machine'), ('container', 'Container'), ('container_vm', 'Container In VM'))", max_length = 30, blank = False, null = False, db_index = False];
+ optional string volumes = 15 [help_text = "Comma-separated list of directories to expose to parent context", null = True, db_index = False, blank = True];
+ optional manytoone parent->Instance:instance = 16 [help_text = "Parent Instance for containers nested inside of VMs", null = True, db_index = True, blank = True];
+ }
+ """
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = 'swagger.xtarget'
+ args.output = OUTPUT_DIR
+ args.write_to_file = "single"
+ args.dest_file = "swagger.yaml"
+ args.quiet = False
+ output = XOSGenerator.generate(args)
+ self.assertIn("/xosapi/v1/core/instances/:", output)
+ self.assertIn("/xosapi/v1/core/instances/{id}:", output)
+ self.assertIn("Instance:", output)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/lib/xos-genx/xos-genx-tests/target_test.py b/lib/xos-genx/xos-genx-tests/target_test.py
new file mode 100644
index 0000000..b45f5ed
--- /dev/null
+++ b/lib/xos-genx/xos-genx-tests/target_test.py
@@ -0,0 +1,129 @@
+
+# Copyright 2017-present Open Networking Foundation
+#
+# 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.
+
+
+import unittest
+import os
+from xosgenx.generator import XOSGenerator
+from helpers import FakeArgs, XProtoTestHelpers, OUTPUT_DIR
+
+TEST_FILE = "test_file"
+
+TEST_OUTPUT = "Do re mi fa so la ti do"
+
+class XProtoTargetTests(unittest.TestCase):
+
+ def setUp(self):
+ test_file = open(os.path.join(OUTPUT_DIR, TEST_FILE), 'w')
+ test_file.write(TEST_OUTPUT)
+ test_file.close()
+
+ def test_file_methods(self):
+ target = XProtoTestHelpers.write_tmp_target(
+"""
+ {%% if file_exists("%s") %%}
+ {{ include_file("%s") }}
+ {%% endif %%}
+"""%(TEST_FILE, TEST_FILE)
+ )
+
+ args = FakeArgs()
+ args.inputs = ''
+ args.target = target
+ args.attic = OUTPUT_DIR
+ output = XOSGenerator.generate(args)
+ self.assertIn(TEST_OUTPUT, output)
+
+ def test_xproto_lib(self):
+ target = XProtoTestHelpers.write_tmp_target(
+"""
+ {{ xproto_first_non_empty([None, None, None, None, None, None, "Eureka"]) }}
+""")
+ args = FakeArgs()
+ args.inputs = ''
+ args.target = target
+ output = XOSGenerator.generate(args)
+ self.assertIn("Eureka", output)
+
+ def test_context(self):
+ target = XProtoTestHelpers.write_tmp_target(
+"""
+ {{ context.what }}
+""")
+ args = FakeArgs()
+ args.inputs = ''
+ args.target = target
+ args.kv='what:what is what'
+ output = XOSGenerator.generate(args)
+ self.assertIn("what is what", output)
+
+ def test_singularize(self):
+ proto = \
+"""
+ message TestSingularize {
+ // The following field has an explicitly specified singular
+ required int many = 1 [singular = "one"];
+ // The following fields have automatically computed singulars
+ required int sheep = 2;
+ required int radii = 2;
+ required int slices = 2;
+ required int networks = 2;
+ required int omf_friendlies = 2;
+ }
+"""
+
+ target = XProtoTestHelpers.write_tmp_target(
+"""
+{% for m in proto.messages.0.fields -%}
+{{ xproto_singularize(m) }},
+{%- endfor %}
+""")
+ args = FakeArgs()
+ args.inputs = proto
+ args.target = target
+ output = XOSGenerator.generate(args)
+ self.assertEqual("one,sheep,radius,slice,network,omf_friendly", output.lstrip().rstrip().rstrip(','))
+
+ def test_pluralize(self):
+ proto = \
+"""
+ message TestPluralize {
+ // The following field has an explicitly specified plural
+ required int anecdote = 1 [plural = "data"];
+ // The following fields have automatically computed plurals
+ required int sheep = 2;
+ required int radius = 2;
+ required int slice = 2;
+ required int network = 2;
+ required int omf_friendly = 2;
+ }
+"""
+
+ target = XProtoTestHelpers.write_tmp_target(
+"""
+{% for m in proto.messages.0.fields -%}
+{{ xproto_pluralize(m) }},
+{%- endfor %}
+""")
+ args = FakeArgs()
+ args.inputs = proto
+ args.target = target
+ output = XOSGenerator.generate(args)
+ self.assertEqual("data,sheep,radii,slices,networks,omf_friendlies", output.lstrip().rstrip().rstrip(','))
+
+if __name__ == '__main__':
+ unittest.main()
+
+
diff --git a/lib/xos-genx/xos-genx-tests/test_cli.py b/lib/xos-genx/xos-genx-tests/test_cli.py
new file mode 100644
index 0000000..29098f1
--- /dev/null
+++ b/lib/xos-genx/xos-genx-tests/test_cli.py
@@ -0,0 +1,55 @@
+
+# Copyright 2017-present Open Networking Foundation
+#
+# 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.
+
+
+import unittest
+import os
+from mock import patch
+from xosgenx.xosgen import XosGen
+
+class Args:
+ pass
+
+class XOSGeneratorTest(unittest.TestCase):
+ """
+ Testing the CLI binding for the XOS Generative Toolchain
+ """
+
+ def test_generator(self):
+ """
+ [XOS-GenX] The CLI entry point should correctly parse params
+ """
+ args = Args()
+ args.files = ['lib/xos-genx/xos-genx-tests/xproto/test.xproto']
+ args.target = 'lib/xos-genx/xos-genx-tests/xtarget/test.xtarget'
+ args.output = 'lib/xos-genx/xos-genx-tests/out/dir/'
+ args.write_to_file = "target"
+ args.dest_file = None
+ args.dest_extension = None
+
+ expected_args = Args()
+ expected_args.files = [os.path.abspath(os.getcwd() + '/' + args.files[0])]
+ expected_args.target = os.path.abspath(os.getcwd() + '/' + args.target)
+ expected_args.output = os.path.abspath(os.getcwd() + '/' + args.output)
+
+ with patch("xosgenx.xosgen.XOSGenerator.generate") as generator:
+ XosGen.init(args)
+ actual_args = generator.call_args[0][0]
+ self.assertEqual(actual_args.files, expected_args.files)
+ self.assertEqual(actual_args.target, expected_args.target)
+ self.assertEqual(actual_args.output, expected_args.output)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/lib/xos-genx/xos-genx-tests/test_generator.py b/lib/xos-genx/xos-genx-tests/test_generator.py
new file mode 100644
index 0000000..f59bbfd
--- /dev/null
+++ b/lib/xos-genx/xos-genx-tests/test_generator.py
@@ -0,0 +1,210 @@
+
+# Copyright 2017-present Open Networking Foundation
+#
+# 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.
+
+
+import unittest
+import os
+from helpers import FakeArgs, OUTPUT_DIR
+from xosgenx.generator import XOSGenerator
+
+TEST_EXPECTED_OUTPUT = """
+ name: XOSModel
+ fields:
+ name:
+ type: string
+ description: "Help Name"
+ files:
+ type: string
+ description: "Help Files"
+"""
+
+BASE_XPROTO = os.path.abspath(os.path.dirname(os.path.realpath(__file__)) + "/xproto/base.xproto")
+TEST_XPROTO = os.path.abspath(os.path.dirname(os.path.realpath(__file__)) + "/xproto/test.xproto")
+SKIP_DJANGO_XPROTO = os.path.abspath(os.path.dirname(os.path.realpath(__file__)) + "/xproto/skip_django.xproto")
+VROUTER_XPROTO = os.path.abspath(os.path.dirname(os.path.realpath(__file__)) + "/xproto/vrouterport.xproto")
+TEST_TARGET = os.path.abspath(os.path.dirname(os.path.realpath(__file__)) + "/xtarget/test.xtarget")
+SPLIT_TARGET = os.path.abspath(os.path.dirname(os.path.realpath(__file__)) + "/xtarget/split.xtarget")
+
+TEST_ATTICS = os.path.abspath(os.path.dirname(os.path.realpath(__file__)) + "/attics/")
+
+class XOSGeneratorTest(unittest.TestCase):
+ """
+ Testing the XOS Generative Toolchain
+ """
+
+ def setUp(self):
+ os.chdir(os.path.join(os.path.abspath(os.path.dirname(os.path.realpath(__file__))), ".."))
+ filesToRemove = [f for f in os.listdir(OUTPUT_DIR)]
+ for f in filesToRemove:
+ if not f.startswith('.'):
+ os.remove(OUTPUT_DIR + '/' + f)
+
+ def test_generator_custom_target_from_file(self):
+ """
+ [XOS-GenX] Generate output from base.xproto
+ """
+ args = FakeArgs()
+ args.files = [TEST_XPROTO]
+ args.target = TEST_TARGET
+ output = XOSGenerator.generate(args)
+ self.assertEqual(output, TEST_EXPECTED_OUTPUT)
+
+ def test_generator_custom_target_from_inputs(self):
+ """
+ [XOS-GenX] Generate output from base.xproto
+ """
+ args = FakeArgs()
+ args.inputs = open(TEST_XPROTO).read()
+ args.target = TEST_TARGET
+ output = XOSGenerator.generate(args)
+ self.assertEqual(output, TEST_EXPECTED_OUTPUT)
+
+ def test_django_with_attic(self):
+ """
+ [XOS-GenX] Generate django output from test.xproto
+ """
+ args = FakeArgs()
+ args.files = [TEST_XPROTO, VROUTER_XPROTO]
+ args.target = 'django.xtarget'
+ args.attic = TEST_ATTICS
+ args.output = OUTPUT_DIR
+ args.dest_extension = 'py'
+ args.write_to_file = 'model'
+ output = XOSGenerator.generate(args)
+
+ # xosmodel has custom header attic
+ self.assertIn('from xosmodel_header import *', output['XOSModel'])
+ self.assertIn('class XOSModel(XOSBase):', output['XOSModel'])
+
+ # vrouter port use the default header
+ self.assertIn('header import *', output['VRouterPort'])
+ self.assertIn('class VRouterPort(XOSBase):', output['VRouterPort'])
+
+ #verify files
+ xosmodel = OUTPUT_DIR + '/xosmodel.py'
+ self.assertTrue(os.path.isfile(xosmodel))
+ xmf = open(xosmodel).read()
+ self.assertIn('from xosmodel_header import *', xmf)
+ self.assertIn('class XOSModel(XOSBase):', xmf)
+
+ vrouterport = OUTPUT_DIR + '/vrouterport.py'
+ self.assertTrue(os.path.isfile(vrouterport))
+ vrpf = open(vrouterport).read()
+ self.assertIn('header import *', vrpf)
+ self.assertIn('class VRouterPort(XOSBase):', vrpf)
+
+ def test_django_with_base(self):
+ args = FakeArgs()
+ args.files = [TEST_XPROTO, BASE_XPROTO]
+ args.target = 'django.xtarget'
+ args.attic = TEST_ATTICS
+ args.output = OUTPUT_DIR
+ args.dest_extension = 'py'
+ args.write_to_file = 'model'
+ output = XOSGenerator.generate(args)
+
+ # verify files
+ xosmodel = OUTPUT_DIR + '/xosmodel.py'
+ self.assertTrue(os.path.isfile(xosmodel))
+ xmf = open(xosmodel).read()
+ self.assertIn('from xosmodel_header import *', xmf)
+ self.assertIn('class XOSModel(XOSBase):', xmf)
+
+ xosbase = OUTPUT_DIR + '/xosbase.py'
+ self.assertTrue(os.path.isfile(xosbase))
+ xbf = open(xosbase).read()
+ self.assertIn('header import *', xbf)
+ self.assertIn('class XOSBase(models.Model, PlModelMixIn):', xbf)
+
+ def test_write_multiple_files(self):
+ """
+ [XOS-GenX] read multiple models as input, print one file per model
+ """
+ args = FakeArgs()
+ args.files = [TEST_XPROTO, VROUTER_XPROTO]
+ args.target = TEST_TARGET
+ args.output = OUTPUT_DIR
+ args.dest_extension = 'txt'
+ args.write_to_file = 'model'
+ XOSGenerator.generate(args)
+
+ generated_files = [f for f in os.listdir(OUTPUT_DIR) if not f.startswith('.')]
+ self.assertEqual(len(generated_files), 2)
+
+ xosmodel = open(os.path.join(OUTPUT_DIR, 'xosmodel.txt'), "r").read()
+ vrouterport = open(os.path.join(OUTPUT_DIR, 'vrouterport.txt'), "r").read()
+
+ self.assertIn("name: XOSModel", xosmodel)
+ self.assertIn("name: VRouterPort", vrouterport)
+
+ def test_write_multiple_files_from_xtarget(self):
+ """
+ [XOS-GenX] read multiple models as input, print separate files based on +++
+ """
+ args = FakeArgs()
+ args.files = [TEST_XPROTO, VROUTER_XPROTO]
+ args.target = SPLIT_TARGET
+ args.output = OUTPUT_DIR
+ args.write_to_file = 'target'
+ XOSGenerator.generate(args)
+
+ generated_files = [f for f in os.listdir(OUTPUT_DIR) if not f.startswith('.')]
+ self.assertEqual(len(generated_files), 2)
+
+ xosmodel = open(os.path.join(OUTPUT_DIR, 'xosmodel.txt'), "r").read()
+ vrouterport = open(os.path.join(OUTPUT_DIR, 'vrouterport.txt'), "r").read()
+
+ self.assertIn("name: XOSModel", xosmodel)
+ self.assertIn("name: VRouterPort", vrouterport)
+
+ def test_skip_django(self):
+ args = FakeArgs()
+ args.files = [SKIP_DJANGO_XPROTO]
+ args.target = 'django.xtarget'
+ args.output = OUTPUT_DIR
+ args.dest_extension = 'py'
+ args.write_to_file = 'model'
+ output = XOSGenerator.generate(args)
+
+ # should not print a file if options.skip_django = True
+ file = OUTPUT_DIR + '/user.py'
+ self.assertFalse(os.path.isfile(file))
+
+ def test_service_order(self):
+ args = FakeArgs()
+ args.files = [BASE_XPROTO, TEST_XPROTO, VROUTER_XPROTO]
+ args.target = 'service.xtarget'
+ args.output = OUTPUT_DIR
+ args.write_to_file = 'target'
+ output = XOSGenerator.generate(args)
+
+ model = OUTPUT_DIR + '/models.py'
+ self.assertTrue(os.path.isfile(model))
+ line_num = 0
+
+ for line in open(model).readlines():
+ line_num += 1
+ if line.find('class XOSBase(models.Model, PlModelMixIn):') >= 0:
+ base_line = line_num
+ if line.find('XOSModel(XOSBase):') >= 0:
+ xosmodel_line = line_num
+ if line.find('class VRouterPort(XOSBase):') >= 0:
+ vrouter_line = line_num
+ self.assertLess(base_line, xosmodel_line)
+ self.assertLess(xosmodel_line, vrouter_line)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/lib/xos-genx/xos-genx-tests/tosca_test.py b/lib/xos-genx/xos-genx-tests/tosca_test.py
new file mode 100644
index 0000000..48a4db9
--- /dev/null
+++ b/lib/xos-genx/xos-genx-tests/tosca_test.py
@@ -0,0 +1,91 @@
+# Copyright 2017-present Open Networking Foundation
+#
+# 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.
+
+import unittest
+from xosgenx.generator import XOSGenerator
+from helpers import FakeArgs, XProtoTestHelpers
+
+
+class XProtoToscaTest(unittest.TestCase):
+
+ def setUp(self):
+ self.target = XProtoTestHelpers.write_tmp_target(
+"""
+{%- for m in proto.messages %}
+ {{ xproto_fields_to_tosca_keys(m.fields) }}
+{% endfor -%}
+""")
+
+ def test_xproto_fields_to_tosca_keys_default(self):
+ """
+ [XOS-GenX] if no "tosca_key" is specified, and a name attribute is present in the model, use that
+ """
+ xproto = \
+"""
+option app_label = "test";
+
+message Foo {
+ required string name = 1 [ null = "False", blank="False"];
+}
+"""
+
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = self.target
+ output = XOSGenerator.generate(args)
+ self.assertIn('name', output)
+
+ def test_xproto_fields_to_tosca_keys_custom(self):
+ """
+ [XOS-GenX] if "tosca_key" is specified, use it
+ """
+ xproto = \
+ """
+ option app_label = "test";
+
+ message Foo {
+ required string name = 1 [ null = "False", blank="False"];
+ required string key_1 = 2 [ null = "False", blank="False", tosca_key=True];
+ required string key_2 = 3 [ null = "False", blank="False", tosca_key=True];
+ }
+ """
+
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = self.target
+ output = XOSGenerator.generate(args)
+ self.assertNotIn('name', output)
+ self.assertIn('key_1', output)
+ self.assertIn('key_2', output)
+
+ def test_xproto_fields_link_to_tosca_keys_custom(self):
+ """
+ [XOS-GenX] if "tosca_key" is specified, use it
+ """
+ xproto = \
+ """
+ option app_label = "test";
+
+ message Foo {
+ required string name = 1 [ null = "False", blank="False"];
+ required manytoone provider_service_instance->ServiceInstance:provided_links = 1 [db_index = True, null = False, blank = False, tosca_key=True];
+ }
+ """
+
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = self.target
+ output = XOSGenerator.generate(args)
+ self.assertNotIn('name', output)
+ self.assertIn('provider_service_instance_id', output)
\ No newline at end of file
diff --git a/lib/xos-genx/xos-genx-tests/translator_test.py b/lib/xos-genx/xos-genx-tests/translator_test.py
new file mode 100644
index 0000000..48c1a81
--- /dev/null
+++ b/lib/xos-genx/xos-genx-tests/translator_test.py
@@ -0,0 +1,321 @@
+
+# Copyright 2017-present Open Networking Foundation
+#
+# 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.
+
+
+import unittest
+import os
+from xosgenx.generator import XOSGenerator
+from helpers import FakeArgs
+import yaml
+
+PROTO_EXPECTED_OUTPUT = """
+message VRouterPort {
+ option bases = "XOSBase";
+ optional string name = 1 [ null = "True", max_length = "20", blank = "True", help_text = ""port friendly name"", modifier = "optional", db_index = "False" ];
+ required string openflow_id = 2 [ null = "False", max_length = "21", blank = "False", help_text = ""port identifier in ONOS"", modifier = "required", db_index = "False" ];
+ required int32 vrouter_device = 3 [ null = "False", blank = "False", model = "VRouterDevice", modifier = "required", type = "link", port = "ports", link_type = "manytoone", db_index = "True" ];
+ required int32 vrouter_service = 4 [ null = "False", blank = "False", model = "VRouterService", modifier = "required", type = "link", port = "device_ports", link_type = "manytoone", db_index = "True" ];
+}
+"""
+VROUTER_XPROTO = os.path.abspath(os.path.dirname(os.path.realpath(__file__)) + "/xproto/vrouterport.xproto")
+
+# Generate other formats from xproto
+class XProtoTranslatorTest(unittest.TestCase):
+ def _test_proto_generator(self):
+ args = FakeArgs()
+ args.files = [VROUTER_XPROTO]
+ args.target = 'proto.xtarget'
+ output = XOSGenerator.generate(args)
+ self.assertEqual(output, PROTO_EXPECTED_OUTPUT)
+
+ def test_yaml_generator(self):
+ xproto = \
+"""
+option app_label = "test";
+
+message Port (PlCoreBase,ParameterMixin){
+ required manytoone network->Network:links = 1 [db_index = True, null = False, blank = False];
+ optional manytoone instance->Instance:ports = 2 [db_index = True, null = True, blank = True];
+ optional string ip = 3 [max_length = 39, content_type = "ip", blank = True, help_text = "Instance ip address", null = True, db_index = False];
+ optional string port_id = 4 [help_text = "Neutron port id", max_length = 256, null = True, db_index = False, blank = True];
+ optional string mac = 5 [help_text = "MAC address associated with this port", max_length = 256, null = True, db_index = False, blank = True];
+ required bool xos_created = 6 [default = False, null = False, db_index = False, blank = True];
+}
+
+message Instance (PlCoreBase){
+ optional string instance_id = 1 [max_length = 200, content_type = "stripped", blank = True, help_text = "Nova instance id", null = True, db_index = False];
+ optional string instance_uuid = 2 [max_length = 200, content_type = "stripped", blank = True, help_text = "Nova instance uuid", null = True, db_index = False];
+ required string name = 3 [max_length = 200, content_type = "stripped", blank = False, help_text = "Instance name", null = False, db_index = False];
+ optional string instance_name = 4 [max_length = 200, content_type = "stripped", blank = True, help_text = "OpenStack generated name", null = True, db_index = False];
+ optional string ip = 5 [max_length = 39, content_type = "ip", blank = True, help_text = "Instance ip address", null = True, db_index = False];
+ required manytoone image->Image:instances = 6 [db_index = True, null = False, blank = False];
+ optional manytoone creator->User:instances = 7 [db_index = True, null = True, blank = True];
+ required manytoone slice->Slice:instances = 8 [db_index = True, null = False, blank = False];
+ required manytoone deployment->Deployment:instance_deployment = 9 [db_index = True, null = False, blank = False];
+ required manytoone node->Node:instances = 10 [db_index = True, null = False, blank = False];
+ required int32 numberCores = 11 [help_text = "Number of cores for instance", default = 0, null = False, db_index = False, blank = False];
+ required manytoone flavor->Flavor:instance = 12 [help_text = "Flavor of this instance", default = "get_default_flavor()", null = False, db_index = True, blank = False];
+ optional string userData = 13 [help_text = "user_data passed to instance during creation", null = True, db_index = False, blank = True];
+ required string isolation = 14 [default = "vm", choices = "(('vm', 'Virtual Machine'), ('container', 'Container'), ('container_vm', 'Container In VM'))", max_length = 30, blank = False, null = False, db_index = False];
+ optional string volumes = 15 [help_text = "Comma-separated list of directories to expose to parent context", null = True, db_index = False, blank = True];
+ optional manytoone parent->Instance:instance = 16 [help_text = "Parent Instance for containers nested inside of VMs", null = True, db_index = True, blank = True];
+ required manytomany tags->Tag = 17 [db_index = False, null = False, blank = True];
+}
+
+message Network (PlCoreBase,ParameterMixin) {
+ required string name = 1 [db_index = False, max_length = 32, null = False, blank = False];
+ required manytoone template->NetworkTemplate:network = 2 [db_index = True, null = False, blank = False];
+ required string subnet = 3 [db_index = False, max_length = 32, null = False, blank = True];
+ required string start_ip = 4 [db_index = False, max_length = 32, null = False, blank = True];
+ required string end_ip = 5 [db_index = False, max_length = 32, null = False, blank = True];
+ optional string ports = 6 [db_index = False, max_length = 1024, null = True, blank = True];
+ optional string labels = 7 [db_index = False, max_length = 1024, null = True, blank = True];
+ required manytoone owner->Slice:ownedNetworks = 8 [help_text = "Slice that owns control of this Network", null = False, db_index = True, blank = False];
+ required int32 guaranteed_bandwidth = 9 [default = 0, null = False, db_index = False, blank = False];
+ required bool permit_all_slices = 10 [default = False, null = False, db_index = False, blank = True];
+ optional string topology_parameters = 11 [db_index = False, null = True, blank = True];
+ optional string controller_url = 12 [db_index = False, max_length = 1024, null = True, blank = True];
+ optional string controller_parameters = 13 [db_index = False, null = True, blank = True];
+ optional string network_id = 14 [help_text = "Quantum network", max_length = 256, null = True, db_index = False, blank = True];
+ optional string router_id = 15 [help_text = "Quantum router id", max_length = 256, null = True, db_index = False, blank = True];
+ optional string subnet_id = 16 [help_text = "Quantum subnet id", max_length = 256, null = True, db_index = False, blank = True];
+ required bool autoconnect = 17 [help_text = "This network can be autoconnected to the slice that owns it", default = True, null = False, db_index = False, blank = True];
+ required manytomany permitted_slices->Slice/Network_permitted_slices:availableNetworks = 18 [db_index = False, null = False, blank = True];
+ required manytomany slices->Slice/NetworkSlice:networks = 19 [db_index = False, null = False, blank = True];
+ required manytomany instances->Instance/Port:networks = 20 [db_index = False, null = False, blank = True];
+}
+
+message Slice (PlCoreBase){
+ required string name = 1 [max_length = 80, content_type = "stripped", blank = False, help_text = "The Name of the Slice", null = False, db_index = False];
+ required bool enabled = 2 [help_text = "Status for this Slice", default = True, null = False, db_index = False, blank = True];
+ required bool omf_friendly = 3 [default = False, null = False, db_index = False, blank = True];
+ required string description = 4 [help_text = "High level description of the slice and expected activities", max_length = 1024, null = False, db_index = False, blank = True];
+ required string slice_url = 5 [db_index = False, max_length = 512, null = False, content_type = "url", blank = True];
+ required manytoone site->Site:slices = 6 [help_text = "The Site this Slice belongs to", null = False, db_index = True, blank = False];
+ required int32 max_instances = 7 [default = 10, null = False, db_index = False, blank = False];
+ optional manytoone service->Service:slices = 8 [db_index = True, null = True, blank = True];
+ optional string network = 9 [blank = True, max_length = 256, null = True, db_index = False, choices = "((None, 'Default'), ('host', 'Host'), ('bridged', 'Bridged'), ('noauto', 'No Automatic Networks'))"];
+ optional string exposed_ports = 10 [db_index = False, max_length = 256, null = True, blank = True];
+ optional manytoone serviceClass->ServiceClass:slices = 11 [db_index = True, null = True, blank = True];
+ optional manytoone creator->User:slices = 12 [db_index = True, null = True, blank = True];
+ optional manytoone default_flavor->Flavor:slices = 13 [db_index = True, null = True, blank = True];
+ optional manytoone default_image->Image:slices = 14 [db_index = True, null = True, blank = True];
+ optional manytoone default_node->Node:slices = 15 [db_index = True, null = True, blank = True];
+ optional string mount_data_sets = 16 [default = "GenBank", max_length = 256, content_type = "stripped", blank = True, null = True, db_index = False];
+ required string default_isolation = 17 [default = "vm", choices = "(('vm', 'Virtual Machine'), ('container', 'Container'), ('container_vm', 'Container In VM'))", max_length = 30, blank = False, null = False, db_index = False];
+ required manytomany tags->Tag = 18 [db_index = False, null = False, blank = True];
+}
+"""
+
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = 'modeldefs.xtarget'
+ output = XOSGenerator.generate(args)
+
+ yaml_ir = yaml.load(output)
+ self.assertEqual(len(yaml_ir['items']), 4)
+
+ def test_gui_hidden_models(self):
+ xproto = \
+"""
+option app_label = "test";
+
+message Foo {
+ option gui_hidden = True;
+ required string name = 1 [ null = "False", blank="False"];
+}
+
+message Bar {
+ option gui_hidden = "False";
+ required string name = 1 [ null = "False", blank="False"];
+}
+"""
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = 'modeldefs.xtarget'
+ output = XOSGenerator.generate(args)
+ yaml_ir = yaml.load(output)
+ self.assertEqual(len(yaml_ir['items']), 1)
+ self.assertIn('Bar', output)
+ self.assertNotIn('Foo', output)
+
+ def test_gui_hidden_model_fields(self):
+ xproto = \
+"""
+option app_label = "test";
+
+message Foo {
+ required string name = 1 [ null = "False", blank="False"];
+ required string secret = 1 [ null = "False", blank="False", gui_hidden = "True"];
+}
+"""
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = 'modeldefs.xtarget'
+ output = XOSGenerator.generate(args)
+ yaml_ir = yaml.load(output)
+ self.assertEqual(len(yaml_ir['items']), 1)
+ self.assertIn('name', output)
+ self.assertNotIn('secret', output)
+
+ def test_static_options(self):
+ xproto = \
+"""
+option app_label = "test";
+
+message Foo {
+ required string name = 1 [ null = "False", blank="False"];
+ required string isolation = 14 [default = "vm", choices = "(('vm', 'Virtual Machine'), ('container', 'Container'), ('container_vm', 'Container In VM'))", max_length = 30, blank = False, null = False, db_index = False];
+}
+"""
+
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = 'modeldefs.xtarget'
+ output = XOSGenerator.generate(args)
+ self.assertIn("options:", output)
+ self.assertIn(" {'id': 'container_vm', 'label': 'Container In VM'}", output)
+
+ def test_not_static_options(self):
+ xproto = \
+"""
+option app_label = "test";
+
+message Foo {
+ required string name = 1 [ null = "False", blank="False"];
+}
+"""
+
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = 'modeldefs.xtarget'
+ output = XOSGenerator.generate(args)
+ self.assertNotIn("options:", output)
+
+ def test_default_value_in_modeldef(self):
+ xproto = \
+"""
+option app_label = "test";
+
+message Foo {
+ required string name = 1 [ null = "False", blank="False", default = "bar"];
+ required string falsetrue = 1 [ null = "False", blank="False", default = False];
+ required string truefalse = 1 [ null = "False", blank="False", default = True];
+ required string some = 1 [ null = "False", blank="False", default = None];
+ required string zero = 1 [ null = "False", blank="False", default = 0];
+}
+"""
+
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = 'modeldefs.xtarget'
+ output = XOSGenerator.generate(args)
+ self.assertIn('default: "bar"', output)
+ self.assertIn('default: "false"', output)
+ self.assertIn('default: "true"', output)
+ self.assertIn('default: "null"', output)
+ self.assertIn('default: "0"', output)
+
+ def test_not_default_value_in_modeldef(self):
+ xproto = \
+"""
+option app_label = "test";
+
+message Foo {
+ required string name = 1 [ null = "False", blank="False"];
+}
+"""
+
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = 'modeldefs.xtarget'
+ output = XOSGenerator.generate(args)
+ self.assertNotIn('default:', output)
+
+ def test_one_to_many_in_modeldef(self):
+ xproto = \
+"""
+option app_label = "test";
+
+message ServiceDependency {
+ required manytoone provider_service->Service:provided_dependencies = 1;
+ required manytoone subscriber_service->Service:subscribed_dependencies = 2;
+}
+
+message Service {
+ required string name = 1;
+}
+"""
+
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = 'modeldefs.xtarget'
+ output = XOSGenerator.generate(args)
+ # Service deps model
+ self.assertIn('{model: Service, type: manytoone, on_field: provider_service}', output)
+ self.assertIn('{model: Service, type: manytoone, on_field: provider_service}', output)
+
+ # Service model
+ self.assertIn('{model: ServiceDependency, type: onetomany, on_field: provider_service}', output)
+ self.assertIn('{model: ServiceDependency, type: onetomany, on_field: provider_service}', output)
+
+ def test_model_description(self):
+ xproto = \
+"""
+option app_label = "test";
+
+message Foo {
+ option description="This is the Foo model";
+ required string name = 1 [ null = "False", blank="False"];
+ required string isolation = 14 [default = "vm", choices = "(('vm', 'Virtual Machine'), ('container', 'Container'), ('container_vm', 'Container In VM'))", max_length = 30, blank = False, null = False, db_index = False];
+}
+
+message Bar {
+ required string name = 1;
+}
+"""
+
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = 'modeldefs.xtarget'
+ output = XOSGenerator.generate(args)
+ self.assertIn('description: "This is the Foo model"', output)
+
+ def test_model_verbose_name(self):
+ xproto = \
+"""
+option app_label = "test";
+
+message Foo {
+ option verbose_name="Verbose Foo Name";
+ required string name = 1 [ null = "False", blank="False"];
+ required string isolation = 14 [default = "vm", choices = "(('vm', 'Virtual Machine'), ('container', 'Container'), ('container_vm', 'Container In VM'))", max_length = 30, blank = False, null = False, db_index = False];
+}
+
+message Bar {
+ required string name = 1;
+}
+"""
+
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = 'modeldefs.xtarget'
+ output = XOSGenerator.generate(args)
+ self.assertIn('verbose_name: "Verbose Foo Name"', output)
+
+if __name__ == '__main__':
+ unittest.main()
+
+
diff --git a/lib/xos-genx/xos-genx-tests/xos_security_test.py b/lib/xos-genx/xos-genx-tests/xos_security_test.py
new file mode 100644
index 0000000..a07bc7b
--- /dev/null
+++ b/lib/xos-genx/xos-genx-tests/xos_security_test.py
@@ -0,0 +1,202 @@
+
+# Copyright 2017-present Open Networking Foundation
+#
+# 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.
+
+
+import unittest
+from xosgenx.generator import XOSGenerator
+from helpers import FakeArgs, XProtoTestHelpers
+import pdb
+import mock
+
+"""The function below is for eliminating warnings arising due to the missing policy_output_enforcer,
+which is generated and loaded dynamically.
+"""
+def policy_output_enforcer(x, y):
+ raise Exception("Security enforcer not generated. Test failed.")
+ return False
+
+"""
+The tests below use the Python code target to generate
+Python security policies, set up an appropriate environment and execute the Python.
+The security policies here deliberately made complex in order to stress the processor.
+"""
+class XProtoXOSSecurityTest(unittest.TestCase):
+ def setUp(self):
+ self.target = XProtoTestHelpers.write_tmp_target("{{ xproto_fol_to_python_test('output',proto.policies.test_policy, None, '0') }}")
+
+ """
+ This is the security policy for controllers
+ """
+ def test_controller_policy(self):
+ xproto = \
+"""
+ policy test_policy < ctx.user.is_admin | exists Privilege: Privilege.accessor_id = ctx.user.id & Privilege.object_type = "Deployment" & Privilege.permission = "role:admin" & Privilege.object_id = obj.id >
+"""
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = self.target
+
+ output = XOSGenerator.generate(args)
+
+ exec(output) # This loads the generated function, which should look like this:
+
+ """
+ def policy_output_enforcer(obj, ctx):
+ i2 = ctx.user.is_admin
+ i3 = Privilege.objects.filter(Q(accessor_id=ctx.user.id), Q(object_type='Deployment'), Q(permission='role:admin'), Q(object_id=obj.id))[0]
+ i1 = (i2 or i3)
+ return i1
+ """
+
+ # FIXME: Test this policy by executing it
+ self.assertTrue(policy_output_enforcer is not None)
+
+ """
+ This is the security policy for ControllerNetworks
+ """
+ def test_controller_network_policy(self):
+ xproto = \
+"""
+ policy test_policy <
+ ctx.user.is_admin
+ | (exists Privilege:
+ Privilege.accessor_id = ctx.user.id
+ & Privilege.accessor_type = "User"
+ & Privilege.object_type = "Slice"
+ & Privilege.object_id = obj.owner.id)
+ | (exists Privilege:
+ Privilege.accessor_id = ctx.user.id
+ & Privilege.accessor_type = "User"
+ & Privilege.object_type = "Site"
+ & Privilege.object_id = obj.owner.site.id
+ & Privilege.permission = "role:admin") >
+"""
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = self.target
+
+ output = XOSGenerator.generate(args)
+ exec(output) # This loads the generated function, which should look like this:
+
+ """
+ def policy_output_enforcer(obj, ctx):
+ i2 = ctx.user.is_admin
+ i4 = Privilege.objects.filter(Q(accessor_id=ctx.user.id), Q(accessor_type='User'), Q(object_type='Slice'), Q(object_id=obj.owner.id))[0]
+ i5 = Privilege.objects.filter(Q(accessor_id=ctx.user.id), Q(accessor_type='User'), Q(object_type='Site'), Q(object_id=obj.owner.site.id), Q(permission='role:admin'))[0]
+ i3 = (i4 or i5)
+ i1 = (i2 or i3)
+ return i1
+ """
+
+ # FIXME: Test this policy by executing it
+ self.assertTrue(policy_output_enforcer is not None)
+
+ """
+ This is the security policy for Slices
+ """
+ def test_slice_policy(self):
+ xproto = \
+"""
+ policy site_policy <
+ ctx.user.is_admin
+ | (ctx.write_access -> exists Privilege: Privilege.object_type = "Site" & Privilege.object_id = obj.id & Privilege.accessor_id = ctx.user.id & Privilege.permission_id = "role:admin") >
+
+ policy test_policy <
+ ctx.user.is_admin
+ | (*site_policy(site)
+ & ((exists Privilege:
+ Privilege.accessor_id = ctx.user.id
+ & Privilege.accessor_type = "User"
+ & Privilege.object_type = "Slice"
+ & Privilege.object_id = obj.id
+ & (ctx.write_access->Privilege.permission="role:admin"))
+ | (exists Privilege:
+ Privilege.accessor_id = ctx.user.id
+ & Privilege.accessor_type = "User"
+ & Privilege.object_type = "Site"
+ & Privilege.object_id = obj.site.id
+ & Privilege.permission = "role:admin"))
+ )>
+
+"""
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = self.target
+
+ output = XOSGenerator.generate(args)
+
+ exec(output) # This loads the generated function, which should look like this:
+
+ """
+ def policy_output_enforcer(obj, ctx):
+ i2 = ctx.user.is_admin
+ i4 = policy_site_policy_enforcer(obj.site, ctx)
+ i10 = ctx.write_access
+ i11 = (not (not Privilege.objects.filter(Q(accessor_id=ctx.user.id), Q(accessor_type='User'), Q(object_type='Slice'), Q(object_id=obj.id), Q(permission='role:admin'))))
+ i8 = (i10 and i11)
+ i14 = ctx.write_access
+ i12 = (not i14)
+ i13 = (not (not Privilege.objects.filter(Q(accessor_id=ctx.user.id), Q(accessor_type='User'), Q(object_type='Slice'), Q(object_id=obj.id))))
+ i9 = (i12 and i13)
+ i6 = (i8 or i9)
+ i7 = (not (not Privilege.objects.filter(Q(accessor_id=ctx.user.id), Q(accessor_type='User'), Q(object_type='Site'), Q(object_id=obj.site.id), Q(permission='role:admin'))))
+ i5 = (i6 or i7)
+ i3 = (i4 and i5)
+ i1 = (i2 or i3)
+ return i1
+ """
+
+ # FIXME: Test this policy by executing it
+ self.assertTrue(policy_output_enforcer is not None)
+
+ """
+ This is the security policy for Users
+ """
+ def test_user_policy(self):
+ xproto = \
+"""
+ policy test_policy <
+ ctx.user.is_admin
+ | ctx.user.id = obj.id
+ | (exists Privilege:
+ Privilege.accessor_id = ctx.user.id
+ & Privilege.accessor_type = "User"
+ & Privilege.permission = "role:admin"
+ & Privilege.object_type = "Site"
+ & Privilege.object_id = ctx.user.site.id) >
+"""
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = self.target
+
+ output = XOSGenerator.generate(args)
+
+ exec(output) # This loads the generated function, which should look like this:
+
+ """
+ def policy_output_enforcer(obj, ctx):
+ i2 = ctx.user.is_admin
+ i4 = (ctx.user.id == obj.id)
+ i5 = Privilege.objects.filter(Q(accessor_id=ctx.user.id), Q(accessor_type='User'), Q(permission='role:admin'), Q(object_type='Site'), Q(object_id=ctx.user.site.id))[0]
+ i3 = (i4 or i5)
+ i1 = (i2 or i3)
+ return i1
+ """
+
+ # FIXME: Test this policy by executing it
+ self.assertTrue(policy_output_enforcer is not None)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/lib/xos-genx/xos-genx-tests/xos_validation_test.py b/lib/xos-genx/xos-genx-tests/xos_validation_test.py
new file mode 100644
index 0000000..69dafc5
--- /dev/null
+++ b/lib/xos-genx/xos-genx-tests/xos_validation_test.py
@@ -0,0 +1,224 @@
+
+# Copyright 2017-present Open Networking Foundation
+#
+# 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.
+
+
+import unittest
+from xosgenx.generator import XOSGenerator
+from helpers import FakeArgs, XProtoTestHelpers
+
+"""The function below is for eliminating warnings arising due to the missing policy_output_validator,
+which is generated and loaded dynamically.
+"""
+def policy_output_validator(x, y):
+ raise Exception("Validator not generated. Test failed.")
+ return False
+
+"""
+The tests below use the Python code target to generate
+Python validation policies, set up an appropriate environment and execute the Python.
+"""
+class XProtoXOSModelValidationTest(unittest.TestCase):
+ def setUp(self):
+ self.target = XProtoTestHelpers.write_tmp_target("{{ xproto_fol_to_python_validator('output', proto.policies.test_policy, None, 'Necessary Failure') }}")
+
+ def test_instance_container(self):
+ xproto = \
+"""
+ policy test_policy < (obj.isolation = "container" | obj.isolation = "container_vm" ) -> (obj.image.kind = "container") >
+"""
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = self.target
+
+ output = XOSGenerator.generate(args)
+
+ obj = FakeArgs()
+ obj.isolation = 'container'
+ obj.kind = 'not a container'
+
+ exec(output) # This loads the generated function, which should look like this:
+
+ """
+ def policy_output_validator(obj, ctx):
+ i4 = (obj.isolation == 'container')
+ i5 = (self.isolation == 'container_vm')
+ i2 = (i4 or i5)
+ i3 = (obj.image.kind == 'container')
+ i1 = (i2 or i3)
+ return i1
+ """
+
+ with self.assertRaises(Exception):
+ policy_output_validator(obj, {})
+
+ def test_slice_name_validation(self):
+ xproto = \
+"""
+ policy test_policy < not obj.id -> {{ obj.name.startswith(obj.site.login_base) }} >
+"""
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = self.target
+
+ output = XOSGenerator.generate(args)
+
+ obj = FakeArgs()
+ obj.isolation = 'container'
+ obj.kind = 'not a container'
+
+ exec(output) # This loads the generated function, which should look like this:
+
+ """
+ def policy_output_validator(obj, ctx):
+ i3 = obj.id
+ i4 = obj.name.startswith(obj.site.login_base)
+ i2 = ((not i3) or i4)
+ i1 = (not i2)
+ if (not i1):
+ raise ValidationError('Necessary Failure')
+ """
+
+ with self.assertRaises(Exception):
+ policy_output_validator(obj, {})
+
+ def test_equal(self):
+ xproto = \
+"""
+ policy test_policy < not (ctx.user = obj.user) >
+"""
+
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = self.target
+
+ output = XOSGenerator.generate(args)
+
+ exec(output) # This loads the generated function, which should look like this:
+
+ """
+ def policy_output_validator(obj, ctx):
+ i2 = (ctx.user == obj.user)
+ i1 = (not i2)
+ if (not i1):
+ raise Exception('Necessary Failure')
+ """
+
+ obj = FakeArgs()
+ obj.user = 1
+ ctx = FakeArgs()
+ ctx.user = 1
+
+ with self.assertRaises(Exception):
+ policy_output_validator(obj, ctx)
+
+ def test_bin(self):
+ xproto = \
+"""
+ policy test_policy < not (ctx.is_admin = True | obj.empty = True) | False>
+"""
+
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = self.target
+
+ output = XOSGenerator.generate(args)
+ exec(output) # This loads the generated function, which should look like this:
+
+ """
+ def policy_output_validator(obj, ctx):
+ i2 = (ctx.is_admin == True)
+ i3 = (obj.empty == True)
+ i1 = (i2 or i3)
+ if (not i1):
+ raise Exception('Necessary Failure')
+ """
+
+ obj = FakeArgs()
+ obj.empty = True
+
+ ctx = FakeArgs()
+ ctx.is_admin = True
+
+ with self.assertRaises(Exception):
+ verdict = policy_output_validator(obj, ctx)
+
+
+ def test_exists(self):
+ xproto = \
+"""
+ policy test_policy < exists Privilege: Privilege.object_id = obj.id >
+"""
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = self.target
+
+ output = XOSGenerator.generate(args)
+ exec(output) # This loads the generated function, which should look like this:
+
+ """
+ def policy_output_validator(obj, ctx):
+ i1 = Privilege.objects.filter(Q(object_id=obj.id))[0]
+ if (not i1):
+ raise Exception('Necessary Failure')
+ """
+
+ self.assertTrue(policy_output_validator is not None)
+
+ def test_python(self):
+ xproto = \
+"""
+ policy test_policy < {{ "jack" in ["the", "box"] }} = True >
+"""
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = self.target
+ output = XOSGenerator.generate(args)
+ exec(output) # This loads the generated function, which should look like this:
+
+ """
+ def policy_output_validator(obj, ctx):
+ i2 = ('jack' in ['the', 'box'])
+ i1 = (i2 == True)
+ if (not i1):
+ raise Exception('Necessary Failure')
+ """
+
+ with self.assertRaises(Exception):
+ self.assertTrue(policy_output_validator({}, {}) is True)
+
+ def test_forall(self):
+ # This one we only parse
+ xproto = \
+"""
+ policy test_policy < forall Credential: Credential.obj_id = obj_id >
+"""
+
+ args = FakeArgs()
+ args.inputs = xproto
+ args.target = self.target
+
+ output = XOSGenerator.generate(args)
+
+ """
+ def policy_output_enforcer(obj, ctx):
+ i2 = Credential.objects.filter((~ Q(obj_id=obj_id)))[0]
+ i1 = (not i2)
+ return i1
+ """
+
+ self.assertIn('policy_output_validator', output)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/lib/xos-genx/xos-genx-tests/xproto/base.xproto b/lib/xos-genx/xos-genx-tests/xproto/base.xproto
new file mode 100644
index 0000000..78cc4e8
--- /dev/null
+++ b/lib/xos-genx/xos-genx-tests/xproto/base.xproto
@@ -0,0 +1,18 @@
+option app_name = "core";
+
+message XOSBase {
+ option skip_init = True;
+ required string created = 1 [content_type = "date", auto_now_add = True];
+ required string updated = 2 [default = "now()", content_type = "date"];
+ optional string enacted = 3 [null = True, content_type = "date", blank = True, default = None];
+ optional string policed = 4 [null = True, content_type = "date", blank = True, default = None];
+ optional string backend_register = 5 [default = "{}", max_length = 1024, null = True];
+ required bool backend_need_delete = 6 [default = False];
+ required bool backend_need_reap = 7 [default = False];
+ required string backend_status = 8 [default = "0 - Provisioning in progress", max_length = 1024];
+ required bool deleted = 9 [default = False];
+ required bool write_protect = 10 [default = False];
+ required bool lazy_blocked = 11 [default = False];
+ required bool no_sync = 12 [default = False];
+ required bool no_policy = 13 [default = False];
+}
\ No newline at end of file
diff --git a/lib/xos-genx/xos-genx-tests/xproto/skip_django.xproto b/lib/xos-genx/xos-genx-tests/xproto/skip_django.xproto
new file mode 100644
index 0000000..8dd1039
--- /dev/null
+++ b/lib/xos-genx/xos-genx-tests/xproto/skip_django.xproto
@@ -0,0 +1,4 @@
+message User (AbstractBaseUser,PlModelMixIn) {
+ option skip_django = True;
+ required string email = 1 [db_index = True, max_length = 255, null = False, blank = False];
+}
\ No newline at end of file
diff --git a/lib/xos-genx/xos-genx-tests/xproto/test.xproto b/lib/xos-genx/xos-genx-tests/xproto/test.xproto
new file mode 100644
index 0000000..54420fa
--- /dev/null
+++ b/lib/xos-genx/xos-genx-tests/xproto/test.xproto
@@ -0,0 +1,4 @@
+message XOSModel (XOSBase) {
+ required string name = 1 [max_length = 200, content_type = "stripped", blank = False, help_text = "Help Name", null = False, db_index = False];
+ required string files = 2 [max_length = 1024, content_type = "stripped", blank = False, help_text = "Help Files", null = False, db_index = False];
+}
\ No newline at end of file
diff --git a/lib/xos-genx/xos-genx-tests/xproto/vrouterport.xproto b/lib/xos-genx/xos-genx-tests/xproto/vrouterport.xproto
new file mode 100644
index 0000000..1b092ac
--- /dev/null
+++ b/lib/xos-genx/xos-genx-tests/xproto/vrouterport.xproto
@@ -0,0 +1,6 @@
+message VRouterPort (XOSBase){
+ optional string name = 1 [help_text = "port friendly name", max_length = 20, null = True, db_index = False, blank = True];
+ required string openflow_id = 2 [help_text = "port identifier in ONOS", max_length = 21, null = False, db_index = False, blank = False];
+ required manytoone vrouter_device->VRouterDevice:ports = 3 [db_index = True, null = False, blank = False];
+ required manytoone vrouter_service->VRouterService:device_ports = 4 [db_index = True, null = False, blank = False];
+}
\ No newline at end of file
diff --git a/lib/xos-genx/xos-genx-tests/xtarget/split.xtarget b/lib/xos-genx/xos-genx-tests/xtarget/split.xtarget
new file mode 100644
index 0000000..2d55f43
--- /dev/null
+++ b/lib/xos-genx/xos-genx-tests/xtarget/split.xtarget
@@ -0,0 +1,10 @@
+{% for m in proto.messages %}
+ name: {{ m.name }}
+ fields:
+ {%- for f in m.fields %}
+ {{ f.name }}:
+ type: {{ f.type }}
+ description: {{ f.options.help_text }}
+ {%- endfor %}
++++ {{ m.name }}.txt
+{% endfor %}
diff --git a/lib/xos-genx/xos-genx-tests/xtarget/test.xtarget b/lib/xos-genx/xos-genx-tests/xtarget/test.xtarget
new file mode 100644
index 0000000..658784c
--- /dev/null
+++ b/lib/xos-genx/xos-genx-tests/xtarget/test.xtarget
@@ -0,0 +1,9 @@
+{% for m in proto.messages %}
+ name: {{ m.name }}
+ fields:
+ {%- for f in m.fields %}
+ {{ f.name }}:
+ type: {{ f.type }}
+ description: {{ f.options.help_text }}
+ {%- endfor %}
+{% endfor %}
\ No newline at end of file