def __init__(self, handler): super(LocalMihf, self).__init__() self.name = util.gen_id('MIHF-') self.links = dict() self.current_link = None self._sock = None self._peers = dict() self._handler = handler self._next_peek = time.time() self._next_refresh = time.time() - 1 self._ready_cache = list() self._msgmngr = MessageManager() # event queue self._equeue = list() #collections.deque()
class LocalMihf(BasicMihf): PEEK_TIME = 10 PORT = 12345 def __init__(self, handler): super(LocalMihf, self).__init__() self.name = util.gen_id('MIHF-') self.links = dict() self.current_link = None self._sock = None self._peers = dict() self._handler = handler self._next_peek = time.time() self._next_refresh = time.time() - 1 self._ready_cache = list() self._msgmngr = MessageManager() # event queue self._equeue = list() #collections.deque() def discover(self, link): if not link.discoverable: return if not isinstance(link, str): link = link.ifname self._send(None, 'mih_discovery.request', daddr=('<broadcast>', self.PORT), link=link) def switch(self, link): success = False # same link, try to reconnect if self.current_link == link: success = link.is_ready() or link.up() if success: link.route_up() logging.info('Link %s reestablished.', link) else: logging.info('Failed to reestablish %s link.', link) return success # start the handover ho_origin = self.current_link ho_begin = time.time() logging.info('Handover from %s to %s started.', ho_origin, link) success = link.is_ready() or link.up() if success: if self.current_link: self.current_link.down() link.route_up() # replaces the old route. self.current_link = link if link.is_mobile(): self._next_peek = time.time() + self.PEEK_TIME else: link.down() ho_time = time.time() - ho_begin ho_status = 'successfully' if success else 'unsuccessfully' logging.info('Handover from %s to %s finished %s in %is', ho_origin, link, ho_status, ho_time) return success def _make_socket(self, bind=False, blocking=True, timeout=0.3): self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) if bind: self._sock.bind(('', self.PORT)) self._sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True) self._sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, True) # XXX: using select() with blocking sockets lowers the CPU usage for # some reason. self._sock.setblocking(blocking) self._sock.settimeout(timeout) def _send(self, dmihf, kind, payload=None, parent=None, daddr=None, link=None): """Put a message on the output queue.""" assert daddr or (not daddr and dmihf) if not daddr: daddr = self._peers[dmihf].addr self._msgmngr.send( self.name, daddr, kind, dmihf=dmihf, payload=payload, parent=parent, link=link) def _handle_message(self, srcaddr, msg): pass def _handle_link_event(self, link, state): # replace previous events on the link for i in xrange(0, len(self._equeue)): if self._equeue and self._equeue[i][0] == link: del self._equeue[i] self._equeue.append((link, state)) def _export_links(self): """Returns a list of link data suitable for remote use.""" exported = [] for link in self.links.values(): dic = link.as_dict() dic['remote'] = True exported.append(dic) return exported def _scan_links(self): """Checks for newly added interfaces and removes nonexistent ones.""" llnames = get_local_ifnames() new = list(set(llnames) - set(self.links.keys())) dead = list(set(self.links.keys()) - set(llnames)) for name in dead: # XXX: send link down? logging.warning('Local link not found %s', name) del self.links[name] for name in new: logging.info('Found link %s.', name) link = make_link(ifname=name) link.on_link_event = self._handle_link_event self.links[name] = link def _refresh_links(self): """Refreshes the MIHF link list.""" if self._next_refresh < time.time(): self._next_refresh = time.time() + 0.5 # 500 ms else: return self._ready_cache ready = [] for link in self.links.values(): link.poll_and_notify() if link.is_ready(): ready.append(link) on_mobile = self.current_link and self.current_link.is_mobile() if on_mobile and time.time() > self._next_peek: self._peek_links() self._next_peek += self.PEEK_TIME self._ready_cache = ready return ready def _peek_links(self): """Check if there are non-mobile links available. When the current link is a mobile one, this method is called every PEEK_TIME seconds to check if there are available links on in the environment.""" # XXX: what is cheaper? # (a) enable + disable (on failure) # (b) keep it enabled and just check to see if it is ready? logging.debug('Peeking links') wifi = [link for link in self.links.values() if not link.is_mobile()] for link in wifi: is_up = link.up() if not is_up or (is_up and util.link_compare(link, self.current_link) < 1): link.down() def _proccess_messages(self): for addr, msg in self._msgmngr.receive(): self._handle_message(addr, msg) def _process_events(self): """Processes the event queue. This method iterates through the event queue and calls the respective event handles, when found. The event queue is cleared.""" events = list(self._equeue) self._equeue = list() for link, state in events: if self._handler: fname = 'link_' + state.replace(' ', '_') if hasattr(self._handler, fname): getattr(self._handler, fname)(self, link)