示例#1
0
    def startup(self):
        """
        startup routine
        Loads from cloned state

        """
        # clean working dir and extract config, creds, and state
        log.info("Cleaning working directory...")
        command = "rm -rf data/creds"
        #shell(command)
        log.info("Extracting cloned state...")
        command = "cd data && tar xzf clone.tar.gz && cd .."
        #shell(command)
        # load config
        log.info("Loading configuration...")
        self.cm = ConfigManager()
        self.conf = self.cm.load_config(configFile)

        # load state
        log.info("Loading state...")
        self.state = StateManager(self.conf)
示例#2
0
class Congregate:
    def __init__(self):
        """ Initialize system state and components """
        try:
            # Load state
            self.startup() # TODO make an init without cloned state an option
            # Create main event loop
            self.loop = asyncio.get_event_loop()
            # Create BPCon instance
            self.bpcon = BPConProtocol(self.conf, self.state)
            self.state.groups['G1'] = self.bpcon.peers # connect BPCon to Congregate instance
            self.paxos_server = websockets.serve(self.bpcon.main_loop, self.conf['ip_addr'], self.conf['port'], ssl=self.conf['ssl'])
            self.loop.run_until_complete(self.paxos_server)
            log.info("Started BPCon on port {}".format(self.conf['port']))

            # Create Congregate instance
            self.c = CongregateProtocol(self.loop, self.conf, self.bpcon)       
            self.congregate_server = websockets.serve(self.c.main_loop, self.conf['ip_addr'], self.conf['port']+1, ssl=self.conf['ssl'])
            self.loop.run_until_complete(self.congregate_server)
            log.info("Started Congregate on port {}".format(self.conf['port']+1))

            # Create API server
            self.web_server = websockets.serve(self.mainloop, self.conf['ip_addr'], self.conf['port']+2) 
            self.loop.run_until_complete(self.web_server)
            log.info("Started Web Server on port {}".format(self.conf['port']+2))

            # Add self to local group
            log.debug("adding self to local group")
            self.join_request()
            

            # Testing 
            if self.conf['is_client']:
                #self.c.reconfig()
                log.debug("is client. making test requests")
                for x in range(1):
                    request = "P,{},hello{}".format(x,x)
                    self.local_request("P,test,value")
                    self.local_request("P,test1,value1")
                    self.local_request("P,test2,value2")
                    self.local_request("P,test,value3")
                    self.local_request("D,test2,")
                    self.local_request(request)

                log.debug("requests complete")     
            else:
                #self.c.request_split()
                self.local_request("S,,")

                #self.c.request_merge("G2")
                self.group_request("M", "G2")

        except Exception as e:
            log.info(e)

    ### State operations ###

    def startup(self):
        """
        startup routine
        Loads from cloned state

        """
        # clean working dir and extract config, creds, and state
        log.info("Cleaning working directory...")
        command = "rm -rf data/creds"
        #shell(command)
        log.info("Extracting cloned state...")
        command = "cd data && tar xzf clone.tar.gz && cd .."
        #shell(command)
        # load config
        log.info("Loading configuration...")
        self.cm = ConfigManager()
        self.conf = self.cm.load_config(configFile)

        # load state
        log.info("Loading state...")
        self.state = StateManager(self.conf)
        #self.state.load_state()

    def clone(self):
        """
        create a copy of db, peers, peer creds, and config
        save to compressed archive
        used to add new nodes to system
        """
        try:
            # save own credentials in clone's peer folder
            ownCert = "data/creds/local/server.crt"
            ownPubKey = "data/creds/local/server.pub"

            
            ID = get_ID(self.conf['p_wss'])  
            
            certCopy = "data/creds/peers/certs/{}.crt".format(ID)
            keyCopy = "data/creds/peers/keys/{}.pub".format(ID)

            shell("cp {} {}".format(ownCert, certCopy))
            shell("cp {} {}".format(ownPubKey, keyCopy))

            # save groups and db to backup_dir
            self.state.image_state() 
            self.cm.save_config()
            backupdir = "backup/"
            cfile = "config.ini"
            command = "cd data/ && tar czf clone.tar.gz {} {} creds/peers".format(cfile,backupdir)
            shell(command)
            log.info("clone of state successfully created")

        except Exception as e:
            log.info("clone of state failed")

    ### Requests ###

    def local_request(self, msg):
        log.info("replicating {}".format(msg))
        self.loop.run_until_complete(self.c.bpcon_request(msg))

    def group_request(self, req_type, target_group):
        log.debug("group request initiated")
        self.loop.run_until_complete(self.c.make_2pc_request(req_type, target_group))

    def join_request(self):
        """ Command Congregate instance to join its peers by supplying credentials """
        log.debug("attempting to join")
        try:
            with open(self.conf['certfile'], 'r') as fh:
                cert = fh.read()
            with open(self.conf['keyfile'], 'r') as fh:
                pubkey = fh.read()
            wss = self.conf['p_wss']
            self.loop.run_until_complete(self.c.handle_join(wss,pubkey,cert))
        except Exception as e:
            log.debug(e)

    def handle_reconfig_request(self):
        """ Create and return state clone """
        self.clone()
        with open('data/clone.tar.gz', 'r') as fh:
            toreturn = fh.read()
            log.debug("cloned state added successfully")
            return toreturn    

    def make_reconfig_request(self, wss):
        """ Request state clone """ 
        # TODO send a request
        pass


    def shutdown(self):
        print("\nShutdown initiated...")
        print("\nDatabase contents:\n{}".format(self.bpcon.state.db.kvstore)) # save state here
        self.paxos_server.close()
        self.congregate_server.close()
        print("\nPeer Groups:")
        for gname, rmgr in self.state.groups.items():
            print("{}: {} {}".format(gname, list(rmgr.peers.keys()), rmgr.keyspace))
    
    def direct_msg(self, msg):
        msg_type = msg[0]
        if msg_type == '0':
        # 0 -> bpcon
            return "hello"
        elif msg_type == '1': #custom or https -> serve or register and pass to congregate
        # 1 -> congregate
            return "hello"
        elif msg_type == '2': 
        # 2 -> external request
            self.handle_external_request(msg[1:])    
        # Client Request Handling

    def handle_db_request(self, request):
        # client request for data
        # route if necessary (manage for client)
        # verification of request and requestor permissions
        # self.db.get(k)
        pass


    def handle_API_request(self, msg):
        # client API requests
        if len(msg) < 4:
            log.info("bad external request: too short")
        else:    
            try:
                a,b,c = msg.split('<>')
            except Exception as e:
                log.info("bad external request: malformed")

            if a == '0': # GET
                #first check cache
                self.state.db.get(b)
            elif a == '1': # PUT
                self.local_request("P,{},{}".format(b,c))
            elif a == '2': # DEL
                self.local_request("D,{},{}".format(b,"null"))
            else: # malformed
                log.info("bad API request")
        

    @asyncio.coroutine
    def mainloop(self, websocket, path):
        try:
            input_msg = yield from websocket.recv()
            log.debug("< {}".format(input_msg))
            if self.conf['use_single_port']:
                output_msg = self.direct_msg(input_msg)
            else:   #using this port for external requests only
                output_msg = yield from self.handle_external_request(input_msg)
                
            if output_msg:
                yield from websocket.send(output_msg)
                #self.bmsgs.append(output_msg)
                
            else:
                yield from websocket.send("Hello from Congregate!")
                log.error("got bad input from peer")

            # adapt here
                # reconfig requests
                

        except Exception as e:
            log.debug(input_msg)
            log.error("mainloop exception: {}".format(e))