Merge branch 'master' of github.com:open-cloud/xos
diff --git a/xos/tools/apigen/fields.template.txt b/xos/tools/apigen/fields.template.txt
new file mode 100644
index 0000000..ad5a0b4
--- /dev/null
+++ b/xos/tools/apigen/fields.template.txt
@@ -0,0 +1,6 @@
+{% for object in generator.all %}
+Object {{ object }}:
+Fields:
+{% for field in object.fields %}{{ field.name }}:{{ field.type }}
+{% endfor %}
+{% endfor %}
diff --git a/xos/tools/apigen/modelgen b/xos/tools/apigen/modelgen
index 414540d..068b7cb 100644
--- a/xos/tools/apigen/modelgen
+++ b/xos/tools/apigen/modelgen
@@ -11,10 +11,14 @@
# Django set up
+import django
sys.path.append('.')
+sys.path.append('/opt/xos')
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "xos.settings")
from django.db.models.fields.related import ForeignKey, ManyToManyField
+django.setup()
+
options = None
@@ -30,7 +34,9 @@
def enum_classes(apps):
global app_map
+ global class_map
app_map = {}
+ class_map = {}
model_classes = []
for app in apps:
orig_app=app
@@ -49,6 +55,16 @@
if type(c)==type(PlCoreBase) and c.__name__ not in options.blacklist:
model_classes.append(c)
app_map[c.__name__]=orig_app
+ c.class_name = c.__name__
+ file_name = c.__module__.rsplit('.',1)[1]
+ try:
+ if (file_name not in class_map[orig_app]):
+ class_map[orig_app].append({file_name:[c]})
+ else:
+ class_map[orig_app][file_name].append(c)
+
+ except KeyError:
+ class_map[orig_app] = [{file_name:[c]}]
return model_classes
@@ -98,6 +114,9 @@
return name
class Generator(dict):
+ def __init__(self):
+ self.apps = {}
+
def all(self):
return self.values()
@@ -117,7 +136,19 @@
obj.app = app_map[o.__name__]
except KeyError:
print "KeyError: %r"%o.__name__
- pdb.set_trace()
+ obj.class_name = o.class_name
+
+ file_name = o.__module__.rsplit('.',1)[1]
+
+ try:
+ if (file_name not in self.apps[obj.app]):
+ self.apps[obj.app][file_name]=[obj]
+ else:
+ self.apps[obj.app][file_name].append(obj)
+
+ except KeyError:
+ self.apps[obj.app] = {file_name:[obj]}
+
self[str(obj).lower()]=obj
def compute_links(self):
@@ -150,7 +181,8 @@
pass
else:
f.type = f.__class__.__name__
-
+ if (type(f)==ForeignKey):
+ f.related.model.class_name = f.related.model.__name__
if (f.name not in base_props):
obj.fields.append(f)
obj.props.append(f.name)
@@ -189,6 +221,9 @@
global options
parser = OptionParser(usage="modelgen [options] <template_fn>", )
+ parser.add_option("-d", "--dict", dest="dict",
+ help="dictionary to replace text in output", metavar="DICT", default=[], action="append")
+
parser.add_option("-a", "--app", dest="apps",
help="list of applications to parse", metavar="APP", default=[], action="append")
parser.add_option("-b", "--blacklist", dest="blacklist",
@@ -228,7 +263,31 @@
template_contents = open(template_name).read()
template = Template(template_contents)
context = Context({'generator':generator})
- print template.render(context)
+ rendered = template.render(context)
+ lines = rendered.splitlines()
+ current_buffer = []
+ for l in lines:
+ if (l.startswith('+++')):
+ filename = l[4:]
+ fil = open(filename,'w')
+ buf = '\n'.join(current_buffer)
+
+ obuf = buf
+ for d in options.dict:
+ df = open(d).read()
+ d = json.loads(df)
+
+ pattern = re.compile(r'\b(' + '|'.join(d.keys()) + r')\b')
+ obuf = pattern.sub(lambda x: d[x.group()], buf)
+ fil.write(obuf)
+ fil.close()
+
+ print 'Written to file %s'%filename
+ current_buffer = []
+ else:
+ current_buffer.append(l)
+ if (current_buffer):
+ print '\n'.join(current_buffer)
if (__name__=='__main__'):
diff --git a/xos/tools/apigen/modelyaml.fields.template.txt b/xos/tools/apigen/modelyaml.fields.template.txt
new file mode 100644
index 0000000..69129ed
--- /dev/null
+++ b/xos/tools/apigen/modelyaml.fields.template.txt
@@ -0,0 +1,12 @@
+{% for object in generator.all %}
+{{ object.camel }}:
+fields:
+ {% for f in object.fields %}
+ - name: {{ f.name }}
+ type: {{ f.type }}
+ {% if f.help_text %}
+ help_text: {{ f.help_text }}
+ {% endif %}
+ null: {{ f.null }}
+{% endfor %}
+{% endfor %}
diff --git a/xos/tools/apigen/synchronizer.template.txt b/xos/tools/apigen/synchronizer.template.txt
new file mode 100644
index 0000000..c784e21
--- /dev/null
+++ b/xos/tools/apigen/synchronizer.template.txt
@@ -0,0 +1,71 @@
+{% for app,files in generator.apps.items %}
+{% for file,models in files.items %}
+{% for m in models %}
+# This file implements the synchronization of the {{ m.class_name }} model
+# TODO (see below):
+
+import os
+import base64
+import socket
+from django.db.models import F, Q
+from xos.config import Config
+from synchronizers.base.syncstep import SyncStep
+from {{app}}.models.{{ file }} import {{ m.class_name.title }}
+from synchronizers.base.ansible import *
+from synchronizers.base.syncstep import *
+from xos.logger import observer_logger as logger
+
+class Sync{{m.camel.title}}(SyncStep):
+ # The model synchronized
+ provides=[{{m.class_name.title}}]
+ # How often do you want this to run. 0 means immediately following changes to the model
+ requested_interval=0
+ # The model in which changes trigger this module
+ observes={{m.class_name}}
+ # The Ansible recipe that does the bulk of the synchronization (hopefully)
+ playbook='sync_{{m.class_name}}.yaml'
+
+ # 1. Populate a data structure to pass into your ansible recipe, based on the data model.
+ def map_sync_inputs(self, {{ m.class_name }}):
+ fields = {
+ {% for f in m.fields %}'{{f.name}}': {{f.name}},
+ {% endfor %}
+ }
+ return fields
+
+ # 2. Store the result of the operation in the model
+ def map_sync_outputs(self, {{ m.class_name }}, res):
+ // Store the output of res in the {{m.class_name}} object
+ return
+
+ # 3. Populate the data structure that identifies objects to delete
+ def map_delete_inputs(self, {{ m.class_name }}, res):
+ fields = {
+ {% for f in m.fields %}'{{f.name}}': {{f.name}},
+ {% endfor %}
+ 'delete':True
+ }
+ return fields
+
+
++++ Sync{{ m.class_name }}.py
+---
+- hosts: 127.0.0.1
+ connection: local
+ tasks:
+ - task_1:
+ {% for f in m.fields %}
+ {{f.name}}: {% templatetag openbrace %}{% templatetag openbrace %}{{f.name}}{% templatetag closebrace %}{% templatetag closebrace %}
+ {% endfor %}
+ {% verbatim %}
+ {% if delete %}
+ state: absent
+ {% else %}
+ state: present
+ {% endif %}
+ {% endif %}
+ {% endverbatim %}
++++ Sync{{ m.class_name }}.yaml
+{% endfor %}
+{% endfor %}
+{% endfor %}
diff --git a/xos/tools/apigen/type_map b/xos/tools/apigen/type_map
new file mode 100644
index 0000000..ac4a8a8
--- /dev/null
+++ b/xos/tools/apigen/type_map
@@ -0,0 +1,10 @@
+{
+ "AutoField":"int64",
+ "ForeignKey":"InstanceIdentifier",
+ "CharField":"string",
+ "TextField":"string",
+ "FloatField":"float64",
+ "BooleanField":"boolean",
+ "StrippedCharField":"string",
+ "DateTimeField":"date-and-time"
+}
diff --git a/xos/tools/apigen/yang.template.txt b/xos/tools/apigen/yang.template.txt
new file mode 100644
index 0000000..ad4b1b1
--- /dev/null
+++ b/xos/tools/apigen/yang.template.txt
@@ -0,0 +1,21 @@
+{% for app,files in generator.apps.items %}
+{% for file,m in files.items %}
+module xos-{{ app }}-{{ file }} {
+ namespace "urn:xos:{{app}}.{{ file }}";
+ prefix xos-cs;
+
+ import complex-types {prefix ct;}
+ revision 2016-2-24 {
+ description "Initial";
+ }
+
+ complex-type {{ m.class_name }} {
+ {% for f in m.fields %}
+
+ leaf {{ f.name }} { type {{ f.type }}{% ifequal f.type "ForeignKey" %} { ct:instance-type {{f.related.model.class_name}};{% if f.null%}{%else%}require-instance true{% endif %}{% endifequal %};{% if f.max_length %} { length {{ f.max_length }};{% endif %}{% if None %}default "{{ f.default }}";{% endif %}}
+ {% endfor %}
+ }
+}
++++ {{ app }}-{{ file}}.yang
+{% endfor %}
+{% endfor %}
diff --git a/xos/tosca/README.md b/xos/tosca/README.md
new file mode 100644
index 0000000..98f0aaf
--- /dev/null
+++ b/xos/tosca/README.md
@@ -0,0 +1,13 @@
+## TOSCA Interface Definition
+
+This directory implements a TOSCA interface for XOS,
+which can be extended to include specifications for
+service models added to XOS. The directory is organized
+as follows:
+
+ * custom_types -- Defines schema for XOS-specific models.
+ * `.m4` files are source.
+ * `.yaml` files are generated.
+ * definitions -- Defines schema for TOSCA's base models.
+ * resources -- Translates TOSCA specification to Django API.
+ * sample -- Example TOSCA models.