def handle_heartbeat_message(self, addr, parts):
        # forward heartbeat message as it is, if not leader
        if self.election.isLeader is False:
            # check, if the heartbeat comes from the neighbour
            left_neighbour = self.hosts.get_neighbour(direction="left")
            right_neighbour = self.hosts.get_neighbour(direction="right")
            # cehck, if heartbeat comes from the right neighbour
            if addr[0] == right_neighbour:
                # forward message
                logging.info("Heartbeat: received. forward to %s" %
                             left_neighbour)
                new_message = "HB:%s:%s" % (parts[1], parts[2])
                self.socket_sender.send_message(new_message,
                                                left_neighbour,
                                                type="unicast")

                # note time of last heartbeat
                self.last_heartbeat_received = time.time()
            else:
                logging.error("Heartbeat: received from wrong neighbour")
        else:
            # if leader, have a look at the message if it is from himself
            if self.heartbeat_message:
                if parts[1] == self.heartbeat_message["id"]:
                    logging.info("Heartbeat: received own heartbeat from %s." %
                                 addr[0])
                    self.heartbeat_message = None
                    self.last_heartbeat_received = time.time()
Beispiel #2
0
def main():
    frontend = "192.168.0.179"
    parser = argparse.ArgumentParser(prog="my_megazord_program")
    parser.add_argument(
        "-f",
        "--frontend",
        help='start with localshit -f "172.17.0.2" to add frontend')
    parser.add_argument(
        "-d",
        "--debug",
        help="start localhost and wait for VS Code debugger",
        action="store_true",
    )
    args = parser.parse_args()
    if args.frontend:
        frontend = args.frontend

    if args.debug:
        import debugpy

        debugpy.listen(5678)
        print("Waiting for debugger attach: %s" % os.getpid())
        debugpy.wait_for_client()

    logging.info("Frontend server is set to %s" % frontend)

    try:
        logging.info("starting manager...")
        _ = LocalsHitManager(frontend=frontend)
    except Exception as e:
        logging.error("Error while starting app: %s" % e)
        traceback.print_exc()
    def work_func(self):
        if self.election.isLeader:
            # only run content service if elected as leader
            if self.server.isRunning is False:
                # check if WebsocketServer is running
                try:
                    self.server.run_forever()
                    self.server.isRunning = True
                except Exception as e:
                    logging.error("Error while restarting server: %s" % e)
            else:
                if config["chuck_norris"] is True:
                    # optionally publish chuck norris quotes in a intervall
                    time_diff = time.time() - self.last_update
                    if time_diff >= config["quote_intervall"]:
                        logging.info("Content: publish new quote")
                        id, quote = self._get_quote("jokes.json")
                        data = "%s:%s:%s" % ("CO", id, quote)
                        self._replicate_and_send(data)

                        self.last_update = time.time()

        else:
            if self.server.isRunning is True:
                self.server.isRunning = False
                data = "%s:%s" % ("CL", "close server")
                self.server.send_message_to_all(data)
                self.server.shutdown()
                self.server.server_close()
                logging.info("Content: publish service stopped")
 def _ask_for_initial_database(self):
     data = "%s:%s" % ("AA", self.election.current_member_ip)
     try:
         self.reliable_socket.multicast(data)
     except Exception as e:
         logging.error("Content: Error while saving quote: %s" % e)
         return
Beispiel #5
0
    def _send_unicast(self, message, address, port):
        if port is None:
            port = self.UCAST_PORT

        if address is None:
            logging.error("No address given. Cannot send message.")
            return

        self.socket_unicast.sendto(message.encode(), (address, port))
 def get_range(self, start=None, end=None):
     try:
         if start and end:
             return self.database[start:end]
         elif start:
             return self.database[start:]
         elif end:
             return self.database[:end]
         else:
             return None
     except Exception as e:
         logging.error("Error at get_range: %s" % e)
 def _replicate_and_send(self, data):
     # 1. replicate with other backend servers and itself to store quote to database
     try:
         self.reliable_socket.multicast(data)
     except Exception as e:
         logging.error("Content: Error while saving quote: %s" % e)
         return
     # 2. Send message to client
     try:
         self.server.send_message_to_all(data)
     except Exception as e:
         logging.error("Content: Error while sending quote: %s" % e)
         return
    def _get_quote(self, filename):
        quote = None
        quote_id = None

        try:
            file = open(filename)
            data = json.load(file)
            quotes = data["value"]
            counts = len(quotes)
            rand = random.randint(0, counts - 1)
            quote = quotes[rand]
            quote = quote["joke"]
            quote_id = uuid.uuid4()
        except Exception as e:
            logging.error("Content: Error while generating quote: %s" % e)

        return (quote_id, quote)
    def work_func(self):
        """
        Manages all incoming messages on unicast and multicast socket
        """
        try:
            # listen to incoming messages on multicast and unicast
            inputready, outputready, exceptready = select(
                [self.socket_multicast, self.socket_unicast], [], [], 1,
            )

            for socket_data in inputready:
                # handle UDP socket connections
                data, addr = socket_data.recvfrom(1024)  # wait for a packet
                if data:
                    parts = data.decode().split(":")
                    if parts[0] == "SA":
                        self.service_announcement.handle_service_announcement(addr)
                    elif parts[0] == "SE":
                        self.election.forward_election_message(parts)
                    elif parts[0] == "HB":
                        # TODO: forward heartbeat
                        self.heartbeat.handle_heartbeat_message(addr, parts)
                    elif parts[0] == "FF":
                        self.heartbeat.handle_failure_message(addr, parts)
                    elif parts[0] == "RP":
                        logging.error("Reply from host: %s" % addr[0])
                        if addr[0] != utils.get_host_address():
                            self.hosts.add_host(addr[0])
                    else:
                        logging.error("Unknown message type: %s" % parts[0])

                    # reset heartbeat beacause of leader election or service announcement
                    self.heartbeat.last_heartbeat_received = time.time()
        except Exception as e:
            logging.error("Error: %s" % e)

        # check if announcement and election is over:
        if self.isActive is True:
            # send heartbeat messages
            if self.election.isLeader is True:
                self.heartbeat.send_heartbeat()

        # watch heartbeats
        th = threading.Thread(target=self.heartbeat.watch_heartbeat)
        th.start()
    def forward_election_message(self, message):
        compare = utils.compare_adresses(message[1], self.current_member_ip)

        sender_id = message[1]
        leader_elected = eval(message[2])

        if leader_elected is False:
            if compare is CompareResult.LARGER:
                # 4.1 if id is larger, forward message to next member
                logging.info("Leader Election: Forward message as it is.")
                self.participant = True
                new_message = "SE:%s:%s" % (sender_id, False)
                self.socket_sender.send_message(new_message,
                                                self.hosts.get_neighbour(),
                                                type="unicast")
            elif compare is CompareResult.LOWER and self.participant is False:
                # 4.2 if id is smaller and not yes marked as participant, replace id and forward message to next member.
                self.participant = True
                logging.info("Leader Election: Forward message with own id.")
                new_message = "SE:%s:%s" % (self.current_member_ip, False)
                self.socket_sender.send_message(new_message,
                                                self.hosts.get_neighbour(),
                                                type="unicast")
            elif compare is CompareResult.LOWER and self.participant is True:
                # 4.3 if id is smaller but already participant, then discard message
                logging.info(
                    "Leader Election: Already participant of an election. Discard message."
                )
            elif compare is CompareResult.SAME:
                # 4.4 if message came back, set itself as leader and start second part algorithm. Inform others about elected leader.
                logging.info(
                    "Leader Election: Message came back to sender. Elected as leader."
                )
                self.participant = False
                self.isLeader = True
                self.elected_leader = self.current_member_ip
                # start second part of algorithm, inform others about election
                new_message = "SE:%s:%s" % (self.current_member_ip, True)
                self.socket_sender.send_message(new_message,
                                                self.hosts.get_neighbour(),
                                                type="unicast")
                self.send_election_to_frontend()
            else:
                logging.error("Leader Election: invalid result")
        else:
            # forward message about elected leader
            if sender_id is self.current_member_ip:
                logging.info(
                    "Leader Election: Message came back to sender. Election is over. Elected Leader: %s"
                    % self.elected_leader)
                self.send_election_to_frontend()
            elif self.participant is True:
                # elected message received. mark as non-participant, record election and forward message
                self.participant = False
                self.isLeader = False
                self.elected_leader = sender_id
                logging.info(
                    "Leader Election: Forward election message. Elected Leader: %s"
                    % self.elected_leader)
                new_message = "SE:%s:%s" % (message[1], message[2])
                self.socket_sender.send_message(new_message,
                                                self.hosts.get_neighbour(),
                                                type="unicast")
            else:
                logging.info(
                    "Leader Election: Election is over. Elected Leader: %s" %
                    self.elected_leader)
                self.send_election_to_frontend()
Beispiel #11
0
    def __init__(self, frontend=None):
        self.threads = []
        if frontend is None:
            self.frontend = config["frontend_server"]
        else:
            self.frontend = frontend
        self.running = True
        self.isActive = False
        logging.info("manager started!")

        # init socket connections
        self.socket_sender = SocketSender()

        self.own_address = utils.get_host_address()

        # init Ring
        self.hosts = Ring(self.own_address)
        # init service announcement object
        self.service_announcement = ServiceAnnouncement(
            self.hosts, self.socket_sender)
        # init election
        self.election = Election(self.socket_sender,
                                 self.hosts,
                                 frontend=frontend)
        # init database
        self.database = Database()

        # init ReliableSocketWorker
        self.reliable_socket = ReliableSocketWorker(
            self.running, self.hosts, port=config["reliable_socket"])

        try:
            self.reliable_socket.run()
            self.heartbeat = Heartbeat(self.hosts, self.election,
                                       self.socket_sender)

            # initiate service discovery thread
            self.discovery_thread = ServiceDiscovery(
                self.service_announcement,
                self.hosts,
                self.election,
                self.heartbeat,
                self.isActive,
            )
            self.discovery_thread.start()
            self.threads.append(self.discovery_thread)

            # start service announcement after discovery
            self.service_announcement.announce_service(
                timeout=config["announcement_timeout"])

            # start election after discovery
            self.election.start_election(await_response=True, timeout=1)

            self.discovery_thread.isActive = True

            # initiate Content Provider
            content_provider = ContentProvider(self.election,
                                               self.reliable_socket,
                                               self.database)
            content_provider.start()
            self.threads.append(content_provider)

            # monitor threads and exit on failing
            while self.running:
                for th in self.threads:
                    if not th.is_alive():
                        logging.info("Thread %s died." % th.__class__.__name__)
                        self.running = False
                        break

                time.sleep(0.2)

        except KeyboardInterrupt:
            logging.info("Process terminated by user")
        except Exception as e:
            logging.error("Error in run.py: %s" % e)
            traceback.print_exc()
        finally:
            # graceful shutdown
            self.isActive = False
            self.discovery_thread.isActive = False
            logging.info("stopping threads...")
            for th in self.threads:
                logging.info("Stopping thread %s." % th.__class__.__name__)
                th.stop()
            for th in self.threads:
                logging.info("Joining thread %s." % th.__class__.__name__)
                th.join()

            for thread in self.reliable_socket.threads:
                logging.info("Joining Thread %s." % thread.name)
                thread.join(0.2)

            main_thread = threading.currentThread()
            for t in threading.enumerate():
                if t is main_thread:
                    continue
                logging.info("Joining Thread %s." % thread.name)
                t.join(0.2)

            logging.info("threads stopped")