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