Support for ELAN, ETREE, and VNoD Global Service

Change-Id: Iea1ee5b5b9224404a16bc9b88bc082719f75d10f
diff --git a/xos/admin.py b/xos/admin.py
index 6aae88b..9f5a2b0 100644
--- a/xos/admin.py
+++ b/xos/admin.py
@@ -47,29 +47,65 @@
 class NetworkEdgeToEdgePointConnectionAdmin(XOSBaseAdmin):
     verbose_name = "Metro Network E-Line Service"
     verbose_name_plural = "Metro Network E-Line Services"
-    list_display = ('id', 'sid', 'type', 'uni1', 'uni2', 'adminstate', 'operstate')
-    list_display_links = ('id', 'sid', 'type', 'uni1', 'uni2', 'adminstate', 'operstate')
+    list_display = ('id', 'name', 'sid', 'type', 'vlanid', 'uni1', 'uni2', 'adminstate', 'operstate')
+    list_display_links = ('id', 'name', 'sid', 'type', 'vlanid', 'uni1', 'uni2', 'adminstate', 'operstate')
 
-    fields = ('id', 'sid', 'type', 'uni1', 'uni2', 'adminstate', 'operstate', 'backend_status')
-    readonly_fields = ('id', 'type', 'uni1', 'uni2', 'operstate', 'backend_status')
+    fields = ('id', 'name', 'sid', 'type', 'vlanid', 'uni1', 'uni2', 'adminstate', 'operstate', 'backend_status')
+    readonly_fields = ('id', 'sid', 'backend_status')
 
 class NetworkMultipointToMultipointConnectionAdmin(XOSBaseAdmin):
     verbose_name = "Metro Network E-LAN Service"
     verbose_name_plural = "Metro Network E-LAN Services"
-    list_display = ('id', 'sid', 'type', 'adminstate', 'operstate')
-    list_display_links = ('id', 'sid', 'type', 'adminstate', 'operstate')
+    list_display = ('id', 'name', 'sid', 'type', 'vlanid', 'adminstate', 'operstate')
+    list_display_links = ('id', 'name', 'sid', 'type', 'vlanid', 'adminstate', 'operstate')
 
-    fields = ('id', 'sid', 'type', 'eps', 'adminstate', 'operstate', 'backend_status')
-    readonly_fields = ('id', 'sid', 'type', 'eps', 'adminstate', 'operstate', 'backend_status')
+    fields = ('id', 'name', 'sid', 'type', 'vlanid', 'eps', 'adminstate', 'operstate', 'backend_status')
+    readonly_fields = ('id', 'sid', 'backend_status')
 
 class NetworkEdgeToMultipointConnectionAdmin(XOSBaseAdmin):
     verbose_name = "Metro Network E-Tree Service"
     verbose_name_plural = "Metro Network E-Tree Services"
-    list_display = ('id', 'sid', 'type', 'adminstate', 'operstate')
-    list_display_links = ('id', 'sid', 'type', 'adminstate', 'operstate')
+    list_display = ('id', 'name', 'sid', 'type', 'vlanid', 'adminstate', 'operstate')
+    list_display_links = ('id', 'name', 'sid', 'type', 'vlanid', 'adminstate', 'operstate')
 
-    fields = ('id', 'sid', 'type', 'root', 'eps', 'adminstate', 'operstate', 'backend_status')
-    readonly_fields = ('id', 'sid', 'type', 'root', 'eps', 'adminstate', 'operstate', 'backend_status')
+    fields = ('id', 'name', 'sid', 'type', 'vlanid', 'root', 'eps', 'adminstate', 'operstate', 'backend_status')
+    readonly_fields = ('id', 'sid', 'backend_status')
+
+class RemotePortAdmin(XOSBaseAdmin):
+    verbose_name = "Remote Port"
+    verbose_name_plural = "Remote Ports"
+    list_display = ('name', 'remoteportsite', 'edgeport')
+    list_display_links = ('name', 'remoteportsite', 'edgeport')
+
+    fields = ('name', 'remoteportsite', 'edgeport')
+
+class BandwidthProfileAdmin(XOSBaseAdmin):
+    verbose_name = "Bandwidth Profile"
+    verbose_name_plural = "Bandwidth Profiles"
+    list_display = ('bwpcfgcbs', 'bwpcfgebs', 'bwpcfgcir', 'bwpcfgeir', 'name')
+    list_display_links = ('bwpcfgcbs', 'bwpcfgebs', 'bwpcfgcir', 'bwpcfgeir', 'name')
+
+    fields = ('bwpcfgcbs', 'bwpcfgebs', 'bwpcfgcir', 'bwpcfgeir', 'name')
+
+class ServiceSpokeAdmin(XOSBaseAdmin):
+    verbose_name = "Service Spoke"
+    verbose_name_plural = "Service Spokes"
+    list_display = ('name','vnodlocalsite', 'remotesubscriber', 'adminstate', 'operstate', 'autoattached')
+    list_display_links = ('name','vnodlocalsite', 'remotesubscriber', 'adminstate', 'operstate', 'autoattached')
+
+    fields = ('name', 'id','vnodlocalsite', 'vnodlocalport', 'remotesubscriber', 'adminstate', 'operstate', 'backend_status', 'autoattached')
+    readonly_fields = ('id', 'remotesubscriber', 'adminstate', 'operstate', 'backend_status')
+
+class VnodGlobalServiceAdmin(XOSBaseAdmin):
+    verbose_name = "VNOD Global Service"
+    verbose_name_plural = "VNOD Global Services"
+    list_display = ('servicehandle', 'vlanid', 'type','operstate', 'adminstate')
+    list_display_links = ('servicehandle', 'vlanid', 'type','operstate', 'adminstate')
+
+    fields = (
+        'id', 'servicehandle', 'vlanid', 'type', 'metronetworkmultipoint', 'metronetworkpointtopoint', 'metronetworkroottomultipoint', 'operstate', 'adminstate', 'spokes', 'bandwidthProfile')
+    readonly_fields = (
+        'id', 'operstate', 'backend_status', 'metronetworkmultipoint', 'metronetworkpointtopoint', 'metronetworkroottomultipoint')
 
 admin.site.register(MetroNetworkSystem, MetroNetworkSystemAdmin)
 admin.site.register(NetworkDevice, NetworkDeviceAdmin)
@@ -77,3 +113,11 @@
 admin.site.register(NetworkEdgeToEdgePointConnection, NetworkEdgeToEdgePointConnectionAdmin)
 admin.site.register(NetworkMultipointToMultipointConnection, NetworkMultipointToMultipointConnectionAdmin)
 admin.site.register(NetworkEdgeToMultipointConnection, NetworkEdgeToMultipointConnectionAdmin)
+admin.site.register(BandwidthProfile, BandwidthProfileAdmin)
+admin.site.register(ServiceSpoke, ServiceSpokeAdmin)
+admin.site.register(VnodGlobalService, VnodGlobalServiceAdmin)
+admin.site.register(RemotePort, RemotePortAdmin)
+
+
+
+
diff --git a/xos/api/service/metronetworkservice/metronetworkservice.py b/xos/api/service/metronetworkservice/metronetworkservice.py
index 8e200ce..764205d 100644
--- a/xos/api/service/metronetworkservice/metronetworkservice.py
+++ b/xos/api/service/metronetworkservice/metronetworkservice.py
@@ -1,7 +1,11 @@
+from django.http import HttpResponseBadRequest
 from rest_framework.response import Response
+from rest_framework.views import APIView
 from rest_framework import serializers, filters, status
 from api.xosapi_helpers import PlusModelSerializer, XOSViewSet, ReadOnlyField
 from services.metronetwork.models import *
+from random import randint
+import json
 from django.core.exceptions import ObjectDoesNotExist
 from django.core import serializers as jsonserializer
 
@@ -18,7 +22,7 @@
                       'operationalState')
 
         def getHumanReadableName(self, obj):
-            return obj.__unicode__()
+            return obj.name
 
 class MetroNetworkSystemViewSet(XOSViewSet):
     base_name = "metronetworksystem"
@@ -143,7 +147,8 @@
         ELineConnection.sid = validated_data.data.get('sid')
         ELineConnection.adminstate = validated_data.data.get('adminstate')
         ELineConnection.operstate = validated_data.data.get('operstate')
-        ELineConnection.type = validated_data.data.get('type')
+        ELineConnection.sid = validated_data.data.get('sid')
+        ELineConnection.type = 'Point_To_Point'
 
         uni1 = validated_data.data.get('uni1')
         uni2 = validated_data.data.get('uni2')
@@ -244,7 +249,8 @@
         ETreeConnection.sid = validated_data.data.get('sid')
         ETreeConnection.adminstate = validated_data.data.get('adminstate')
         ETreeConnection.operstate = validated_data.data.get('operstate')
-        ETreeConnection.type = validated_data.data.get('type')
+        ETreeConnection.sid = validated_data.data.get('sid')
+        ETreeConnection.type = 'Root_Multipoint'
 
         root_id = validated_data.data.get('root')
         eps_list = validated_data.data.get('eps')
@@ -349,7 +355,8 @@
         ELanConnection.sid = validated_data.data.get('sid')
         ELanConnection.adminstate = validated_data.data.get('adminstate')
         ELanConnection.operstate = validated_data.data.get('operstate')
-        ELanConnection.type = validated_data.data.get('type')
+        ELanConnection.sid = validated_data.data.get('sid')
+        ELanConnection.type = 'Multipoint_To_Multipoint'
 
         eps_list = validated_data.data.get('eps')
         ELanConnection.save()
@@ -381,3 +388,455 @@
         response_data['eps'] = eps_data
 
         return Response(response_data)
+
+############################
+
+class BandwidthProfileSerializer(PlusModelSerializer):
+    humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+
+    class Meta:
+        model = BandwidthProfile
+
+        fields = ('humanReadableName',
+                  'id',
+                  'bwpcfgcbs',
+                  'bwpcfgebs',
+                  'bwpcfgcir',
+                  'bwpcfgeir',
+                  'name'
+                  )
+
+    def getHumanReadableName(self, obj):
+        return obj.name
+
+class BandwidthProfileViewSet(XOSViewSet):
+    base_name = "BANDWIDTH_PROFILE"
+    method_name = "BANDWIDTH_PROFILE"
+    method_kind = "viewset"
+    queryset = BandwidthProfile.objects.all()
+    serializer_class = BandwidthProfileSerializer
+
+    @classmethod
+    def get_urlpatterns(self, api_path="^"):
+        patterns = super(BandwidthProfileViewSet, self).get_urlpatterns(api_path=api_path)
+
+        return patterns
+
+    def list(self, request):
+
+        object_list = self.filter_queryset(self.get_queryset())
+
+        serializer = self.get_serializer(object_list, many=True)
+
+        return Response(serializer.data)
+
+    def create(self, validated_data):
+
+        bandwidthProfile = BandwidthProfile()
+        bandwidthProfile.name = validated_data.data.get('name')
+        bandwidthProfile.bwpcfgcbs = validated_data.data.get('bwpcfgcbs')
+        bandwidthProfile.bwpcfgebs = validated_data.data.get('bwpcfgebs')
+        bandwidthProfile.bwpcfgcir = validated_data.data.get('bwpcfgcir')
+        bandwidthProfile.bwpcfgeir = validated_data.data.get('bwpcfgeir')
+
+        bandwidthProfile.save()
+
+        response_data = {}
+        response_data['name'] = bandwidthProfile.name
+        response_data['bwpcfgcbs'] = bandwidthProfile.bwpcfgcbs
+        response_data['bwpcfgebs'] = bandwidthProfile.bwpcfgebs
+        response_data['bwpcfgcir'] = bandwidthProfile.bwpcfgeir
+        response_data['bwpcfgcir'] = bandwidthProfile.bwpcfgcir
+        response_data['id'] = bandwidthProfile.id
+
+        return Response(response_data)
+
+class VnodSiteSerializer(PlusModelSerializer):
+    humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+
+    class Meta:
+        model = Site
+
+        fields = ('humanReadableName',
+                  'site_url',
+                  'enabled',
+                  'longitude',
+                  'latitude',
+                  'name'
+                  )
+
+    def getHumanReadableName(self, obj):
+        return obj.name
+
+class RemotePortSerializer(PlusModelSerializer):
+    humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+    edgeport = NetworkEdgePortSerializer(read_only=True)
+    remoteportsite = VnodSiteSerializer(read_only=True)
+
+    class Meta:
+        model = RemotePort
+
+        fields = ('humanReadableName',
+                  'name',
+                  'edgeport',
+                  'id',
+                  'remoteportsite'
+                  )
+
+    def getHumanReadableName(self, obj):
+        return obj.name
+
+class ServiceSpokeSerializer(PlusModelSerializer):
+    humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+    vnodlocalsite = VnodSiteSerializer(read_only=True)
+    vnodlocalport = RemotePortSerializer(read_only=True)
+
+    class Meta:
+        model = ServiceSpoke
+
+        fields = ('humanReadableName',
+                  'id',
+                  'name',
+                  'remotesubscriber',
+                  'remotevnodid',
+                  'autoattached',
+                  'operstate',
+                  'vnodlocalsite',
+                  'vnodlocalport'
+                  )
+
+    def getHumanReadableName(self, obj):
+        return obj.name
+
+class VnodGlobalServiceSerializer(PlusModelSerializer):
+    humanReadableName = serializers.SerializerMethodField("getHumanReadableName")
+    metronetworkroottomultipoint = NetworkEdgeToMultipointConnectionSerializer(read_only=True)
+    metronetworkmultipoint = NetworkMultipointToMultipointConnectionSerializer(read_only=True)
+    metronetworkpointtopoint = NetworkEdgeToEdgePointConnectionSerializer(read_only=True)
+    spokes = ServiceSpokeSerializer(read_only=True, many=True)
+    bandwidthProfile = BandwidthProfileSerializer(read_only=True)
+
+    class Meta:
+        model = VnodGlobalService
+
+        fields = ('humanReadableName',
+                  'servicehandle',
+                  'vlanid',
+                  'id',
+                  'type',
+                  'operstate',
+                  'adminstate',
+                  'metronetworkroottomultipoint',
+                  'metronetworkmultipoint',
+                  'metronetworkpointtopoint',
+                  'spokes',
+                  'bandwidthProfile',
+                  'name'
+                  )
+
+    def getHumanReadableName(self, obj):
+        return obj.name
+
+class VnodGlobalServiceViewSet(XOSViewSet):
+    base_name = "VNOD_GLOBAL_SERVICE"
+    method_name = "VNOD_GLOBAL_SERVICE"
+    method_kind = "viewset"
+    queryset = VnodGlobalService.get_service_objects().all()
+    serializer_class = VnodGlobalServiceSerializer
+
+    @classmethod
+    def get_urlpatterns(self, api_path="^"):
+        patterns = super(VnodGlobalServiceViewSet, self).get_urlpatterns(api_path=api_path)
+
+        return patterns
+
+    def list(self, request):
+
+        object_list = self.filter_queryset(self.get_queryset())
+
+        serializer = self.get_serializer(object_list, many=True)
+
+        return Response(serializer.data)
+
+    def destroy(self, request, pk=None):
+        VnodGlobalServiceToDelete = VnodGlobalService.objects.get(pk=pk)
+
+        if (VnodGlobalServiceToDelete):
+            VnodGlobalServiceToDelete.adminstate = 'deactivationrequested'
+            VnodGlobalServiceToDelete.save()
+        else:
+            return Response(status=status.HTTP_400_BAD_REQUEST)
+
+        return Response(status=status.HTTP_200_OK)
+
+    def create(self, validated_data):
+
+        vnodGlobalService = VnodGlobalService()
+
+        vnodGlobalService.name = validated_data.data.get('name')
+
+        if VnodGlobalService.objects.filter(
+                name=vnodGlobalService.name).exists():
+            return HttpResponseBadRequest('Error: VnodGlobalService name \'' +  vnodGlobalService.name
+                            + '\' already exists.')
+
+        vnodGlobalService.servicehandle = validated_data.data.get('servicehandle')
+        vnodGlobalService.adminstate = 'enabled'
+        vnodGlobalService.operstate = 'inactive'
+        vnodGlobalService.type = validated_data.data.get('type')
+
+        vnodGlobalService.vlanid = self.getUniqueVlandId()
+
+        bandwidth_profile = validated_data.data.get('bandwidthProfile')
+        bandwidthprofile = BandwidthProfile.objects.get(pk=bandwidth_profile['id'])
+        if (bandwidth_profile):
+            vnodGlobalService.bandwidthProfile = bandwidthprofile
+
+        vnodGlobalService.save()
+
+        spokes_list = validated_data.data.get('spokes')
+        if (spokes_list is None):
+            vnodGlobalService.delete()
+            return HttpResponseBadRequest('Error: No spokes found in request.')
+
+        for spoke in spokes_list:
+            vnodlocalsite = Site.objects.get(pk=spoke['vnodlocalsite']['id'])
+            servicespoke = ServiceSpoke()
+            servicespoke.vnodlocalsite = vnodlocalsite
+            servicespoke.vnodlocalport = self.getRandomRemotePort(vnodlocalsite)
+            servicespoke.name = spoke['name']
+            if (spoke.get('autoattached')):
+                servicespoke.autoattached = spoke['autoattached']
+            servicespoke.operstate = 'inactive'
+            servicespoke.adminstate = 'disabled'
+            servicespoke.save()
+            vnodGlobalService.spokes.add(servicespoke)
+
+        serializer = self.get_serializer(vnodGlobalService)
+        return Response(serializer.data)
+
+    def getUniqueVlandId(self):
+        unique = False
+        while not unique:
+            vlanid = randint(1, 4095)
+            vnodglobalservice = VnodGlobalService.get_service_objects().filter(vlanid=vlanid)
+            if (not vnodglobalservice):
+                unique = True
+        return vlanid
+
+    def getRandomRemotePort(self, site):
+        remotePort = RemotePort.objects.get(remoteportsite__name=site.name)
+        if (remotePort):
+            return remotePort
+        return None
+
+    @classmethod
+    def calculateVnodGlobalOperState(self, servicehandle):
+        vnodglobalservice = VnodGlobalService.get_service_objects().filter(servicehandle=servicehandle)
+        if (not vnodglobalservice):
+            HttpResponseBadRequest('Error: Could not find VnodGlobalObject with servicehandle=' + servicehandle)
+
+        vnodglobalservice = vnodglobalservice[0]
+        all_spokes_active_and_enabled = True
+
+        for spoke in vnodglobalservice.spokes.all():
+            if (spoke.operstate != 'active' or spoke.adminstate != 'enabled'):
+                all_spokes_active_and_enabled = False
+                break;
+
+        if (all_spokes_active_and_enabled):
+            vnodglobalservice.operstate = 'active'
+        else:
+            vnodglobalservice.operstate = 'inactive'
+
+        vnodglobalservice.save()
+        return all_spokes_active_and_enabled
+
+    @classmethod
+    def createService(self, servicehandle):
+        vnodglobalservice = VnodGlobalService.get_service_objects().filter(servicehandle=servicehandle)
+        if (not vnodglobalservice):
+            HttpResponseBadRequest('Error: Could not find VnodGlobalObject with servicehandle=' + servicehandle)
+
+        vnodglobalservice = vnodglobalservice[0]
+        if (vnodglobalservice.type == 'eline'):
+
+            spokes = vnodglobalservice.spokes.all()
+            uni1 = spokes[0].vnodlocalport.edgeport
+            uni2 = spokes[1].vnodlocalport.edgeport
+            name = 'ELine-' + str(vnodglobalservice.id)
+            type = 'Point_To_Point'
+            operstate = 'active'
+            adminstate = 'activationrequested'
+
+            eline = NetworkEdgeToEdgePointConnection()
+            eline.name = name
+            eline.type = type
+            eline.operstate = operstate
+            eline.adminstate = adminstate
+            eline.vlanid = vnodglobalservice.vlanid
+            eline.sid = name
+            eline.uni1 = NetworkEdgePort.objects.get(pid=uni1.pid)
+            eline.uni2 = NetworkEdgePort.objects.get(pid=uni2.pid)
+
+            eline.save()
+            vnodglobalservice.metronetworkpointtopoint = eline
+            vnodglobalservice.save()
+
+        elif (vnodglobalservice.type == 'elan'):
+
+            spokes = vnodglobalservice.spokes.all()
+
+            name = 'ELAN-' + str(vnodglobalservice.id)
+            type = 'Multipoint_To_Multipoint'
+            operstate = 'active'
+            adminstate = 'activationrequested'
+
+            elan = NetworkMultipointToMultipointConnection()
+            elan.name = name
+            elan.type = type
+            elan.operstate = 'inactive'
+            elan.adminstate = 'disabled'
+            elan.vlanid = vnodglobalservice.vlanid
+            elan.save()
+
+            for spoke in spokes:
+                uni = NetworkEdgePort.objects.get(pid=spoke.vnodlocalport.edgeport.pid)
+                elan.eps.add(uni)
+
+            elan.operstate = operstate
+            elan.adminstate = adminstate
+            elan.save()
+
+            vnodglobalservice.metronetworkmultipoint = elan
+            vnodglobalservice.save()
+
+        # TODO: elif (vnodglobalservice.type == 'etree'):
+
+class VnodGlobalServiceAutoAttachmentView(APIView):
+    method_kind = "list"
+    method_name = "vnodglobal_api_autoattach"
+
+    def get(self, request, format=None):
+        params = request.query_params
+        sitename = params.get('sitename')
+
+        if ( sitename is None):
+            HttpResponseBadRequest("Error: Request requires] 'sitename' as a query param.")
+
+        vnodglobalservices = VnodGlobalService.get_service_objects().filter(spokes__autoattached=True,
+                                                                            spokes__operstate='inactive',
+                                                                            spokes__vnodlocalsite__name=sitename)
+
+        if (not vnodglobalservices):
+            HttpResponseBadRequest({"handles" : []})
+
+        handles = []
+        for vnodglobalservice in vnodglobalservices:
+            if (vnodglobalservice.adminstate != 'disabled'):
+                handles.append(vnodglobalservice.servicehandle)
+
+        response_data = {'servicehandles' : handles}
+        return Response(response_data)
+
+class VnodGlobalServiceConfigurationView(APIView):
+    method_kind = "list"
+    method_name = "vnodglobal_api_configuration"
+
+    def get(self, request, format=None):
+        params = request.query_params
+        servicehandle = params.get('servicehandle')
+        sitename = params.get('sitename')
+
+        if (servicehandle is None or sitename is None):
+            HttpResponseBadRequest("Error: Request requires 'servicehandle' and 'sitename' as query params.")
+
+        vnodglobalservice = VnodGlobalService.get_service_objects().filter(servicehandle=servicehandle)
+        if (not vnodglobalservice):
+            HttpResponseBadRequest('Error: Could not find VnodGlobalObject with servicehandle=' + servicehandle)
+
+        vnodglobalservice = vnodglobalservice[0]
+        response_data = {}
+        response_data['vlanid'] = vnodglobalservice.vlanid
+
+        for spoke in vnodglobalservice.spokes.all():
+            if (spoke.vnodlocalsite.name == sitename and spoke.adminstate != 'configured'):
+                response_data['port'] = {}
+                response_data['port']['name'] = spoke.vnodlocalport.name
+                break;
+
+        return Response(response_data)
+
+class VnodGlobalServiceActivationView(APIView):
+    method_kind = "list"
+    method_name = "vnodglobal_api_activation"
+
+    def post(self, request, format=None):
+        body_json = request.body
+        body = json.loads(body_json)
+
+        servicehandle=body['servicehandle']
+        sitename=body['sitename']
+        activate = body['activate']
+        vnodlocalid = body['vnodlocalid']
+        portid = body.get('portid')
+
+        if (activate == 'true' or activate == 'True'):
+            isActivate = True
+        else:
+            isActivate = False
+
+        vnodglobalservice = VnodGlobalService.get_service_objects().filter(servicehandle=servicehandle)
+        if (not vnodglobalservice):
+            HttpResponseBadRequest('Error: Could not find VnodGlobalObject with servicehandle=' + servicehandle)
+
+        vnodglobalservice = vnodglobalservice[0]
+
+        for spoke in vnodglobalservice.spokes.all():
+            if (spoke.vnodlocalsite.name == sitename and spoke.vnodlocalport.name == portid):
+                spoke_id = spoke.id
+                servicespoke = ServiceSpoke.objects.get(id=spoke_id)
+                servicespoke.remotevnodid = vnodlocalid
+                servicespoke.save()
+                break;
+
+        return Response()
+
+class VnodGlobalServiceAdminOperationalStateView(APIView):
+    method_kind = "list"
+    method_name = "vnodglobal_api_status"
+
+    def post(self, request, format=None):
+        body_json = request.body
+        body = json.loads(body_json)
+
+        servicehandle = body['servicehandle']
+        sitename = body['sitename']
+        operstate = body.get('operstate')
+        adminstate = body.get('adminstate')
+        portid = body.get('portid')
+
+        vnodglobalservice = VnodGlobalService.get_service_objects().filter(servicehandle=servicehandle)
+        if (not vnodglobalservice):
+            HttpResponseBadRequest('Error: Could not find VnodGlobalObject with servicehandle=' + servicehandle)
+
+        vnodglobalservice = vnodglobalservice[0]
+
+        for spoke in vnodglobalservice.spokes.all():
+            if (spoke.vnodlocalsite.name == sitename and spoke.vnodlocalport.name == portid):
+                spoke_id = spoke.id
+                servicespoke = ServiceSpoke.objects.get(id=spoke_id)
+                if (operstate):
+                    servicespoke.operstate = operstate
+                if (adminstate):
+                    servicespoke.adminstate = adminstate
+                servicespoke.save()
+                break;
+
+        isGlobalActive = VnodGlobalServiceViewSet.calculateVnodGlobalOperState(servicehandle=servicehandle)
+
+        if (vnodglobalservice.metronetworkmultipoint is None and vnodglobalservice.metronetworkpointtopoint is None and
+                    vnodglobalservice.metronetworkroottomultipoint is None and isGlobalActive):
+            VnodGlobalServiceViewSet.createService(servicehandle=servicehandle)
+
+        return Response()
diff --git a/xos/models.py b/xos/models.py
index bb069a5..4ebda3b 100644
--- a/xos/models.py
+++ b/xos/models.py
@@ -3,6 +3,7 @@
 from django.db import models
 from core.models import Service
 from core.models import PlCoreBase
+from core.models import Site
 
 METRONETWORK_KIND = "metronetwork"
 SERVICE_NAME = 'metronetwork'
@@ -133,6 +134,9 @@
     def __init__(self, *args, **kwargs):
         super(NetworkEdgePort, self).__init__(*args, **kwargs)
 
+    def __unicode__(self):
+        return u'%s' % (self.pid)
+
     def save(self, *args, **kwargs):
 
         if self.latlng:
@@ -180,8 +184,9 @@
         ('deactivationrequested', 'DeactivationRequested')
     )
 
-    sid = models.CharField(unique=True, verbose_name="Service ID", max_length=256, editable=True)
+    sid = models.CharField(verbose_name="Service ID", max_length=256, editable=True, blank=True)
     type = models.CharField(choices=TYPE, verbose_name="Type", max_length=256, editable=True)
+    vlanid = models.CharField(verbose_name="Vlanid", max_length=64, editable=True)
     uni1 = models.ForeignKey(NetworkEdgePort,
                             related_name='EdgePointToEdgePointSrc',
                             verbose_name="UNI 1",
@@ -212,6 +217,7 @@
         ('vlan', 'VLAN'),
         ('ip', 'IP'),
         ('ethernet', 'Ethernet'),
+        ('Root_Multipoint', 'Root Multipoint')
     )
 
     OPERATIONALSTATE = (
@@ -227,8 +233,9 @@
         ('deactivationrequested', 'DeactivationRequested')
     )
 
-    sid = models.CharField(unique=True, verbose_name="Service ID", max_length=256, editable=True)
-    type = models.CharField(choices=TYPE, verbose_name="Type", max_length=256, editable=False)
+    sid = models.CharField(verbose_name="Service ID", max_length=256, editable=True, blank=True)
+    type = models.CharField(choices=TYPE, verbose_name="Type", max_length=256, editable=True)
+    vlanid = models.CharField(verbose_name="Vlanid", max_length=64, editable=True)
     root = models.ForeignKey(NetworkEdgePort,
                             related_name='EdgeToMultipointRoot',
                             verbose_name="Root",
@@ -237,7 +244,7 @@
     eps = models.ManyToManyField(NetworkEdgePort,
                                  related_name='%(class)s_eps',
                                  verbose_name="Endpoints",
-                                 editable=False)
+                                 editable=True)
     operstate = models.CharField(choices=OPERATIONALSTATE, verbose_name="OperationalState", max_length=256,
                                  editable=True)
     adminstate = models.CharField(choices=ADMINISTRATIVESTATE, verbose_name="AdministrativeState", max_length=256,
@@ -260,6 +267,7 @@
         ('vlan', 'VLAN'),
         ('ip', 'IP'),
         ('ethernet', 'Ethernet'),
+        ('Multipoint_To_Multipoint', 'Multipoint To Multipoint')
     )
 
     OPERATIONALSTATE = (
@@ -275,12 +283,13 @@
         ('deactivationrequested', 'DeactivationRequested')
     )
 
-    sid = models.CharField(unique=True, verbose_name="Service ID", max_length=256, editable=True)
-    type = models.CharField(choices=TYPE, verbose_name="Type", max_length=256, editable=False)
+    sid = models.CharField(verbose_name="Service ID", max_length=256, editable=True, blank=True)
+    type = models.CharField(choices=TYPE, verbose_name="Type", max_length=256, editable=True)
+    vlanid = models.CharField(verbose_name="Vlanid", max_length=64, editable=True)
     eps = models.ManyToManyField(NetworkEdgePort,
                                  related_name='%(class)s_eps',
                                  verbose_name="Endpoints",
-                                 editable=False)
+                                 editable=True)
 
     operstate = models.CharField(choices=OPERATIONALSTATE, verbose_name="OperationalState", max_length=256,
                                  editable=True)
@@ -293,3 +302,162 @@
     def __init__(self, *args, **kwargs):
         super(NetworkMultipointToMultipointConnection, self).__init__(*args, **kwargs)
 
+class BandwidthProfile(PlCoreBase):
+
+    class Meta:
+        app_label = METRONETWORK_KIND
+        verbose_name = "Bandwidth Profile"
+
+    id = models.AutoField(verbose_name="id", primary_key=True, editable=False)
+    bwpcfgcbs = models.IntegerField(verbose_name="Committed Burst Size", editable=True)
+    bwpcfgebs = models.IntegerField(verbose_name="Excess Burst Size", editable=True)
+    bwpcfgcir = models.IntegerField(verbose_name="Committed Information Rate", editable=True)
+    bwpcfgeir = models.IntegerField(verbose_name="Excess Information Rate", editable=True)
+    name = models.CharField(unique=True, verbose_name="Name", max_length=256, editable=True)
+
+    def __init__(self, *args, **kwargs):
+        super(BandwidthProfile, self).__init__(*args, **kwargs)
+
+    def __unicode__(self):  return u'%s' % (self.name)
+
+# VNoD Global Objects - model is included in metro-net for simplicity
+
+class RemotePort(PlCoreBase):
+    class Meta:
+        app_label = METRONETWORK_KIND
+        verbose_name = "Remote Port"
+
+    remoteportsite = models.ForeignKey(Site,
+                                       related_name='RemotePortSite',
+                                       verbose_name="RemotePortSite",
+                                       editable=True,
+                                       on_delete=models.CASCADE)
+
+    edgeport = models.ForeignKey(NetworkEdgePort,
+                                 related_name='RemotePortEdgePort',
+                                 verbose_name="RemotePortEdgePort",
+                                 editable=True,
+                                 on_delete=models.CASCADE)
+
+    id = models.AutoField(verbose_name="id", primary_key=True, editable=False)
+    name = models.CharField(unique=True, verbose_name="Name", max_length=256, editable=True)
+
+    def __init__(self, *args, **kwargs):
+        super(RemotePort, self).__init__(*args, **kwargs)
+
+    def __unicode__(self):  return u'%s' % (self.name)
+
+class ServiceSpoke(PlCoreBase):
+
+    class Meta:
+        app_label = METRONETWORK_KIND
+        verbose_name = "Service Spoke"
+
+    OPERATIONALSTATE = (
+        ('active', 'Active'),
+        ('inactive', 'Inactive')
+    )
+
+    ADMINISTRATIVE_STATE = (
+        ('disabled', 'Disabled'),
+        ('configured', 'Configured'),
+        ('impaired', 'Impaired'),
+        ('enabled', 'Enabled')
+    )
+
+    vnodlocalsite = models.ForeignKey(Site,
+                      related_name='VnodLocalSite',
+                      verbose_name="VnodLocalSite",
+                      editable=True,
+                      on_delete=models.CASCADE)
+
+    vnodlocalport = models.ForeignKey(RemotePort,
+                                      related_name='VnodLocalPort',
+                                      verbose_name="VnodLocalPort",
+                                      editable=True,
+                                      on_delete=models.CASCADE)
+
+    id = models.AutoField(verbose_name="id", primary_key=True, editable=False)
+    name = models.CharField(unique=True, verbose_name="Name", max_length=256, editable=True)
+    remotesubscriber = models.CharField(verbose_name="RemoteSubscriber", max_length=256, blank=True, editable=False)
+    remotevnodid = models.CharField(verbose_name="RemoteVnodId", max_length=256, blank=True, editable=False)
+    autoattached = models.BooleanField(verbose_name="Auto-Attached", default=False, editable=True)
+    operstate = models.CharField(choices=OPERATIONALSTATE, verbose_name="OperationalState", max_length=256,
+                                 editable=True, default='inactive')
+    adminstate = models.CharField(choices=ADMINISTRATIVE_STATE, default='disabled', verbose_name="AdministrativeState",
+                                           max_length=64, editable=True)
+
+    def __init__(self, *args, **kwargs):
+        super(ServiceSpoke, self).__init__(*args, **kwargs)
+
+    def __unicode__(self):  return u'%s' % (self.name)
+
+class VnodGlobalService(Service):
+
+    class Meta:
+        app_label = SERVICE_NAME
+        verbose_name = "Virtual Network On Demand Global Service"
+
+    TYPE = (
+        ('eline', SERVICE_NAME_ELINE_VERBOSE),
+        ('elan', SERVICE_NAME_ELAN_VERBOSE),
+        ('etree', SERVICE_NAME_ETREE_VERBOSE),
+    )
+
+    OPERATIONALSTATE = (
+        ('active', 'Active'),
+        ('inactive', 'Inactive')
+    )
+
+    ADMINISTRATIVESTATE = (
+        ('disabled', 'Disabled'),
+        ('activationrequested', 'ActivationRequested'),
+        ('enabled', 'Enabled'),
+        ('invalid', 'Invalid'),
+        ('deactivationrequested', 'DeactivationRequested')
+    )
+
+    servicehandle = models.CharField(unique=True, verbose_name="Servicehandle", max_length=64, editable=True)
+    vlanid = models.CharField(verbose_name="Vlanid", max_length=64, editable=True)
+    type = models.CharField(choices=TYPE, verbose_name="Type", max_length=256, editable=True)
+
+    metronetworkroottomultipoint = models.ForeignKey(NetworkEdgeToMultipointConnection,
+                            related_name='EtreeService',
+                            verbose_name="EtreeService",
+                            null=True,
+                            editable=True,
+                            on_delete=models.CASCADE)
+
+    metronetworkmultipoint = models.ForeignKey(NetworkMultipointToMultipointConnection,
+                                                     related_name='ElanService',
+                                                     verbose_name="ElanService",
+                                                     null=True,
+                                                     editable=True,
+                                                     on_delete=models.CASCADE)
+
+    metronetworkpointtopoint = models.ForeignKey(NetworkEdgeToEdgePointConnection,
+                                               related_name='ElineService',
+                                               verbose_name="ElineService",
+                                               null=True,
+                                               editable=True,
+                                               on_delete=models.CASCADE)
+
+    operstate = models.CharField(choices=OPERATIONALSTATE, verbose_name="OperationalState", max_length=256,
+                                 editable=True)
+
+    adminstate = models.CharField(choices=ADMINISTRATIVESTATE, verbose_name="AdministrativeState", max_length=256,
+                                  editable=True, default='enabled')
+
+    spokes = models.ManyToManyField(ServiceSpoke,
+                                 related_name='ServiceSpokes',
+                                 verbose_name="Spokes",
+                                 editable=True)
+
+    bandwidthProfile = models.ForeignKey(BandwidthProfile,
+                                               related_name='BandwidthProfile',
+                                               verbose_name="BandwidthProfile",
+                                               editable=True,
+                                               on_delete=models.CASCADE)
+
+    def __init__(self, *args, **kwargs):
+        super(VnodGlobalService, self).__init__(*args, **kwargs)
diff --git a/xos/synchronizer/invokers/invokerfactory.py b/xos/synchronizer/invokers/invokerfactory.py
index 786f99d..b3628fe 100644
--- a/xos/synchronizer/invokers/invokerfactory.py
+++ b/xos/synchronizer/invokers/invokerfactory.py
@@ -2,6 +2,9 @@
 from synchronizers.metronetwork.invokers.networkmultipointtomultipointinvoker import NetworkMultipointToMultipointInvoker
 from synchronizers.metronetwork.invokers.networkedgetoedgepointinvoker import NetworkEdgeToEdgePointInvoker
 from synchronizers.metronetwork.invokers.networkedgetomultipointinvoker import NetworkEdgeToMultipointInvoker
+from synchronizers.metronetwork.invokers.servicespokeinvoker import ServiceSpokeInvoker
+from synchronizers.metronetwork.invokers.vnodglobalserviceinvoker import VnodGlobalServiceInvoker
+from synchronizers.metronetwork.invokers.remoteportinvoker import RemotePortInvoker
 
 
 class InvokerFactory(object):
@@ -16,5 +19,11 @@
             return NetworkEdgeToEdgePointInvoker()
         elif isinstance(obj, NetworkEdgeToMultipointConnection):
             return NetworkEdgeToMultipointInvoker()
+        elif isinstance(obj, ServiceSpoke):
+            return ServiceSpokeInvoker()
+        elif isinstance(obj, VnodGlobalService):
+            return VnodGlobalServiceInvoker()
+        elif isinstance(obj, RemotePort):
+            return RemotePortInvoker()
         else:
             return None
diff --git a/xos/synchronizer/invokers/networkedgetoedgepointinvoker.py b/xos/synchronizer/invokers/networkedgetoedgepointinvoker.py
index ebc4d12..2c30d79 100644
--- a/xos/synchronizer/invokers/networkedgetoedgepointinvoker.py
+++ b/xos/synchronizer/invokers/networkedgetoedgepointinvoker.py
@@ -17,10 +17,11 @@
     def presave(self, obj):
         # Now that the Ports are created - get a proper reference to them and update the
         # src and dst fields
-        uni1port = NetworkEdgePort.objects.get(pid=obj.uni1_createbuffer)
-        uni2port = NetworkEdgePort.objects.get(pid=obj.uni2_createbuffer)
-        obj.uni1 = uni1port
-        obj.uni2 = uni2port
+        if hasattr(obj, 'uni1_createbuffer'):
+            uni1port = NetworkEdgePort.objects.get(pid=obj.uni1_createbuffer)
+            uni2port = NetworkEdgePort.objects.get(pid=obj.uni2_createbuffer)
+            obj.uni1 = uni1port
+            obj.uni2 = uni2port
 
     # Method for handline post save semantics
     #      content here would be model specific but could include handling Many-to-Many relationship
diff --git a/xos/synchronizer/invokers/networkedgetomultipointinvoker.py b/xos/synchronizer/invokers/networkedgetomultipointinvoker.py
index a0b7ffd..9b8e88b 100644
--- a/xos/synchronizer/invokers/networkedgetomultipointinvoker.py
+++ b/xos/synchronizer/invokers/networkedgetomultipointinvoker.py
@@ -17,8 +17,9 @@
     def presave(self, obj):
         # Now that the Ports are created - get a proper reference to them and update the
         # root field
-        rootEdgePort = NetworkEdgePort.objects.get(pid=obj.root_createbuffer)
-        obj.root = rootEdgePort
+        if hasattr(obj, 'root_createbuffer'):
+            rootEdgePort = NetworkEdgePort.objects.get(pid=obj.root_createbuffer)
+            obj.root = rootEdgePort
 
 
     # Method for handline post save semantics
@@ -37,9 +38,10 @@
         # called 'eps' that just containts a reference to a bunch of NetworkEdgePorts
         #
         #
-        scratchpad = json.loads(obj.eps_createbuffer)
-        eps = scratchpad['eps']
+        if hasattr(obj, 'eps_createbuffer'):
+            scratchpad = json.loads(obj.eps_createbuffer)
+            eps = scratchpad['eps']
 
-        for ep in eps:
-            port = NetworkEdgePort.objects.get(pid=ep)
-            obj.eps.add(port)
+            for ep in eps:
+                port = NetworkEdgePort.objects.get(pid=ep)
+                obj.eps.add(port)
diff --git a/xos/synchronizer/invokers/networkmultipointtomultipointinvoker.py b/xos/synchronizer/invokers/networkmultipointtomultipointinvoker.py
index 78b617c..6b2ab42 100644
--- a/xos/synchronizer/invokers/networkmultipointtomultipointinvoker.py
+++ b/xos/synchronizer/invokers/networkmultipointtomultipointinvoker.py
@@ -34,9 +34,10 @@
         # called 'eps' that just containts a reference to a bunch of NetworkEdgePorts
         #
         #
-        scratchpad = json.loads(obj.eps_createbuffer)
-        eps = scratchpad['eps']
+        if hasattr(obj, 'eps_createbuffer'):
+            scratchpad = json.loads(obj.eps_createbuffer)
+            eps = scratchpad['eps']
 
-        for ep in eps:
-            port = NetworkEdgePort.objects.get(pid=ep)
-            obj.eps.add(port)
+            for ep in eps:
+                port = NetworkEdgePort.objects.get(pid=ep)
+                obj.eps.add(port)
diff --git a/xos/synchronizer/invokers/remoteportinvoker.py b/xos/synchronizer/invokers/remoteportinvoker.py
new file mode 100644
index 0000000..1061dc4
--- /dev/null
+++ b/xos/synchronizer/invokers/remoteportinvoker.py
@@ -0,0 +1,35 @@
+import json
+from synchronizers.metronetwork.invokers.invoker import Invoker
+from core.models import Site
+from services.metronetwork.models import NetworkEdgePort
+
+class RemotePortInvoker(Invoker):
+    def __init__(self, **args):
+        pass
+
+    # Method for handline pre save semantics
+    #      content here would be model specific but could include handling Many-to-Many relationship
+    #      creation - which must occur post save
+    #
+    # obj     - Whatever obj was just saved
+    # returns - None - this is a pure invoke() call, return type is None
+    #
+    def presave(self, obj):
+        # Now that the Site and EdgePorts are created set the foreign keys
+        if hasattr(obj, 'sitename'):
+            site = Site.objects.get(login_base=obj.sitename)
+            obj.remoteportsite = site
+
+        if hasattr(obj, 'edgeportname'):
+            edgeport = NetworkEdgePort.objects.get(pid=obj.edgeportname)
+            obj.edgeport = edgeport
+
+    # Method for handline post save semantics
+    #      content here would be model specific but could include handling Many-to-Many relationship
+    #      creation - which must occur post save
+    #
+    # obj     - Whatever obj was just saved
+    # returns - N/A - this is a pure invoke() call
+    #
+    def postsave(self, obj):
+        pass
\ No newline at end of file
diff --git a/xos/synchronizer/invokers/servicespokeinvoker.py b/xos/synchronizer/invokers/servicespokeinvoker.py
new file mode 100644
index 0000000..8470cf5
--- /dev/null
+++ b/xos/synchronizer/invokers/servicespokeinvoker.py
@@ -0,0 +1,37 @@
+import json
+from synchronizers.metronetwork.invokers.invoker import Invoker
+from core.models import Site
+from services.metronetwork.models import RemotePort
+
+
+class ServiceSpokeInvoker(Invoker):
+    def __init__(self, **args):
+        pass
+
+    # Method for handline pre save semantics
+    #      content here would be model specific but could include handling Many-to-Many relationship
+    #      creation - which must occur post save
+    #
+    # obj     - Whatever obj was just saved
+    # returns - None - this is a pure invoke() call, return type is None
+    #
+    def presave(self, obj):
+        # Now that the Ports are created - get a proper reference to them and update the
+        # src and dst fields
+        if hasattr(obj, 'sitename'):
+            site = Site.objects.get(login_base=obj.sitename)
+            obj.vnodlocalsite = site
+
+        if hasattr(obj, 'remoteportname'):
+            remoteport = RemotePort.objects.get(name=obj.remoteportname)
+            obj.vnodlocalport = remoteport
+
+    # Method for handline post save semantics
+    #      content here would be model specific but could include handling Many-to-Many relationship
+    #      creation - which must occur post save
+    #
+    # obj     - Whatever obj was just saved
+    # returns - N/A - this is a pure invoke() call
+    #
+    def postsave(self, obj):
+        pass
\ No newline at end of file
diff --git a/xos/synchronizer/invokers/vnodglobalserviceinvoker.py b/xos/synchronizer/invokers/vnodglobalserviceinvoker.py
new file mode 100644
index 0000000..a313171
--- /dev/null
+++ b/xos/synchronizer/invokers/vnodglobalserviceinvoker.py
@@ -0,0 +1,50 @@
+import json
+from synchronizers.metronetwork.invokers.invoker import Invoker
+from services.metronetwork.models import ServiceSpoke
+from services.metronetwork.models import BandwidthProfile
+from services.metronetwork.models import NetworkEdgeToEdgePointConnection
+
+class VnodGlobalServiceInvoker(Invoker):
+
+    def __init__(self, **args):
+        pass
+
+    # Method for handline pre save semantics
+    #      content here would be model specific but could include handling Many-to-Many relationship
+    #      creation - which must occur post save
+    #
+    # obj     - Whatever obj was just saved
+    # returns - None - this is a pure invoke() call, return type is None
+    #
+    def presave(self, obj):
+
+        if hasattr(obj, 'bwpname'):
+            bwprofile = BandwidthProfile.objects.get(name=obj.bwpname)
+            obj.bandwidthProfile = bwprofile
+
+        if hasattr(obj, 'pointtopointsid'):
+            connection = NetworkEdgeToEdgePointConnection.objects.get(sid=obj.pointtopointsid)
+            obj.metronetworkpointtopoint = connection
+
+    # Method for handline post save semantics
+    #      content here would be model specific but could include handling Many-to-Many relationship
+    #      creation - which must occur post save
+    #
+    # obj     - Whatever obj was just saved
+    # returns - N/A - this is a pure invoke() call
+    #
+    def postsave(self, obj):
+        #
+        # Ok - we need to handle the multipoint many-to-many relationships in here
+        #
+        # By design convnetion we will look for them in the 'backend_register' object field
+        # this is a json field that is general purpose - we will expect to find a JSON array
+        # called 'eps' that just containts a reference to a bunch of NetworkEdgePorts
+        #
+        if hasattr(obj, 'spokes_createbuffer'):
+            scratchpad = json.loads(obj.spokes_createbuffer)
+            spokes = scratchpad['spokes']
+
+            for spokeid in spokes:
+                spoke = ServiceSpoke.objects.get(name=spokeid)
+                obj.spokes.add(spoke)
diff --git a/xos/synchronizer/manifest b/xos/synchronizer/manifest
index 98cb0d9..03f1088 100644
--- a/xos/synchronizer/manifest
+++ b/xos/synchronizer/manifest
@@ -6,15 +6,18 @@
 providers/providerfactory.py
 providers/__init__.py
 providers/metronetworkprovider.py
-invokers/invoker.py
-invokers/invokerfactory.py
-invokers/networkedgetoedgepointinvoker.py
-invokers/networkedgetomultipointinvoker.py
-invokers/networkmultipointtomultipointinvoker.py
-invokers/__init__.py
 run_devel.sh
 metronetwork-synchronizer-devel.py
 manifest
 steps/sync_metronetworkservice.py
+invokers/servicespokeinvoker.py
+invokers/networkedgetoedgepointinvoker.py
+invokers/vnodglobalserviceinvoker.py
+invokers/networkmultipointtomultipointinvoker.py
+invokers/__init__.py
+invokers/invoker.py
+invokers/networkedgetomultipointinvoker.py
+invokers/invokerfactory.py
+invokers/remoteportinvoker.py
 run.sh
 metronetwork_synchronizer_config
diff --git a/xos/synchronizer/providers/metronetworkrestprovider.py b/xos/synchronizer/providers/metronetworkrestprovider.py
index 1e00a90..2cb03ab 100644
--- a/xos/synchronizer/providers/metronetworkrestprovider.py
+++ b/xos/synchronizer/providers/metronetworkrestprovider.py
@@ -55,10 +55,171 @@
         else:

             raise Exception("OnosApiError: get_network_ports()")

 

+    def get_network_ports(self):

+

+        objs = []

+

+        restCtrlUrl = self.networkdevice.restCtrlUrl

+        username = self.networkdevice.username

+        password = self.networkdevice.password

+

+        resp = requests.get("{}/mef-sca-api/SCA_ETH_FPP_UNI_N".format(restCtrlUrl),

+                            auth=HTTPBasicAuth(username, password))

+

+        if resp.status_code == 200:

+            for uni in resp.json():

+                hostname = uni['transportPort']['Hostname']

+                port = uni['transportPort']['Port']

+

+                # Default values

+                bwpCfgCbs = 0

+                bwpCfgEbs = 0

+                bwpCfgCir = 0

+                bwpCfgEir = 0

+

+                if 'interfaceCfgIngressBwp' in uni:

+                    bwpCfgCbs = uni['interfaceCfgIngressBwp']['bwpCfgCbs']

+                    bwpCfgEbs = uni['interfaceCfgIngressBwp']['bwpCfgEbs']

+                    bwpCfgCir = uni['interfaceCfgIngressBwp']['bwpCfgCir']

+                    bwpCfgEir = uni['interfaceCfgIngressBwp']['bwpCfgEir']

+

+                uniPort = NetworkEdgePort()

+                uniPort.element = self.networkdevice

+                uniPort.pid = "{}.{}/{}".format(self.networkdevice.id, hostname, port)

+                uniPort.bwpCfgCbs = bwpCfgCbs

+                uniPort.bwpCfgEbs = bwpCfgEbs

+                uniPort.bwpCfgCir = bwpCfgCir

+                uniPort.bwpCfgEir = bwpCfgEir

+

+                objs.append(uniPort)

+

+            return objs

+

+        else:

+            raise Exception("OnosApiError: get_network_ports()")

+

+    def get_network_eline_link(self, networkDevice, evc):

+

+        sid = evc['id']

+        uni1 = evc['SCA_ETH_Flow_Points'][0]

+        hostname = uni1['scaEthFppUniN']['transportPort']['Hostname']

+        port = uni1['scaEthFppUniN']['transportPort']['Port']

+

+        edgePort1 = NetworkEdgePort()

+        edgePort1.element = networkDevice

+        edgePort1.pid = "{}.{}/{}".format(networkDevice.id, hostname, port)

+

+        if 'interfaceCfgIngressBwp' in uni1['scaEthFppUniN']:

+            edgePort1.bwpCfgCbs = uni1['scaEthFppUniN']['interfaceCfgIngressBwp']['bwpCfgCbs']

+            edgePort1.bwpCfgEbs = uni1['scaEthFppUniN']['interfaceCfgIngressBwp']['bwpCfgEbs']

+            edgePort1.bwpCfgCir = uni1['scaEthFppUniN']['interfaceCfgIngressBwp']['bwpCfgCir']

+            edgePort1.bwpCfgEir = uni1['scaEthFppUniN']['interfaceCfgIngressBwp']['bwpCfgEir']

+

+        uni2 = evc['SCA_ETH_Flow_Points'][1]

+        hostname = uni2['scaEthFppUniN']['transportPort']['Hostname']

+        port = uni2['scaEthFppUniN']['transportPort']['Port']

+

+        edgePort2 = NetworkEdgePort()

+        edgePort2.element = networkDevice

+        edgePort2.pid = "{}.{}/{}".format(networkDevice.id, hostname, port)

+

+        if 'interfaceCfgIngressBwp' in uni1['scaEthFppUniN']:

+            edgePort2.bwpCfgCbs = uni2['scaEthFppUniN']['interfaceCfgIngressBwp']['bwpCfgCbs']

+            edgePort2.bwpCfgEbs = uni2['scaEthFppUniN']['interfaceCfgIngressBwp']['bwpCfgEbs']

+            edgePort2.bwpCfgCir = uni2['scaEthFppUniN']['interfaceCfgIngressBwp']['bwpCfgCir']

+            edgePort2.bwpCfgEir = uni2['scaEthFppUniN']['interfaceCfgIngressBwp']['bwpCfgEir']

+

+        edgeToEdgeConnectivity = NetworkEdgeToEdgePointConnection()

+        edgeToEdgeConnectivity.sid = sid

+        edgeToEdgeConnectivity.type = "Point_To_Point"

+        edgeToEdgeConnectivity.uni1 = edgePort1

+        edgeToEdgeConnectivity.uni2 = edgePort2

+        edgeToEdgeConnectivity.operstate = "active"

+        edgeToEdgeConnectivity.adminstate = "enabled"

+

+        return(edgeToEdgeConnectivity)

+

+    def get_network_elan_link(self, networkDevice, evc):

+

+        sid = evc['id']

+        eps = []

+

+        for ep in evc['SCA_ETH_Flow_Points']:

+            hostname = ep['scaEthFppUniN']['transportPort']['Hostname']

+            port = ep['scaEthFppUniN']['transportPort']['Port']

+

+            edgePort = NetworkEdgePort()

+            edgePort.element = networkDevice

+            edgePort.pid = "{}.{}/{}".format(networkDevice.id, hostname, port)

+

+            if 'interfaceCfgIngressBwp' in ep['scaEthFppUniN']:

+                edgePort.bwpCfgCbs = ep['scaEthFppUniN']['interfaceCfgIngressBwp']['bwpCfgCbs']

+                edgePort.bwpCfgEbs = ep['scaEthFppUniN']['interfaceCfgIngressBwp']['bwpCfgEbs']

+                edgePort.bwpCfgCir = ep['scaEthFppUniN']['interfaceCfgIngressBwp']['bwpCfgCir']

+                edgePort.bwpCfgEir = ep['scaEthFppUniN']['interfaceCfgIngressBwp']['bwpCfgEir']

+

+            eps.append(edgePort)

+

+        multipointToMultipointConnectivity = NetworkMultipointToMultipointConnection()

+        multipointToMultipointConnectivity.sid = sid

+        multipointToMultipointConnectivity.type = "Multipoint_To_Multipoint"

+        multipointToMultipointConnectivity.eps = eps

+        multipointToMultipointConnectivity.operstate = "active"

+        multipointToMultipointConnectivity.adminstate = "enabled"

+

+        return(multipointToMultipointConnectivity)

+

+    def get_network_etree_link(self, networkDevice, evc):

+

+        sid = evc['id']

+        eps = []

+

+        root = evc['SCA_ETH_Flow_Points'][0]

+        hostname = root['scaEthFppUniN']['transportPort']['Hostname']

+        port = root['scaEthFppUniN']['transportPort']['Port']

+

+        edgePort = NetworkEdgePort()

+        edgePort.element = networkDevice

+        edgePort.pid = "{}.{}/{}".format(networkDevice.id, hostname, port)

+

+        if 'interfaceCfgIngressBwp' in root['scaEthFppUniN']:

+            edgePort.bwpCfgCbs = root['scaEthFppUniN']['interfaceCfgIngressBwp']['bwpCfgCbs']

+            edgePort.bwpCfgEbs = root['scaEthFppUniN']['interfaceCfgIngressBwp']['bwpCfgEbs']

+            edgePort.bwpCfgCir = root['scaEthFppUniN']['interfaceCfgIngressBwp']['bwpCfgCir']

+            edgePort.bwpCfgEir = root['scaEthFppUniN']['interfaceCfgIngressBwp']['bwpCfgEir']

+

+        edgeToMultipointConnectivity = NetworkEdgeToMultipointConnection()

+        edgeToMultipointConnectivity.sid = sid

+        edgeToMultipointConnectivity.type = "Root_Multipoint"

+        edgeToMultipointConnectivity.root = edgePort

+

+        for ep in evc['SCA_ETH_Flow_Points'][1:]:

+            hostname = ep['scaEthFppUniN']['transportPort']['Hostname']

+            port = ep['scaEthFppUniN']['transportPort']['Port']

+

+            edgePort = NetworkEdgePort()

+            edgePort.element = networkDevice

+            edgePort.pid = "{}.{}/{}".format(networkDevice.id, hostname, port)

+

+            if 'interfaceCfgIngressBwp' in ep['scaEthFppUniN']:

+                edgePort.bwpCfgCbs = ep['scaEthFppUniN']['interfaceCfgIngressBwp']['bwpCfgCbs']

+                edgePort.bwpCfgEbs = ep['scaEthFppUniN']['interfaceCfgIngressBwp']['bwpCfgEbs']

+                edgePort.bwpCfgCir = ep['scaEthFppUniN']['interfaceCfgIngressBwp']['bwpCfgCir']

+                edgePort.bwpCfgEir = ep['scaEthFppUniN']['interfaceCfgIngressBwp']['bwpCfgEir']

+

+            eps.append(edgePort)

+

+        edgeToMultipointConnectivity.eps = eps

+        edgeToMultipointConnectivity.operstate = "active"

+        edgeToMultipointConnectivity.adminstate = "enabled"

+

+        return(edgeToMultipointConnectivity)

+

     def get_network_links(self):

 

         objs = []

 

+        networkDevice = self.networkdevice

         restCtrlUrl = self.networkdevice.restCtrlUrl

         username = self.networkdevice.username

         password = self.networkdevice.password

@@ -68,69 +229,24 @@
 

         if resp.status_code == 200:

             for evc in resp.json():

-                id = evc['id']

                 evcServiceType = evc['evcServiceType']

-

                 if (evcServiceType == "Point_To_Point"):

-                    uni1 = evc['SCA_ETH_Flow_Points'][0]

-                    hostname = uni1['scaEthFppUniN']['transportPort']['Hostname']

-                    port = uni1['scaEthFppUniN']['transportPort']['Port']

-

-                    if 'interfaceCfgIngressBwp' in uni1['scaEthFppUniN']:

-                        bwpCfgCbs = uni1['scaEthFppUniN']['interfaceCfgIngressBwp']['bwpCfgCbs']

-                        bwpCfgEbs = uni1['scaEthFppUniN']['interfaceCfgIngressBwp']['bwpCfgEbs']

-                        bwpCfgCir = uni1['scaEthFppUniN']['interfaceCfgIngressBwp']['bwpCfgCir']

-                        bwpCfgEir = uni1['scaEthFppUniN']['interfaceCfgIngressBwp']['bwpCfgEir']

-

-                    edgePort1 = NetworkEdgePort()

-                    edgePort1.element = self.networkdevice

-                    edgePort1.pid = "{}.{}/{}".format(self.networkdevice.id, hostname, port)

-                    edgePort1.bwpCfgCbs = bwpCfgCbs

-                    edgePort1.bwpCfgEbs = bwpCfgEbs

-                    edgePort1.bwpCfgCir = bwpCfgCir

-                    edgePort1.bwpCfgEir = bwpCfgEir

-

-                    uni2 = evc['SCA_ETH_Flow_Points'][1]

-                    hostname = uni2['scaEthFppUniN']['transportPort']['Hostname']

-                    port = uni2['scaEthFppUniN']['transportPort']['Port']

-

-                    if 'interfaceCfgIngressBwp' in uni1['scaEthFppUniN']:

-                        bwpCfgCbs = uni2['scaEthFppUniN']['interfaceCfgIngressBwp']['bwpCfgCbs']

-                        bwpCfgEbs = uni2['scaEthFppUniN']['interfaceCfgIngressBwp']['bwpCfgEbs']

-                        bwpCfgCir = uni2['scaEthFppUniN']['interfaceCfgIngressBwp']['bwpCfgCir']

-                        bwpCfgEir = uni2['scaEthFppUniN']['interfaceCfgIngressBwp']['bwpCfgEir']

-

-                    edgePort2 = NetworkEdgePort()

-                    edgePort2.element = self.networkdevice

-                    edgePort2.pid = "{}.{}/{}".format(self.networkdevice.id, hostname, port)

-                    edgePort2.bwpCfgCbs = bwpCfgCbs

-                    edgePort2.bwpCfgEbs = bwpCfgEbs

-                    edgePort2.bwpCfgCir = bwpCfgCir

-                    edgePort2.bwpCfgEir = bwpCfgEir

-

-                edgeToEdgeConnectivity = NetworkEdgeToEdgePointConnection()

-                edgeToEdgeConnectivity.sid = id

-                edgeToEdgeConnectivity.type = evcServiceType

-                edgeToEdgeConnectivity.uni1 = edgePort1

-                edgeToEdgeConnectivity.uni2 = edgePort2

-                edgeToEdgeConnectivity.operstate = "active"

-                edgeToEdgeConnectivity.adminstate = "enabled"

-

-                objs.append(edgeToEdgeConnectivity)

-

-            return objs

-

+                    objs.append(self.get_network_eline_link(networkDevice, evc))

+                elif (evcServiceType == "Multipoint_To_Multipoint"):

+                    objs.append(self.get_network_elan_link(networkDevice, evc))

+                elif (evcServiceType == "Root_Multipoint"):

+                    objs.append(self.get_network_etree_link(networkDevice, evc))

+                else:

+                    raise Exception("OnosApiError: get_network_links() - unknown link type")

         else:

             raise Exception("OnosApiError: get_network_links()")

 

-    def create_point_to_point_connectivity(self, obj):

+        return objs

 

-        restCtrlUrl = self.networkdevice.restCtrlUrl

-        username = self.networkdevice.username

-        password = self.networkdevice.password

+    def create_point_to_point_connectivity_json_data(self, obj):

 

-        evcServiceType = obj.type

-        # evcServiceType = "Point_To_Point"

+        p2p_json_data = {}

+        p2p_json_data["evcServiceType"] = "Point_To_Point"

 

         uni1 = obj.uni1

         uni1Id = uni1.pid

@@ -142,6 +258,19 @@
         uni1BwpCfgCir = uni1.bwpCfgCir

         uni1BwpCfgEir = uni1.bwpCfgEir

 

+        uni1_json_data = {}

+        uni1_json_data['scaEthFppUniN'] = {}

+        uni1_json_data['scaEthFppUniN']['ceVlanId'] = obj.vlanid

+        uni1_json_data['scaEthFppUniN']["transportPort"] = {}

+        uni1_json_data['scaEthFppUniN']["interfaceCfgIngressBwp"] = {}

+

+        uni1_json_data['scaEthFppUniN']["transportPort"]["Hostname"] = uni1Hostname

+        uni1_json_data['scaEthFppUniN']["transportPort"]["Port"] = uni1Port

+        uni1_json_data['scaEthFppUniN']["interfaceCfgIngressBwp"]["bwpCfgCbs"] = uni1BwpCfgCbs

+        uni1_json_data['scaEthFppUniN']["interfaceCfgIngressBwp"]["bwpCfgEbs"] = uni1BwpCfgEbs

+        uni1_json_data['scaEthFppUniN']["interfaceCfgIngressBwp"]["bwpCfgCir"] = uni1BwpCfgCir

+        uni1_json_data['scaEthFppUniN']["interfaceCfgIngressBwp"]["bwpCfgEir"] = uni1BwpCfgEir

+

         uni2 = obj.uni2

         uni2Id = uni2.pid

         uni2IdToken = (uni2Id.split('.', 1))[1].split('/', 1)

@@ -152,27 +281,137 @@
         uni2BwpCfgCir = uni2.bwpCfgCir

         uni2BwpCfgEir = uni2.bwpCfgEir

 

-        data = {

-            "evcServiceType": evcServiceType,

-            "SCA_ETH_Flow_Points": [

-                {

-                    "scaEthFppUniN": {"transportPort": {"Hostname": uni1Hostname, "Port": uni1Port},

-                                      "interfaceCfgIngressBwp": {"bwpCfgCbs": uni1BwpCfgCbs,

-                                                                 "bwpCfgEbs": uni1BwpCfgEbs,

-                                                                 "bwpCfgCir": uni1BwpCfgCir,

-                                                                 "bwpCfgEir": uni1BwpCfgEir}}},

-                {

-                    "scaEthFppUniN": {"transportPort": {"Hostname": uni2Hostname, "Port": uni2Port},

-                                      "interfaceCfgIngressBwp": {"bwpCfgCbs": uni2BwpCfgCbs,

-                                                                 "bwpCfgEbs": uni2BwpCfgEbs,

-                                                                 "bwpCfgCir": uni2BwpCfgCir,

-                                                                 "bwpCfgEir": uni2BwpCfgEir}}}]

-        }

+        uni2_json_data = {}

+        uni2_json_data['scaEthFppUniN'] = {}

+        uni2_json_data['scaEthFppUniN']['ceVlanId'] = obj.vlanid

+        uni2_json_data['scaEthFppUniN']["transportPort"] = {}

+        uni2_json_data['scaEthFppUniN']["interfaceCfgIngressBwp"] = {}

+

+        uni2_json_data['scaEthFppUniN']["transportPort"]["Hostname"] = uni2Hostname

+        uni2_json_data['scaEthFppUniN']["transportPort"]["Port"] = uni2Port

+        uni2_json_data['scaEthFppUniN']["interfaceCfgIngressBwp"]["bwpCfgCbs"] = uni2BwpCfgCbs

+        uni2_json_data['scaEthFppUniN']["interfaceCfgIngressBwp"]["bwpCfgEbs"] = uni2BwpCfgEbs

+        uni2_json_data['scaEthFppUniN']["interfaceCfgIngressBwp"]["bwpCfgCir"] = uni2BwpCfgCir

+        uni2_json_data['scaEthFppUniN']["interfaceCfgIngressBwp"]["bwpCfgEir"] = uni2BwpCfgEir

+

+        p2p_json_data["SCA_ETH_Flow_Points"] = []

+        p2p_json_data["SCA_ETH_Flow_Points"].append(uni1_json_data)

+        p2p_json_data["SCA_ETH_Flow_Points"].append(uni2_json_data)

+

+        return p2p_json_data

+

+    # nchoi: create elan service json data

+    def create_multipoint_to_multipoint_connectivity_json_data(self, obj):

+

+        mp2mp_json_data = {}

+        mp2mp_json_data["evcServiceType"] = "Multipoint_To_Multipoint"

+        mp2mp_json_data["SCA_ETH_Flow_Points"] = []

+

+        for ep in obj.eps.all():

+            uniId = ep.pid

+            uniIdToken = (uniId.split('.', 1))[1].split('/', 1)

+            uniHostname = uniIdToken[0]

+            uniPort = uniIdToken[1]

+            uniBwpCfgCbs = ep.bwpCfgCbs

+            uniBwpCfgEbs = ep.bwpCfgEbs

+            uniBwpCfgCir = ep.bwpCfgCir

+            uniBwpCfgEir = ep.bwpCfgEir

+

+            uni_json_data = {}

+            uni_json_data['scaEthFppUniN'] = {}

+            uni_json_data['scaEthFppUniN']['ceVlanId'] = obj.vlanid

+            uni_json_data['scaEthFppUniN']["transportPort"] = {}

+            uni_json_data['scaEthFppUniN']["interfaceCfgIngressBwp"] = {}

+

+            uni_json_data['scaEthFppUniN']["transportPort"]["Hostname"] = uniHostname

+            uni_json_data['scaEthFppUniN']["transportPort"]["Port"] = uniPort

+            uni_json_data['scaEthFppUniN']["interfaceCfgIngressBwp"]["bwpCfgCbs"] = uniBwpCfgCbs

+            uni_json_data['scaEthFppUniN']["interfaceCfgIngressBwp"]["bwpCfgEbs"] = uniBwpCfgEbs

+            uni_json_data['scaEthFppUniN']["interfaceCfgIngressBwp"]["bwpCfgCir"] = uniBwpCfgCir

+            uni_json_data['scaEthFppUniN']["interfaceCfgIngressBwp"]["bwpCfgEir"] = uniBwpCfgEir

+

+            mp2mp_json_data["SCA_ETH_Flow_Points"].append(uni_json_data)

+

+        return mp2mp_json_data

+

+    # nchoi: create etree service json data

+    def create_root_multipoint_connectivity_json_data(self, obj):

+

+        r2mp_json_data = {}

+        r2mp_json_data["evcServiceType"] = "Root_Multipoint"

+        r2mp_json_data["SCA_ETH_Flow_Points"] = []

+

+        root = obj.root

+        uniId = root.pid

+        uniIdToken = (uniId.split('.', 1))[1].split('/', 1)

+        uniHostname = uniIdToken[0]

+        uniPort = uniIdToken[1]

+        uniBwpCfgCbs = root.bwpCfgCbs

+        uniBwpCfgEbs = root.bwpCfgEbs

+        uniBwpCfgCir = root.bwpCfgCir

+        uniBwpCfgEir = root.bwpCfgEir

+

+        uni_json_data = {}

+        uni_json_data['scaEthFppUniN'] = {}

+        uni_json_data['scaEthFppUniN']['ceVlanId'] = obj.vlanid

+        uni_json_data['scaEthFppUniN']["transportPort"] = {}

+        uni_json_data['scaEthFppUniN']["interfaceCfgIngressBwp"] = {}

+

+        uni_json_data['scaEthFppUniN']["transportPort"]["Hostname"] = uniHostname

+        uni_json_data['scaEthFppUniN']["transportPort"]["Port"] = uniPort

+        uni_json_data['scaEthFppUniN']["interfaceCfgIngressBwp"]["bwpCfgCbs"] = uniBwpCfgCbs

+        uni_json_data['scaEthFppUniN']["interfaceCfgIngressBwp"]["bwpCfgEbs"] = uniBwpCfgEbs

+        uni_json_data['scaEthFppUniN']["interfaceCfgIngressBwp"]["bwpCfgCir"] = uniBwpCfgCir

+        uni_json_data['scaEthFppUniN']["interfaceCfgIngressBwp"]["bwpCfgEir"] = uniBwpCfgEir

+

+        r2mp_json_data["SCA_ETH_Flow_Points"].append(uni_json_data)

+

+        for ep in obj.eps.all():

+            uniId = ep.pid

+            uniIdToken = (uniId.split('.', 1))[1].split('/', 1)

+            uniHostname = uniIdToken[0]

+            uniPort = uniIdToken[1]

+            uniBwpCfgCbs = ep.bwpCfgCbs

+            uniBwpCfgEbs = ep.bwpCfgEbs

+            uniBwpCfgCir = ep.bwpCfgCir

+            uniBwpCfgEir = ep.bwpCfgEir

+

+            uni_json_data = {}

+            uni_json_data['scaEthFppUniN'] = {}

+            uni_json_data['scaEthFppUniN']['ceVlanId'] = obj.vlanid

+            uni_json_data['scaEthFppUniN']["transportPort"] = {}

+            uni_json_data['scaEthFppUniN']["interfaceCfgIngressBwp"] = {}

+

+            uni_json_data['scaEthFppUniN']["transportPort"]["Hostname"] = uniHostname

+            uni_json_data['scaEthFppUniN']["transportPort"]["Port"] = uniPort

+            uni_json_data['scaEthFppUniN']["interfaceCfgIngressBwp"]["bwpCfgCbs"] = uniBwpCfgCbs

+            uni_json_data['scaEthFppUniN']["interfaceCfgIngressBwp"]["bwpCfgEbs"] = uniBwpCfgEbs

+            uni_json_data['scaEthFppUniN']["interfaceCfgIngressBwp"]["bwpCfgCir"] = uniBwpCfgCir

+            uni_json_data['scaEthFppUniN']["interfaceCfgIngressBwp"]["bwpCfgEir"] = uniBwpCfgEir

+

+            r2mp_json_data["SCA_ETH_Flow_Points"].append(uni_json_data)

+

+        return r2mp_json_data

+

+    def create_network_connectivity(self, obj):

+

+        restCtrlUrl = self.networkdevice.restCtrlUrl

+        username = self.networkdevice.username

+        password = self.networkdevice.password

+

+        evcServiceType = obj.type

+        if (evcServiceType == "Point_To_Point"):

+            network_connectivity_json_data = self.create_point_to_point_connectivity_json_data(obj)

+        elif (evcServiceType == "Multipoint_To_Multipoint"):

+            network_connectivity_json_data = self.create_multipoint_to_multipoint_connectivity_json_data(obj)

+        elif (evcServiceType == "Root_Multipoint"):

+            network_connectivity_json_data = self.create_root_multipoint_connectivity_json_data(obj)

+        else:

+            raise Exception("OnosApiError: get_network_links() - unknown link type")

 

         headers = {'Content-Type': 'application/json'}

-

         resp = requests.post('{}/mef-sca-api/SCA_ETH_FDFr_EC'.format(restCtrlUrl),

-                             data=json.dumps(data), headers=headers, auth=HTTPBasicAuth(username, password))

+                             data=json.dumps(network_connectivity_json_data), headers=headers, auth=HTTPBasicAuth(username, password))

 

         if resp.status_code == 201:

             result = resp.json()

@@ -192,6 +431,7 @@
             obj.adminstate = "invalid"  # what's the meaning?

             obj.operstate = "inactive"

             obj.backend_status = '204 - No network resource'

+

             return False

 

         elif resp.status_code == 500:

@@ -201,9 +441,9 @@
             return False

 

         else:

-            raise Exception("OnosApiError: create_point_to_point_connectivity()")

+            raise Exception("OnosApiError: create_network_connectivity()")

 

-    def delete_point_to_point_connectivity(self, obj):

+    def delete_network_connectivity(self, obj):

 

         restCtrlUrl = self.networkdevice.restCtrlUrl

         username = self.networkdevice.username

@@ -229,4 +469,4 @@
             return False

 

         else:

-            raise Exception("OnosApiError: delete_point_to_point_connectivity()")

+            raise Exception("OnosApiError: delete_network_connectivity()")

diff --git a/xos/synchronizer/providers/metronetworktestprovider.py b/xos/synchronizer/providers/metronetworktestprovider.py
index 3c3f179..3bcbb92 100644
--- a/xos/synchronizer/providers/metronetworktestprovider.py
+++ b/xos/synchronizer/providers/metronetworktestprovider.py
@@ -3,6 +3,7 @@
 
 from xos.logger import Logger, logging
 from services.metronetwork.models import *
+from core.models import Site
 from synchronizers.metronetwork.providers.metronetworkprovider import MetroNetworkProvider
 
 logger = Logger(level=logging.INFO)
@@ -15,40 +16,18 @@
     # Method for retrieving all network ports from the backend system
     def get_network_ports(self):
         #
-        # Our Test Network Consists of three NetworkDevices (which correspond to ONOS instances):
+        # Our Test Network Consists of one NetworkDevice (which correspond to ONOS instance):
         #
-        #                    ONOS1-CORDPOD1
-        #                    ONOS2-MetroNetwork
-        #                    ONOW3-CORDPOD2
+        #  8 Ports
+        #  1 Eline (2 ports)
+        #  1 Etree (3 ports)
+        #  1 Elan (3 ports)
         #
-        #
-        #    Uni-NetworkEdgePort3--
-        #    Uni-NetworkEdgePort11-
-        #    Uni-NetworkEdgePort5--ONOS1-CORDPOD1-NetworkPort6
-        #            NetworkPort4--                     |
-        #                                        NetworkPort1-ONOS2-MetroNetwork
-        #                                        NetworkPort2-
-        #                                             |
-        #    Uni-NetworkEdgePort7--ONOS3-CORDPOD2-NetworkPort10
-        #    Uni-NetworkEdgePort9--
-        #    Uni-NetworkEdgePort12-
-        #        NetworkPort8--
-        #
-        #  Note: NetworkPorts can be endpoints of Interlinks and NetworkPointToPointConnections
-        #              they can be seem as a simple port.
-        #        NetworkEdgePorts are UNIs in the network, so are specicially user facing.
-        #
-        #
-        # InterLinks - Port1 - Port6
-        #              Port2 - Port10
-        #
-        # NetworkPointToPointConnections: Port1 - Port2
-        #                                 Port4 - Port6
-        #                                 Port8 - Port10
-        #
-        # NetworkEdgeToEdgePointConnections: Port3 - Port7
-        #
-        # NetworkMultipointConnection: Port11 - Port5 - Port9 - Port12
+        #  2 Sites - Representing Two R-CORD Pods
+        #  2 Ports, One-per-site
+        #  1 Bandwidth Profile
+        #  2 Service Spokes
+        #  1 VnodGlobalService
 
         objs = []
 
@@ -56,14 +35,9 @@
         if self.networkdevice.id != 'TestMetroNet':
             return objs
 
-        # Ok - in the test class we cheat and create one NetworkDevice with 8 NetworkEdgePorts
+        # Set the parent device id to just be the Test NetworkDevice
         device1 = NetworkDevice()
-        device1.id = 'TestCORDNet'
-        device1.administrativeState = 'enabled'
-        device1.restCtrlUrl = 'testCordPod1.onlab.net:8000'
-        device1.username = 'karaf'
-        device1.password = 'karaf'
-        objs.append(device1)
+        device1.id = self.networkdevice.id
 
         port1 = NetworkEdgePort()
         port1.element = device1
@@ -176,11 +150,6 @@
         for port in allports:
             objs.append(port)
 
-        # Ok - in the test class we cheat and take down the adjunct Fake NetworkDevices Devices
-        device1 = NetworkDevice()
-        device1.id = 'TestCORDNet'
-        objs.append(device1)
-
         return objs
 
     def get_network_links(self):
@@ -189,7 +158,7 @@
 
         # Connectivity object - Point to Point
         cordpod1device = NetworkDevice()
-        cordpod1device.id = 'TestCORDNet'
+        cordpod1device.id = self.networkdevice.id
 
         # Edge to Edge Point Connectivity Objects
         edgetoedgeconnectivity = NetworkEdgeToEdgePointConnection()
@@ -239,6 +208,81 @@
         edge2multipointconnectivity.eps_createbuffer = json.dumps(myjsonstr)
         objs.append(edge2multipointconnectivity)
 
+        # Create Objects for VnodGlobal Sort of Testing
+
+        # Bandwidth Profile
+
+        bwprofile = BandwidthProfile()
+        bwprofile.bwpcfgcbs  = 0
+        bwprofile.bwpcfgcir = 0
+        bwprofile.bwpcfgebs = 0
+        bwprofile.bwpcfgeir = 0
+        bwprofile.name = 'TestBWP'
+        objs.append(bwprofile)
+
+        # Two Sites
+        site1 = Site()
+        site1.name = 'CORDPod1'
+        site1.login_base = 'CordPod1'
+        site1.site_url = 'http://1.2.3.4:8080/VnodLocalApi'
+        objs.append(site1)
+
+        site2 = Site()
+        site2.name = 'CORDPod2'
+        site2.login_base = 'CordPod2'
+        site2.site_url = 'http://10.11.12.13:8080/VnodLocalApi'
+        objs.append(site2)
+
+        # Two Ports - one per Site
+
+        remoteport1 = RemotePort()
+        remoteport1.name = "CORDPOD1:Port1"
+        remoteport1.sitename = 'CordPod1'
+        remoteport1.edgeportname = cordpod1device.id + "." + "of:000000001/1"
+        objs.append(remoteport1)
+
+        remoteport2 = RemotePort()
+        remoteport2.name = "CORDPOD2:Port1"
+        remoteport2.sitename = 'CordPod2'
+        remoteport2.edgeportname = cordpod1device.id + "." + "of:000000001/2"
+        objs.append(remoteport2)
+
+        # One Spoke/Site
+        spoke1 = ServiceSpoke()
+        spoke1.name = 'TestSpoke1'
+        spoke1.remoteportname = "CORDPOD1:Port1"
+        spoke1.remotevnodid = 'CORDPod1:VnodLocal:1'
+        spoke1.operstate = 'inactive'
+        spoke1.adminstate = 'enabled'
+        spoke1.sitename = 'CordPod1'
+        objs.append(spoke1)
+
+        spoke2 = ServiceSpoke()
+        spoke2.name = 'TestSpoke2'
+        spoke2.remoteportname = "CORDPOD2:Port1"
+        spoke2.remotevnodid = 'CORDPod2:VnodLocal:1'
+        spoke2.operstate = 'active'
+        spoke2.adminstate = 'enabled'
+        spoke2.sitename = 'CordPod2'
+        objs.append(spoke2)
+
+        # One VnodGlobal Service
+        vnodglobal = VnodGlobalService()
+        vnodglobal.name = 'VnodGlobalPtToPtTest1'
+        vnodglobal.type = 'eline'
+        vnodglobal.vlanid = '100'
+        vnodglobal.operstate = 'active'
+        vnodglobal.adminstate = 'enabled'
+        vnodglobal.servicehandle = 'testhandle1'
+        vnodglobal.pointtopointsid = 'onos_eline_id'
+        vnodglobal.bwpname = 'TestBWP'
+
+        # Create JSON array for post-save behaviour
+        #
+        spokes = ['TestSpoke1', 'TestSpoke2']
+        myjsonstr = {'spokes': spokes}
+        vnodglobal.spokes_createbuffer = json.dumps(myjsonstr)
+        objs.append(vnodglobal)
 
         return objs
 
diff --git a/xos/synchronizer/steps/sync_metronetworkservice.py b/xos/synchronizer/steps/sync_metronetworkservice.py
index 5c3fe70..56fd5d5 100644
--- a/xos/synchronizer/steps/sync_metronetworkservice.py
+++ b/xos/synchronizer/steps/sync_metronetworkservice.py
@@ -1,5 +1,5 @@
-import os
-import sys
+import os, sys
+from itertools import chain
 
 from synchronizers.base.syncstep import SyncStep
 from services.metronetwork.models import *
@@ -119,12 +119,15 @@
 
                 # Handle changes XOS -> ONOS
                 # Check for ConnectivityObjects that are in acticationequested state - creates to the backend
-                activatereqs = NetworkEdgeToEdgePointConnection.objects.filter(adminstate='activationrequested')
+                p2pactivatereqs = NetworkEdgeToEdgePointConnection.objects.filter(adminstate='activationrequested')
+                mp2mpactivatereqs = NetworkMultipointToMultipointConnection.objects.filter(adminstate='activationrequested')
+                r2mpactivatereqs = NetworkEdgeToMultipointConnection.objects.filter(adminstate='activationrequested')
+                activatereqs = list(chain(p2pactivatereqs, mp2mpactivatereqs, r2mpactivatereqs))
                 for activatereq in activatereqs:
 
                     # Call the XOS Interface to create the service
                     logger.debug("Attempting to create EdgePointToEdgePointConnectivity: %s" % activatereq.id)
-                    if (provider.create_point_to_point_connectivity(activatereq)):
+                    if (provider.create_network_connectivity(activatereq)):
                         # Everyting is OK, lets let the system handle the persist
                         objs.append(activatereq)
                     else:
@@ -133,12 +136,15 @@
                         activatereq.save()
 
                 # Check for ConnectivityObjects that are in deacticationequested state - deletes to the backend
-                deactivatereqs = NetworkEdgeToEdgePointConnection.objects.filter(adminstate='deactivationrequested')
+                p2pdeactivatereqs = NetworkEdgeToEdgePointConnection.objects.filter(adminstate='deactivationrequested')
+                mp2mpdeactivatereqs = NetworkMultipointToMultipointConnection.objects.filter(adminstate='deactivationrequested')
+                r2mpdeactivatereqs = NetworkEdgeToMultipointConnection.objects.filter(adminstate='deactivationrequested')
+                deactivatereqs = list(chain(p2pdeactivatereqs, mp2mpdeactivatereqs, r2mpdeactivatereqs))
                 for deactivatereq in deactivatereqs:
 
                     # Call the XOS Interface to delete the service
                     logger.debug("Attempting to delete EdgePointToEdgePointConnectivity: %s" % deactivatereq.id)
-                    if provider.delete_point_to_point_connectivity(deactivatereq):
+                    if provider.delete_network_connectivity(deactivatereq):
                         # Everyting is OK, lets let the system handle the persist
                         objs.append(deactivatereq)
                     else:
@@ -160,6 +166,57 @@
                     # Simply put in the queue for update - this will handle both new and changed objects
                     objs.append(eventobj)
 
+                # Handle the case where we have deleted Eline Services from our side - if the Service is in
+                # enabled state then we call the provider, otherwise just queue it for deletion
+                elinedeletedobjs = NetworkEdgeToEdgePointConnection.deleted_objects.all()
+                for elinedeletedobj in elinedeletedobjs:
+                    if elinedeletedobj.adminstate == 'enabled':
+                        provider.delete_network_connectivity(elinedeletedobj)
+                    # Either way queue it for deletion
+                    objs.append(elinedeletedobj)
+
+                # Handle the case where we have deleted Etree Services from our side - if the Service is in
+                # enabled state then we call the provider, otherwise just queue it for deletion
+                etreedeletedobjs = NetworkEdgeToMultipointConnection.deleted_objects.all()
+                for etreedeletedobj in etreedeletedobjs:
+                    # TODO: Handle the case where its connected, we need to disconnect first
+                    if etreedeletedobj.adminstate == 'enabled':
+                        provider.delete_network_connectivity(etreedeletedobj)
+                    # Either way queue it for deletion
+                    objs.append(etreedeletedobj)
+
+                # Handle the case where we have deleted Elan Services from our side - if the Service is in
+                # enabled state then we call the provider, otherwise just queue it for deletion
+                elandeletedobjs = NetworkMultipointToMultipointConnection.deleted_objects.all()
+                for elandeletedobj in elandeletedobjs:
+                    # TODO: Handle the case where its connected, we need to disconnect first
+                    if elandeletedobj.adminstate == 'enabled':
+                        provider.delete_network_connectivity(elandeletedobj)
+                    # Either way queue it for deletion
+                    objs.append(elandeletedobj)
+
+                # Handle the case where we have deleted VnodGlobal Services from our side - if there is
+                # an attached Eline/Etree/Elan we set that to deleted
+                vnodbloaldeletedobjs = VnodGlobalService.deleted_objects.all()
+                for vnodbloaldeletedobj in vnodbloaldeletedobjs:
+                    # Check for dependent eline service
+                    if vnodbloaldeletedobj.metronetworkpointtopoint is not None:
+                        elineobj = vnodbloaldeletedobj.metronetworkpointtopoint
+                        elineobj.deleted = True
+                        objs.append(elineobj)
+                    # Check for dependent elan service
+                    if vnodbloaldeletedobj.metronetworkmultipoint is not None:
+                        elanobj = vnodbloaldeletedobj.metronetworkmultipoint
+                        elanobj.deleted = True
+                        objs.append(elanobj)
+                    # Check for dependent etree service
+                    if vnodbloaldeletedobj.metronetworkroottomultipoint is not None:
+                        etreeobj = vnodbloaldeletedobj.metronetworkroottomultipoint
+                        etreeobj.deleted = True
+                        objs.append(etreeobj)
+
+                    objs.append(vnodbloaldeletedobj)
+
         # In add cases return the objects we are interested in
         return objs