Beispiel #1
0
 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 = []
Beispiel #2
0
 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 = []
Beispiel #3
0
        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
Beispiel #4
0
    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))
Beispiel #5
0
 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
Beispiel #6
0
 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
Beispiel #7
0
 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))