Rizwan Haider | e6ffdc0 | 2016-11-08 13:43:48 -0500 | [diff] [blame] | 1 | import os, sys |
| 2 | from itertools import chain |
Rizwan Haider | 30b3379 | 2016-08-18 02:11:18 -0400 | [diff] [blame] | 3 | |
| 4 | from synchronizers.base.syncstep import SyncStep |
Rizwan Haider | eb2cc77 | 2016-09-08 12:14:55 -0400 | [diff] [blame] | 5 | from services.metronetwork.models import * |
Rizwan Haider | 30b3379 | 2016-08-18 02:11:18 -0400 | [diff] [blame] | 6 | from xos.logger import Logger, logging |
| 7 | from synchronizers.metronetwork.providers.providerfactory import ProviderFactory |
Rizwan Haider | 65baf55 | 2016-09-28 16:47:28 -0400 | [diff] [blame] | 8 | from synchronizers.metronetwork.invokers.invokerfactory import InvokerFactory |
Rizwan Haider | 30b3379 | 2016-08-18 02:11:18 -0400 | [diff] [blame] | 9 | |
| 10 | # metronetwork will be in steps/.. |
| 11 | parentdir = os.path.join(os.path.dirname(__file__), "..") |
| 12 | sys.path.insert(0, parentdir) |
| 13 | |
| 14 | logger = Logger(level=logging.INFO) |
| 15 | |
| 16 | |
Rizwan Haider | 65baf55 | 2016-09-28 16:47:28 -0400 | [diff] [blame] | 17 | class SyncMetroNetworkSystem(SyncStep): |
| 18 | provides = [MetroNetworkSystem] |
| 19 | observes = MetroNetworkSystem |
Rizwan Haider | 30b3379 | 2016-08-18 02:11:18 -0400 | [diff] [blame] | 20 | requested_interval = 0 |
| 21 | initialized = False |
| 22 | |
| 23 | def __init__(self, **args): |
| 24 | SyncStep.__init__(self, **args) |
| 25 | |
| 26 | def fetch_pending(self, deletion=False): |
| 27 | |
| 28 | # The general idea: |
| 29 | # We do one of two things in here: |
| 30 | # 1. Full Synchronization of the DBS (XOS <-> MetroONOS) |
| 31 | # 2. Look for updates between the two stores |
| 32 | # The first thing is potentially a much bigger |
| 33 | # operation and should not happen as often |
| 34 | # |
| 35 | # The Sync operation must take into account the 'deletion' flag |
| 36 | |
| 37 | objs = [] |
| 38 | |
Rizwan Haider | 65baf55 | 2016-09-28 16:47:28 -0400 | [diff] [blame] | 39 | # Get the NetworkSystem object - if it exists it will test us |
Rizwan Haider | 30b3379 | 2016-08-18 02:11:18 -0400 | [diff] [blame] | 40 | # whether we should do a full sync or not - it all has our config |
| 41 | # information about the REST interface |
| 42 | |
Rizwan Haider | 65baf55 | 2016-09-28 16:47:28 -0400 | [diff] [blame] | 43 | metronetworksystem = self.get_metronetwork_system() |
| 44 | if not metronetworksystem: |
Rizwan Haider | 30b3379 | 2016-08-18 02:11:18 -0400 | [diff] [blame] | 45 | logger.debug("No Service configured") |
| 46 | return objs |
| 47 | |
Rizwan Haider | 65baf55 | 2016-09-28 16:47:28 -0400 | [diff] [blame] | 48 | # Check to make sure the Metro Network System is enabled |
| 49 | metronetworksystem = self.get_metronetwork_system() |
| 50 | if metronetworksystem.administrativeState == 'disabled': |
Rizwan Haider | 30b3379 | 2016-08-18 02:11:18 -0400 | [diff] [blame] | 51 | # Nothing to do |
| 52 | logger.debug("MetroService configured - state is Disabled") |
| 53 | return objs |
| 54 | |
| 55 | # The Main Loop - retrieve all the NetworkDevice objects - for each of these |
| 56 | # Apply synchronization aspects |
| 57 | networkdevices = NetworkDevice.objects.all() |
| 58 | |
| 59 | for dev in networkdevices: |
| 60 | |
| 61 | # Set up the provider |
| 62 | provider = ProviderFactory.getprovider(dev) |
| 63 | |
| 64 | # First check is for the AdminState of Disabled - do nothing |
| 65 | if dev.administrativeState == 'disabled': |
| 66 | # Nothing to do with this device |
| 67 | logger.debug("NetworkDevice %s: administrativeState set to Disabled - continuing" % dev.id) |
| 68 | |
| 69 | # Now to the main options - are we syncing - deletion portion |
| 70 | elif dev.administrativeState == 'syncrequested' and deletion is True: |
| 71 | |
| 72 | logger.info("NetworkDevice %s: administrativeState set to SyncRequested" % dev.id) |
| 73 | |
| 74 | # Kill Links |
| 75 | networklinks = provider.get_network_links_for_deletion() |
| 76 | for link in networklinks: |
| 77 | objs.append(link) |
| 78 | |
| 79 | # Kill Ports |
| 80 | allports = provider.get_network_ports_for_deletion() |
| 81 | for port in allports: |
| 82 | objs.append(port) |
| 83 | |
| 84 | logger.info("NetworkDevice %s: Deletion part of Sync completed" % dev.id) |
| 85 | dev.administrativeState = 'syncinprogress' |
| 86 | dev.save(update_fields=['administrativeState']) |
| 87 | |
| 88 | # Now to the main options - are we syncing - creation portion |
| 89 | elif dev.administrativeState == 'syncinprogress' and deletion is False: |
| 90 | |
| 91 | logger.info("NetworkDevice %s: administrativeState set to SyncRequested" % dev.id) |
| 92 | # Reload objects in the reverse order of deletion |
| 93 | |
| 94 | # Add Ports |
| 95 | networkports = provider.get_network_ports() |
| 96 | for port in networkports: |
| 97 | objs.append(port) |
| 98 | |
| 99 | # Add Links |
| 100 | networklinks = provider.get_network_links() |
| 101 | for link in networklinks: |
| 102 | objs.append(link) |
| 103 | |
| 104 | logger.info("NetworkDevice %s: Creation part of Sync completed" % dev.id) |
| 105 | dev.administrativeState = 'enabled' |
| 106 | dev.save(update_fields=['administrativeState']) |
| 107 | |
| 108 | # If we are enabled - then check for events - in either direction and sync |
| 109 | elif dev.administrativeState == 'enabled' and deletion is False: |
| 110 | logger.debug("NetworkDevice: administrativeState set to Enabled - non deletion phase") |
| 111 | |
| 112 | # This should be the 'normal running state' when we are not deleting - a few things to do in here |
| 113 | |
| 114 | # Get the changed objects from the provider - deletions are handled separately |
| 115 | eventobjs = provider.get_updated_or_created_objects() |
| 116 | for eventobj in eventobjs: |
| 117 | # Simply put in the queue for update - this will handle both new and changed objects |
| 118 | objs.append(eventobj) |
| 119 | |
| 120 | # Handle changes XOS -> ONOS |
| 121 | # Check for ConnectivityObjects that are in acticationequested state - creates to the backend |
Rizwan Haider | e6ffdc0 | 2016-11-08 13:43:48 -0500 | [diff] [blame] | 122 | p2pactivatereqs = NetworkEdgeToEdgePointConnection.objects.filter(adminstate='activationrequested') |
| 123 | mp2mpactivatereqs = NetworkMultipointToMultipointConnection.objects.filter(adminstate='activationrequested') |
| 124 | r2mpactivatereqs = NetworkEdgeToMultipointConnection.objects.filter(adminstate='activationrequested') |
| 125 | activatereqs = list(chain(p2pactivatereqs, mp2mpactivatereqs, r2mpactivatereqs)) |
Rizwan Haider | 30b3379 | 2016-08-18 02:11:18 -0400 | [diff] [blame] | 126 | for activatereq in activatereqs: |
| 127 | |
| 128 | # Call the XOS Interface to create the service |
| 129 | logger.debug("Attempting to create EdgePointToEdgePointConnectivity: %s" % activatereq.id) |
Rizwan Haider | e6ffdc0 | 2016-11-08 13:43:48 -0500 | [diff] [blame] | 130 | if (provider.create_network_connectivity(activatereq)): |
Rizwan Haider | 30b3379 | 2016-08-18 02:11:18 -0400 | [diff] [blame] | 131 | # Everyting is OK, lets let the system handle the persist |
| 132 | objs.append(activatereq) |
| 133 | else: |
| 134 | # In the case of an error we persist the state of the object directly to preserve |
| 135 | # the error code - and because that is how the base synchronizer is designed |
| 136 | activatereq.save() |
| 137 | |
| 138 | # Check for ConnectivityObjects that are in deacticationequested state - deletes to the backend |
Rizwan Haider | e6ffdc0 | 2016-11-08 13:43:48 -0500 | [diff] [blame] | 139 | p2pdeactivatereqs = NetworkEdgeToEdgePointConnection.objects.filter(adminstate='deactivationrequested') |
| 140 | mp2mpdeactivatereqs = NetworkMultipointToMultipointConnection.objects.filter(adminstate='deactivationrequested') |
| 141 | r2mpdeactivatereqs = NetworkEdgeToMultipointConnection.objects.filter(adminstate='deactivationrequested') |
| 142 | deactivatereqs = list(chain(p2pdeactivatereqs, mp2mpdeactivatereqs, r2mpdeactivatereqs)) |
Rizwan Haider | 30b3379 | 2016-08-18 02:11:18 -0400 | [diff] [blame] | 143 | for deactivatereq in deactivatereqs: |
| 144 | |
| 145 | # Call the XOS Interface to delete the service |
| 146 | logger.debug("Attempting to delete EdgePointToEdgePointConnectivity: %s" % deactivatereq.id) |
Rizwan Haider | e6ffdc0 | 2016-11-08 13:43:48 -0500 | [diff] [blame] | 147 | if provider.delete_network_connectivity(deactivatereq): |
Rizwan Haider | 30b3379 | 2016-08-18 02:11:18 -0400 | [diff] [blame] | 148 | # Everyting is OK, lets let the system handle the persist |
| 149 | objs.append(deactivatereq) |
| 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 | deactivatereq.save() |
| 154 | |
| 155 | # If we are enabled - and in our deletion pass then look for objects waiting for deletion |
| 156 | elif dev.administrativeState == 'enabled' and deletion is True: |
| 157 | logger.debug("NetworkDevice: administrativeState set to Enabled - deletion phase") |
| 158 | |
| 159 | # Any object that is simply deleted in the model gets removed automatically - the synchronizer |
| 160 | # doesn't get involved - we just need to check for deleted objects in the domain and reflect that |
| 161 | # in the model |
| 162 | # |
| 163 | # Get the deleted objects from the provider |
| 164 | eventobjs = provider.get_deleted_objects() |
| 165 | for eventobj in eventobjs: |
| 166 | # Simply put in the queue for update - this will handle both new and changed objects |
| 167 | objs.append(eventobj) |
| 168 | |
Rizwan Haider | e6ffdc0 | 2016-11-08 13:43:48 -0500 | [diff] [blame] | 169 | # Handle the case where we have deleted Eline Services from our side - if the Service is in |
| 170 | # enabled state then we call the provider, otherwise just queue it for deletion |
| 171 | elinedeletedobjs = NetworkEdgeToEdgePointConnection.deleted_objects.all() |
| 172 | for elinedeletedobj in elinedeletedobjs: |
| 173 | if elinedeletedobj.adminstate == 'enabled': |
| 174 | provider.delete_network_connectivity(elinedeletedobj) |
| 175 | # Either way queue it for deletion |
| 176 | objs.append(elinedeletedobj) |
| 177 | |
| 178 | # Handle the case where we have deleted Etree Services from our side - if the Service is in |
| 179 | # enabled state then we call the provider, otherwise just queue it for deletion |
| 180 | etreedeletedobjs = NetworkEdgeToMultipointConnection.deleted_objects.all() |
| 181 | for etreedeletedobj in etreedeletedobjs: |
| 182 | # TODO: Handle the case where its connected, we need to disconnect first |
| 183 | if etreedeletedobj.adminstate == 'enabled': |
| 184 | provider.delete_network_connectivity(etreedeletedobj) |
| 185 | # Either way queue it for deletion |
| 186 | objs.append(etreedeletedobj) |
| 187 | |
| 188 | # Handle the case where we have deleted Elan Services from our side - if the Service is in |
| 189 | # enabled state then we call the provider, otherwise just queue it for deletion |
| 190 | elandeletedobjs = NetworkMultipointToMultipointConnection.deleted_objects.all() |
| 191 | for elandeletedobj in elandeletedobjs: |
| 192 | # TODO: Handle the case where its connected, we need to disconnect first |
| 193 | if elandeletedobj.adminstate == 'enabled': |
| 194 | provider.delete_network_connectivity(elandeletedobj) |
| 195 | # Either way queue it for deletion |
| 196 | objs.append(elandeletedobj) |
| 197 | |
| 198 | # Handle the case where we have deleted VnodGlobal Services from our side - if there is |
| 199 | # an attached Eline/Etree/Elan we set that to deleted |
| 200 | vnodbloaldeletedobjs = VnodGlobalService.deleted_objects.all() |
| 201 | for vnodbloaldeletedobj in vnodbloaldeletedobjs: |
| 202 | # Check for dependent eline service |
| 203 | if vnodbloaldeletedobj.metronetworkpointtopoint is not None: |
| 204 | elineobj = vnodbloaldeletedobj.metronetworkpointtopoint |
| 205 | elineobj.deleted = True |
| 206 | objs.append(elineobj) |
| 207 | # Check for dependent elan service |
| 208 | if vnodbloaldeletedobj.metronetworkmultipoint is not None: |
| 209 | elanobj = vnodbloaldeletedobj.metronetworkmultipoint |
| 210 | elanobj.deleted = True |
| 211 | objs.append(elanobj) |
| 212 | # Check for dependent etree service |
| 213 | if vnodbloaldeletedobj.metronetworkroottomultipoint is not None: |
| 214 | etreeobj = vnodbloaldeletedobj.metronetworkroottomultipoint |
| 215 | etreeobj.deleted = True |
| 216 | objs.append(etreeobj) |
| 217 | |
| 218 | objs.append(vnodbloaldeletedobj) |
| 219 | |
Rizwan Haider | 30b3379 | 2016-08-18 02:11:18 -0400 | [diff] [blame] | 220 | # In add cases return the objects we are interested in |
| 221 | return objs |
| 222 | |
| 223 | def sync_record(self, o): |
Rizwan Haider | 65baf55 | 2016-09-28 16:47:28 -0400 | [diff] [blame] | 224 | |
| 225 | # First we call and see if there is an invoker for this object - the idea of the invoker |
| 226 | # is to wrap the save with a pre/post paradigm to handle special cases |
| 227 | # It will only exist for a subset of ojbects |
| 228 | invoker = InvokerFactory.getinvoker(o) |
| 229 | |
| 230 | # Call Pre-save on the inovker (if it exists) |
| 231 | if invoker is not None: |
| 232 | invoker.presave(o) |
| 233 | |
Rizwan Haider | 30b3379 | 2016-08-18 02:11:18 -0400 | [diff] [blame] | 234 | # Simply save the record to the DB - both updates and adds are handled the same way |
| 235 | o.save() |
| 236 | |
Rizwan Haider | 65baf55 | 2016-09-28 16:47:28 -0400 | [diff] [blame] | 237 | # Call Post-save on the inovker (if it exists) |
| 238 | if invoker is not None: |
| 239 | invoker.postsave(o) |
| 240 | |
Rizwan Haider | 30b3379 | 2016-08-18 02:11:18 -0400 | [diff] [blame] | 241 | def delete_record(self, o): |
| 242 | # Overriden to customize our behaviour - the core sync step for will remove the record directly |
| 243 | # We just log and return |
| 244 | logger.debug("deleting Object %s" % str(o), extra=o.tologdict()) |
| 245 | |
Rizwan Haider | 65baf55 | 2016-09-28 16:47:28 -0400 | [diff] [blame] | 246 | def get_metronetwork_system(self): |
Rizwan Haider | 30b3379 | 2016-08-18 02:11:18 -0400 | [diff] [blame] | 247 | # We only expect to have one of these objects in the system in the curent design |
| 248 | # So get the first element from the query |
Rizwan Haider | 65baf55 | 2016-09-28 16:47:28 -0400 | [diff] [blame] | 249 | metronetworksystem = MetroNetworkSystem.objects.all() |
| 250 | if not metronetworksystem: |
Rizwan Haider | 30b3379 | 2016-08-18 02:11:18 -0400 | [diff] [blame] | 251 | return None |
| 252 | |
Rizwan Haider | 65baf55 | 2016-09-28 16:47:28 -0400 | [diff] [blame] | 253 | return metronetworksystem[0] |