Merge branch 'master' of https://github.com/open-cloud/xos into feature/contentProvider
diff --git a/synchronizers b/synchronizers
new file mode 120000
index 0000000..d587898
--- /dev/null
+++ b/synchronizers
@@ -0,0 +1 @@
+observers
\ No newline at end of file
diff --git a/xos/apigen/hpc-api.template.py b/xos/apigen/hpc-api.template.py
new file mode 100644
index 0000000..291403a
--- /dev/null
+++ b/xos/apigen/hpc-api.template.py
@@ -0,0 +1,211 @@
+from rest_framework.decorators import api_view
+from rest_framework.response import Response
+from rest_framework.reverse import reverse
+from rest_framework import serializers
+from rest_framework import generics
+from rest_framework import status
+from rest_framework.generics import GenericAPIView
+from hpc.models import *
+from django.forms import widgets
+from rest_framework import filters
+from django.conf.urls import patterns, url
+from rest_framework.exceptions import PermissionDenied as RestFrameworkPermissionDenied
+from django.core.exceptions import PermissionDenied as DjangoPermissionDenied
+from apibase import XOSRetrieveUpdateDestroyAPIView, XOSListCreateAPIView, XOSNotAuthenticated
+
+if hasattr(serializers, "ReadOnlyField"):
+    # rest_framework 3.x
+    IdField = serializers.ReadOnlyField
+else:
+    # rest_framework 2.x
+    IdField = serializers.Field
+
+"""
+    Schema of the generator object:
+        all: Set of all Model objects
+        all_if(regex): Set of Model objects that match regex
+
+    Model object:
+        plural: English plural of object name
+        camel: CamelCase version of object name
+        refs: list of references to other Model objects
+        props: list of properties minus refs
+
+    TODO: Deal with subnets
+"""
+
+def get_hpc_REST_patterns():
+    return patterns('',
+        url(r'^hpcapi/$', hpc_api_root),
+    {% for object in generator.all %}
+        url(r'hpcapi/{{ object.rest_name }}/$', {{ object.camel }}List.as_view(), name='{{ object.singular }}-list'),
+        url(r'hpcapi/{{ object.rest_name }}/(?P<pk>[a-zA-Z0-9\-]+)/$', {{ object.camel }}Detail.as_view(), name ='{{ object.singular }}-detail'),
+    {% endfor %}
+    )
+
+@api_view(['GET'])
+def hpc_api_root(request, format=None):
+    return Response({
+        {% for object in generator.all %}'{{ object.plural }}': reverse('{{ object }}-list', request=request, format=format),
+        {% endfor %}
+    })
+
+# Based on serializers.py
+
+class XOSModelSerializer(serializers.ModelSerializer):
+    def save_object(self, obj, **kwargs):
+
+        """ rest_framework can't deal with ManyToMany relations that have a
+            through table. In xos, most of the through tables we have
+            use defaults or blank fields, so there's no reason why we shouldn't
+            be able to save these objects.
+
+            So, let's strip out these m2m relations, and deal with them ourself.
+        """
+        obj._complex_m2m_data={};
+        if getattr(obj, '_m2m_data', None):
+            for relatedObject in obj._meta.get_all_related_many_to_many_objects():
+                if (relatedObject.field.rel.through._meta.auto_created):
+                    # These are non-trough ManyToMany relations and
+                    # can be updated just fine
+                    continue
+                fieldName = relatedObject.get_accessor_name()
+                if fieldName in obj._m2m_data.keys():
+                    obj._complex_m2m_data[fieldName] = (relatedObject, obj._m2m_data[fieldName])
+                    del obj._m2m_data[fieldName]
+
+        serializers.ModelSerializer.save_object(self, obj, **kwargs);
+
+        for (accessor, stuff) in obj._complex_m2m_data.items():
+            (relatedObject, data) = stuff
+            through = relatedObject.field.rel.through
+            local_fieldName = relatedObject.field.m2m_reverse_field_name()
+            remote_fieldName = relatedObject.field.m2m_field_name()
+
+            # get the current set of existing relations
+            existing = through.objects.filter(**{local_fieldName: obj});
+
+            data_ids = [item.id for item in data]
+            existing_ids = [getattr(item,remote_fieldName).id for item in existing]
+
+            #print "data_ids", data_ids
+            #print "existing_ids", existing_ids
+
+            # remove relations that are in 'existing' but not in 'data'
+            for item in list(existing):
+               if (getattr(item,remote_fieldName).id not in data_ids):
+                   print "delete", getattr(item,remote_fieldName)
+                   item.delete() #(purge=True)
+
+            # add relations that are in 'data' but not in 'existing'
+            for item in data:
+               if (item.id not in existing_ids):
+                   #print "add", item
+                   newModel = through(**{local_fieldName: obj, remote_fieldName: item})
+                   newModel.save()
+
+{% for object in generator.all %}
+
+class {{ object.camel }}Serializer(serializers.HyperlinkedModelSerializer):
+    id = IdField()
+    {% for ref in object.refs %}
+    {% if ref.multi %}
+    {{ ref.plural }} = serializers.HyperlinkedRelatedField(many=True, read_only=True, view_name='{{ ref }}-detail')
+    {% else %}
+    {{ ref }} = serializers.HyperlinkedRelatedField(read_only=True, view_name='{{ ref }}-detail')
+    {% endif %}
+    {% endfor %}
+    humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+    validators = serializers.SerializerMethodField("getValidators")
+    def getHumanReadableName(self, obj):
+        return str(obj)
+    def getValidators(self, obj):
+        try:
+            return obj.getValidators()
+        except:
+            return None
+    class Meta:
+        model = {{ object.camel }}
+        fields = ('humanReadableName', 'validators', {% for prop in object.props %}'{{ prop }}',{% endfor %}{% for ref in object.refs %}{%if ref.multi %}'{{ ref.plural }}'{% else %}'{{ ref }}'{% endif %},{% endfor %})
+
+class {{ object.camel }}IdSerializer(XOSModelSerializer):
+    id = IdField()
+    {% for ref in object.refs %}
+    {% if ref.multi %}
+    {{ ref.plural }} = serializers.PrimaryKeyRelatedField(many=True,  queryset = {{ ref.camel }}.objects.all())
+    {% else %}
+    {{ ref }} = serializers.PrimaryKeyRelatedField( queryset = {{ ref.camel }}.objects.all())
+    {% endif %}
+    {% endfor %}
+    humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+    validators = serializers.SerializerMethodField("getValidators")
+    def getHumanReadableName(self, obj):
+        return str(obj)
+    def getValidators(self, obj):
+        try:
+            return obj.getValidators()
+        except:
+            return None
+    class Meta:
+        model = {{ object.camel }}
+        fields = ('humanReadableName', 'validators', {% for prop in object.props %}'{{ prop }}',{% endfor %}{% for ref in object.refs %}{%if ref.multi %}'{{ ref.plural }}'{% else %}'{{ ref }}'{% endif %},{% endfor %})
+
+
+{% endfor %}
+
+serializerLookUp = {
+{% for object in generator.all %}
+                 {{ object.camel }}: {{ object.camel }}Serializer,
+{% endfor %}
+                 None: None,
+                }
+
+# Based on core/views/*.py
+{% for object in generator.all %}
+
+class {{ object.camel }}List(XOSListCreateAPIView):
+    queryset = {{ object.camel }}.objects.select_related().all()
+    serializer_class = {{ object.camel }}Serializer
+    id_serializer_class = {{ object.camel }}IdSerializer
+    filter_backends = (filters.DjangoFilterBackend,)
+    filter_fields = ({% for prop in object.props %}'{{ prop }}',{% endfor %}{% for ref in object.refs %}{%if ref.multi %}'{{ ref.plural }}'{% else %}'{{ ref }}'{% endif %},{% endfor %})
+
+    def get_serializer_class(self):
+        no_hyperlinks=False
+        if hasattr(self.request,"QUERY_PARAMS"):
+            no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
+        if (no_hyperlinks):
+            return self.id_serializer_class
+        else:
+            return self.serializer_class
+
+    def get_queryset(self):
+        if (not self.request.user.is_authenticated()):
+            raise XOSNotAuthenticated()
+        return {{ object.camel }}.select_by_user(self.request.user)
+
+
+class {{ object.camel }}Detail(XOSRetrieveUpdateDestroyAPIView):
+    queryset = {{ object.camel }}.objects.select_related().all()
+    serializer_class = {{ object.camel }}Serializer
+    id_serializer_class = {{ object.camel }}IdSerializer
+
+    def get_serializer_class(self):
+        no_hyperlinks=False
+        if hasattr(self.request,"QUERY_PARAMS"):
+            no_hyperlinks = self.request.QUERY_PARAMS.get('no_hyperlinks', False)
+        if (no_hyperlinks):
+            return self.id_serializer_class
+        else:
+            return self.serializer_class
+
+    def get_queryset(self):
+        if (not self.request.user.is_authenticated()):
+            raise XOSNotAuthenticated()
+        return {{ object.camel }}.select_by_user(self.request.user)
+
+    # update() is handled by XOSRetrieveUpdateDestroyAPIView
+
+    # destroy() is handled by XOSRetrieveUpdateDestroyAPIView
+
+{% endfor %}
diff --git a/xos/configurations/cord/README.md b/xos/configurations/cord/README.md
index 5683601..423f4d6 100644
--- a/xos/configurations/cord/README.md
+++ b/xos/configurations/cord/README.md
@@ -38,9 +38,20 @@
 
 ## How to run it
 
-The configuration is intended to be run on [CloudLab](http://cloudlab.us), on the *ctl* node set up by the OpenStack profile.
+The configuration is intended to be run on [CloudLab](http://cloudlab.us).
 It launches an XOS container on Cloudlab that runs the XOS develserver.  The container is left running in the background.
 
+To get started on CloudLab:
+* Create an experiment using the *OpenStack-CORD* profile.  (You can also use the *OpenStack* profile, but choose *Kilo*
+and disable security groups.)
+* Wait until you get an email from CloudLab with title "OpenStack Instance Finished Setting Up".
+* Login to the *ctl* node of your experiment and run:
+```
+$ git clone https://github.com/open-cloud/xos.git
+$ cd xos/xos/configurations/cord/
+$ make
+```
+
 Running `make` in this directory creates the XOS Docker container and runs the TOSCA engine with `cord.yaml` to
 configure XOS with the CORD services.  In addition, a number of VMs are created:
 
@@ -50,10 +61,6 @@
 1. *Slice mysite_volt*: for running OvS with the `olt` app as controller
 1. *Slice mysite_clients*: a subscriber client for end-to-end testing
 
-After the first VM is created (for running the `virtualbng` app) it is necessary to configure XOS's *service_vbng* with its URL.
-Log into XOS, click on *Services* tab at left, then *service_vbng* icon.  Change **Vbng url:** to point to the IP address on
-`flat-lan-1-net` of the VM (it will start with 10.11).
-
 Once all the VMs are up and the ONOS apps are configured, XOS should be able to get an address mapping from the `virtualbng`
 ONOS app when creating a vCPE.  To test this, enter the XOS Docker container and run:
 
diff --git a/xos/dmdot b/xos/dmdot
index 124c7cf..f438aad 100755
--- a/xos/dmdot
+++ b/xos/dmdot
@@ -22,7 +22,7 @@
     if arg.startswith("-"):
         output = arg
     else:
-        apps = [arg]
+        apps+= [arg]
 
 model_classes = []
 class_names = []
diff --git a/xos/helloworld/models.py b/xos/helloworld/models.py
index 37f6751..a657f3a 100644
--- a/xos/helloworld/models.py
+++ b/xos/helloworld/models.py
@@ -11,7 +11,7 @@
 
 class Hello(PlCoreBase):
     name = models.CharField(max_length=254,help_text="Salutation e.g. Hello or Bonjour")
-    sliver_backref = models.ForeignKey(Instance)
+    instance_backref = models.ForeignKey(Instance)
     
 class World(PlCoreBase):
     name = models.CharField(max_length=254,help_text="Name of planet")
diff --git a/xos/openstack_synchronizer b/xos/openstack_synchronizer
new file mode 120000
index 0000000..ae75af5
--- /dev/null
+++ b/xos/openstack_synchronizer
@@ -0,0 +1 @@
+openstack_observer
\ No newline at end of file