CORD-879 eliminate proxy models in vtr service

Change-Id: Iab26025c87b18c26c83b5857138a17fc3b04b10d
diff --git a/xos/admin.py b/xos/admin.py
index 4e7de44..24bad70 100644
--- a/xos/admin.py
+++ b/xos/admin.py
@@ -19,6 +19,7 @@
 from django.contrib.admin.views.main import ChangeList
 from django.core.urlresolvers import reverse
 from django.contrib.admin.utils import quote
+from django.contrib.contenttypes.models import ContentType
 
 class VTRServiceAdmin(ReadOnlyAwareAdmin):
     model = VTRService
@@ -48,42 +49,23 @@
         return VTRService.get_service_objects_by_user(request.user)
 
 class VTRTenantForm(forms.ModelForm):
-    test = forms.ChoiceField(choices=VTRTenant.TEST_CHOICES, required=True)
-    scope = forms.ChoiceField(choices=VTRTenant.SCOPE_CHOICES, required=True)
-    argument = forms.CharField(required=False)
-    result_code = forms.CharField(required=False)
-    result = forms.CharField(required=False, widget=forms.Textarea(attrs={'rows': 10, 'cols': 80, 'class': 'input-xxlarge'}))
     target = forms.ModelChoiceField(queryset=CordSubscriberRoot.objects.all())
 
     def __init__(self,*args,**kwargs):
         super (VTRTenantForm,self ).__init__(*args,**kwargs)
         self.fields['provider_service'].queryset = VTRService.get_service_objects().all()
         if self.instance:
-            # fields for the attributes
-            self.fields['test'].initial = self.instance.test
-            self.fields['argument'].initial = self.instance.argument
-            self.fields['target'].initial = self.instance.target
-            self.fields['scope'].initial = self.instance.scope
-            if (self.instance.enacted is not None) and (self.instance.enacted >= self.instance.updated):
-                self.fields['result'].initial = self.instance.result
-                self.fields['result_code'].initial = self.instance.result_code
-            else:
-                self.fields['result'].initial = ""
-                self.fields['result_code'].initial= ""
+            if self.instance.target_id:
+                self.fields["target"].initial = CordSubscriberRoot.objects.get(id=self.instance.target_id)
         if (not self.instance) or (not self.instance.pk):
-            # default fields for an 'add' form
             self.fields['kind'].initial = VTR_KIND
-            self.fields["scope"].initial = VTRTenant.get_default_attribute("scope")
             if VTRService.get_service_objects().exists():
                self.fields["provider_service"].initial = VTRService.get_service_objects().all()[0]
 
     def save(self, commit=True):
-        self.instance.test = self.cleaned_data.get("test")
-        self.instance.argument = self.cleaned_data.get("argument")
-        self.instance.target = self.cleaned_data.get("target")
-        self.instance.result = self.cleaned_data.get("result")
-        self.instance.result_code = self.cleaned_data.get("result_code")
-        self.instance.scope = self.cleaned_data.get("scope")
+        if self.cleaned_data.get("target"):
+            self.instance.target_type = ContentType.objects.get_for_model(CordSubscriberRoot)
+            self.instance.target_id = self.cleaned_data.get("target").id
         return super(VTRTenantForm, self).save(commit=commit)
 
     class Meta:
@@ -93,7 +75,7 @@
 class VTRTenantAdmin(ReadOnlyAwareAdmin):
     list_display = ('backend_status_icon', 'id', 'target', 'test', 'argument' )
     list_display_links = ('backend_status_icon', 'id')
-    fieldsets = [ (None, {'fields': ['backend_status_text', 'kind', 'provider_service', # 'subscriber_root', 'service_specific_id', 'service_specific_attribute',
+    fieldsets = [ (None, {'fields': ['backend_status_text', 'kind', 'provider_service',
                                      'target', 'scope', 'test', 'argument', 'is_synced', 'result_code', 'result'],
                           'classes':['suit-tab suit-tab-general']})]
     readonly_fields = ('backend_status_text', 'service_specific_attribute', 'is_synced')
diff --git a/xos/api/tenant/truckroll.py b/xos/api/tenant/truckroll.py
index cc8d62b..ac781bf 100644
--- a/xos/api/tenant/truckroll.py
+++ b/xos/api/tenant/truckroll.py
@@ -7,8 +7,10 @@
 from core.models import *
 from django.forms import widgets
 from services.vtr.models import VTRTenant, VTRService
+from services.volt.models import CordSubscriberRoot
 from xos.apibase import XOSListCreateAPIView, XOSRetrieveUpdateDestroyAPIView, XOSPermissionDenied
 from api.xosapi_helpers import PlusModelSerializer, XOSViewSet, ReadOnlyField
+from django.contrib.contenttypes.models import ContentType
 
 def get_default_vtr_service():
     vtr_services = VTRService.get_service_objects().all()
@@ -45,6 +47,11 @@
         def isSynced(self, obj):
             return (obj.enacted is not None) and (obj.enacted >= obj.updated)
 
+        def create(self, validated_data):
+            # force the target_type to be CordSubscriberRoot
+            validated_data["target_type_id"] = ContentType.objects.get_for_model(CordSubscriberRoot).id
+            return super(VTRTenantSerializer, self).create(validated_data)
+
 class TruckRollViewSet(XOSViewSet):
     base_name = "truckroll"
     method_name = "truckroll"
diff --git a/xos/models.py b/xos/models.py
index ce2e345..f3f9f2b 100644
--- a/xos/models.py
+++ b/xos/models.py
@@ -12,6 +12,8 @@
 import traceback
 from xos.exceptions import *
 from xos.config import Config
+from django.contrib.contenttypes.models import ContentType
+from django.contrib.contenttypes.fields import GenericForeignKey
 
 class ConfigurationError(Exception):
     pass
@@ -29,24 +31,24 @@
 
     class Meta:
         app_label = "vtr"
-        verbose_name = "vTR Service"
-        proxy = True
 
 class VTRTenant(Tenant):
-    class Meta:
-        proxy = True
-
     KIND = VTR_KIND
 
+    class Meta:
+        app_label = "vtr"
+
     TEST_CHOICES = ( ("ping", "Ping"), ("traceroute", "Trace Route"), ("tcpdump", "Tcp Dump") )
     SCOPE_CHOICES = ( ("container", "Container"), ("vm", "VM") )
 
-    simple_attributes = ( ("test", None),
-                          ("argument", None),
-                          ("result", None),
-                          ("result_code", None),
-                          ("target_id", None),
-                          ("scope", "container") )
+    test = StrippedCharField(help_text="type of test", max_length=30, choices=TEST_CHOICES, null=False, blank=False)
+    scope = StrippedCharField(help_text="scope of test", max_length=30, choices=SCOPE_CHOICES, null=False, blank=False)
+    argument = StrippedCharField(max_length=40, null=True, blank=True)
+    result = models.TextField(blank=True, null=True)
+    result_code = StrippedCharField(max_length=32, blank=True, null=True)
+    target_type = models.ForeignKey(ContentType)
+    target_id = models.PositiveIntegerField()
+    target = GenericForeignKey("target_type", "target_id")
 
     sync_attributes = ( 'test', 'argument', "scope" )
 
@@ -56,34 +58,9 @@
             self._meta.get_field("provider_service").default = vtr_services[0].id
         super(VTRTenant, self).__init__(*args, **kwargs)
 
-    @property
-    def target(self):
-        if getattr(self, "cached_target", None):
-            return self.cached_target
-        target_id=self.target_id
-        if not target_id:
-            return None
-        users=CordSubscriberRoot.objects.filter(id=target_id)
-        if not users:
-            return None
-        user=users[0]
-        self.cached_target = users[0]
-        return user
-
-    @target.setter
-    def target(self, value):
-        if value:
-            value = value.id
-        if (value != self.get_attribute("target_id", None)):
-            self.cached_target=None
-        self.target_id = value
-
     def save(self, *args, **kwargs):
         super(VTRTenant, self).save(*args, **kwargs)
 
     def delete(self, *args, **kwargs):
         super(VTRTenant, self).delete(*args, **kwargs)
 
-
-VTRTenant.setup_simple_attributes()
-
diff --git a/xos/synchronizer/steps/sync_vtrtenant.py b/xos/synchronizer/steps/sync_vtrtenant.py
index b983689..8cabfb1 100644
--- a/xos/synchronizer/steps/sync_vtrtenant.py
+++ b/xos/synchronizer/steps/sync_vtrtenant.py
@@ -11,6 +11,7 @@
 from core.models import Service, Slice, Tag
 from services.vsg.models import VSGService, VCPE_KIND
 from services.vtr.models import VTRService, VTRTenant
+from services.volt.models import CordSubscriberRoot
 from xos.logger import Logger, logging
 
 # hpclibrary will be in steps/..
@@ -31,37 +32,39 @@
     def __init__(self, *args, **kwargs):
         super(SyncVTRTenant, self).__init__(*args, **kwargs)
 
-    def fetch_pending(self, deleted):
-        if (not deleted):
-            objs = VTRTenant.get_tenant_objects().filter(Q(enacted__lt=F('updated')) | Q(enacted=None),Q(lazy_blocked=False))
-        else:
-            objs = VTRTenant.get_deleted_tenant_objects()
-
-        return objs
-
     def get_vtr_service(self, o):
         if not o.provider_service:
             return None
 
-        vtrs = VTRService.get_service_objects().filter(id=o.provider_service.id)
+        vtrs = VTRService.objects.filter(id=o.provider_service.id)
         if not vtrs:
             return None
 
         return vtrs[0]
 
+    def get_target(self, o):
+        target = o.target
+        if target:
+            # CordSubscriberRoot is a Proxy object, and o.target will point to
+            # the base class... so fix it up.
+            if target.__class__.__name__ == "TenantRoot":
+                target = CordSubscriberRoot.objects.get(id=target.id)
+            return target
+        return None
+
     def get_vcpe_service(self, o):
-        if o.target:
-            # o.target is a CordSubscriberRoot
-            if o.target.volt and o.target.volt.vcpe:
-                vcpes = VSGService.get_service_objects().filter(id=o.target.volt.vcpe.provider_service.id)
-                if not vcpes:
-                    return None
-                return vcpes[0]
+        target = self.get_target(o)
+        if target and target.volt and target.volt.vcpe:
+            vcpes = VSGService.get_service_objects().filter(id=target.volt.vcpe.provider_service.id)
+            if not vcpes:
+                return None
+            return vcpes[0]
         return None
 
     def get_instance(self, o):
-        if o.target and o.target.volt and o.target.volt.vcpe:
-            return o.target.volt.vcpe.instance
+        target = self.get_target(o)
+        if target and target.volt and target.volt.vcpe:
+            return target.volt.vcpe.instance
         else:
             return None
 
@@ -86,11 +89,13 @@
         if not instance:
             raise Exception("No instance")
 
+        target = self.get_target(o)
+
         s_tags = []
         c_tags = []
-        if o.target and o.target.volt:
-            s_tags.append(o.target.volt.s_tag)
-            c_tags.append(o.target.volt.c_tag)
+        if target and target.volt:
+            s_tags.append(target.volt.s_tag)
+            c_tags.append(target.volt.c_tag)
 
         fields = {"s_tags": s_tags,
                 "c_tags": c_tags,
@@ -103,14 +108,14 @@
 
         # add in the sync_attributes that come from the vSG object
         # this will be wan_ip, wan_mac, wan_container_ip, wan_container_mac, ...
-        if o.target and o.target.volt and o.target.volt.vcpe:
-            for attribute_name in o.target.volt.vcpe.sync_attributes:
-                fields[attribute_name] = getattr(o.target.volt.vcpe, attribute_name)
+        if target and target.volt and target.volt.vcpe:
+            for attribute_name in target.volt.vcpe.sync_attributes:
+                fields[attribute_name] = getattr(target.volt.vcpe, attribute_name)
 
         # add in the sync_attributes that come from the SubscriberRoot object
-        if o.target and hasattr(o.target, "sync_attributes"):
-            for attribute_name in o.target.sync_attributes:
-                fields[attribute_name] = getattr(o.target, attribute_name)
+        if target and hasattr(target, "sync_attributes"):
+            for attribute_name in target.sync_attributes:
+                fields[attribute_name] = getattr(target, attribute_name)
 
         for attribute_name in o.sync_attributes:
             fields[attribute_name] = getattr(o,attribute_name)
diff --git a/xos/tosca/custom_types/macros.m4 b/xos/tosca/custom_types/macros.m4
new file mode 100644
index 0000000..1f48f10
--- /dev/null
+++ b/xos/tosca/custom_types/macros.m4
@@ -0,0 +1,84 @@
+# Note: Tosca derived_from isn't working the way I think it should, it's not
+#    inheriting from the parent template. Until we get that figured out, use
+#    m4 macros do our inheritance
+
+define(xos_base_props,
+            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)
+# Service
+define(xos_base_service_caps,
+            scalable:
+                type: tosca.capabilities.Scalable
+            service:
+                type: tosca.capabilities.xos.Service)
+define(xos_base_service_props,
+            kind:
+                type: string
+                default: generic
+                description: Type of service.
+            view_url:
+                type: string
+                required: false
+                description: URL to follow when icon is clicked in the Service Directory.
+            icon_url:
+                type: string
+                required: false
+                description: ICON to display in the Service Directory.
+            enabled:
+                type: boolean
+                default: true
+            published:
+                type: boolean
+                default: true
+                description: If True then display this Service in the Service Directory.
+            public_key:
+                type: string
+                required: false
+                description: Public key to install into Instances to allows Services to SSH into them.
+            private_key_fn:
+                type: string
+                required: false
+                description: Location of private key file
+            versionNumber:
+                type: string
+                required: false
+                description: Version number of Service.)
+# Subscriber
+define(xos_base_subscriber_caps,
+            subscriber:
+                type: tosca.capabilities.xos.Subscriber)
+define(xos_base_subscriber_props,
+            kind:
+                type: string
+                default: generic
+                description: Kind of subscriber
+            service_specific_id:
+                type: string
+                required: false
+                description: Service specific ID opaque to XOS but meaningful to service)
+define(xos_base_tenant_props,
+            kind:
+                type: string
+                default: generic
+                description: Kind of tenant
+            service_specific_id:
+                type: string
+                required: false
+                description: Service specific ID opaque to XOS but meaningful to service)
+
+# end m4 macros
+
diff --git a/xos/tosca/custom_types/vtr.m4 b/xos/tosca/custom_types/vtr.m4
new file mode 100644
index 0000000..86a3def
--- /dev/null
+++ b/xos/tosca/custom_types/vtr.m4
@@ -0,0 +1,18 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+# compile this with "m4 vtr.m4 > vtr.yaml"
+
+# include macros
+include(macros.m4)
+
+node_types:
+    tosca.nodes.VTRService:
+        derived_from: tosca.nodes.Root
+        description: >
+            VTR Service
+        capabilities:
+            xos_base_service_caps
+        properties:
+            xos_base_props
+            xos_base_service_props
+
diff --git a/xos/tosca/custom_types/vtr.yaml b/xos/tosca/custom_types/vtr.yaml
new file mode 100644
index 0000000..0a441de
--- /dev/null
+++ b/xos/tosca/custom_types/vtr.yaml
@@ -0,0 +1,81 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+# compile this with "m4 vtr.m4 > vtr.yaml"
+
+# include macros
+# Note: Tosca derived_from isn't working the way I think it should, it's not
+#    inheriting from the parent template. Until we get that figured out, use
+#    m4 macros do our inheritance
+
+
+# Service
+
+
+# Subscriber
+
+
+
+
+# end m4 macros
+
+
+
+node_types:
+    tosca.nodes.VTRService:
+        derived_from: tosca.nodes.Root
+        description: >
+            VTR Service
+        capabilities:
+            scalable:
+                type: tosca.capabilities.Scalable
+            service:
+                type: tosca.capabilities.xos.Service
+        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
+            kind:
+                type: string
+                default: generic
+                description: Type of service.
+            view_url:
+                type: string
+                required: false
+                description: URL to follow when icon is clicked in the Service Directory.
+            icon_url:
+                type: string
+                required: false
+                description: ICON to display in the Service Directory.
+            enabled:
+                type: boolean
+                default: true
+            published:
+                type: boolean
+                default: true
+                description: If True then display this Service in the Service Directory.
+            public_key:
+                type: string
+                required: false
+                description: Public key to install into Instances to allows Services to SSH into them.
+            private_key_fn:
+                type: string
+                required: false
+                description: Location of private key file
+            versionNumber:
+                type: string
+                required: false
+                description: Version number of Service.
+
diff --git a/xos/tosca/resources/vtrservice.py b/xos/tosca/resources/vtrservice.py
new file mode 100644
index 0000000..352796e
--- /dev/null
+++ b/xos/tosca/resources/vtrservice.py
@@ -0,0 +1,9 @@
+from xosresource import XOSResource
+from service import XOSService
+from core.models import Service, User, CoarseTenant
+from services.vtr.models import VTRService
+
+class XOSVTRService(XOSService):
+    provides = "tosca.nodes.VTRService"
+    xos_model = VTRService
+
diff --git a/xos/vtr-onboard.yaml b/xos/vtr-onboard.yaml
index 0110aad..880faa1 100644
--- a/xos/vtr-onboard.yaml
+++ b/xos/vtr-onboard.yaml
@@ -18,6 +18,8 @@
           admin_template: templates/vtradmin.html
           synchronizer: synchronizer/manifest
           synchronizer_run: vtr-synchronizer.py
+          tosca_custom_types: tosca/custom_types/vtr.yaml
+          tosca_resource: tosca/resources/vtrservice.py
           rest_tenant: api/tenant/truckroll.py
           private_key: file:///opt/xos/key_import/vsg_rsa
           public_key: file:///opt/xos/key_import/vsg_rsa.pub