[CORD-1440] Moving the generative toolchain in a library

Change-Id: Ifa8e8f930ac34e1f8952099b7e34842a52f4664d
diff --git a/lib/xos-genx/xosgenx/targets/chameleon_list_test.xtarget b/lib/xos-genx/xosgenx/targets/chameleon_list_test.xtarget
new file mode 100644
index 0000000..fcb02fb
--- /dev/null
+++ b/lib/xos-genx/xosgenx/targets/chameleon_list_test.xtarget
@@ -0,0 +1,19 @@
+source /opt/xos/coreapi/tests/testconfig-chameleon.sh
+
+# test modeldefs
+curl -f --silent http://$HOSTNAME:8080/xosapi/v1/modeldefs > /dev/null
+if [[ $? -ne 0 ]]; then
+    echo fail modeldefs
+fi
+
+{% for object in proto.messages %}
+{%- if object.name!='XOSBase' -%}
+curl -f --silent http://$HOSTNAME:8080/xosapi/v1/{{ xproto_unquote(options.app_label) }}/{{ xproto_pluralize(object) | lower }} > /dev/null
+if [[ $? -ne 0 ]]; then
+    echo fail {{ object.name }}
+fi
+{%endif-%}
+{%- endfor %}
+
+echo "okay"
+
diff --git a/lib/xos-genx/xosgenx/targets/django-split.xtarget b/lib/xos-genx/xosgenx/targets/django-split.xtarget
new file mode 100644
index 0000000..13212ae
--- /dev/null
+++ b/lib/xos-genx/xosgenx/targets/django-split.xtarget
@@ -0,0 +1,42 @@
+{% for m in proto.messages %}{% if not m.options.skip_django -%}
+{% if file_exists(xproto_base_name(m.name)|lower+'_header.py') -%}from {{xproto_base_name(m.name)|lower }}_header import *{%- else -%}from 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.name!='XOSBase' and 'Mixin' not in b.name %}
+from core.models.{{b.name | lower}} import {{ b.name }}
+{% endif %}
+{% endfor %}
+
+
+class {{ m.name }}{{ xproto_base_def(m.name, 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 %}{% set peer_name=l.peer.name %}
+  {{ l.src_port }} = {{ xproto_django_link_type(l) }}( {%- if peer_name==m.name -%}'self'{%- else -%}{{ peer_name }} {%- endif -%}, {{ xproto_django_link_options_str(l, l.dst_port ) }} )
+  {%- endfor %}
+
+  # Meta
+  {%- set uniques = xproto_field_graph_components(m.fields) %}
+  {%- if uniques %}
+  class Meta:
+      unique_together = {{ xproto_tuplify(uniques) }}
+  {%- endif %}
+  {% 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 %}
++++ {{m.name|lower}}.py
+{% endif %}{% endfor %}
\ No newline at end of file
diff --git a/lib/xos-genx/xosgenx/targets/django.xtarget b/lib/xos-genx/xosgenx/targets/django.xtarget
new file mode 100644
index 0000000..20b4e51
--- /dev/null
+++ b/lib/xos-genx/xosgenx/targets/django.xtarget
@@ -0,0 +1,42 @@
+{% for m in proto.messages %}{% if not m.options.skip_django -%}
+{% if file_exists(xproto_base_name(m.name)|lower+'_header.py') -%}from {{xproto_base_name(m.name)|lower }}_header import *{%- else -%}from 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.name!='XOSBase' and 'Mixin' not in b.name %}
+from core.models.{{b.name | lower}} import {{ b.name }}
+{% endif %}
+{% endfor %}
+
+
+class {{ m.name }}{{ xproto_base_def(m.name, 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 %}{% set peer_name=l.peer.name %}
+  {{ l.src_port }} = {{ xproto_django_link_type(l) }}( {%- if peer_name==m.name -%}'self'{%- else -%}{{ peer_name }} {%- endif -%}, {{ xproto_django_link_options_str(l, l.dst_port ) }} )
+  {%- endfor %}
+
+  # Meta
+  {%- set uniques = xproto_field_graph_components(m.fields) %}
+  {%- if uniques %}
+  class Meta:
+      unique_together = {{ xproto_tuplify(uniques) }}
+  {%- endif %}
+  {% 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 %}
+{% endif %}{% endfor %}
\ No newline at end of file
diff --git a/lib/xos-genx/xosgenx/targets/dot.xtarget b/lib/xos-genx/xosgenx/targets/dot.xtarget
new file mode 100644
index 0000000..96aceec
--- /dev/null
+++ b/lib/xos-genx/xosgenx/targets/dot.xtarget
@@ -0,0 +1,7 @@
+digraph {
+{% for m in proto.messages %}
+  {%- for l in m.links %}
+  {{ m.fqn }} -> {{ l.peer.name }};
+  {%- endfor %}
+{% endfor %}
+}
diff --git a/lib/xos-genx/xosgenx/targets/grpc_api.xtarget b/lib/xos-genx/xosgenx/targets/grpc_api.xtarget
new file mode 100644
index 0000000..a0373a3
--- /dev/null
+++ b/lib/xos-genx/xosgenx/targets/grpc_api.xtarget
@@ -0,0 +1,58 @@
+import base64
+import time
+from protos import xos_pb2
+from google.protobuf.empty_pb2 import Empty
+
+from django.contrib.auth import authenticate as django_authenticate
+from xos.exceptions import *
+from apihelper import XOSAPIHelperMixin, translate_exceptions
+
+class XosService(xos_pb2.xosServicer, XOSAPIHelperMixin):
+    def __init__(self, thread_pool):
+        self.thread_pool = thread_pool
+        XOSAPIHelperMixin.__init__(self)
+
+    def stop(self):
+        pass
+
+{% for object in proto.messages | sort(attribute='name') %}
+{%- if object.name!='XOSBase' %}
+    @translate_exceptions
+    def List{{ object.name }}(self, request, context):
+      user=self.authenticate(context)
+      model=self.get_model("{{ object.name }}")
+      return self.querysetToProto(model, model.objects.all())
+
+    @translate_exceptions
+    def Filter{{ object.name }}(self, request, context):
+      user=self.authenticate(context)
+      model=self.get_model("{{ object.name }}")
+      return self.filter(model, request)
+
+    @translate_exceptions
+    def Get{{ object.name }}(self, request, context):
+      user=self.authenticate(context)
+      model=self.get_model("{{ object.name }}")
+      return self.get(model, request.id)
+
+    @translate_exceptions
+    def Create{{ object.name }}(self, request, context):
+      user=self.authenticate(context)
+      model=self.get_model("{{ object.name }}")
+      return self.create(model, user, request)
+
+    @translate_exceptions
+    def Delete{{ object.name }}(self, request, context):
+      user=self.authenticate(context)
+      model=self.get_model("{{ object.name }}")
+      return self.delete(model, user, request.id)
+
+    @translate_exceptions
+    def Update{{ object.name }}(self, request, context):
+      user=self.authenticate(context)
+      model=self.get_model("{{ object.name }}")
+      return self.update(model, user, request.id, request, context)
+{%- endif %}
+{% endfor %}
+
+
diff --git a/lib/xos-genx/xosgenx/targets/grpc_list_test.xtarget b/lib/xos-genx/xosgenx/targets/grpc_list_test.xtarget
new file mode 100644
index 0000000..e968959
--- /dev/null
+++ b/lib/xos-genx/xosgenx/targets/grpc_list_test.xtarget
@@ -0,0 +1,49 @@
+import grpc_client
+from grpc_client import Empty
+from testconfig import *
+
+c=grpc_client.InsecureClient("xos-core.cord.lab")
+
+{% for object in proto.messages %}
+{%- if object.name!='XOSBase' %}
+print "testing insecure List{{ object.name }}...",
+c.stub.List{{ object.name }}(Empty())
+print "Okay"
+{%- endif %}
+{%- endfor %}
+
+c=grpc_client.SecureClient("xos-core.cord.lab", username=USERNAME, password=PASSWORD)
+
+{% for object in proto.messages %}
+{%- if object.name!='XOSBase' %}
+print "testing basic secure List{{ object.name }}...",
+c.stub.List{{ object.name }}(Empty())
+print "Okay"
+{%- endif %}
+{%- endfor %}
+
+# now try to login
+c=grpc_client.InsecureClient("xos-core.cord.lab")
+lr=grpc_client.LoginRequest()
+lr.username=USERNAME
+lr.password=PASSWORD
+session=c.utility.Login(lr)
+
+c=grpc_client.SecureClient("xos-core.cord.lab", sessionid=session.sessionid)
+{% for object in proto.messages %}
+{%- if object.name!='XOSBase' %}
+print "testing session secure List{{ object.name }}...",
+c.stub.List{{ object.name }}(Empty())
+print "Okay"
+{%- endif %}
+{%- endfor %}
+
+c=grpc_client.SecureClient("xos-core.cord.lab", sessionid=session.sessionid)
+{% for object in proto.messages %}
+{%- if object.name!='XOSBase' %}
+print "testing session secure xos_orm.{{ object.name }}.objects.all() ...",
+c.xos_orm.{{ object.name }}.objects.all()
+print "Okay"
+{%- endif %}
+{%- endfor %}
+
diff --git a/lib/xos-genx/xosgenx/targets/init.xtarget b/lib/xos-genx/xosgenx/targets/init.xtarget
new file mode 100644
index 0000000..235a657
--- /dev/null
+++ b/lib/xos-genx/xosgenx/targets/init.xtarget
@@ -0,0 +1,15 @@
+# The hardcoded entries cannot be inferred from the models. To get rid of in the long run
+# 
+
+from .xosbase import XOSBase,XOSBaseManager,XOSBaseDeletionManager,PlModelMixIn,ModelLink
+from .contenttype import ContentType
+from .site import Site
+from .dashboardview import DashboardView
+from .user import User
+from .user import UserDashboardView
+
+{% for m in proto.messages -%}
+{% if not m.options.skip_init -%}
+from .{{ m.name | lower }} import {{ m.name }} 
+{% endif -%}
+{% endfor -%}
diff --git a/lib/xos-genx/xosgenx/targets/link-graph.xtarget b/lib/xos-genx/xosgenx/targets/link-graph.xtarget
new file mode 100644
index 0000000..04c9dba
--- /dev/null
+++ b/lib/xos-genx/xosgenx/targets/link-graph.xtarget
@@ -0,0 +1,14 @@
+{
+{%- for model in proto.messages %}
+{%- if model.links %}
+    "{{ model.name }}": [
+        {% for l in model.links -%}
+            ["{{ l.peer }}", "{{ l.src_port }}", "{{ l.dst_port }}"]{% if not loop.last %},{% endif %}
+        {%- endfor %}
+        {%- if model.rlinks %},{% endif %}        
+        {% for l in model.rlinks -%}
+            ["{{ l.peer }}", "{{ l.src_port }}", "{{ l.dst_port }}"]{% if not loop.last %},{% endif %}
+        {%- endfor %}
+    ],{% endif -%}
+{% endfor %}
+}
diff --git a/lib/xos-genx/xosgenx/targets/model-deps-graphviz.xtarget b/lib/xos-genx/xosgenx/targets/model-deps-graphviz.xtarget
new file mode 100644
index 0000000..b855a4a
--- /dev/null
+++ b/lib/xos-genx/xosgenx/targets/model-deps-graphviz.xtarget
@@ -0,0 +1,7 @@
+digraph {{ context.app_label }} {
+{%- for model in proto.messages %}
+    {%- for l in model.links %}
+        "{{ model.name }}" -> "{{ l.peer }}" [label="{{ l.src_port }}"];
+    {%- endfor %}
+{%- endfor %}
+}
diff --git a/lib/xos-genx/xosgenx/targets/model-deps.xtarget b/lib/xos-genx/xosgenx/targets/model-deps.xtarget
new file mode 100644
index 0000000..65a3742
--- /dev/null
+++ b/lib/xos-genx/xosgenx/targets/model-deps.xtarget
@@ -0,0 +1,8 @@
+{
+{%- for model in proto.messages %}
+{%- if model.links %}
+    "{{ model.name }}": [
+        {{ model.links | map(attribute='peer') | format_list("\"%s\"") |join(',\n\t\t') }}
+    ],{% endif -%}
+{% endfor %}
+}
diff --git a/lib/xos-genx/xosgenx/targets/modeldefs.xtarget b/lib/xos-genx/xosgenx/targets/modeldefs.xtarget
new file mode 100644
index 0000000..615a8cc
--- /dev/null
+++ b/lib/xos-genx/xosgenx/targets/modeldefs.xtarget
@@ -0,0 +1,37 @@
+items:
+{%- for m in proto.messages | sort(attribute='name') %}
+{%- if m.name != 'XOSBase' %}
+- app: {{ xproto_unquote(xproto_first_non_empty([m.options.app_label, context.app_label, options.app_label])) }}
+  fields: 
+  {%- set id_field = {'type':'int32', 'name':'id', 'options':{}} %}
+  {% for f in (xproto_base_fields(m, proto.message_table) + m.fields + [id_field]) | sort(attribute='name') -%}
+  {% if not f.link or f.options.link_type != 'manytomany' -%}
+  - hint: {% if f.options.help_text %}{{ xproto_unquote(f.options.help_text) }}{% else %}''{% endif %}
+    {% if not f.link -%}
+    name: {{ f.name }}
+    {%- else -%}
+    name: {{ f.name }}_id
+    relation: {model: {{ f.options.model }}, type: {{ f.options.link_type }}}
+    {% endif %}
+    type: {{ xproto_type_to_ui_type(f) }}
+    {% set validators = xproto_validators(f) -%}
+    {% if validators -%}
+    validators:
+    {% for v in validators | sort(attribute='name',reverse=True) -%}
+    - {{ v | yaml }}
+    {% endfor %}
+    {% else -%}
+    validators: []
+    {% endif %}
+  {% endif -%}
+  {% endfor %}
+  name: {{ m.name }}  
+  {%- set goodlinks = xproto_links_to_modeldef_relations( xproto_base_links(m, proto.message_table) + m.links ) %}
+  {% if goodlinks %}
+  relations:
+  {{ goodlinks | join('\n') | indent(width=2)}}
+  {%- else %}
+  relations: []
+  {%- endif %}
+{%- endif %}
+{% endfor -%} 
diff --git a/lib/xos-genx/xosgenx/targets/plurals.xtarget b/lib/xos-genx/xosgenx/targets/plurals.xtarget
new file mode 100644
index 0000000..9cdd6c6
--- /dev/null
+++ b/lib/xos-genx/xosgenx/targets/plurals.xtarget
@@ -0,0 +1,6 @@
+{% for m in proto.messages %}
+Model {{ m.name }}:
+{% for f in m.fields -%}
+The plural of {{ f.name}} is {{ xproto_pluralize(f) }}.
+{% endfor %}
+{%- endfor %}
diff --git a/lib/xos-genx/xosgenx/targets/proto.xtarget b/lib/xos-genx/xosgenx/targets/proto.xtarget
new file mode 100644
index 0000000..27b9490
--- /dev/null
+++ b/lib/xos-genx/xosgenx/targets/proto.xtarget
@@ -0,0 +1,8 @@
+{% for m in proto.messages %}
+message {{ m.name }} {
+  option bases = "{{ m.bases | map(attribute='name') | join(",") }}";
+  {%- for f in m.fields %}
+  {{ f.modifier }} {{f.type}} {{f.name}} = {{ f.id }}{% if f.options %} [{% for k,v in f.options.iteritems() %} {{ k }} = "{{ v}}"{% if not loop.last %},{% endif %} {% endfor %}]{% endif %};
+  {%- endfor %}
+}
+{% endfor %}
diff --git a/lib/xos-genx/xosgenx/targets/protoapi.xtarget b/lib/xos-genx/xosgenx/targets/protoapi.xtarget
new file mode 100644
index 0000000..f375dae
--- /dev/null
+++ b/lib/xos-genx/xosgenx/targets/protoapi.xtarget
@@ -0,0 +1,88 @@
+syntax = "proto3";
+
+package xos;
+
+import "google/protobuf/empty.proto";
+import "google/api/annotations.proto";
+import "common.proto";
+import "xosoptions.proto";
+
+// Note: all fields are wrapped in a "oneof". This causes proto3 to always send
+// fields that are set by the caller, regardless if they are set to a default
+// value. XOS uses this to know when to apply a default value.
+
+{% for object in proto.messages|sort(attribute='name') %}
+{% if object.name != 'XOSBase' -%}
+message {{ object.name }} {
+    {%- if object.name=='CordSubscriberRoot' %}
+    option (contentTypeId) = "rcord.{{ object.name | lower }}";
+    {%- else %}
+    option (contentTypeId) = "{{ xproto_unquote(xproto_first_non_empty([object.options.name, object.options.app_label, options.name, context.app_label])) }}.{{ object.name | lower }}";
+    {%- endif %}
+    {%- set id_field = {'type':'int32', 'name':'id', 'options':{}} -%}
+  {%- for field in (xproto_base_fields(object, proto.message_table) + object.fields + [id_field]) | sort(attribute='name')%}
+    oneof {{ field.name }}_present {
+      {{ xproto_api_type(field) }} {{ field.name }}{% if field.link -%}_id{% endif %} = {{ loop.index }}{{ xproto_api_opts(field) }};
+    }
+  {%- endfor -%}
+
+  {%- for ref in xproto_base_rlinks(object, proto.message_table) + object.rlinks | sort(attribute='src_port') %}
+  {%- if '+' not in ref.src_port and '+' not in ref.dst_port %}
+    repeated int32 {{ ref.src_port }}_ids  = {{ loop.index + 100 }} [(reverseForeignKey).modelName = "{{ ref.peer.name }}"];
+  {%- endif -%}
+  {%- endfor %}
+  string class_names = 201;
+  string self_content_type_id = 202;
+}
+
+message {{ xproto_pluralize(object) }} {
+    repeated {{ object.name }} items = 1;
+}
+
+{%- endif %}
+{% endfor %}
+
+service xos {
+{% for object in proto.messages | sort(attribute='name')%}
+{% if object.name != 'XOSBase' -%}
+  rpc List{{ object.name }}(google.protobuf.Empty) returns ({{ xproto_pluralize(object) }}) {
+        option (google.api.http) = {
+        {%- if object.name=='CordSubscriberRoot' %}
+            get: "/xosapi/v1/rcord/{{ xproto_pluralize(object) | lower }}"
+        {%- else %}
+            get: "/xosapi/v1/{{ xproto_unquote(xproto_first_non_empty([object.options.name, object.options.app_label, options.name, context.app_label])) }}/{{ xproto_pluralize(object) | lower }}"
+        {%- endif %}
+        };
+  }
+  rpc Filter{{ object.name }}(Query) returns ({{ xproto_pluralize(object) }}) {
+  }
+  rpc Get{{ object.name }}(ID) returns ({{ object.name }}) {
+        option (google.api.http) = {
+        {%- if object.name=='CordSubscriberRoot' %}
+            get: "/xosapi/v1/rcord/{{ xproto_pluralize(object) | lower }}/{id}"
+        {%- else %}
+            get: "/xosapi/v1/{{ xproto_unquote(xproto_first_non_empty([object.options.name, object.options.app_label, options.name, context.app_label])) }}/{{ xproto_pluralize(object) | lower }}/{id}"
+        {%- endif %}
+        };
+  }
+  rpc Create{{ object.name }}({{ object.name }}) returns ({{ object.name }}) {
+        option (google.api.http) = {
+            post: "/xosapi/v1/{{ xproto_unquote(xproto_first_non_empty([object.options.name, object.options.app_label, options.name, context.app_label])) }}/{{ xproto_pluralize(object) | lower }}"
+            body: "*"
+        };
+  }
+  rpc Update{{ object.name }}({{ object.name }}) returns ({{ object.name }}) {
+        option (google.api.http) = {
+            put: "/xosapi/v1/{{ xproto_unquote(xproto_first_non_empty([object.options.name, object.options.app_label, options.name, context.app_label])) }}/{{ xproto_pluralize(object) | lower }}/{id}"
+            body: "*"
+        };
+  }
+  rpc Delete{{ object.name }}(ID) returns (google.protobuf.Empty) {
+        option (google.api.http) = {
+            delete: "/xosapi/v1/{{ xproto_unquote(xproto_first_non_empty([object.options.name, object.options.app_label, options.name, context.app_label])) }}/{{ xproto_pluralize(object) | lower }}/{id}"
+        };
+  }
+{%- endif %}
+{% endfor %}
+}
+
diff --git a/lib/xos-genx/xosgenx/targets/service.xtarget b/lib/xos-genx/xosgenx/targets/service.xtarget
new file mode 100644
index 0000000..1ba3cff
--- /dev/null
+++ b/lib/xos-genx/xosgenx/targets/service.xtarget
@@ -0,0 +1,56 @@
+{% if options.legacy =='"True"' -%}
+{% set legacy_tag = '_decl' %}
+{% set legacy = True %}
+from core.models.xosbase import *
+{% else %}
+{% set legacy = False %}
+{% set legacy_tag = '' %}
+from header import *
+{% endif %}
+
+{% for m in proto.messages %}
+{% if file_exists(m.name|lower+'_header.py') -%}from {{m.name|lower }}_header import *{% endif %}
+{% if file_exists(m.name|lower+'_top.py') -%}{{ include_file(m.name|lower+'_top.py') }} {% endif %}
+
+{%- for l in m.links -%}{% set peer_name=l.peer.name %}
+{% if peer_name not in proto.message_names -%}
+from core.models import {{ peer_name }} 
+{%- endif -%}
+{%- endfor -%}
+{%- for b in m.bases -%}
+{%- if b.name!='XOSBase' and 'Mixin' not in b.name %}
+from core.models import {{ b.name }}
+{%- endif -%}
+{% endfor %}
+
+{% endfor %}
+
+{% for m in proto.messages %}
+class {{ m.name }}{{ legacy_tag }}{{ xproto_base_def(m.name, m.bases) }}:
+
+  KIND = {{ xproto_first_non_empty([m.options.kind, options.kind, options.name, "Set a kind in your xproto!"]) }}
+
+  class Meta:
+      app_label = {{ xproto_first_non_empty([m.options.app_label, options.app_label, options.name, "Set an app label in your xproto!"]) | lower}}
+      # name = {{ xproto_first_non_empty([m.options.name, options.name, "Set a name in your xproto!"]) }}
+      verbose_name = {{ xproto_first_non_empty([m.options.verbose_name, options.verbose_name, "Set a verbose name in your xproto!"]) }}
+
+  # 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 %}{% set peer_name=l.peer.name %}
+  {% if legacy and peer_name in proto.message_names %}{% set peer_tag = legacy_tag %}{% else %}{% set peer_tag = '' %}{% endif -%}
+  {{ l.src_port }} = {{ xproto_django_link_type(l) }}( {%- if peer_name==m.name -%}'self'{%- else -%}{{ peer_name }}{{ peer_tag }} {%- endif -%}, {{ xproto_django_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(m.name|lower+'_bottom.py') -%}{{ include_file(m.name|lower+'_bottom.py') }}{% endif %} 
+{% endfor %}
++++ models{{ legacy_tag }}.py
\ No newline at end of file
diff --git a/lib/xos-genx/xosgenx/targets/service_extender.xtarget b/lib/xos-genx/xosgenx/targets/service_extender.xtarget
new file mode 100644
index 0000000..5b4bc03
--- /dev/null
+++ b/lib/xos-genx/xosgenx/targets/service_extender.xtarget
@@ -0,0 +1,20 @@
+from core.models.xosbase import *
+{% for m in proto.messages %}
+from models_decl import {{ m.name }}_decl
+{%- endfor %}
+
+{% for m in proto.messages %}
+{% for l in m.links %}{% set peer_name=l.peer.name -%}
+{% if peer_name not in proto.message_names -%}
+from core.models import {{ peer_name }} 
+{% endif -%}
+{% endfor -%}
+{% endfor -%}
+
+{% for m in proto.messages %}
+class {{ m.name }}({{ m.name }}_decl):
+   class Meta:
+        proxy = True 
+
+{% endfor %}
++++ models.py
diff --git a/lib/xos-genx/xosgenx/targets/tosca.xtarget b/lib/xos-genx/xosgenx/targets/tosca.xtarget
new file mode 100644
index 0000000..b5e51ae
--- /dev/null
+++ b/lib/xos-genx/xosgenx/targets/tosca.xtarget
@@ -0,0 +1,31 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+node_types:
+{% for m in proto.messages %}
+    tosca.nodes.{{ m.name }}:
+        derived_from: tosca.nodes.Root
+        description: {{ m.name }}
+        properties:
+            no-delete:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to delete this object
+            no-create:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to create this object
+            no-update:
+                type: boolean
+                default: false
+                description: Do not allow Tosca to update this object
+            replaces:
+                type: string
+                required: false
+                descrption: Replaces/renames this object
+            {%- for f in m.fields %}
+            {{ f.name }}:
+                type: {{ f.type }}
+                required: {{ xproto_tosca_required(f.options.blank) }}
+                description: {{ f.options.help_text }}
+            {%- endfor %}
+{%- endfor %}
\ No newline at end of file
diff --git a/lib/xos-genx/xosgenx/targets/xproto.xtarget b/lib/xos-genx/xosgenx/targets/xproto.xtarget
new file mode 100644
index 0000000..513d7a7
--- /dev/null
+++ b/lib/xos-genx/xosgenx/targets/xproto.xtarget
@@ -0,0 +1,18 @@
+{% for k,v in options.iteritems() %}
+option {{ k }} = {{ v }};
+{% endfor %}
+
+{% for m in proto.messages %}
+message {{ m.name }} ({{ m.bases | join(",") }}){
+  {%- for f in m.fields %}
+  {% if not f.link %}
+  {{ f.modifier }} {{f.type}} {{f.name}} = {{ f.id }}{% if f.options %} [{% for k,v in f.options.iteritems() %} {{ k }} = {{ v }}{% if not loop.last %},{% endif %} {% endfor %}]{% endif %};
+  {% endif %}
+  {%- endfor %}
+  
+  {%- for l in m.links %}{% set peer_name=l.peer.name %}
+  {{ l }}
+  {{ l.modifier }} {{ l.link_type }} {{ l.name }} -> {{ l.peer.name }}:{{ l.dst_port }} = 1 {% if l.options %} [{% for k,v in l.options.iteritems() %} {{ k }} = {{ v }}{% if not loop.last %},{% endif %} {% endfor %}]{% endif %};
+  {%- endfor -%}
+}
+{% endfor %}