blob: 36dec3bcb8e691acb6fdcdfd49059518cd5ebd44 [file] [log] [blame]
Matteo Scandolofcf842e2017-08-08 13:05:25 -07001
2# Copyright 2017-present Open Networking Foundation
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16
Rizwan Haidere6ffdc02016-11-08 13:43:48 -050017import os, sys
18from itertools import chain
Rizwan Haider30b33792016-08-18 02:11:18 -040019
Andrea Campanellaade88482017-04-05 12:39:52 +020020from synchronizers.new_base.SyncInstanceUsingAnsible import SyncInstanceUsingAnsible #if needed
21from synchronizers.new_base.ansible_helper import run_template_ssh #if needed
22from synchronizers.new_base.modelaccessor import *
Rizwan Haider30b33792016-08-18 02:11:18 -040023from xos.logger import Logger, logging
24from synchronizers.metronetwork.providers.providerfactory import ProviderFactory
Rizwan Haider65baf552016-09-28 16:47:28 -040025from synchronizers.metronetwork.invokers.invokerfactory import InvokerFactory
Rizwan Haider30b33792016-08-18 02:11:18 -040026
27# metronetwork will be in steps/..
28parentdir = os.path.join(os.path.dirname(__file__), "..")
29sys.path.insert(0, parentdir)
30
31logger = Logger(level=logging.INFO)
32
33
Rizwan Haider65baf552016-09-28 16:47:28 -040034class SyncMetroNetworkSystem(SyncStep):
35 provides = [MetroNetworkSystem]
36 observes = MetroNetworkSystem
Rizwan Haider30b33792016-08-18 02:11:18 -040037 requested_interval = 0
38 initialized = False
39
40 def __init__(self, **args):
41 SyncStep.__init__(self, **args)
42
43 def fetch_pending(self, deletion=False):
44
45 # The general idea:
46 # We do one of two things in here:
47 # 1. Full Synchronization of the DBS (XOS <-> MetroONOS)
48 # 2. Look for updates between the two stores
49 # The first thing is potentially a much bigger
50 # operation and should not happen as often
51 #
52 # The Sync operation must take into account the 'deletion' flag
53
54 objs = []
55
Rizwan Haider65baf552016-09-28 16:47:28 -040056 # Get the NetworkSystem object - if it exists it will test us
Rizwan Haider30b33792016-08-18 02:11:18 -040057 # whether we should do a full sync or not - it all has our config
58 # information about the REST interface
59
Rizwan Haider65baf552016-09-28 16:47:28 -040060 metronetworksystem = self.get_metronetwork_system()
61 if not metronetworksystem:
Rizwan Haider30b33792016-08-18 02:11:18 -040062 logger.debug("No Service configured")
63 return objs
64
Rizwan Haider65baf552016-09-28 16:47:28 -040065 # Check to make sure the Metro Network System is enabled
66 metronetworksystem = self.get_metronetwork_system()
67 if metronetworksystem.administrativeState == 'disabled':
Rizwan Haider30b33792016-08-18 02:11:18 -040068 # Nothing to do
69 logger.debug("MetroService configured - state is Disabled")
70 return objs
71
72 # The Main Loop - retrieve all the NetworkDevice objects - for each of these
73 # Apply synchronization aspects
74 networkdevices = NetworkDevice.objects.all()
75
76 for dev in networkdevices:
77
78 # Set up the provider
79 provider = ProviderFactory.getprovider(dev)
80
81 # First check is for the AdminState of Disabled - do nothing
82 if dev.administrativeState == 'disabled':
83 # Nothing to do with this device
84 logger.debug("NetworkDevice %s: administrativeState set to Disabled - continuing" % dev.id)
85
86 # Now to the main options - are we syncing - deletion portion
87 elif dev.administrativeState == 'syncrequested' and deletion is True:
88
89 logger.info("NetworkDevice %s: administrativeState set to SyncRequested" % dev.id)
90
91 # Kill Links
92 networklinks = provider.get_network_links_for_deletion()
93 for link in networklinks:
94 objs.append(link)
95
96 # Kill Ports
97 allports = provider.get_network_ports_for_deletion()
98 for port in allports:
99 objs.append(port)
100
101 logger.info("NetworkDevice %s: Deletion part of Sync completed" % dev.id)
102 dev.administrativeState = 'syncinprogress'
103 dev.save(update_fields=['administrativeState'])
104
105 # Now to the main options - are we syncing - creation portion
106 elif dev.administrativeState == 'syncinprogress' and deletion is False:
107
108 logger.info("NetworkDevice %s: administrativeState set to SyncRequested" % dev.id)
109 # Reload objects in the reverse order of deletion
110
111 # Add Ports
112 networkports = provider.get_network_ports()
113 for port in networkports:
114 objs.append(port)
115
116 # Add Links
117 networklinks = provider.get_network_links()
118 for link in networklinks:
119 objs.append(link)
120
121 logger.info("NetworkDevice %s: Creation part of Sync completed" % dev.id)
122 dev.administrativeState = 'enabled'
123 dev.save(update_fields=['administrativeState'])
124
125 # If we are enabled - then check for events - in either direction and sync
126 elif dev.administrativeState == 'enabled' and deletion is False:
127 logger.debug("NetworkDevice: administrativeState set to Enabled - non deletion phase")
128
129 # This should be the 'normal running state' when we are not deleting - a few things to do in here
130
131 # Get the changed objects from the provider - deletions are handled separately
132 eventobjs = provider.get_updated_or_created_objects()
133 for eventobj in eventobjs:
134 # Simply put in the queue for update - this will handle both new and changed objects
135 objs.append(eventobj)
136
137 # Handle changes XOS -> ONOS
138 # Check for ConnectivityObjects that are in acticationequested state - creates to the backend
Rizwan Haidere6ffdc02016-11-08 13:43:48 -0500139 p2pactivatereqs = NetworkEdgeToEdgePointConnection.objects.filter(adminstate='activationrequested')
140 mp2mpactivatereqs = NetworkMultipointToMultipointConnection.objects.filter(adminstate='activationrequested')
141 r2mpactivatereqs = NetworkEdgeToMultipointConnection.objects.filter(adminstate='activationrequested')
142 activatereqs = list(chain(p2pactivatereqs, mp2mpactivatereqs, r2mpactivatereqs))
Rizwan Haider30b33792016-08-18 02:11:18 -0400143 for activatereq in activatereqs:
144
145 # Call the XOS Interface to create the service
146 logger.debug("Attempting to create EdgePointToEdgePointConnectivity: %s" % activatereq.id)
Rizwan Haidere6ffdc02016-11-08 13:43:48 -0500147 if (provider.create_network_connectivity(activatereq)):
Rizwan Haider30b33792016-08-18 02:11:18 -0400148 # Everyting is OK, lets let the system handle the persist
149 objs.append(activatereq)
150 else:
151 # In the case of an error we persist the state of the object directly to preserve
152 # the error code - and because that is how the base synchronizer is designed
153 activatereq.save()
154
155 # Check for ConnectivityObjects that are in deacticationequested state - deletes to the backend
Rizwan Haidere6ffdc02016-11-08 13:43:48 -0500156 p2pdeactivatereqs = NetworkEdgeToEdgePointConnection.objects.filter(adminstate='deactivationrequested')
157 mp2mpdeactivatereqs = NetworkMultipointToMultipointConnection.objects.filter(adminstate='deactivationrequested')
158 r2mpdeactivatereqs = NetworkEdgeToMultipointConnection.objects.filter(adminstate='deactivationrequested')
159 deactivatereqs = list(chain(p2pdeactivatereqs, mp2mpdeactivatereqs, r2mpdeactivatereqs))
Rizwan Haider30b33792016-08-18 02:11:18 -0400160 for deactivatereq in deactivatereqs:
161
162 # Call the XOS Interface to delete the service
163 logger.debug("Attempting to delete EdgePointToEdgePointConnectivity: %s" % deactivatereq.id)
Rizwan Haidere6ffdc02016-11-08 13:43:48 -0500164 if provider.delete_network_connectivity(deactivatereq):
Rizwan Haider30b33792016-08-18 02:11:18 -0400165 # Everyting is OK, lets let the system handle the persist
166 objs.append(deactivatereq)
167 else:
168 # In the case of an error we persist the state of the object directly to preserve
169 # the error code - and because that is how the base synchronizer is designed
170 deactivatereq.save()
171
172 # If we are enabled - and in our deletion pass then look for objects waiting for deletion
173 elif dev.administrativeState == 'enabled' and deletion is True:
174 logger.debug("NetworkDevice: administrativeState set to Enabled - deletion phase")
175
176 # Any object that is simply deleted in the model gets removed automatically - the synchronizer
177 # doesn't get involved - we just need to check for deleted objects in the domain and reflect that
178 # in the model
179 #
180 # Get the deleted objects from the provider
181 eventobjs = provider.get_deleted_objects()
182 for eventobj in eventobjs:
183 # Simply put in the queue for update - this will handle both new and changed objects
184 objs.append(eventobj)
185
Rizwan Haidere6ffdc02016-11-08 13:43:48 -0500186 # Handle the case where we have deleted Eline Services from our side - if the Service is in
187 # enabled state then we call the provider, otherwise just queue it for deletion
188 elinedeletedobjs = NetworkEdgeToEdgePointConnection.deleted_objects.all()
189 for elinedeletedobj in elinedeletedobjs:
190 if elinedeletedobj.adminstate == 'enabled':
191 provider.delete_network_connectivity(elinedeletedobj)
192 # Either way queue it for deletion
193 objs.append(elinedeletedobj)
194
195 # Handle the case where we have deleted Etree Services from our side - if the Service is in
196 # enabled state then we call the provider, otherwise just queue it for deletion
197 etreedeletedobjs = NetworkEdgeToMultipointConnection.deleted_objects.all()
198 for etreedeletedobj in etreedeletedobjs:
199 # TODO: Handle the case where its connected, we need to disconnect first
200 if etreedeletedobj.adminstate == 'enabled':
201 provider.delete_network_connectivity(etreedeletedobj)
202 # Either way queue it for deletion
203 objs.append(etreedeletedobj)
204
205 # Handle the case where we have deleted Elan Services from our side - if the Service is in
206 # enabled state then we call the provider, otherwise just queue it for deletion
207 elandeletedobjs = NetworkMultipointToMultipointConnection.deleted_objects.all()
208 for elandeletedobj in elandeletedobjs:
209 # TODO: Handle the case where its connected, we need to disconnect first
210 if elandeletedobj.adminstate == 'enabled':
211 provider.delete_network_connectivity(elandeletedobj)
212 # Either way queue it for deletion
213 objs.append(elandeletedobj)
214
215 # Handle the case where we have deleted VnodGlobal Services from our side - if there is
216 # an attached Eline/Etree/Elan we set that to deleted
217 vnodbloaldeletedobjs = VnodGlobalService.deleted_objects.all()
218 for vnodbloaldeletedobj in vnodbloaldeletedobjs:
219 # Check for dependent eline service
220 if vnodbloaldeletedobj.metronetworkpointtopoint is not None:
221 elineobj = vnodbloaldeletedobj.metronetworkpointtopoint
222 elineobj.deleted = True
223 objs.append(elineobj)
224 # Check for dependent elan service
225 if vnodbloaldeletedobj.metronetworkmultipoint is not None:
226 elanobj = vnodbloaldeletedobj.metronetworkmultipoint
227 elanobj.deleted = True
228 objs.append(elanobj)
229 # Check for dependent etree service
230 if vnodbloaldeletedobj.metronetworkroottomultipoint is not None:
231 etreeobj = vnodbloaldeletedobj.metronetworkroottomultipoint
232 etreeobj.deleted = True
233 objs.append(etreeobj)
234
235 objs.append(vnodbloaldeletedobj)
236
Rizwan Haider30b33792016-08-18 02:11:18 -0400237 # In add cases return the objects we are interested in
238 return objs
239
240 def sync_record(self, o):
Rizwan Haider65baf552016-09-28 16:47:28 -0400241
242 # First we call and see if there is an invoker for this object - the idea of the invoker
243 # is to wrap the save with a pre/post paradigm to handle special cases
244 # It will only exist for a subset of ojbects
245 invoker = InvokerFactory.getinvoker(o)
246
247 # Call Pre-save on the inovker (if it exists)
248 if invoker is not None:
249 invoker.presave(o)
250
Rizwan Haider30b33792016-08-18 02:11:18 -0400251 # Simply save the record to the DB - both updates and adds are handled the same way
252 o.save()
253
Rizwan Haider65baf552016-09-28 16:47:28 -0400254 # Call Post-save on the inovker (if it exists)
255 if invoker is not None:
256 invoker.postsave(o)
257
Rizwan Haider30b33792016-08-18 02:11:18 -0400258 def delete_record(self, o):
259 # Overriden to customize our behaviour - the core sync step for will remove the record directly
260 # We just log and return
261 logger.debug("deleting Object %s" % str(o), extra=o.tologdict())
262
Rizwan Haider65baf552016-09-28 16:47:28 -0400263 def get_metronetwork_system(self):
Rizwan Haider30b33792016-08-18 02:11:18 -0400264 # We only expect to have one of these objects in the system in the curent design
265 # So get the first element from the query
Rizwan Haider65baf552016-09-28 16:47:28 -0400266 metronetworksystem = MetroNetworkSystem.objects.all()
267 if not metronetworksystem:
Rizwan Haider30b33792016-08-18 02:11:18 -0400268 return None
269
Rizwan Haider65baf552016-09-28 16:47:28 -0400270 return metronetworksystem[0]