def handleHeartbeatRequest(self, data): log.high_debug("Hit handleHeartbeatRequest!") log.info("Operation: {} from client-sn: {} => OK".format( data["operation"], data["client-sn"])) return self.buildResponse("heartbeat")
def handleCmdListAuctions(self, auctions_filter="all"): log.high_debug("Hit handleCmdListAuctions!") # try: auctions = self.__client.sendListAuctionsRequest(auctions_filter) log.high_debug("Received auctions: " + str(auctions)) if (len(auctions) == 0): log.info("No active auctions were found!") return print(" {:10} {:15} {:3} {:14} {:25} {:6}" .format("[Active]", "Name", "SN", "Duration (s)", "Description", "Type")) # Pretty print auctions list for a in auctions: print(" {:10} {:15} {:3} {:14} {:25} {:6}" .format( "Yes" if a["isActive"] else "No", a["name"], a["serialNumber"], a["duration"], a["description"], a["type_of_auction"]))
def handleHeartbeatRequest(self, data): log.high_debug("Hit handleHeartbeatRequest!") log.info("Operation: {} from client-sn: {} => OK".format( data["operation"], data["client-sn"])) return { "id-type": "auction-manager", "packet-type": "response", "operation": "heartbeat" }
def handleListBidsRequest(self, data): log.high_debug("Hit handleListBidsRequest!") bids_list = [] params = {} if data["bids-list-filter"] == "client": client_sn = int(data["client-sn"]) for a in self.__auctionsList: bids_list = bids_list + [ d for d in a.bidsList() if d.clientId == client_sn ] elif data["bids-list-filter"] == "auction": auction_sn = int(data["auction-sn"]) target_auction = [ d for d in self.__auctionsList if d.serialNumber == auction_sn ] if len(target_auction) > 1: log.error( "INTERNAL ERROR. Duplicate serial numbers found on auctions list." ) raise Exception( "INTERNAL ERROR. Duplicate serial numbers found on auctions list." ) elif len(target_auction) == 1: target_auction = target_auction[0] bids_list = target_auction.bidsList() log.info("Operation: {} from client-sn: {} => OK [#Bids: {}]". format(data["operation"], data["client-sn"], len(bids_list))) # Operation OK, no need for additional parameters on response else: log.error( "Operation: {} from client-sn: {} => FAILED [Could NOT find ACTIVE auction {}]" .format(data["operation"], data["client-sn"], data["auction-sn"])) params = { "operation-error": "No ACTIVE auction was found by the specified serial number!" } params["bids-list"] = [d.__dict__() for d in bids_list] log.high_debug(params["bids-list"]) return self.buildResponse("list-bids", params)
def getMinValue(self, choices, answer, auctions): # Get the id portion of the string serial_number = int(answer.split(" -> ")[0].strip()) target_auction = [d for d in auctions if d["serialNumber"] == serial_number][0] if target_auction["type_of_auction"] == "Blind": return "1" log.high_debug(target_auction) return str(int(target_auction["minBidValue"]))
def handleValidateReceipt(self): log.high_debug("Hit handleValidateReceipt!") # Join serial number and name as the name may not be unique # choices = [str(d["serialNumber"]) + " -> " + d["name"] for d in auctions] receipts = self.__client.loadCurrentClientReceipts() log.high_debug("CHOiceS:\n" + str(receipts)) if receipts == None or len(receipts) == 0: log.warning("No receipts were found!") return choices = [] for r in receipts: option = "AuctionSN: {}, Bid: {}".format(r[2], r[3]) r.append(option) choices.append(option) questions = [{ 'type': 'rawlist', 'message': 'Choose the receipt you want to validate', 'name': 'receipt', 'choices': choices, 'validate': lambda answer: 'You need to choose ONE receipt!' \ if len(answer) == 0 else True }] answers = prompt(questions, style=style) selected_receipt = None for r in receipts: if r[-1] == answers["receipt"]: selected_receipt = r[0] if selected_receipt == None: log.warning("Could not determine which receipt you wanted to validate! This should not happen.") return if self.__client.validateReceipt(selected_receipt): log.info("Receipt successfully validated!") else: log.error("Could not validate receipt!")
def listenLoop(self): log.high_debug("Hit listenLoop!") while True: log.info("Listening...") self.__socket.settimeout(None) data, address = self.__socket.recvfrom(16384) decoded_data = data.decode() native_data = json.loads(decoded_data) # print(native_data) log.high_debug("Received {} bytes from {}".format( len(data), address)) # TODO: handle client disconnection if data: response_data = None if native_data["operation"] == "heartbeat": response_data = self.handleHeartbeatRequest(native_data) elif native_data["operation"] == "create-auction": response_data = self.handleCreateAuctionRequest( native_data) elif native_data["operation"] == "terminate-auction": response_data = self.handleTerminateAuctionRequest( native_data) elif native_data["operation"] == "list-auctions": response_data = self.handleListAuctionsRequest(native_data) elif native_data["operation"] == "create-bid": response_data = self.handleCreateBidRequest(native_data) elif native_data["operation"] == "list-bids": response_data = self.handleListBidsRequest(native_data) else: log.error("Unknown operation requested!") log.high_debug("Processed request...") # log.high_debug("HERE: " + str(response_data)) if response_data != None: log.high_debug("Sending response to origin...") self.__socket.sendto( json.dumps(response_data).encode(), address) # log.info("Sent {} bytes as a response to {}".format( # sent_bytes, # address)) # # self.handleRequest(address, data # log.info("Operation: {} from {} => OK".format(native_data["operation"], address)) else: log.error("Internal socket error!!!")
def __sendRequestAndWait(self, target, data): log.high_debug("Hit sendRequestAndWait!") # self.__socket = skt.socket(skt.AF_INET, skt.SOCK_DGRAM) dict_key = "AuctionRepo" if (target.lower() == "manager"): dict_key = "AuctionManager" ip = cfg.CONFIG[dict_key]["IP"] port = int(cfg.CONFIG[dict_key]["PORT"]) server_address = (ip, port) log.debug(str(server_address)) response_data = None try: serialized_data = json.dumps(data) log.debug("Sending {} bytes to {}:{}".format( len(serialized_data), ip, port)) encoded_data = serialized_data.encode("UTF-8") log.debug("Sending {} bytes".format(len(encoded_data))) sent_bytes = self.__socket.sendto(serialized_data.encode("UTF-8"), server_address) self.__socket.settimeout(2) response_data, server = self.__socket.recvfrom(16384) log.debug("Received {!r}".format(response_data)) # except Exception as e: # log.error(str(e)) except skt.timeout as e: log.error("No response from peer, closing socket...") raise e finally: log.high_debug("Hit finally clause of __sendRequestAndWait") if response_data != None: return json.loads(response_data) return None
def handleCmdListBids(self, bids_filter="client"): log.high_debug("Hit handleCmdListBids!") try: # Retrieve list for later use auctions = self.__client.sendListAuctionsRequest() # if bids_filter == "client": # serial_number = None if bids_filter == "auction": # Join serial number and name as the name may not be unique choices = [str(d["serialNumber"]) + " -> " + d["name"] for d in auctions] questions = [ { 'type': 'rawlist', 'message': 'Choose the auction you want to peek', 'name': 'auction', 'choices': choices, 'validate': lambda answer: 'You need to choose at least one auction!' \ if len(answer) == 0 else True }] answers = prompt(questions, style=style) # Get the id portion of the string serial_number = answers["auction"].split(" -> ")[0].strip() response = self.__client.sendListBidsRequest(bids_filter, serial_number) bids = response["bids-list"] print(" {:10} {:10} {:5} {:3}" .format("Client-SN", "Auction-SN", "Index", "Value")) for b in bids: print(" {:10} {:10} {:5} {:3}" .format(b["clientId"], b["auctionSN"], b["index"], b["bidValue"])) # log.debug(bids) except Exception as e: log.error(str(e))
def handleCreateAuctionRequest(self, data): log.high_debug("Hit handleCreateAuctionRequest!") log.debug("------------") log.debug(data) if not self.validate_manager_request(data): log.warning( "Request failed because manager failed to be verified or the signature did not match!" ) # TODO: handle gracefully. Should send back an answer to client explaining why it failed return self.buildResponse( data["operation"], {"operation-error": "Certificate or signature check failed!"}) else: log.debug("Manager authenticity verified") self.sign_data(data) # Generate unique serial number # TODO: actually generate a unique serial number data_dict = data["data"] auction = Auction(data_dict["auction-name"], self.__auctionIndex, data_dict["auction-duration"], time.time(), data_dict["auction-description"], data_dict["auction-type"]) self.__auctionIndex = self.__auctionIndex + 1 lock.acquire() self.__auctionsList.append(auction) lock.release() # TODO: SIGN DATA FROM MANAGER log.info("Operation: {} from client-sn: {} => OK [ADDED auction: {}]". format(data["operation"], data["client-sn"], data_dict["auction-name"])) log.debug(data) return self.buildResponse("create-auction", data)
def checkAuctionsDurationLoop(self): log.high_debug("Hit checkAuctionsDurationLoop!") while True: lock.acquire() for a in [d for d in self.__auctionsList if d.isActive]: if a.endTime < time.time(): a.isActive = False log.info( "Auction \'{}\' with SN:{} set to terminate on {} has reached an end." .format(a.name, a.serialNumber, datetime.datetime.utcfromtimestamp(a.endTime))) ### Determine who won. There's no need to do this now, but it makes sense a.getHighestBid() lock.release() time.sleep(.500)
def handleCmdCheckAuctionOutcome(self): log.high_debug("Hit handleCmdCheckAuctionOutcome!") auctions = self.__client.sendListAuctionsRequest("client-outcome") if (len(auctions) == 0): log.info("No active auctions were found!") return log.high_debug(str(auctions)) # Join serial number and name as the name may not be unique choices = [str(d["serialNumber"]) + " -> " + d["name"] for d in auctions] questions = [{ 'type': 'rawlist', 'message': 'Choose the auction whose outcome you want to see', 'name': 'auction', 'choices': choices, 'validate': lambda answer: 'You need to choose ONE auction!' \ if len(answer) == 0 else True }] answers = prompt(questions, style=style) # Get the id portion of the string serial_number = int(answers["auction"].split(" -> ")[0].strip()) # TODO: finish this # auction = self.__client.sendCheckAuctionOutcomeRequest(serial_number) target_auction = [d for d in auctions if d["serialNumber"] == serial_number][0] if target_auction["highestBid"] != None: print("Client with SN: {} won Auction '{}' [SN:{}] with bid value of {}".format( target_auction["highestBid"]["clientId"], target_auction["name"], target_auction["serialNumber"], target_auction["highestBid"]["bidValue"])) else: print ("Nobody bet on Auction '{}'".format(target_auction["name"]))
def handleCreateAuctionRequest(self, data): log.high_debug("Hit handleCreateAuctionRequest!") log.high_debug("Received Create Auction Request:\n " + str(data)) # Check certificate chain and signature self.validate_client_request(data) # Sign with Manager private key self.sign_data(data) repo_response = self.__sendRequestAndWait("repo", data) # Validate repo response if not "operation-error" in repo_response: log.info( "Operation: {} from client-sn: {} => OK [ADDED auction: {}]". format(data["operation"], data["client-sn"], data["data"]["auction-name"])) else: log.info( "Operation: {} from client-sn: {} => FAILED [Could NOT add auction: {}]" .format(data["operation"], data["client-sn"], data["data"]["auction-name"])) return repo_response log.debug(repo_response) if not self.validate_repo_request(repo_response): log.warning("Could not verify Repository's authenticity! Aborting") repo_response[ "operation-error"] = "Could not verify Repository's authenticity! Aborting" else: log.debug("Repository authenticity verified") return repo_response
def handleTerminateAuctionRequest(self, data): log.high_debug("Hit handleDeleteAuctionRequest!") # data["id-type"] = "auction-manager" repo_response = self.__sendRequestAndWait("repo", data) if not "operation-error" in repo_response: log.info( "Operation: {} from client-sn: {} => OK [TERMINATED auction-sn: {}]" .format(data["operation"], data["client-sn"], data["auction-sn"])) else: log.info( "Operation: {} from client-sn: {} => FAILED [Could NOT find ACTIVE auction-sn {}]" .format(data["operation"], data["client-sn"], data["auction-sn"])) # repo_response["id-type"] = "auction-manager" return repo_response
def startListening(self): self.__IP = cfg.CONFIG["AuctionRepo"]["IP"] # TODO: Should check if int conversion goes wrong self.__PORT = int(cfg.CONFIG["AuctionRepo"]["PORT"]) # log.debug("Read {0}:{1}".format( # self.__IP, # self.__PORT)) # Create UDP socket self.__socket = skt.socket(skt.AF_INET, skt.SOCK_DGRAM) # Bind socket to the port server_address = (self.__IP, self.__PORT) log.high_debug("Trying to listening on {0}:{1}".format( self.__IP, self.__PORT)) self.__socket.bind(server_address) log.info("Listening on {0}:{1}".format(self.__IP, self.__PORT)) self.listenLoop()
def handleBidValidationRequest(self, data): log.high_debug("Hit handleBidValidationRequest!") is_valid = self.validate_client_request(data) if is_valid != True: # In case of error, is_valid holds the error message data["operation-error"] = is_valid return data data_dict = data["data"] data["bid-is-valid"] = True # response = self.buildResponse("validate-bid", {"bid-is-valid": True}) # Sign data self.sign_data(data) log.info( "Operation: {} from Auction Repo => OK [Client-SN: {} Auction SN: {} Bid Value: {}]" .format(data["operation"], data["client-sn"], data_dict["auction-sn"], data_dict["bid-value"])) return data
def handleListAuctionsRequest(self, data): log.high_debug("Hit handleListAuctionsRequest!") auctions_list = [] params = {} data_dict = data["data"] lock.acquire() if data_dict["auctions-list-filter"] == "active": auctions_list = [ d.__dict__() for d in self.__auctionsList if d.isActive ] elif data_dict["auctions-list-filter"] == "inactive": auctions_list = [ d.__dict__() for d in self.__auctionsList if not d.isActive ] elif data_dict["auctions-list-filter"] == "client-outcome": log.high_debug("HIT CLIENT OUTCOME FILTER") for a in self.__auctionsList: if a.isActive: continue # log.high_debug("DONE") # log.high_debug(a.bidsList()) # log.high_debug([b.clientId for b in a.bidsList()]) if int(data["client-sn"]) in [ b.clientId for b in a.bidsList() ]: auctions_list.append(a.__dict__()) else: auctions_list = [d.__dict__() for d in self.__auctionsList] params["auctions-list"] = auctions_list log.high_debug("AUCTIONS:" + str(auctions_list)) lock.release() log.info( "Operation: {} with filter: {} from client-sn: {} => OK ".format( data["operation"], data_dict["auctions-list-filter"], data["client-sn"])) return self.buildResponse("list-auctions", params)
def handleTerminateAuctionRequest(self, data): log.high_debug("Hit handleTerminateAuctionRequest!") log.high_debug("Incoming data:" + str(data)) lock.acquire() target_auct = [ d for d in self.__auctionsList if d.serialNumber == data["auction-sn"] and d.isActive ] lock.release() log.high_debug("Target auctions: " + str(target_auct)) params = None if len(target_auct) > 1: log.error( "INTERNAL ERROR. Duplicate serial numbers found on auctions list." ) raise Exception( "INTERNAL ERROR. Duplicate serial numbers found on auctions list." ) elif len(target_auct) == 1: # self.__auctionsList.remove(target_auct[0]) target_auct[0].isActive = False log.info( "Operation: {} from client-sn: {} => OK [TERMINATED auction: {}]" .format(data["operation"], data["client-sn"], target_auct[0].name)) # Operation OK, no need for additional parameters on response else: log.error( "Operation: {} from client-sn: {} => FAILED [Could NOT find ACTIVE auction {}]" .format(data["operation"], data["client-sn"], data["auction-sn"])) params = { "operation-error": "No ACTIVE auction was found by the specified serial number!" } return self.buildResponse("terminate-auction", params)
def handleCmdTerminateAuction(self): log.high_debug("Hit handleCmdTerminateAuction!") # try: # auctions = self.__client.sendListAuctionsRequest() auctions = self.__client.sendListAuctionsRequest() if (len(auctions) == 0): log.info("No active auctions were found!") return log.high_debug(str(auctions)) # Join serial number and name as the name may not be unique choices = [str(d["serialNumber"]) + " -> " + d["name"] for d in auctions] questions = [ { 'type': 'rawlist', 'message': 'Choose the auction to terminate (only those created by you are shown)', 'name': 'auction', 'choices': choices, 'validate': lambda answer: 'You need to choose at least one auction!' \ if len(answer) == 0 else True } ] answers = prompt(questions, style=style) # Get the id portion of the string serialNumber = answers["auction"].split(" -> ")[0].strip() log.high_debug("SERIAL NUMBER: " + str(serialNumber)) # auction_sn = [d["serialNumber"] for d in auctions if d["name"] == serialNumber] try: self.__client.sendTerminateAuctionRequest(serialNumber) log.info("Successfully terminated auction!") except Exception as e: log.error("Failed to terminate Auction!\n" + str(e))
def handleCmdBid(self): auctions = self.__client.sendListAuctionsRequest() if (len(auctions) == 0): log.info("No active auctions were found!") return log.high_debug("Auctions [raw]: " + str(auctions)) # Join serial number and name as the name may not be unique choices = [] for d in auctions: if not d["isActive"]: continue if d["type_of_auction"] == "English": choices.append("{} -> {} [Min:{}] ({})".format( str(d["serialNumber"]), d["name"], d["highestBid"]["bidValue"] if d["highestBid"] != None and d["highestBid"] != 0 else "0", d["type_of_auction"] )) else: choices.append("{} -> {} ({})".format( str(d["serialNumber"]), d["name"], d["type_of_auction"])) if len(choices) == 0: log.info("No active auctions were found!") return questions = [ { 'type': 'rawlist', 'message': 'Choose the auction to bid on', 'name': 'auction', 'choices': choices, 'validate': lambda answer: 'You need to choose at least one auction!' \ if len(answer) == 0 else True }, { 'type': 'input', 'name': 'bid', 'default': lambda a: self.getMinValue(choices, a["auction"], auctions), 'message': 'Set the BID value!', 'validate': lambda dur: 'Invalid number. Must be greater than 0!' \ if (not IsInt(dur) or int(dur) <= 0) else True } ] answers = prompt(questions, style=style) # Get the id portion of the string serialNumber = answers["auction"].split(" -> ")[0].strip() target_auction = [d for d in auctions if str(d["serialNumber"]) == str(serialNumber)][0] print(target_auction) # try: # if int(answers["bid"]) <= self.__client.sendCreateBidRequest(serialNumber, answers["bid"], target_auction["type_of_auction"]) log.info("Successfully created bid!")
def stopWorking(self): log.high_debug("Asking loop to return...") self.__stop_listening = True
def handleCreateBidRequest(self, data): log.high_debug("Hit handleCreateBidRequest!") log.high_debug("Data:\n " + str(data)) data_dict = data["data"] # Ask manager to validate bid # request_params = { # "auction-sn": data_dict["auction-sn"], # "client-sn": data["client-sn"], # "bid-value": data_dict["bid-value"] # } # Sign and ask manager to validate client self.sign_data(data) validated_response = self.__sendRequestAndWait("manager", data) if validated_response["bid-is-valid"] == False: log.warning( "Bid did not pass Manager's validation process! Dropping it..." ) # Return right away if not valid response_params = { "operation-error": "Bid did not pass the validation process by the Auction Manager!" } response = self.buildResponse("create-bid", response_params) # Sign again since data has been updated self.sign_data(response) return response # Validate manager's authenticity if not self.validate_manager_request(validated_response): log.warning("Could not validate Manager's authenticity!") # Return right away if not valid response_params = { "operation-error": "Failed to verify Manager's authenticity! Aborting." } response = self.buildResponse("create-bid", response_params) # Sign again since data has been updated self.sign_data(response) return response # Sign client's original packet # self.sign_data(validated_response) response_params = validated_response log.debug("Auction Manager validated bid...") try: auction_sn = int(data_dict["auction-sn"]) matched_auctions = [ d for d in self.__auctionsList if d.serialNumber == auction_sn and d.isActive ] if len(matched_auctions) == 0: log.error( "Operation: {} from client-sn: {} => FAILED [{}] ".format( data["operation"], data["client-sn"], "No ACTIVE auctions were found by SN!")) response_params = { "operation-error": "No ACTIVE auctions were found by that Serial Number!" } else: target_auction = matched_auctions[0] log.high_debug(target_auction.getMinBidValue()) # Check if greater than min if target_auction.type_of_auction == "English" and int( data_dict["bid-value"] ) <= target_auction.getMinBidValue(): response_params = { "operation-error": "Bid value is less or equal than the minimum value" } log.info( "Operation: {} from client-sn: {} => FAILED [Bid of: {} on auction-sn: {} <= MIN value]" .format(data["operation"], data["client-sn"], data_dict["bid-value"], data_dict["auction-sn"])) else: target_auction.addNewBid( data["client-sn"], data_dict["bid-value"], json.dumps(validated_response, sort_keys=True)) log.high_debug(target_auction) log.info( "Operation: {} from client-sn: {} => OK [Bid of: {} on auction-sn: {}]" .format(data["operation"], data["client-sn"], data_dict["bid-value"], data_dict["auction-sn"])) except Exception as e: log.error( "Operation: {} from client-sn: {} => FAILED [{}] ".format( data["operation"], data["client-sn"], str(e))) response_params = { "operation-error": "A server internal error occured!" } log.error(str(e)) response = self.buildResponse("create-bid", response_params) # self.sign_data(response_data) return response
def listenLoop(self): log.high_debug("Hit listenLoop!") while True: # Check if we've been asked to stop listening if self.__stop_listening: log.high_debug("STOPPING listenLoop!") return None log.info("Listening...") # Restore socket to blocking mode self.__socket.settimeout(None) # try: data, address = self.__socket.recvfrom(16384) decoded_data = data.decode() native_data = json.loads(decoded_data) # print(native_data) log.high_debug("Received {} bytes from {}".format( len(data), address)) # TODO: handle client disconnection if data: response_data = None if native_data["operation"] == "heartbeat": response_data = self.handleHeartbeatRequest(native_data) elif native_data["operation"] == "create-auction": response_data = self.handleCreateAuctionRequest( native_data) elif native_data["operation"] == "terminate-auction": response_data = self.handleTerminateAuctionRequest( native_data) elif native_data["operation"] == "create-bid": response_data = self.handleBidValidationRequest( native_data) else: log.error("Unknown operation requested!") if response_data != None: # log.high_debug(str(self.__socket)) log.high_debug("Sending response to origin...") self.__socket.sendto( json.dumps(response_data).encode(), address) # log.info("Sent {} bytes as a response to {}".format( # sent_bytes, # address)) # # self.handleRequest(address, data # log.info("Successfully processed request from {} of operation type: {}".format(address, native_data["operation"])) # log.info("Operation: {} from {} => OK".format(native_data["operation"], address)) else: log.error("Data is corrupted or client disconneted!")