def _keepalive (self, proto): need_ka = False generator = None while True: # SEND KEEPALIVES need_ka |= self.send_timer.need_ka() if need_ka: if not generator: generator = proto.new_keepalive() need_ka = False if not generator: yield False continue try: # try to close the generator and raise a StopIteration in one call six.next(generator) six.next(generator) # still running yield True except NetworkError: raise Notify(4,0,'problem with network while trying to send keepalive') except StopIteration: generator = None yield False
def schedule (self): try: # read at least on message per process if there is some and parse it for service,command in self.processes.received(): self.api.text(self,service,command) # if we have nothing to do, return or save the work if not self._running: if not self._pending: return False self._running,name = self._pending.popleft() self.logger.reactor('callback | installing %s' % name) if self._running: # run it try: self.logger.reactor('callback | running') six.next(self._running) # run # should raise StopIteration in most case # and prevent us to have to run twice to run one command six.next(self._running) # run except StopIteration: self._running = None self.logger.reactor('callback | removing') return True except StopIteration: pass except KeyboardInterrupt: self._shutdown = True self.logger.reactor('^C received','error')
def schedule(self): try: # read at least on message per process if there is some and parse it for service, command in self.processes.received(): self.api.text(self, service, command) # if we have nothing to do, return or save the work if not self._running: if not self._pending: return False self._running, name = self._pending.popleft() self.logger.reactor('callback | installing %s' % name) if self._running: # run it try: self.logger.reactor('callback | running') six.next(self._running) # run # should raise StopIteration in most case # and prevent us to have to run twice to run one command six.next(self._running) # run except StopIteration: self._running = None self.logger.reactor('callback | removing') return True except StopIteration: pass except KeyboardInterrupt: self._shutdown = True self.logger.reactor('^C received', 'error')
def run(self): if not self.ready(): return False length = range(len(self._async)) uid, generator = self._async.popleft() for _ in length: try: six.next(generator) six.next(generator) except StopIteration: if not self._async: return False uid, generator = self._async.popleft() except KeyboardInterrupt: raise except Exception as exc: self.logger.error('async | %s | problem with function' % uid, 'reactor') for line in str(exc).split('\n'): self.logger.error('async | %s | %s' % (uid, line), 'reactor') self._async.appendleft((uid, generator)) return True
def test (): OPEN = ''.join([chr(int(_,16)) for _ in "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 00 1D 01 04 78 14 00 5A 52 DB 00 45 00".split()]) KEEP = ''.join([chr(int(_,16)) for _ in "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 00 00 04".split()]) from exabgp.reactor.network.outgoing import Outgoing connection = Outgoing(1,'82.219.0.69','82.219.212.34') writer = connection.writer(OPEN) while six.next(writer) is False: pass writer = connection.writer(KEEP) while six.next(writer) is False: pass reader = connection.reader() for size,msg,header,body,notification in reader: if size: print(od(header+body)) else: sys.stdout.write('-') reader = connection.reader() for size,msg,header,body,notification in reader: if size: print(od(header+body)) else: sys.stdout.write('+') connection.close()
def formated(): while True: line = six.next(fileobject).rstrip() self.index_line += 1 while line.endswith('\\'): line = line[:-1] + six.next(fileobject).rstrip() self.index_line += 1 yield line
def _scheduled_listener(self, flipflop=[]): try: for generator in self._async: try: six.next(generator) six.next(generator) flipflop.append(generator) except StopIteration: pass self._async, flipflop = flipflop, self._async return len(self._async) > 0 except KeyboardInterrupt: self._termination('^C received') return False
def connect (self): # allows to test the protocol code using modified StringIO with a extra 'pending' function if not self.connection: local = self.neighbor.md5_ip peer = self.neighbor.peer_address md5 = self.neighbor.md5_password ttl_out = self.neighbor.ttl_out self.connection = Outgoing(peer.afi,peer.top(),local.top(),self.port,md5,ttl_out) try: generator = self.connection.establish() while True: connected = six.next(generator) if not connected: yield False continue if self.peer.neighbor.api['neighbor-changes']: self.peer.reactor.processes.connected(self.peer.neighbor) yield True return except StopIteration: # close called by the caller # self.close('could not connect to remote end') yield False return
def connect(self): # allows to test the protocol code using modified StringIO with a extra 'pending' function if not self.connection: local = self.neighbor.md5_ip peer = self.neighbor.peer_address md5 = self.neighbor.md5_password ttl_out = self.neighbor.ttl_out self.connection = Outgoing(peer.afi, peer.top(), local.top(), self.port, md5, ttl_out) try: generator = self.connection.establish() while True: connected = six.next(generator) if not connected: yield False continue if self.peer.neighbor.api['neighbor-changes']: self.peer.reactor.processes.connected( self.peer.neighbor) yield True return except StopIteration: # close called by the caller # self.close('could not connect to remote end') yield False return
def peek(self): try: peaked = six.next(self.generator) self.next.append(peaked) return peaked except StopIteration: return ''
def run (self): if self.reactor.processes.broken(self.neighbor): # XXX: we should perhaps try to restart the process ?? self.logger.error('ExaBGP lost the helper process for this peer - stopping','process') if self.reactor.processes.terminate_on_error: self.reactor.api_shutdown() else: self.stop() return True if self.generator: try: # This generator only stops when it raises # otherwise return one of the ACTION return six.next(self.generator) except StopIteration: # Trying to run a closed loop, no point continuing self.generator = None if self._restart: return ACTION.LATER return ACTION.CLOSE elif self.generator is None: if self.fsm in [FSM.OPENCONFIRM,FSM.ESTABLISHED]: self.logger.debug('stopping, other connection is established',self.id()) self.generator = False return ACTION.LATER if self._delay.backoff(): return ACTION.LATER if self._restart: self.logger.debug('initialising connection to %s' % self.id(),'reactor') self.generator = self._run() return ACTION.LATER # make sure we go through a clean loop return ACTION.CLOSE
def connect (self): # allows to test the protocol code using modified StringIO with a extra 'pending' function if not self.connection: local = self.neighbor.md5_ip.top() if not self.neighbor.auto_discovery else None peer = self.neighbor.peer_address.top() afi = self.neighbor.peer_address.afi md5 = self.neighbor.md5_password md5_base64 = self.neighbor.md5_base64 ttl_out = self.neighbor.ttl_out self.connection = Outgoing(afi,peer,local,self.port,md5,md5_base64,ttl_out) if not self.connection.init: yield False return if not local: self.neighbor.local_address = IP.create(self.connection.local) if self.neighbor.router_id is None and self.neighbor.local_address.afi == AFI.ipv4: self.neighbor.router_id = self.neighbor.local_address try: generator = self.connection.establish() while True: connected = six.next(generator) if not connected: yield False continue if self.peer.neighbor.api['neighbor-changes']: self.peer.reactor.processes.connected(self.peer.neighbor) yield True return except StopIteration: # close called by the caller # self.close('could not connect to remote end') yield False return
def run(self): if not self._async: return False running = [] for (uid, generator) in self._async[0]: try: six.next(generator) six.next(generator) running.append((uid, generator)) except StopIteration: pass self._async.pop() if running: self._async.append(running) return True
def __call__ (self): # True if we need or are trying # False if we do not need to send one try: return six.next(self._generator) except StopIteration: raise Notify(4,0,'could not send keepalive')
def connect (self): # allows to test the protocol code using modified StringIO with a extra 'pending' function if not self.connection: local = self.neighbor.md5_ip.top() if not self.neighbor.auto_discovery else None peer = self.neighbor.peer_address.top() afi = self.neighbor.peer_address.afi md5 = self.neighbor.md5_password md5_base64 = self.neighbor.md5_base64 ttl_out = self.neighbor.ttl_out self.connection = Outgoing(afi,peer,local,self.port,md5,md5_base64,ttl_out) if not local and self.connection.init: self.neighbor.local_address = IP.create(self.connection.local) if self.neighbor.router_id is None and self.neighbor.local_address.afi == AFI.ipv4: self.neighbor.router_id = self.neighbor.local_address try: generator = self.connection.establish() while True: connected = six.next(generator) if not connected: yield False continue if self.peer.neighbor.api['neighbor-changes']: self.peer.reactor.processes.connected(self.peer.neighbor) yield True return except StopIteration: # close called by the caller # self.close('could not connect to remote end') yield False return
def __call__(self): if self.next: return self.next.popleft() try: return six.next(self.generator) except StopIteration: return ''
def __init__ (self, rd, endpoint, base, offset, size): NLRI.__init__(self,AFI.l2vpn,SAFI.vpls) self.action = OUT.ANNOUNCE self.nexthop = None self.rd = rd self.base = base self.offset = offset self.size = size self.endpoint = endpoint self.unique = six.next(unique)
def run(self): if self.reactor.processes.broken(self.neighbor): # XXX: we should perhaps try to restart the process ?? self.logger.processes( 'ExaBGP lost the helper process for this peer - stopping', 'error') self.stop() return True back = ACTION.LATER if self._restart else ACTION.CLOSE for direction in (self._incoming, self._outgoing): if direction.generator: try: # This generator only stops when it raises r = six.next(direction.generator) # if r is ACTION.NOW: status = 'immediately' # elif r is ACTION.LATER: status = 'next second' # elif r is ACTION.CLOSE: status = 'stop' # else: status = 'buggy' # self.logger.network('%s loop %11s, state is %s' % (direction.name,status,direction.fsm),'debug') if r == ACTION.NOW: back = ACTION.NOW elif r == ACTION.LATER: back = ACTION.LATER if back != ACTION.NOW else ACTION.NOW except StopIteration: # Trying to run a closed loop, no point continuing direction.generator = direction.enabled elif direction.generator is None: if direction.opposite.fsm in [ FSM.OPENCONFIRM, FSM.ESTABLISHED ]: self.logger.network( '%s loop, stopping, other one is established' % direction.name, 'debug') direction.generator = False continue if direction.name == 'out' and self._delay.backoff(): self.logger.network( '%s loop, skipping, not time yet' % direction.name, 'debug') back = ACTION.LATER continue if self._restart: self.logger.network( '%s loop, intialising' % direction.name, 'debug') direction.generator = self._run(direction) back = ACTION.LATER # make sure we go through a clean loop return back
def run (self): if not self._async: return False running = [] for (uid,generator) in self._async[0]: try: six.next(generator) six.next(generator) running.append((uid,generator)) except StopIteration: pass except KeyboardInterrupt: raise except Exception as exc: self.logger.error('async | %s | problem with function' % uid,'reactor') for line in str(exc).split('\n'): self.logger.error('async | %s | %s' % (uid,line),'reactor') self._async.pop() if running: self._async.append(running) return True
def test(): OPEN = ''.join([ chr(int(_, 16)) for _ in "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 00 1D 01 04 78 14 00 5A 52 DB 00 45 00" .split() ]) KEEP = ''.join([ chr(int(_, 16)) for _ in "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 00 00 04".split() ]) from exabgp.reactor.network.outgoing import Outgoing connection = Outgoing(1, '82.219.0.69', '82.219.212.34') writer = connection.writer(OPEN) while six.next(writer) is False: pass writer = connection.writer(KEEP) while six.next(writer) is False: pass reader = connection.reader() for size, msg, header, body, notification in reader: if size: print(od(header + body)) else: sys.stdout.write('-') reader = connection.reader() for size, msg, header, body, notification in reader: if size: print(od(header + body)) else: sys.stdout.write('+') connection.close()
def run (self): if not self.ready(): return False length = range(min(len(self._async),self.LIMIT)) uid, generator = self._async.popleft() for _ in length: try: six.next(generator) six.next(generator) except StopIteration: if not self._async: return False uid, generator = self._async.popleft() except KeyboardInterrupt: raise except Exception as exc: self.logger.error('async | %s | problem with function' % uid,'reactor') for line in str(exc).split('\n'): self.logger.error('async | %s | %s' % (uid,line),'reactor') self._async.appendleft((uid, generator)) return True
def _set (self, function): try: self._tokens = function self._next = six.next(self._tokens) except IOError as exc: error = str(exc) if error.count(']'): self.error.set(error.split(']')[1].strip()) else: self.error.set(error) self._tokens = Tokeniser._off self._next = [] return self.error.set('issue setting the configuration parser') except StopIteration: self._tokens = Tokeniser._off self._next = [] return self.error.set('issue setting the configuration parser, no data') return True
def __call__(self): self.number += 1 try: self.line, self._next = self._next, six.next(self._tokens) self.end = self.line[-1] except StopIteration: if not self.finished: self.finished = True self.line, self._next = self._next, [] self.end = self.line[-1] else: self.line = [] self.end = '' # should we raise a Location if called with no more data ? self.iterate.replenish(self.line[:-1]) return self.line
def establish (self): if not self.init: yield False return try: generator = ready(self.io) while True: connected = six.next(generator) if not connected: yield False continue yield True return except StopIteration: # self.io MUST NOT be closed here, it is closed by the caller yield False return nagle(self.io,self.peer) # Not working after connect() at least on FreeBSD TTL(self.io,self.peer,self.ttl) yield True
def establish(self): if not self.init: yield False return try: generator = ready(self.io) while True: connected = six.next(generator) if not connected: yield False continue yield True return except StopIteration: # self.io MUST NOT be closed here, it is closed by the caller yield False return nagle(self.io, self.peer) # Not working after connect() at least on FreeBSD TTL(self.io,self.peer,self.ttl) yield True
def _connect (self): proto = Protocol(self) generator = proto.connect() connected = False try: while not connected: if self._teardown: raise StopIteration() connected = six.next(generator) # we want to come back as soon as possible yield ACTION.LATER self.proto = proto except StopIteration: # Connection failed if not connected and self.proto: self.proto.close('connection to %s:%d failed' % (self.neighbor.peer_address,self.neighbor.connect)) # A connection arrived before we could establish ! if not connected or self.proto: yield ACTION.NOW raise Interrupted()
def _run(self, direction): """yield True if we want the reactor to give us back the hand with the same peer loop, None if we do not have any more work to do""" try: for action in direction.code(): yield action for action in self._main(direction): yield action # CONNECTION FAILURE except NetworkError as network: # we tried to connect once, it failed and it was not a manual request, we stop if self.once and not self._teardown: self.logger.network( 'only one attempt to connect is allowed, stopping the peer' ) self.stop() self._reset(direction, 'closing connection', network) return # NOTIFY THE PEER OF AN ERROR except Notify as notify: if direction.proto: try: generator = direction.proto.new_notification(notify) try: maximum = 20 while maximum: six.next(generator) maximum -= 1 yield ACTION.NOW if maximum > 10 else ACTION.LATER except StopIteration: pass except (NetworkError, ProcessError): self.logger.network(self.me('NOTIFICATION NOT SENT'), 'error') self._reset( direction, 'notification sent (%d,%d)' % (notify.code, notify.subcode), notify) else: self._reset(direction) return # THE PEER NOTIFIED US OF AN ERROR except Notification as notification: # we tried to connect once, it failed and it was not a manual request, we stop if self.once and not self._teardown: self.logger.network( 'only one attempt to connect is allowed, stopping the peer' ) self.stop() self._reset(direction,'notification received (%d,%d)' \ % (notification.code, notification.subcode), notification) return # RECEIVED a Message TYPE we did not expect except Message as message: self._reset(direction, 'unexpected message received', message) return # PROBLEM WRITING TO OUR FORKED PROCESSES except ProcessError as process: self._reset(direction, 'process problem', process) return # .... except Interrupted as interruption: self._reset(interruption.direction) return # UNHANDLED PROBLEMS except Exception as exc: # Those messages can not be filtered in purpose self.logger.raw('\n'.join([ no_panic, self.me(''), '', str(type(exc)), str(exc), trace(), footer ])) self._reset(direction) return
def _run (self): """yield True if we want the reactor to give us back the hand with the same peer loop, None if we do not have any more work to do""" try: for action in self._establish(): yield action for action in self._main(): yield action # CONNECTION FAILURE except NetworkError as network: # we tried to connect once, it failed and it was not a manual request, we stop if self.once and not self._teardown: self.logger.debug('only one attempt to connect is allowed, stopping the peer',self.id()) self.stop() self._reset('closing connection',network) return # NOTIFY THE PEER OF AN ERROR except Notify as notify: if self.proto: try: generator = self.proto.new_notification(notify) try: while True: six.next(generator) yield ACTION.NOW except StopIteration: pass except (NetworkError,ProcessError): self.logger.error('Notification not sent',self.id()) self._reset('notification sent (%d,%d)' % (notify.code,notify.subcode),notify) else: self._reset() return # THE PEER NOTIFIED US OF AN ERROR except Notification as notification: # we tried to connect once, it failed and it was not a manual request, we stop if self.once and not self._teardown: self.logger.debug('only one attempt to connect is allowed, stopping the peer',self.id()) self.stop() self._reset( 'notification received (%d,%d)' % ( notification.code, notification.subcode), notification ) return # RECEIVED a Message TYPE we did not expect except Message as message: self._reset('unexpected message received',message) return # PROBLEM WRITING TO OUR FORKED PROCESSES except ProcessError as process: self._reset('process problem',process) return # .... except Interrupted as interruption: self._reset('connection received before we could fully establish one') return # UNHANDLED PROBLEMS except Exception as exc: # Those messages can not be filtered in purpose self.logger.debug('\n'.join([ NO_PANIC, '', '', str(type(exc)), str(exc), trace(), FOOTER ]),'reactor') self._reset() return
def _main (self): """yield True if we want to come back to it asap, None if nothing urgent, and False if stopped""" if self._teardown: raise Notify(6,3) self.neighbor.rib.incoming.clear() include_withdraw = False # Announce to the process BGP is up self.logger.notice('connected to %s with %s' % (self.id(),self.proto.connection.name()),'reactor') self.stats['up'] = self.stats.get('up',0) + 1 if self.neighbor.api['neighbor-changes']: try: self.reactor.processes.up(self.neighbor) except ProcessError: # Can not find any better error code than 6,0 ! # XXX: We can not restart the program so this will come back again and again - FIX # XXX: In the main loop we do exit on this kind of error raise Notify(6,0,'ExaBGP Internal error, sorry.') send_eor = not self.neighbor.manual_eor new_routes = None self._resend_routes = SEND.NORMAL send_families = [] # Every last asm message should be re-announced on restart for family in self.neighbor.asm: if family in self.neighbor.families(): self.neighbor.messages.appendleft(self.neighbor.asm[family]) operational = None refresh = None command_eor = None number = 0 refresh_enhanced = True if self.proto.negotiated.refresh == REFRESH.ENHANCED else False send_ka = KA(self.proto.connection.session,self.proto) while not self._teardown: for message in self.proto.read_message(): self.recv_timer.check_ka(message) if send_ka() is not False: # we need and will send a keepalive while send_ka() is None: yield ACTION.NOW # Received update if message.TYPE == Update.TYPE: number += 1 self.logger.debug('<< UPDATE #%d' % number,self.id()) for nlri in message.nlris: self.neighbor.rib.incoming.update_cache(Change(nlri,message.attributes)) self.logger.debug(LazyFormat(' UPDATE #%d nlri ' % number,nlri,str),self.id()) elif message.TYPE == RouteRefresh.TYPE: if message.reserved == RouteRefresh.request: self._resend_routes = SEND.REFRESH send_families.append((message.afi,message.safi)) # SEND OPERATIONAL if self.neighbor.operational: if not operational: new_operational = self.neighbor.messages.popleft() if self.neighbor.messages else None if new_operational: operational = self.proto.new_operational(new_operational,self.proto.negotiated) if operational: try: six.next(operational) except StopIteration: operational = None # make sure that if some operational message are received via the API # that we do not eat memory for nothing elif self.neighbor.messages: self.neighbor.messages.popleft() # SEND REFRESH if self.neighbor.route_refresh: if not refresh: new_refresh = self.neighbor.refresh.popleft() if self.neighbor.refresh else None if new_refresh: refresh = self.proto.new_refresh(new_refresh) if refresh: try: six.next(refresh) except StopIteration: refresh = None # Take the routes already sent to that peer and resend them if self._reconfigure: self._reconfigure = False # we are here following a configuration change if self._neighbor: # see what changed in the configuration self.neighbor.rib.outgoing.replace(self._neighbor.backup_changes,self._neighbor.changes) # do not keep the previous routes in memory as they are not useful anymore self._neighbor.backup_changes = [] # Take the routes already sent to that peer and resend them if self._resend_routes != SEND.DONE: enhanced = True if refresh_enhanced and self._resend_routes == SEND.REFRESH else False self._resend_routes = SEND.DONE self.neighbor.rib.outgoing.resend(send_families,enhanced) send_families = [] # Need to send update if not new_routes and self.neighbor.rib.outgoing.pending(): # XXX: in proto really. hum to think about ? new_routes = self.proto.new_update(include_withdraw) if new_routes: try: for _ in range(25): # This can raise a NetworkError six.next(new_routes) except StopIteration: new_routes = None include_withdraw = True elif send_eor: send_eor = False for _ in self.proto.new_eors(): yield ACTION.NOW self.logger.debug('>> EOR(s)',self.id()) # SEND MANUAL KEEPALIVE (only if we have no more routes to send) elif not command_eor and self.neighbor.eor: new_eor = self.neighbor.eor.popleft() command_eor = self.proto.new_eors(new_eor.afi,new_eor.safi) if command_eor: try: six.next(command_eor) except StopIteration: command_eor = None if new_routes or message.TYPE != NOP.TYPE: yield ACTION.NOW elif self.neighbor.messages or operational: yield ACTION.NOW elif self.neighbor.eor or command_eor: yield ACTION.NOW else: yield ACTION.LATER # read_message will loop until new message arrives with NOP if self._teardown: break # If graceful restart, silent shutdown if self.neighbor.graceful_restart and self.proto.negotiated.sent_open.capabilities.announced(Capability.CODE.GRACEFUL_RESTART): self.logger.error('closing the session without notification',self.id()) self.proto.close('graceful restarted negotiated, closing without sending any notification') raise NetworkError('closing') # notify our peer of the shutdown raise Notify(6,self._teardown)
def _run(self): """yield True if we want the reactor to give us back the hand with the same peer loop, None if we do not have any more work to do""" try: for action in self._establish(): yield action for action in self._main(): yield action # CONNECTION FAILURE except NetworkError as network: # we tried to connect once, it failed and it was not a manual request, we stop if self.once and not self._teardown: self.logger.debug( 'only one attempt to connect is allowed, stopping the peer', self.id()) self.stop() self._reset('closing connection', network) return # NOTIFY THE PEER OF AN ERROR except Notify as notify: if self.proto: try: generator = self.proto.new_notification(notify) try: while True: six.next(generator) yield ACTION.NOW except StopIteration: pass except (NetworkError, ProcessError): self.logger.error('Notification not sent', self.id()) self._reset( 'notification sent (%d,%d)' % (notify.code, notify.subcode), notify) else: self._reset() return # THE PEER NOTIFIED US OF AN ERROR except Notification as notification: # we tried to connect once, it failed and it was not a manual request, we stop if self.once and not self._teardown: self.logger.debug( 'only one attempt to connect is allowed, stopping the peer', self.id()) self.stop() self._reset( 'notification received (%d,%d)' % (notification.code, notification.subcode), notification) return # RECEIVED a Message TYPE we did not expect except Message as message: self._reset('unexpected message received', message) return # PROBLEM WRITING TO OUR FORKED PROCESSES except ProcessError as process: self._reset('process problem', process) return # .... except Interrupted as interruption: self._reset( 'connection received before we could fully establish one') return # UNHANDLED PROBLEMS except Exception as exc: # Those messages can not be filtered in purpose self.logger.debug( '\n'.join([ NO_PANIC, '', '', str(type(exc)), str(exc), trace(), FOOTER ]), 'reactor') self._reset() return
def _main(self): """yield True if we want to come back to it asap, None if nothing urgent, and False if stopped""" if self._teardown: raise Notify(6, 3) self.neighbor.rib.incoming.clear() include_withdraw = False # Announce to the process BGP is up self.logger.notice( 'connected to %s with %s' % (self.id(), self.proto.connection.name()), 'reactor') self.stats['up'] = self.stats.get('up', 0) + 1 if self.neighbor.api['neighbor-changes']: try: self.reactor.processes.up(self.neighbor) except ProcessError: # Can not find any better error code than 6,0 ! # XXX: We can not restart the program so this will come back again and again - FIX # XXX: In the main loop we do exit on this kind of error raise Notify(6, 0, 'ExaBGP Internal error, sorry.') send_eor = not self.neighbor.manual_eor new_routes = None self._resend_routes = SEND.NORMAL send_families = [] # Every last asm message should be re-announced on restart for family in self.neighbor.asm: if family in self.neighbor.families(): self.neighbor.messages.appendleft(self.neighbor.asm[family]) operational = None refresh = None command_eor = None number = 0 refresh_enhanced = True if self.proto.negotiated.refresh == REFRESH.ENHANCED else False send_ka = KA(self.proto.connection.session, self.proto) while not self._teardown: for message in self.proto.read_message(): self.recv_timer.check_ka(message) if send_ka() is not False: # we need and will send a keepalive while send_ka() is None: yield ACTION.NOW # Received update if message.TYPE == Update.TYPE: number += 1 self.logger.debug('<< UPDATE #%d' % number, self.id()) for nlri in message.nlris: self.neighbor.rib.incoming.update_cache( Change(nlri, message.attributes)) self.logger.debug( LazyFormat(' UPDATE #%d nlri ' % number, nlri, str), self.id()) elif message.TYPE == RouteRefresh.TYPE: if message.reserved == RouteRefresh.request: self._resend_routes = SEND.REFRESH send_families.append((message.afi, message.safi)) # SEND OPERATIONAL if self.neighbor.operational: if not operational: new_operational = self.neighbor.messages.popleft( ) if self.neighbor.messages else None if new_operational: operational = self.proto.new_operational( new_operational, self.proto.negotiated) if operational: try: six.next(operational) except StopIteration: operational = None # make sure that if some operational message are received via the API # that we do not eat memory for nothing elif self.neighbor.messages: self.neighbor.messages.popleft() # SEND REFRESH if self.neighbor.route_refresh: if not refresh: new_refresh = self.neighbor.refresh.popleft( ) if self.neighbor.refresh else None if new_refresh: refresh = self.proto.new_refresh(new_refresh) if refresh: try: six.next(refresh) except StopIteration: refresh = None # Take the routes already sent to that peer and resend them if self._reconfigure: self._reconfigure = False # we are here following a configuration change if self._neighbor: # see what changed in the configuration self.neighbor.rib.outgoing.replace( self._neighbor.backup_changes, self._neighbor.changes) # do not keep the previous routes in memory as they are not useful anymore self._neighbor.backup_changes = [] # Take the routes already sent to that peer and resend them if self._resend_routes != SEND.DONE: enhanced = True if refresh_enhanced and self._resend_routes == SEND.REFRESH else False self._resend_routes = SEND.DONE self.neighbor.rib.outgoing.resend(send_families, enhanced) send_families = [] # Need to send update if not new_routes and self.neighbor.rib.outgoing.pending(): # XXX: in proto really. hum to think about ? new_routes = self.proto.new_update(include_withdraw) if new_routes: try: for _ in range(25): # This can raise a NetworkError six.next(new_routes) except StopIteration: new_routes = None include_withdraw = True elif send_eor: send_eor = False for _ in self.proto.new_eors(): yield ACTION.NOW self.logger.debug('>> EOR(s)', self.id()) # SEND MANUAL KEEPALIVE (only if we have no more routes to send) elif not command_eor and self.neighbor.eor: new_eor = self.neighbor.eor.popleft() command_eor = self.proto.new_eors(new_eor.afi, new_eor.safi) if command_eor: try: six.next(command_eor) except StopIteration: command_eor = None if new_routes or message.TYPE != NOP.TYPE: yield ACTION.NOW elif self.neighbor.messages or operational: yield ACTION.NOW elif self.neighbor.eor or command_eor: yield ACTION.NOW else: yield ACTION.LATER # read_message will loop until new message arrives with NOP if self._teardown: break # If graceful restart, silent shutdown if self.neighbor.graceful_restart and self.proto.negotiated.sent_open.capabilities.announced( Capability.CODE.GRACEFUL_RESTART): self.logger.error('closing the session without notification', self.id()) self.proto.close( 'graceful restarted negotiated, closing without sending any notification' ) raise NetworkError('closing') # notify our peer of the shutdown raise Notify(6, self._teardown)
def _connect(self): # try to establish the outgoing connection self._outgoing.fsm.change(FSM.CONNECT) proto = Protocol(self) generator = proto.connect() connected = False try: while not connected: if self._teardown: raise StopIteration() connected = six.next(generator) # we want to come back as soon as possible yield ACTION.LATER except StopIteration: # Connection failed if not connected: proto.close('connection to %s:%d failed' % (self.neighbor.peer_address, proto.port)) # A connection arrived before we could establish ! if not connected or self._incoming.proto: yield ACTION.NOW raise Interrupted(self._outgoing) self._outgoing.proto = proto # send OPEN # Only yield if we have not the open, otherwise the reactor can run the other connection # which would be bad as we need to set the state without going to the other peer message = Message.CODE.NOP for message in proto.new_open(self._restarted): if ord(message.TYPE) == Message.CODE.NOP: yield ACTION.NOW proto.negotiated.sent(message) self._outgoing.fsm.change(FSM.OPENSENT) # Read OPEN wait = environment.settings().bgp.openwait opentimer = ReceiveTimer( self.me, wait, 1, 1, 'waited for open too long, we do not like stuck in active') for message in self._outgoing.proto.read_open( self.neighbor.peer_address.top()): opentimer.check_ka(message) # XXX: FIXME: change the whole code to use the ord and not the chr version # Only yield if we have not the open, otherwise the reactor can run the other connection # which would be bad as we need to do the collission check if ord(message.TYPE) == Message.CODE.NOP: yield ACTION.LATER self._outgoing.fsm.change(FSM.OPENCONFIRM) proto.negotiated.received(message) proto.validate_open() if self._incoming.fsm == FSM.OPENCONFIRM: self.logger.network( 'outgoing connection finds the incoming connection is in openconfirm' ) local_id = self.neighbor.router_id.pack() remote_id = proto.negotiated.received_open.router_id.pack() if local_id < remote_id: self.logger.network('aborting the outgoing connection') raise Interrupted(self._outgoing) else: self.logger.network('closing the incoming connection') self._stop(self._incoming, 'collision local id < remote id') yield ACTION.LATER # Send KEEPALIVE for message in proto.new_keepalive('OPENCONFIRM'): yield ACTION.NOW # Start keeping keepalive timer self.recv_timer = ReceiveTimer(self.me, proto.negotiated.holdtime, 4, 0) # Read KEEPALIVE for message in self._outgoing.proto.read_keepalive(): self.recv_timer.check_ka(message) yield ACTION.NOW self._outgoing.fsm.change(FSM.ESTABLISHED) # let the caller know that we were sucesfull yield ACTION.NOW
def start(*args, **kwargs): generator = function(*args, **kwargs) return lambda: six.next(generator) # noqa
def start (*args, **kwargs): generator = function(*args, **kwargs) return lambda: six.next(generator) # noqa