blob: 36dec3bcb8e691acb6fdcdfd49059518cd5ebd44 [file] [log] [blame]
# Copyright 2017-present Open Networking Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os, sys
from itertools import chain
from synchronizers.new_base.SyncInstanceUsingAnsible import SyncInstanceUsingAnsible #if needed
from synchronizers.new_base.ansible_helper import run_template_ssh #if needed
from synchronizers.new_base.modelaccessor import *
from xos.logger import Logger, logging
from synchronizers.metronetwork.providers.providerfactory import ProviderFactory
from synchronizers.metronetwork.invokers.invokerfactory import InvokerFactory
# metronetwork will be in steps/..
parentdir = os.path.join(os.path.dirname(__file__), "..")
sys.path.insert(0, parentdir)
logger = Logger(level=logging.INFO)
class SyncMetroNetworkSystem(SyncStep):
provides = [MetroNetworkSystem]
observes = MetroNetworkSystem
requested_interval = 0
initialized = False
def __init__(self, **args):
SyncStep.__init__(self, **args)
def fetch_pending(self, deletion=False):
# The general idea:
# We do one of two things in here:
# 1. Full Synchronization of the DBS (XOS <-> MetroONOS)
# 2. Look for updates between the two stores
# The first thing is potentially a much bigger
# operation and should not happen as often
#
# The Sync operation must take into account the 'deletion' flag
objs = []
# Get the NetworkSystem object - if it exists it will test us
# whether we should do a full sync or not - it all has our config
# information about the REST interface
metronetworksystem = self.get_metronetwork_system()
if not metronetworksystem:
logger.debug("No Service configured")
return objs
# Check to make sure the Metro Network System is enabled
metronetworksystem = self.get_metronetwork_system()
if metronetworksystem.administrativeState == 'disabled':
# Nothing to do
logger.debug("MetroService configured - state is Disabled")
return objs
# The Main Loop - retrieve all the NetworkDevice objects - for each of these
# Apply synchronization aspects
networkdevices = NetworkDevice.objects.all()
for dev in networkdevices:
# Set up the provider
provider = ProviderFactory.getprovider(dev)
# First check is for the AdminState of Disabled - do nothing
if dev.administrativeState == 'disabled':
# Nothing to do with this device
logger.debug("NetworkDevice %s: administrativeState set to Disabled - continuing" % dev.id)
# Now to the main options - are we syncing - deletion portion
elif dev.administrativeState == 'syncrequested' and deletion is True:
logger.info("NetworkDevice %s: administrativeState set to SyncRequested" % dev.id)
# Kill Links
networklinks = provider.get_network_links_for_deletion()
for link in networklinks:
objs.append(link)
# Kill Ports
allports = provider.get_network_ports_for_deletion()
for port in allports:
objs.append(port)
logger.info("NetworkDevice %s: Deletion part of Sync completed" % dev.id)
dev.administrativeState = 'syncinprogress'
dev.save(update_fields=['administrativeState'])
# Now to the main options - are we syncing - creation portion
elif dev.administrativeState == 'syncinprogress' and deletion is False:
logger.info("NetworkDevice %s: administrativeState set to SyncRequested" % dev.id)
# Reload objects in the reverse order of deletion
# Add Ports
networkports = provider.get_network_ports()
for port in networkports:
objs.append(port)
# Add Links
networklinks = provider.get_network_links()
for link in networklinks:
objs.append(link)
logger.info("NetworkDevice %s: Creation part of Sync completed" % dev.id)
dev.administrativeState = 'enabled'
dev.save(update_fields=['administrativeState'])
# If we are enabled - then check for events - in either direction and sync
elif dev.administrativeState == 'enabled' and deletion is False:
logger.debug("NetworkDevice: administrativeState set to Enabled - non deletion phase")
# This should be the 'normal running state' when we are not deleting - a few things to do in here
# Get the changed objects from the provider - deletions are handled separately
eventobjs = provider.get_updated_or_created_objects()
for eventobj in eventobjs:
# Simply put in the queue for update - this will handle both new and changed objects
objs.append(eventobj)
# Handle changes XOS -> ONOS
# Check for ConnectivityObjects that are in acticationequested state - creates to the backend
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_network_connectivity(activatereq)):
# Everyting is OK, lets let the system handle the persist
objs.append(activatereq)
else:
# In the case of an error we persist the state of the object directly to preserve
# the error code - and because that is how the base synchronizer is designed
activatereq.save()
# Check for ConnectivityObjects that are in deacticationequested state - deletes to the backend
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_network_connectivity(deactivatereq):
# Everyting is OK, lets let the system handle the persist
objs.append(deactivatereq)
else:
# In the case of an error we persist the state of the object directly to preserve
# the error code - and because that is how the base synchronizer is designed
deactivatereq.save()
# If we are enabled - and in our deletion pass then look for objects waiting for deletion
elif dev.administrativeState == 'enabled' and deletion is True:
logger.debug("NetworkDevice: administrativeState set to Enabled - deletion phase")
# Any object that is simply deleted in the model gets removed automatically - the synchronizer
# doesn't get involved - we just need to check for deleted objects in the domain and reflect that
# in the model
#
# Get the deleted objects from the provider
eventobjs = provider.get_deleted_objects()
for eventobj in eventobjs:
# 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
def sync_record(self, o):
# First we call and see if there is an invoker for this object - the idea of the invoker
# is to wrap the save with a pre/post paradigm to handle special cases
# It will only exist for a subset of ojbects
invoker = InvokerFactory.getinvoker(o)
# Call Pre-save on the inovker (if it exists)
if invoker is not None:
invoker.presave(o)
# Simply save the record to the DB - both updates and adds are handled the same way
o.save()
# Call Post-save on the inovker (if it exists)
if invoker is not None:
invoker.postsave(o)
def delete_record(self, o):
# Overriden to customize our behaviour - the core sync step for will remove the record directly
# We just log and return
logger.debug("deleting Object %s" % str(o), extra=o.tologdict())
def get_metronetwork_system(self):
# We only expect to have one of these objects in the system in the curent design
# So get the first element from the query
metronetworksystem = MetroNetworkSystem.objects.all()
if not metronetworksystem:
return None
return metronetworksystem[0]