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