def add_port_interfaces(puuid): # If the uuid does not appear in update info, we have no choice but to query interfaces with select # we cannot use data from other bridges; the port may be moved from a bridge which is not tracked try: method, params = ovsdb.transact('Open_vSwitch', ovsdb.select('Port', [["_uuid", "==", ovsdb.uuid(puuid)]], ["interfaces"])) for m in protocol.querywithreply(method, params, connection, self.apiroutine): yield m r = self.apiroutine.jsonrpc_result[0] if 'error' in r: raise JsonRPCErrorResultException('Error when query interfaces from port ' + repr(puuid) + ': ' + r['error']) if r['rows']: interfaces = ovsdb.getlist(r['rows'][0]['interfaces']) with closing(self.apiroutine.executeAll([self._get_interface_info(connection, protocol, buuid, iuuid, puuid) for _,iuuid in interfaces])) as g: for m in g: yield m self.apiroutine.retvalue = list(itertools.chain(r[0] for r in self.apiroutine.retvalue)) else: self.apiroutine.retvalue = [] except JsonRPCProtocolException: self.apiroutine.retvalue = [] except ConnectionResetException: self.apiroutine.retvalue = []
def process_bridge(buuid, uo): try: nv = uo['new'] if 'datapath_id' in nv: if ovsdb.getoptional(nv['datapath_id']) is None: # This bridge is not initialized. Wait for the bridge to be initialized. for m in callAPI( self.apiroutine, 'ovsdbmanager', 'waitbridge', { 'connection': connection, 'name': nv['name'], 'timeout': 5 }): yield m datapath_id = self.apiroutine.retvalue else: datapath_id = int(nv['datapath_id'], 16) self.bridge_datapathid[buuid] = datapath_id elif buuid in self.bridge_datapathid: datapath_id = self.bridge_datapathid[buuid] else: # This should not happen, but just in case... for m in callAPI(self.apiroutine, 'ovsdbmanager', 'waitbridge', { 'connection': connection, 'name': nv['name'], 'timeout': 5 }): yield m datapath_id = self.apiroutine.retvalue self.bridge_datapathid[buuid] = datapath_id if 'ports' in nv: nset = set((p for _, p in ovsdb.getlist(nv['ports']))) else: nset = set() if 'old' in uo: ov = uo['old'] if 'ports' in ov: oset = set((p for _, p in ovsdb.getlist(ov['ports']))) else: # new ports are not really added; it is only sent because datapath_id is modified nset = set() oset = set() if 'datapath_id' in ov and ovsdb.getoptional( ov['datapath_id']) is not None: old_datapathid = int(ov['datapath_id'], 16) else: old_datapathid = datapath_id else: oset = set() old_datapathid = datapath_id # For every deleted port, remove the interfaces with this port _uuid remove = [] add_routine = [] for puuid in oset - nset: remove += self._remove_all_interface( connection, protocol, old_datapathid, puuid, buuid) # For every port not changed, check if the interfaces are modified; for puuid in oset.intersection(nset): if puuid in port_update: # The port is modified, there should be an 'old' set and 'new' set pu = port_update[puuid] if 'old' in pu: poset = set((p for _, p in ovsdb.getlist( pu['old']['interfaces']))) else: poset = set() if 'new' in pu: pnset = set((p for _, p in ovsdb.getlist( pu['new']['interfaces']))) else: pnset = set() # Remove old interfaces remove += [ r for r in (self._remove_interface( connection, protocol, datapath_id, iuuid, puuid) for iuuid in (poset - pnset)) if r is not None ] # Prepare to add new interfaces add_routine += [ self._get_interface_info(connection, protocol, buuid, iuuid, puuid) for iuuid in (pnset - poset) ] # For every port added, add the interfaces def add_port_interfaces(puuid): # If the uuid does not appear in update info, we have no choice but to query interfaces with select # we cannot use data from other bridges; the port may be moved from a bridge which is not tracked try: method, params = ovsdb.transact( 'Open_vSwitch', ovsdb.select('Port', [["_uuid", "==", ovsdb.uuid(puuid)]], ["interfaces"])) for m in protocol.querywithreply( method, params, connection, self.apiroutine): yield m r = self.apiroutine.jsonrpc_result[0] if 'error' in r: raise JsonRPCErrorResultException( 'Error when query interfaces from port ' + repr(puuid) + ': ' + r['error']) if r['rows']: interfaces = ovsdb.getlist( r['rows'][0]['interfaces']) with closing( self.apiroutine.executeAll([ self._get_interface_info( connection, protocol, buuid, iuuid, puuid) for _, iuuid in interfaces ])) as g: for m in g: yield m self.apiroutine.retvalue = list( itertools.chain( r[0] for r in self.apiroutine.retvalue)) else: self.apiroutine.retvalue = [] except JsonRPCProtocolException: self.apiroutine.retvalue = [] except ConnectionResetException: self.apiroutine.retvalue = [] for puuid in nset - oset: self.ports_uuids[puuid] = buuid if puuid in port_update and 'new' in port_update[puuid] \ and 'old' not in port_update[puuid]: # Add all the interfaces in 'new' interfaces = ovsdb.getlist( port_update[puuid]['new']['interfaces']) add_routine += [ self._get_interface_info(connection, protocol, buuid, iuuid, puuid) for _, iuuid in interfaces ] else: add_routine.append(add_port_interfaces(puuid)) # Execute the add_routine try: with closing(self.apiroutine.executeAll(add_routine)) as g: for m in g: yield m except: add = [] raise else: add = list( itertools.chain(r[0] for r in self.apiroutine.retvalue)) finally: if update: self.scheduler.emergesend( ModuleNotification(self.getServiceName(), 'update', datapathid=datapath_id, connection=connection, vhost=protocol.vhost, add=add, remove=remove, reason='bridgemodify' if 'old' in uo else 'bridgeup')) except JsonRPCProtocolException: pass except ConnectionResetException: pass except OVSDBBridgeNotAppearException: pass
def _update_interfaces(self, connection, protocol, updateinfo, update=True): """ There are several kinds of updates, they may appear together: 1. New bridge created (or from initial updateinfo). We should add all the interfaces to the list. 2. Bridge removed. Remove all the ports. 3. name and datapath_id may be changed. We will consider this as a new bridge created, and an old bridge removed. 4. Bridge ports modification, i.e. add/remove ports. a) Normally a port record is created/deleted together. A port record cannot exist without a bridge containing it. b) It is also possible that a port is removed from one bridge and added to another bridge, in this case the ports do not appear in the updateinfo 5. Port interfaces modification, i.e. add/remove interfaces. The bridge record may not appear in this situation. We must consider these situations carefully and process them in correct order. """ port_update = updateinfo.get('Port', {}) bridge_update = updateinfo.get('Bridge', {}) working_routines = [] def process_bridge(buuid, uo): try: nv = uo['new'] if 'datapath_id' in nv: if ovsdb.getoptional(nv['datapath_id']) is None: # This bridge is not initialized. Wait for the bridge to be initialized. for m in callAPI( self.apiroutine, 'ovsdbmanager', 'waitbridge', { 'connection': connection, 'name': nv['name'], 'timeout': 5 }): yield m datapath_id = self.apiroutine.retvalue else: datapath_id = int(nv['datapath_id'], 16) self.bridge_datapathid[buuid] = datapath_id elif buuid in self.bridge_datapathid: datapath_id = self.bridge_datapathid[buuid] else: # This should not happen, but just in case... for m in callAPI(self.apiroutine, 'ovsdbmanager', 'waitbridge', { 'connection': connection, 'name': nv['name'], 'timeout': 5 }): yield m datapath_id = self.apiroutine.retvalue self.bridge_datapathid[buuid] = datapath_id if 'ports' in nv: nset = set((p for _, p in ovsdb.getlist(nv['ports']))) else: nset = set() if 'old' in uo: ov = uo['old'] if 'ports' in ov: oset = set((p for _, p in ovsdb.getlist(ov['ports']))) else: # new ports are not really added; it is only sent because datapath_id is modified nset = set() oset = set() if 'datapath_id' in ov and ovsdb.getoptional( ov['datapath_id']) is not None: old_datapathid = int(ov['datapath_id'], 16) else: old_datapathid = datapath_id else: oset = set() old_datapathid = datapath_id # For every deleted port, remove the interfaces with this port _uuid remove = [] add_routine = [] for puuid in oset - nset: remove += self._remove_all_interface( connection, protocol, old_datapathid, puuid, buuid) # For every port not changed, check if the interfaces are modified; for puuid in oset.intersection(nset): if puuid in port_update: # The port is modified, there should be an 'old' set and 'new' set pu = port_update[puuid] if 'old' in pu: poset = set((p for _, p in ovsdb.getlist( pu['old']['interfaces']))) else: poset = set() if 'new' in pu: pnset = set((p for _, p in ovsdb.getlist( pu['new']['interfaces']))) else: pnset = set() # Remove old interfaces remove += [ r for r in (self._remove_interface( connection, protocol, datapath_id, iuuid, puuid) for iuuid in (poset - pnset)) if r is not None ] # Prepare to add new interfaces add_routine += [ self._get_interface_info(connection, protocol, buuid, iuuid, puuid) for iuuid in (pnset - poset) ] # For every port added, add the interfaces def add_port_interfaces(puuid): # If the uuid does not appear in update info, we have no choice but to query interfaces with select # we cannot use data from other bridges; the port may be moved from a bridge which is not tracked try: method, params = ovsdb.transact( 'Open_vSwitch', ovsdb.select('Port', [["_uuid", "==", ovsdb.uuid(puuid)]], ["interfaces"])) for m in protocol.querywithreply( method, params, connection, self.apiroutine): yield m r = self.apiroutine.jsonrpc_result[0] if 'error' in r: raise JsonRPCErrorResultException( 'Error when query interfaces from port ' + repr(puuid) + ': ' + r['error']) if r['rows']: interfaces = ovsdb.getlist( r['rows'][0]['interfaces']) with closing( self.apiroutine.executeAll([ self._get_interface_info( connection, protocol, buuid, iuuid, puuid) for _, iuuid in interfaces ])) as g: for m in g: yield m self.apiroutine.retvalue = list( itertools.chain( r[0] for r in self.apiroutine.retvalue)) else: self.apiroutine.retvalue = [] except JsonRPCProtocolException: self.apiroutine.retvalue = [] except ConnectionResetException: self.apiroutine.retvalue = [] for puuid in nset - oset: self.ports_uuids[puuid] = buuid if puuid in port_update and 'new' in port_update[puuid] \ and 'old' not in port_update[puuid]: # Add all the interfaces in 'new' interfaces = ovsdb.getlist( port_update[puuid]['new']['interfaces']) add_routine += [ self._get_interface_info(connection, protocol, buuid, iuuid, puuid) for _, iuuid in interfaces ] else: add_routine.append(add_port_interfaces(puuid)) # Execute the add_routine try: with closing(self.apiroutine.executeAll(add_routine)) as g: for m in g: yield m except: add = [] raise else: add = list( itertools.chain(r[0] for r in self.apiroutine.retvalue)) finally: if update: self.scheduler.emergesend( ModuleNotification(self.getServiceName(), 'update', datapathid=datapath_id, connection=connection, vhost=protocol.vhost, add=add, remove=remove, reason='bridgemodify' if 'old' in uo else 'bridgeup')) except JsonRPCProtocolException: pass except ConnectionResetException: pass except OVSDBBridgeNotAppearException: pass ignore_ports = set() for buuid, uo in bridge_update.items(): # Bridge removals are ignored because we process OVSDBBridgeSetup event instead if 'old' in uo: if 'ports' in uo['old']: oset = set( (puuid for _, puuid in ovsdb.getlist(uo['old']['ports']))) ignore_ports.update(oset) if 'new' not in uo: if buuid in self.bridge_datapathid: del self.bridge_datapathid[buuid] if 'new' in uo: # If bridge contains this port is updated, we process the port update totally in bridge, # so we ignore it later if 'ports' in uo['new']: nset = set( (puuid for _, puuid in ovsdb.getlist(uo['new']['ports']))) ignore_ports.update(nset) working_routines.append(process_bridge(buuid, uo)) def process_port(buuid, port_uuid, interfaces, remove_ids): if buuid not in self.bridge_datapathid: return datapath_id = self.bridge_datapathid[buuid] ports = self.managed_ports.get((protocol.vhost, datapath_id)) remove = [] if ports is not None: remove = [p for _, p in ports if p['_uuid'] in remove_ids] not_remove = [(_, p) for _, p in ports if p['_uuid'] not in remove_ids] ports[:len(not_remove)] = not_remove del ports[len(not_remove):] if interfaces: try: with closing( self.apiroutine.executeAll([ self._get_interface_info( connection, protocol, buuid, iuuid, port_uuid) for iuuid in interfaces ])) as g: for m in g: yield m add = list( itertools.chain( (r[0] for r in self.apiroutine.retvalue if r[0]))) except Exception: self._logger.warning("Cannot get new port information", exc_info=True) add = [] else: add = [] if update: for m in self.apiroutine.waitForSend( ModuleNotification(self.getServiceName(), 'update', datapathid=datapath_id, connection=connection, vhost=protocol.vhost, add=add, remove=remove, reason='bridgemodify')): yield m for puuid, po in port_update.items(): if puuid not in ignore_ports: bridge_id = self.ports_uuids.get(puuid) if bridge_id is not None: datapath_id = self.bridge_datapathid[bridge_id] if datapath_id is not None: # This port is modified if 'new' in po: nset = set((iuuid for _, iuuid in ovsdb.getlist( po['new']['interfaces']))) else: nset = set() if 'old' in po: oset = set((iuuid for _, iuuid in ovsdb.getlist( po['old']['interfaces']))) else: oset = set() working_routines.append( process_port(bridge_id, puuid, nset - oset, oset - nset)) if update: for r in working_routines: self.apiroutine.subroutine(r) else: try: with closing( self.apiroutine.executeAll(working_routines, None, ())) as g: for m in g: yield m finally: self.scheduler.emergesend( OVSDBConnectionPortsSynchronized(connection))
def _get_bridges(self, connection, protocol): try: try: vhost = protocol.vhost if not hasattr(connection, 'ovsdb_systemid'): method, params = ovsdb.transact('Open_vSwitch', ovsdb.select('Open_vSwitch', [], ['external_ids'])) for m in protocol.querywithreply(method, params, connection, self.apiroutine): yield m result = self.apiroutine.jsonrpc_result[0] system_id = ovsdb.omap_getvalue(result['rows'][0]['external_ids'], 'system-id') connection.ovsdb_systemid = system_id else: system_id = connection.ovsdb_systemid if (vhost, system_id) in self.managed_systemids: oc = self.managed_systemids[(vhost, system_id)] ep = _get_endpoint(oc) econns = self.endpoint_conns.get((vhost, ep)) if econns: try: econns.remove(oc) except ValueError: pass del self.managed_systemids[(vhost, system_id)] self.managed_systemids[(vhost, system_id)] = connection self.managed_bridges[connection] = [] ep = _get_endpoint(connection) self.endpoint_conns.setdefault((vhost, ep), []).append(connection) method, params = ovsdb.monitor('Open_vSwitch', 'ovsdb_manager_bridges_monitor', {'Open_vSwitch':ovsdb.monitor_request(['bridges'])}) for m in protocol.querywithreply(method, params, connection, self.apiroutine): yield m if 'error' in self.apiroutine.jsonrpc_result: # The monitor is already set, cancel it first method, params = ovsdb.monitor_cancel('ovsdb_manager_bridges_monitor') for m in protocol.querywithreply(method, params, connection, self.apiroutine, False): yield m method, params = ovsdb.monitor('Open_vSwitch', 'ovsdb_manager_bridges_monitor', {'Open_vSwitch':ovsdb.monitor_request(['bridges'], True, False, False, True)}) for m in protocol.querywithreply(method, params, connection, self.apiroutine): yield m if 'error' in self.apiroutine.jsonrpc_result: raise JsonRPCErrorResultException('OVSDB request failed: ' + repr(self.apiroutine.jsonrpc_result)) except Exception: for m in self.apiroutine.waitForSend(OVSDBConnectionSetup(system_id, connection, connection.connmark, vhost)): yield m raise else: # Process initial bridges init_subprocesses = [self._update_bridge(connection, protocol, buuid, vhost) for v in self.apiroutine.jsonrpc_result['Open_vSwitch'].values() for _, buuid in ovsdb.getlist(v['new']['bridges'])] def init_process(): try: for m in self.apiroutine.executeAll(init_subprocesses, retnames = ()): yield m except Exception: for m in self.apiroutine.waitForSend(OVSDBConnectionSetup(system_id, connection, connection.connmark, vhost)): yield m raise else: for m in self.apiroutine.waitForSend(OVSDBConnectionSetup(system_id, connection, connection.connmark, vhost)): yield m self.apiroutine.subroutine(init_process()) # Wait for notify notification = JsonRPCNotificationEvent.createMatcher('update', connection, connection.connmark, _ismatch = lambda x: x.params[0] == 'ovsdb_manager_bridges_monitor') conn_down = protocol.statematcher(connection) while True: yield (conn_down, notification) if self.apiroutine.matcher is conn_down: break else: for v in self.apiroutine.event.params[1]['Open_vSwitch'].values(): if 'old' not in v: old = set() else: old = set((oid for _, oid in ovsdb.getlist(v['old']['bridges']))) if 'new' not in v: new = set() else: new = set((oid for _, oid in ovsdb.getlist(v['new']['bridges']))) for buuid in (new - old): self.apiroutine.subroutine(self._update_bridge(connection, protocol, buuid, vhost)) for buuid in (old - new): bridges = self.managed_bridges[connection] for i in range(0, len(bridges)): if buuid == bridges[i][3]: self.scheduler.emergesend(OVSDBBridgeSetup(OVSDBBridgeSetup.DOWN, bridges[i][1], system_id, bridges[i][2], connection, connection.connmark, vhost, bridges[i][3])) del self.managed_conns[(vhost, bridges[i][1])] del bridges[i] break except JsonRPCProtocolException: pass finally: del connection._ovsdb_manager_get_bridges
def process_bridge(buuid, uo): try: nv = uo['new'] if 'datapath_id' in nv: datapath_id = int(nv['datapath_id'], 16) self.bridge_datapathid[buuid] = datapath_id elif buuid in self.bridge_datapathid: datapath_id = self.bridge_datapathid[buuid] else: # This should not happen, but just in case... for m in callAPI(self.apiroutine, 'ovsdbmanager', 'waitbridge', {'connection': connection, 'name': nv['name'], 'timeout': 5}): yield m datapath_id = self.apiroutine.retvalue if 'ports' in nv: nset = set((p for _,p in ovsdb.getlist(nv['ports']))) else: nset = set() if 'old' in uo: ov = uo['old'] if 'ports' in ov: oset = set((p for _,p in ovsdb.getlist(ov['ports']))) else: # new ports are not really added; it is only sent because datapath_id is modified nset = set() oset = set() if 'datapath_id' in ov: old_datapathid = int(ov['datapath_id'], 16) else: old_datapathid = datapath_id else: oset = set() old_datapathid = datapath_id # For every deleted port, remove the interfaces with this port _uuid remove = [] add_routine = [] for puuid in oset - nset: remove += self._remove_all_interface(connection, protocol, old_datapathid, puuid, buuid) # For every port not changed, check if the interfaces are modified; for puuid in oset.intersection(nset): if puuid in port_update: # The port is modified, there should be an 'old' set and 'new' set pu = port_update[puuid] if 'old' in pu: poset = set((p for _,p in ovsdb.getlist(pu['old']['interfaces']))) else: poset = set() if 'new' in pu: pnset = set((p for _,p in ovsdb.getlist(pu['new']['interfaces']))) else: pnset = set() # Remove old interfaces remove += [r for r in (self._remove_interface(connection, protocol, datapath_id, iuuid, puuid) for iuuid in (poset - pnset)) if r is not None] # Prepare to add new interfaces add_routine += [self._get_interface_info(connection, protocol, buuid, iuuid, puuid) for iuuid in (pnset - poset)] # For every port added, add the interfaces def add_port_interfaces(puuid): # If the uuid does not appear in update info, we have no choice but to query interfaces with select # we cannot use data from other bridges; the port may be moved from a bridge which is not tracked try: method, params = ovsdb.transact('Open_vSwitch', ovsdb.select('Port', [["_uuid", "==", ovsdb.uuid(puuid)]], ["interfaces"])) for m in protocol.querywithreply(method, params, connection, self.apiroutine): yield m r = self.apiroutine.jsonrpc_result[0] if 'error' in r: raise JsonRPCErrorResultException('Error when query interfaces from port ' + repr(puuid) + ': ' + r['error']) if r['rows']: interfaces = ovsdb.getlist(r['rows'][0]['interfaces']) with closing(self.apiroutine.executeAll([self._get_interface_info(connection, protocol, buuid, iuuid, puuid) for _,iuuid in interfaces])) as g: for m in g: yield m self.apiroutine.retvalue = list(itertools.chain(r[0] for r in self.apiroutine.retvalue)) else: self.apiroutine.retvalue = [] except JsonRPCProtocolException: self.apiroutine.retvalue = [] except ConnectionResetException: self.apiroutine.retvalue = [] for puuid in nset - oset: self.ports_uuids[puuid] = buuid if puuid in port_update and 'new' in port_update[puuid] \ and 'old' not in port_update[puuid]: # Add all the interfaces in 'new' interfaces = ovsdb.getlist(port_update[puuid]['new']['interfaces']) add_routine += [self._get_interface_info(connection, protocol, buuid, iuuid, puuid) for _,iuuid in interfaces] else: add_routine.append(add_port_interfaces(puuid)) # Execute the add_routine try: with closing(self.apiroutine.executeAll(add_routine)) as g: for m in g: yield m except: add = [] raise else: add = list(itertools.chain(r[0] for r in self.apiroutine.retvalue)) finally: if update: self.scheduler.emergesend( ModuleNotification(self.getServiceName(), 'update', datapathid = datapath_id, connection = connection, vhost = protocol.vhost, add = add, remove = remove, reason = 'bridgemodify' if 'old' in uo else 'bridgeup' )) except JsonRPCProtocolException: pass except ConnectionResetException: pass except OVSDBBridgeNotAppearException: pass
def _update_interfaces(self, connection, protocol, updateinfo, update = True): """ There are several kinds of updates, they may appear together: 1. New bridge created (or from initial updateinfo). We should add all the interfaces to the list. 2. Bridge removed. Remove all the ports. 3. name and datapath_id may be changed. We will consider this as a new bridge created, and an old bridge removed. 4. Bridge ports modification, i.e. add/remove ports. a) Normally a port record is created/deleted together. A port record cannot exist without a bridge containing it. b) It is also possible that a port is removed from one bridge and added to another bridge, in this case the ports do not appear in the updateinfo 5. Port interfaces modification, i.e. add/remove interfaces. The bridge record may not appear in this situation. We must consider these situations carefully and process them in correct order. """ port_update = updateinfo.get('Port', {}) bridge_update = updateinfo.get('Bridge', {}) working_routines = [] def process_bridge(buuid, uo): try: nv = uo['new'] if 'datapath_id' in nv: datapath_id = int(nv['datapath_id'], 16) self.bridge_datapathid[buuid] = datapath_id elif buuid in self.bridge_datapathid: datapath_id = self.bridge_datapathid[buuid] else: # This should not happen, but just in case... for m in callAPI(self.apiroutine, 'ovsdbmanager', 'waitbridge', {'connection': connection, 'name': nv['name'], 'timeout': 5}): yield m datapath_id = self.apiroutine.retvalue if 'ports' in nv: nset = set((p for _,p in ovsdb.getlist(nv['ports']))) else: nset = set() if 'old' in uo: ov = uo['old'] if 'ports' in ov: oset = set((p for _,p in ovsdb.getlist(ov['ports']))) else: # new ports are not really added; it is only sent because datapath_id is modified nset = set() oset = set() if 'datapath_id' in ov: old_datapathid = int(ov['datapath_id'], 16) else: old_datapathid = datapath_id else: oset = set() old_datapathid = datapath_id # For every deleted port, remove the interfaces with this port _uuid remove = [] add_routine = [] for puuid in oset - nset: remove += self._remove_all_interface(connection, protocol, old_datapathid, puuid, buuid) # For every port not changed, check if the interfaces are modified; for puuid in oset.intersection(nset): if puuid in port_update: # The port is modified, there should be an 'old' set and 'new' set pu = port_update[puuid] if 'old' in pu: poset = set((p for _,p in ovsdb.getlist(pu['old']['interfaces']))) else: poset = set() if 'new' in pu: pnset = set((p for _,p in ovsdb.getlist(pu['new']['interfaces']))) else: pnset = set() # Remove old interfaces remove += [r for r in (self._remove_interface(connection, protocol, datapath_id, iuuid, puuid) for iuuid in (poset - pnset)) if r is not None] # Prepare to add new interfaces add_routine += [self._get_interface_info(connection, protocol, buuid, iuuid, puuid) for iuuid in (pnset - poset)] # For every port added, add the interfaces def add_port_interfaces(puuid): # If the uuid does not appear in update info, we have no choice but to query interfaces with select # we cannot use data from other bridges; the port may be moved from a bridge which is not tracked try: method, params = ovsdb.transact('Open_vSwitch', ovsdb.select('Port', [["_uuid", "==", ovsdb.uuid(puuid)]], ["interfaces"])) for m in protocol.querywithreply(method, params, connection, self.apiroutine): yield m r = self.apiroutine.jsonrpc_result[0] if 'error' in r: raise JsonRPCErrorResultException('Error when query interfaces from port ' + repr(puuid) + ': ' + r['error']) if r['rows']: interfaces = ovsdb.getlist(r['rows'][0]['interfaces']) with closing(self.apiroutine.executeAll([self._get_interface_info(connection, protocol, buuid, iuuid, puuid) for _,iuuid in interfaces])) as g: for m in g: yield m self.apiroutine.retvalue = list(itertools.chain(r[0] for r in self.apiroutine.retvalue)) else: self.apiroutine.retvalue = [] except JsonRPCProtocolException: self.apiroutine.retvalue = [] except ConnectionResetException: self.apiroutine.retvalue = [] for puuid in nset - oset: self.ports_uuids[puuid] = buuid if puuid in port_update and 'new' in port_update[puuid] \ and 'old' not in port_update[puuid]: # Add all the interfaces in 'new' interfaces = ovsdb.getlist(port_update[puuid]['new']['interfaces']) add_routine += [self._get_interface_info(connection, protocol, buuid, iuuid, puuid) for _,iuuid in interfaces] else: add_routine.append(add_port_interfaces(puuid)) # Execute the add_routine try: with closing(self.apiroutine.executeAll(add_routine)) as g: for m in g: yield m except: add = [] raise else: add = list(itertools.chain(r[0] for r in self.apiroutine.retvalue)) finally: if update: self.scheduler.emergesend( ModuleNotification(self.getServiceName(), 'update', datapathid = datapath_id, connection = connection, vhost = protocol.vhost, add = add, remove = remove, reason = 'bridgemodify' if 'old' in uo else 'bridgeup' )) except JsonRPCProtocolException: pass except ConnectionResetException: pass except OVSDBBridgeNotAppearException: pass ignore_ports = set() for buuid, uo in bridge_update.items(): # Bridge removals are ignored because we process OVSDBBridgeSetup event instead if 'old' in uo: if 'ports' in uo['old']: oset = set((puuid for _, puuid in ovsdb.getlist(uo['old']['ports']))) ignore_ports.update(oset) if 'new' not in uo: if buuid in self.bridge_datapathid: del self.bridge_datapathid[buuid] if 'new' in uo: # If bridge contains this port is updated, we process the port update totally in bridge, # so we ignore it later if 'ports' in uo['new']: nset = set((puuid for _, puuid in ovsdb.getlist(uo['new']['ports']))) ignore_ports.update(nset) working_routines.append(process_bridge(buuid, uo)) def process_port(buuid, port_uuid, interfaces, remove_ids): if buuid not in self.bridge_datapathid: return datapath_id = self.bridge_datapathid[buuid] ports = self.managed_ports.get((protocol.vhost, datapath_id)) remove = [] if ports is not None: remove = [p for _,p in ports if p['_uuid'] in remove_ids] not_remove = [(_,p) for _,p in ports if p['_uuid'] not in remove_ids] ports[:len(not_remove)] = not_remove del ports[len(not_remove):] if interfaces: try: with closing(self.apiroutine.executeAll([self._get_interface_info(connection, protocol, buuid, iuuid, port_uuid) for iuuid in interfaces])) as g: for m in g: yield m add = list(itertools.chain((r[0] for r in self.apiroutine.retvalue if r[0]))) except Exception: self._logger.warning("Cannot get new port information", exc_info = True) add = [] else: add = [] if update: for m in self.apiroutine.waitForSend(ModuleNotification(self.getServiceName(), 'update', datapathid = datapath_id, connection = connection, vhost = protocol.vhost, add = add, remove = remove, reason = 'bridgemodify' )): yield m for puuid, po in port_update.items(): if puuid not in ignore_ports: bridge_id = self.ports_uuids.get(puuid) if bridge_id is not None: datapath_id = self.bridge_datapathid[bridge_id] if datapath_id is not None: # This port is modified if 'new' in po: nset = set((iuuid for _, iuuid in ovsdb.getlist(po['new']['interfaces']))) else: nset = set() if 'old' in po: oset = set((iuuid for _, iuuid in ovsdb.getlist(po['old']['interfaces']))) else: oset = set() working_routines.append(process_port(bridge_id, puuid, nset - oset, oset - nset)) if update: for r in working_routines: self.apiroutine.subroutine(r) else: try: with closing(self.apiroutine.executeAll(working_routines, None, ())) as g: for m in g: yield m finally: self.scheduler.emergesend(OVSDBConnectionPortsSynchronized(connection))