def __init__(self, myPortNumber, orangeIP, orangePortNumber): ''' Constructor de objetos de clase GreenNode. Recibe como parametros el numero de puerto del nodo por construir, y la IP y puerto del nodo naranja que usa para conectarse. Incluye la etapa de inicialización del nodo verde. ''' # Inicializar servicios basicos self.myPort = myPortNumber self.myID = -1 self.orangeIP = orangeIP self.orangePort = orangePortNumber self.tcpl = TCPL() self.tcpl.startService(self.myPort) self.processSystem = ProcessSystem(self.myPort, True) # Diccionario con GreenNodeToken self.neighboursTable = dict() # Registros en tabla: nodoDestino [0] | distancia [1] | vecino [2] self.routingTable = dict() self.fileSystem = FileSystem(self.myPort) self.assemblePackage = AssemblePackageFactory()
def __init__(self, greenNum, greenIp, greenPort, ownPort): self._greenId = greenNum self._greenIp = greenIp self._greenPort = greenPort # TCPL self._greenReliTransPort = greenPort + 1000 self._ownPort = ownPort self._processSystem = ProcessSystem(ownPort, False) self._tcpl = TCPL() self._tcpl.startService(self._ownPort)
def __init__(self, id, ip, port): self.adyacentNodes = dict() self.id = id self.tcplService = TCPL() # Estas listas deberían ser atrributos de instancia self.freeNodeList = [] self.orangeNodesList = [] # Lista de nombres de nodos esperando por ser instanciados self.instantiatingList = [] self.localIp = ip self.localPort = port self.assemblePackage = AssemblePackageFactory() ''' Lista (dict) utilizada para llevar registro de cuáles solicitudes naranja - naranja han sido confirmadas, por ejemplo, aumentar la entrada [546] de la solicitud REQUESTPOS con ese mismo número cuando se reciba un REQUESTPOSACK ''' self.confirmationCounters = dict()
def recibirInfo(): packet, address = tcplService.receivePackage() numeroDeRequest, inicioConfirmacionRespuesta, numeroDeServicio, tamCuerpoPrioridad, datos = factoria.unpackPackage( packet) print("ID = ", inicioConfirmacionRespuesta) while 1: packet, address = tcplService.receivePackage() numeroDeRequest, inicioConfirmacionRespuesta, numeroDeServicio, tamCuerpoPrioridad, datos = factoria.unpackPackage( packet) print("Vecino = ", int.from_bytes(datos[0:2], byteorder='big')) puerto = sys.argv[1] ipNaranja = sys.argv[2] puertoNaranja = sys.argv[3] tcplService = TCPL() tcplService.startService(puerto) factoria = AssemblePackageFactory() paquete = factoria.assemblePackage(37, 1, 200, 1, int(1).to_bytes(256, byteorder='big')) enviado = tcplService.sendPackage(paquete, ipNaranja, puertoNaranja) print(enviado) if (enviado): print("Esperando respuesta naranja") threadReceiving = threading.Thread(target=recibirInfo()) threadReceiving.start() threadReceiving.join() else: print("Fallo")
class GreenNode: ''' CÓDIGOS DE MENSAJES ''' ROUTING_MESSAGE = 80 GREET_NEIGHBOR = 100 GREET_NEIGHBOR_ACK = 101 FILE_EXISTS = 102 FILE_EXISTS_ACK = 103 FILE_COMPLETE = 104 FILE_COMPLETE_ACK = 105 LOCATE_FILE = 106 LOCATE_FILE_ACK = 107 REMOVE_FILE = 108 REMOVE_FILE_ACK = 109 PUT_FILE = 110 PUT_FILE_ACK = 111 GET_FILE = 112 GET_FILE_ACK = 113 EXEC = 114 EXEC_ACK = 115 EXEC_STOP = 116 EXEC_STOP_ACK = 117 SEND_ROUTE = 118 SEND_ROUTE_ACK = 119 CONNECT_ACK = 201 SEND_PROCESS = 50 RUN_PROCESS = 51 ASK_FOR_PROCESS = 52 PROCESS_OUTPUT = 53 # ... WAITFORACKTIMEOUT = 5 DATA_MAX_SIZE = 1015 MAX_RANDOM = 65000 FILE_NAME_SIZE = 32 ''' # # # # # # # # # Procedimientos dentro del mismo nodo # # # # # # # # # ''' def __init__(self, myPortNumber, orangeIP, orangePortNumber): ''' Constructor de objetos de clase GreenNode. Recibe como parametros el numero de puerto del nodo por construir, y la IP y puerto del nodo naranja que usa para conectarse. Incluye la etapa de inicialización del nodo verde. ''' # Inicializar servicios basicos self.myPort = myPortNumber self.myID = -1 self.orangeIP = orangeIP self.orangePort = orangePortNumber self.tcpl = TCPL() self.tcpl.startService(self.myPort) self.processSystem = ProcessSystem(self.myPort, True) # Diccionario con GreenNodeToken self.neighboursTable = dict() # Registros en tabla: nodoDestino [0] | distancia [1] | vecino [2] self.routingTable = dict() self.fileSystem = FileSystem(self.myPort) self.assemblePackage = AssemblePackageFactory() # Solicitar unirse al grafo # Esperar identificacion def _execution(self): ''' Etapa de ejecucion del nodo verde. ''' self.isRunning = True # Esperar nuevos vecinos (de parte de naranja) paquete = self.assemblePackage.assemblePackageConnect() #Si logra enviar su paquete connect puede empezar a escuchar request. if (self.tcpl.sendPackage(paquete, self.orangeIP, self.orangePort)): package, address = self.tcpl.receivePackage() requestNumber, beginConfirmationAnswer, serviceNumber, sizeBodyPriority, ttl, fuente, destino, data = self.assemblePackage.unpackPackage( package) self.myID = int.from_bytes(data[0:2], byteorder='big') threadReceiving = threading.Thread(target=self._receiveMessages) threadRouting = threading.Thread(target=self._routingThread) threadRouting.start() threadReceiving.start() else: print( "No fue posible enviar el paquete para solicitar ID al nodo Naranja" ) return -1 def _termination(self): ''' Etapa de finalizacion del nodo verde. ''' # Rechazar solicitudes de otros nodos # Avisar a un naranja sobre la terminacion # Avisar a sus vecinos verdes sobre la finalizacion pass def run(self): ''' Ejecuta toda la funcionalidad del nodo verde, incluyendo las etapas de ejecucion y ,terminacion. ''' self._execution() self._termination() def _extractPortAndIp(self, data): ''' la informacion :return: retorna el numero de puerto y la ip. ''' ip = "" ip += str(data[0]) + "." ip += str(data[1]) + "." ip += str(data[2]) + "." ip += str(data[3]) port = str(int.from_bytes(data[4:6], byteorder='big')) return port, ip def _receiveMessages(self): ''' Recibe constantemente mensajes de otros nodos, y los atiende si son solicitudes para el nodo actual. ''' while self.isRunning: #print("Estoy recibiendo mensajes.") package, address = self.tcpl.receivePackage() # Si es el nodo destino se ruteo destination = int.from_bytes(package[13:15], byteorder='big') #Si el ID destino del mensaje no coincide con el mio entonces tengo que rutearlo. if destination != self.myID and destination != 0: if destination in self.routingTable: ip = self.neighboursTable[self.routingTable[destination] [2]].ip port = self.neighboursTable[self.routingTable[destination] [2]].port print("Ruteando paquete hacia ", destination, " por vecino: ", self.routingTable[destination][2], ip, port) self.tcpl.sendPackage(package, ip, port) else: print("Mensaje hacia nodo: ", destination, " desechado, no hay entrada en la tabla de ruteo.") else: hiloDeAtencionRequest = threading.Thread( target=self._attendRequests, args=(package, address)) hiloDeAtencionRequest.start() def sendGreetNeighbor(self, indice): ''' Subrutina que se encarga de saludar a los vecinos de los que se tienen conocimiento de que estan instanciados. ''' if self.neighboursTable.get(indice).ip != "0.0.0.0": package = self.assemblePackage.assemblePackageGreetNeighbor( self.myID, self.myID, self.neighboursTable.get(indice).id) self.tcpl.sendPackage(package, self.neighboursTable.get(indice).ip, self.neighboursTable.get(indice).port) pass def _attendRequests(self, package, ipPort): ''' Atiende una solicitud hecha al nodo actual. ''' requestNumber, beginConfirmationAnswer, serviceNumber, sizeBodyPriority, ttl, fuente, destino, data = self.assemblePackage.unpackPackage( package) if serviceNumber == self.CONNECT_ACK: #Se reciben los vecio self._connect(beginConfirmationAnswer, data) elif serviceNumber == self.GREET_NEIGHBOR: #Se me informa que tengo un vecino print("Mi vecino me saludo y tiene la direccion: ", ipPort) self._greetNeighbor(requestNumber, beginConfirmationAnswer, ipPort) self.imprimirListVecinos() elif serviceNumber == self.GREET_NEIGHBOR_ACK: # recibo un ack de que mi vecino ya sabe que existo pass elif serviceNumber == self.SEND_ROUTE: #Se me envia la tabla de enrutamiento. self._checkRouteTable(data, fuente) elif serviceNumber == self.SEND_ROUTE_ACK: #Se me indica que la tabla que mande se recibio correctamente. pass elif serviceNumber == self.FILE_EXISTS: #Recibo un mensaje de pregunta si un archivo existe self._fileExistInSelf(requestNumber, data, ipPort) pass elif serviceNumber == self.FILE_EXISTS_ACK: #Se medio una respuesta acerca de la existencia de un archivo. pass elif serviceNumber == self.FILE_COMPLETE: #Se me pregunta si un archivo x(viene en los datos) esta completo pass elif serviceNumber == self.LOCATE_FILE: #Se me pregunta por la lista de nodos que tiene x archivo pass elif serviceNumber == self.LOCATE_FILE_ACK: # Respuesta con una lista de ids que contienen el archivo. pass elif serviceNumber == self.REMOVE_FILE: # Se me indica que debo borrar X archivo de mi almacenamiento #self.removeFile(requestNumber) pass elif serviceNumber == self.REMOVE_FILE_ACK: # Respuesta de que un archivo pudo ser borrado. pass elif serviceNumber == self.PUT_FILE: # Se me indica que debo almacenar x archivo. pass elif serviceNumber == self.PUT_FILE_ACK: #Se me contesta acerca de si se guardo el archivo. pass elif serviceNumber == self.GET_FILE: #Se me solicita y fragmento de x archivo. pass elif serviceNumber == self.GET_FILE_ACK: #Se me da Y fragmento de x archivo que solicite. pass elif serviceNumber == self.EXEC: # Se me indica que debo correr un proceso. pass elif serviceNumber == self.SEND_PROCESS: print("Recibi una solicitud para recibir proceso.") # Acá se pueden poner condiciones para recibir el proceso o no if True: answer = bytearray(package) # Activamos trans conf por medio del sistema de procesos receiveProcessThread = threading.Thread( target=self._receiveProcess, args=(bytes(package), )) receiveProcessThread.start() # Aceptamos la solicitud answer[15] = 1 else: # Denegamos la solicitud answer[15] = 0 self.tcpl.sendPackage(answer, ipPort[0], ipPort[1]) elif serviceNumber == self.RUN_PROCESS: print("Recibi una solicitud para correr proceso:", package[15:75].decode(), ".") runProcessThread = threading.Thread(target=self._runProcess, args=(bytes(package), )) runProcessThread.start() self.tcpl.sendPackage(package, ipPort[0], ipPort[1]) elif serviceNumber == self.ASK_FOR_PROCESS: procName = package[15:75].decode().strip('\x00') print("Se me consultó el estado del proceso:", procName, ".") answer = bytearray(package) state = self.processSystem.isProcessDone(str(procName)) answer[15] = state # Enviamos la respuesta print("El proceso:", procName, "está en estado", state, ".") self.tcpl.sendPackage(answer, ipPort[0], ipPort[1]) ''' # # # # # # # # # Solicitudes de azules # # # # # # # # # ''' def _receiveProcess(self, requestPack): ''' Recibe un proceso de un nodo azul. ''' program = requestPack[15:75].decode('utf-8').strip('\x00') executable = requestPack[75:125].decode('utf-8').strip('\x00') print("Proceso:", program, ", ejecutable:", executable) # Recibimos el archivo por trans confiable self.processSystem.receiveProcess(program, executable, self.myPort) def _migrateProcess(self, requestPack): ''' Migrar un proces 3 en la lista de adyacencias o reanudable a otro nodo. ''' pass def _runProcess(self, requestPack): ''' Ejecutar un proceso. ''' program = requestPack[15:75].decode('utf-8').strip('\x00') self.processSystem.executeProcess(program) ''' # # # # # # # # # Transacciones con otros verdes # # # # # # # # # ''' def _greetNeighbor(self, requestNumber, beginConfirmationAnswer, ipPort): ''' Envía un mensaje saludando a un vecino instanciado. ''' self.neighboursTable[beginConfirmationAnswer].ip = ipPort[0] self.neighboursTable[beginConfirmationAnswer].port = ipPort[1] self.neighboursTable[beginConfirmationAnswer].state = True package = self.assemblePackage.assemblePackageGreetNeighborACK( requestNumber, self.myID, self.neighboursTable.get(beginConfirmationAnswer).id) self.tcpl.sendPackage(package, ipPort[0], ipPort[1]) def _routingThread(self): #Hilo que se va a encargar de enviar la tabla de ruteo cada 1 segundo. while (1): print("Esoty enviando la tabla de enrutamiento") self.imprimirTablaDeEnrutamiento() self._sendRouteTable() time.sleep(25) def _sendRouteTable(self): ''' Enviar tabla de enrutamiento a vecinos (actualizar tabla de enrutamiento) ''' routingTable = self.routingTable neighboursTable = self.neighboursTable # Se crea el payload con la tabla, cada registro son 2 bytes table = bytearray(1000) offset = 0 for node in routingTable: table[offset:(offset + 2)] = routingTable[node][0].to_bytes( 2, byteorder='big') # nodo table[(offset + 2):(offset + 4)] = routingTable[node][1].to_bytes( 2, byteorder='big') # distancia offset += 4 # Se envían los mensajes a los vecinos tableMessage = bytearray(self.DATA_MAX_SIZE) # ToDo: Agregar código de enviar la tabla tableMessage[4:6] = bytearray(2) # 0 tableMessage[6] = self.SEND_ROUTE tableMessage[7:9] = bytearray(2) # sin prioridad tableMessage[9:11] = int(50).to_bytes(2, byteorder='big') tableMessage[15:] = table # ... for neighbour in neighboursTable: # fuente - destino if neighboursTable[neighbour].state is True: tableMessage[0:4] = random.randrange(self.MAX_RANDOM).to_bytes( 4, byteorder='big') tableMessage[11:13] = int(self.myID).to_bytes(2, byteorder='big') tableMessage[13:15] = int(neighbour).to_bytes(2, byteorder='big') ip = neighboursTable[neighbour].ip port = neighboursTable[neighbour].port self.tcpl.sendPackage(tableMessage, ip, port) def _checkRouteTable(self, receivedTable, sender): ''' Confirmar que la tabla de enrutamiento está actualizada (tras recibir tabla de enrutamiento) ''' ownTable = self.routingTable items = 0 offset = 0 while int.from_bytes(receivedTable[offset:offset + 2], byteorder='big') != 0: items += 1 # 2 bytes de nodo + 2 bytes de distancia = 4 bytes nodeNum = int.from_bytes(receivedTable[offset:offset + 2], byteorder='big') distance = int.from_bytes(receivedTable[offset + 2:offset + 4], byteorder='big') # Se compraran los datos con nuestra tabla # Si ya tenemos la ruta en la tabla checkeamos su distancia if nodeNum in ownTable: if int.from_bytes(receivedTable[offset + 2:offset + 4], byteorder='big') < ownTable[nodeNum][1]: # Nueva distancia ownTable[nodeNum][1] = distance + 1 # Nuevo nodo enlace (quizá) ownTable[nodeNum][2] = sender else: # Si no tenemos esa ruta, la agregamos ownTable[nodeNum] = [nodeNum, distance + 1, sender] offset += 4 #print("Recibi una tabla de tamaño: ", items, " del vecino ", sender) def _receiveRelocatedProcess(self, package): ''' Recibir un proceso reanudable. ''' pass def _updateRoutingTable(self, requestPack): ''' Actualiza entradas de la tabla de enrutamiento segun nuevos datos enviados por un vecino. ''' pass def imprimirListVecinos(self): diccionario = self.neighboursTable.copy() print("Mi numero de ID es: ", self.myID, " Y la tabla de adyacencias") for i in diccionario: print("ID: ", diccionario.get(i).id) print("Ip: ", diccionario.get(i).ip) print("Puerto: ", diccionario.get(i).port) print("°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°") print( "--------------------------Aqui termina la tabla de adyacencias-----------------------" ) ''' # # # # # # # # # Solicitudes de/ a naranjas # # # # # # # # # ''' ''' Agregar un vecino nuevo y presentarse si esta instanciado. ''' def _addNeighbour(self, requestPack): # Agregar a tabla de vecinos # Presentarse al vecino si está instanciado pass def _connect(self, beginConfirmationAnswer, data): ''' Se recibe la respuesta de un connect y se añade su id propio y el de sus vecinos con sus respectivas IPs si estan instanciados. :param beginConfirmationAnswer: :param data: :return: ''' neighbourID = int.from_bytes(data[0:2], byteorder='big') # El primer mensaje que se envia es su ID. Asi que revisamos que este mensaje no sea el primero para añadirlo en la lista de adyacencias. if (beginConfirmationAnswer != neighbourID): neighbourNode = GreenNodeToken(neighbourID) port, ip = self._extractPortAndIp(data[2:8]) # Si viene una IP validad en el paquete debemos proceder añadirla a la lista y saludar, de lo contrario no se añade ninguna IP. if ip != "0.0.0.0": neighbourNode.ip = ip neighbourNode.port = port neighbourNode.state = True self.neighboursTable[neighbourID] = neighbourNode self.sendGreetNeighbor(neighbourID) else: self.neighboursTable[neighbourID] = neighbourNode # Siempre añadimos a nuestra tabla de ruteo al vecino aunque no este instanciado. arrayTable = [neighbourID, 1, neighbourID] self.routingTable[neighbourID] = arrayTable self.imprimirListVecinos() else: # Guardamos cual es nuestro ID. self.myID = beginConfirmationAnswer def imprimirTablaDeEnrutamiento(self): tabla = self.routingTable print("Esta es la tabla de enrutamiento actual: ") for indice in tabla: print(tabla[indice]) print( "-------------------------------------------Final Tabla enrutamiento--------------------------------------------" )
class BlueNode: MESSAGE_MAX_SIZE = 1015 MAX_RANDOM = 65000 SEND_PROCESS = 50 RUN_PROCESS = 51 ASK_FOR_PROCESS = 52 PROCESS_OUTPUT = 53 def __init__(self, greenNum, greenIp, greenPort, ownPort): self._greenId = greenNum self._greenIp = greenIp self._greenPort = greenPort # TCPL self._greenReliTransPort = greenPort + 1000 self._ownPort = ownPort self._processSystem = ProcessSystem(ownPort, False) self._tcpl = TCPL() self._tcpl.startService(self._ownPort) ''' def start(self): pass def runInterface(self): pass ''' def displayMenuOptions(self): print("\n\n # # # # # # # # # Nodo azul # # # # # # # # # \n") num = -1 while True: print("1. Enviar un programa.") print("2. Ejecutar un programa.") print("3. Preguntar el estado de un proceso.") print("4. Salir.") try: num = int(input("\nSeleccione una opción: ")) if 0 < num and num < 5: break except ValueError: pass print("\nPor favor seleccione una opción válida.\n") return num def menu(self): option = 0 while option is not 4: option = self.displayMenuOptions() if option is 1: print("\n\n # # # Envío de un programa # # #") processName = input( "Ingrese el nombre del proceso que quiere enviar al nodo verde:\n" ) print( "Ingrese la ruta del archivo ejecutable que desea correr, seguido por" ) print( "las rutas de los demás archivos necesarios para la ejecución, separadas" ) print("por espacios.") routes = input("\nArchivos: ") print("\n") routes = routes.split() # Remove a linebreak if it exists: if routes[-1][-1] is '\n': routes[-1] = routes[-1][:-1] allFound = True for route in routes: if not os.path.isfile(route): allFound = False print("Archivo", route, "no fue encontrado...") if allFound: # Run the program sender self.sendProcessToGreen(processName, routes) pass else: print("Por favor revise las rutas e intente de nuevo.") elif option is 2: pName = input("Ingrese el nombre del proceso a ejecutar: ") self.sendExecutionToGreen(pName) elif option is 3: pName = input("Ingrese el nombre del proceso a consultar: ") self.askForProcessToGreen(pName) else: # option is 4: pass def sendProcessToGreen(self, processName, filesRoutes): # Creamos un mensaje de solicitud de enviar proceso a verde message = bytearray(self.MESSAGE_MAX_SIZE) message[0:4] = int(random.randrange(self.MAX_RANDOM)).to_bytes( 4, byteorder='big') message[4:6] = bytearray(2) # 0 message[6] = self.SEND_PROCESS message[7:9] = bytearray(2) # sin prioridad message[9:11] = int(10).to_bytes(2, byteorder='big') # TTL irrelevante message[11:13] = bytearray(2) # sin fuente message[13:15] = int(self._greenId).to_bytes(2, byteorder='big') # Agregamos el nombre del proceso y los archivos que necesita message[15:75] = processName.encode() message[75:125] = filesRoutes[0].encode( ) # el primero es el ejecutable print("Proceso:", processName, ", ejecutable:", filesRoutes) # Enviamos la solicitud self._tcpl.sendPackage(message, self._greenIp, self._greenPort) # Esperamos la respuesta print("Esperando respuesta del verde...") answer, ipPort = self._tcpl.receivePackage() # Si la respuesta es positiva, el verde activó trans confiable if answer[15] == 1: self._processSystem.sendProcess\ (processName, filesRoutes, self._greenIp, self._greenPort, self._ownPort) print("Proceso enviado.") else: print("Error: El nodo verde se niega a recibir el archivo.") def sendExecutionToGreen(self, processName): # Creamos un mensaje de solicitud de enviar proceso a verde message = bytearray(self.MESSAGE_MAX_SIZE) message[0:4] = int(random.randrange(self.MAX_RANDOM)).to_bytes( 4, byteorder='big') message[4:6] = bytearray(2) # 0 message[6] = self.RUN_PROCESS message[7:9] = bytearray(2) # sin prioridad message[9:11] = int(10).to_bytes(2, byteorder='big') # TTL irrelevante message[11:13] = bytearray(2) # sin fuente message[13:15] = int(self._greenId).to_bytes(2, byteorder='big') # Agregamos el nombre del proceso y los archivos que necesita message[15:75] = processName.encode() # Enviamos la solicitud self._tcpl.sendPackage(message, self._greenIp, self._greenPort) # Esperamos la respuesta print("Esperando respuesta del verde...") answer, ipPort = self._tcpl.receivePackage() print("El proceso:", processName, "se mandó a ejecutar.") def askForProcessToGreen(self, processName): # Creamos un mensaje de solicitud de enviar proceso a verde message = bytearray(self.MESSAGE_MAX_SIZE) message[0:4] = int(random.randrange(self.MAX_RANDOM)).to_bytes( 4, byteorder='big') message[4:6] = bytearray(2) # 0 message[6] = self.ASK_FOR_PROCESS message[7:9] = bytearray(2) # sin prioridad message[9:11] = int(10).to_bytes(2, byteorder='big') # TTL irrelevante message[11:13] = bytearray(2) # sin fuente message[13:15] = int(self._greenId).to_bytes(2, byteorder='big') # Agregamos el nombre del proceso y los archivos que necesita message[15:75] = processName.encode() # Enviamos la solicitud self._tcpl.sendPackage(message, self._greenIp, self._greenPort) # Esperamos la respuesta print("Esperando respuesta del verde...") answer, ipPort = self._tcpl.receivePackage() # Si la respuesta es positiva, el verde activó trans confiable if answer[15] == 0: print("\n***El programa", processName, "no ha sido ejecutado***\n") elif answer[15] == 1: print("El programa", processName, " esta corriendo***\n") elif answer[15] == 2: print("\n***El programa", processName, " ya terminó***\n") else: print("\n***El programa", processName, " no se encuentra en el nodo verde***\n")
class OrangeNode: # deberia ser un atributo de instancia (ver constructor) #PUERTO = "6666" DATA_MAX_SIZE = 1009 POSNUMEROSERVICIO = 6 CONNECT = 200 REQUESTPOS = 205 REQUESTPOSACK = 206 CONFIRMPOS = 210 CONFIRMPOSACK = 211 DISCONNECT = 216 DISCONNECTACK = 220 REMOVE = 220 REMOVEACK = 221 WAITFORACKDELAY = 1 WAITFORACKTIMEOUT = 5 def __init__(self, id, ip, port): self.adyacentNodes = dict() self.id = id self.tcplService = TCPL() # Estas listas deberían ser atrributos de instancia self.freeNodeList = [] self.orangeNodesList = [] # Lista de nombres de nodos esperando por ser instanciados self.instantiatingList = [] self.localIp = ip self.localPort = port self.assemblePackage = AssemblePackageFactory() ''' Lista (dict) utilizada para llevar registro de cuáles solicitudes naranja - naranja han sido confirmadas, por ejemplo, aumentar la entrada [546] de la solicitud REQUESTPOS con ese mismo número cuando se reciba un REQUESTPOSACK ''' self.confirmationCounters = dict() # Inicia el nodo, creando hilos e iniciando objetos/estructuras necesarias # ToDo: TCPL requiere su propio hilo def start(self, csvPath, orangesPath): #1 debe cargar lista de nodos naranjas. #2 cargamos el grafo verde. #3 Empezamos tcpl para que escuche solicitudes #4 Empezamos un hilo que retire mensajes de la bolsa de tcpl y atienda solicutes. # ruta = orangesPath textReader = TxtReader() self.loadOrangeNeighboring(textReader.readTxt(ruta)) self.adyacentNodes = self.loadGreenGraph(csvPath) #self.freeNodeList.append(1) #self.freeNodeList.append(72) self.tcplService.startService(self.localPort) threadReceiving = threading.Thread(target=self.popPackage()) threadReceiving.start() # ''' esto es en otra subrutina while 1: package = self.popPackage() ''' pass ''' Construye el grafo de nodos verdes para un nodo naranja a partir del archivo csv en la ruta filePath. Retorna un diccionario donde la llave indica el número de un nodo y el valor corresponde a la lista de números de los nodos adyacentes. ToDo: crear una subrutina o implementar en el cosntructor que se llame esta subrutina para cargar el grafo, asì como llenar la lista dde nodos verdes libres freeNodeList() ''' def loadGreenGraph(self, filePath): ''' NEWER VERSION: graphFile = open (filePath, 'r') reader = csv.reader(graphFile) # Diccionario vacío graphDictionary = dict() # Build dictionary for row in reader: graphDictionary[row[0]] = row[1:] readLine = graphFile.readline() graphFile.close() return graphDictionary ''' graphFile = open(filePath, 'r') readLine = graphFile.readline() # Diccionario vacío graphDictionary = dict() while readLine: # Separa la línea leída usando co splitLine = readLine.split(",") # El nodo actual es el primero en la línea currentNodeId = int(splitLine[0]) # Creamos una lista de nodo/vecinos para cada nodo graphDictionary[currentNodeId] = list() # El primero de la lista que precede a sus vecinos graphDictionary[currentNodeId].append( GreenNodeToken(currentNodeId)) # Los nodos adyacentes son los demás adyacentNodes = list() for nodeId in splitLine[1:]: adyacentNodes.append(GreenNodeToken(int(nodeId))) # Agregar lista de vecinos a la cabeza de la lista graphDictionary[currentNodeId].extend(adyacentNodes) # Lee la siguiente línea readLine = graphFile.readline() self.freeNodeList.append(currentNodeId) graphFile.close() return graphDictionary ''' INTERFACE ''' def checkNodeNumber(self, nodeNumber): """Si devuelve un True es que el nodo ya ha sido instanciado. """ #self.lis pass def instantiateNode(self, numeroDeNodo, ip, port): self.adyacentNodes.get(numeroDeNodo)[0].ip = ip self.adyacentNodes.get(numeroDeNodo)[0].port = port self.adyacentNodes.get(numeroDeNodo)[0].state = True print("Instancié el nodo:", numeroDeNodo, "en la lista de adyacencias") #self.printAdyacencyList(self.adyacentNodes) # Subrutina que atiende requests y actúa según la que recibe # Envía el nombre a un nodo verde cuándo se logró ser instanciado, además de su # lista de vecinos def sendGreenInfo(self): pass #Bug nunca envia el confirmPosAck def attendRequests(self, package, ipPort): numeroDeRequest, inicioConfirmacionRespuesta, numeroDeServicio, tamCuerpoPrioridad, ttl, fuente, destino, datos = self.assemblePackage.unpackPackage( package) #print(numeroDeRequest, inicioConfirmacionRespuesta, numeroDeServicio, tamCuerpoPrioridad, ipPort, "\n") ''' Para REQUESTPOS es mas facil si la subrutina genera el número de nodo las veces que sean necesarias y retorna la instanciada. Por esto no estara "position" como parámetro de la funcion. ''' ipFuente, puertoFuente = ipPort if numeroDeServicio == self.REQUESTPOS: #print("Este es el numero de de tamaño cuerpo prioridad", tamCuerpoPrioridad) print("Recibí un request pos de:", tamCuerpoPrioridad) #Si es un request service se debe sacar un nodo verde no instanciado #preguntar a los demas si no lo tienen instancido self.requestPosACK(inicioConfirmacionRespuesta, ipPort, package) elif numeroDeServicio == self.REQUESTPOSACK: #Confirma que un id de nodo verde no esta usado. #Algun tipo de contador para cuando reciba los if inicioConfirmacionRespuesta == 1 and numeroDeRequest in self.confirmationCounters: self.confirmationCounters[ numeroDeRequest] += 1 #Aumentamos el contador de request ack recibidos. else: #Nota para los programadores: Esto nunca esta pasando, ya que antes habian un remove y como era un diccionario debia caerse. if numeroDeRequest in self.confirmationCounters: #print("Estoy popeando el numero de request (esoy en request pos ack) ", numeroDeRequest) self.confirmationCounters.pop(numeroDeRequest) #print(self.instantiatingList) #self.instantiatingList.remove(inicioConfirmacionRespuesta) elif numeroDeServicio == self.CONFIRMPOS: #Debe armar un corfirm pos ack print("Recibí un request pos de:", tamCuerpoPrioridad) #self.freeNodeList.remove(inicioConfirmacionRespuesta) #removemos el nodo que ya fue instanciado port, ip = self.extractPortAndIp( datos) # Extraemos la direccion del nodo que instanciaron. self.instantiateNode( inicioConfirmacionRespuesta, ip, port) #Instanciamos ese nodo con un puerto e ip. self.freeNodeList.remove(inicioConfirmacionRespuesta) #Armamos el paquete. self.tcplService.sendPackage( self.assemblePackage.assemblePackageConfirmPosACK( 1, numeroDeRequest), ipFuente, puertoFuente) elif numeroDeServicio == self.CONFIRMPOSACK: self.confirmationCounters[numeroDeRequest] += 1 #dependiendo si ya me confirmaron todos los nodos elif numeroDeServicio == self.CONNECT: #print("Si me llego un connect") #Cuando recibe una solicitud de conexion: #1- Se busca un nodo que no este instanciado #se pregunta a los demas nodos si lo tienen libre. listaPaquetes = [] numeroDeNodo = self.requestPos(ipPort) #No hay direccion broadcast if numeroDeNodo is not 0: #Si no es 0 es que habia un nodo disponible. #print("Esta es la lista de adyacentes",numeroDeNodo , self.adyacentNodes.get(numeroDeNodo)) listaDeAdyacencia = self.listAdyacentGenerator(numeroDeNodo) listaPaquetes = self.assemblePackage.assemblePackageConnectACK( package, numeroDeNodo, listaDeAdyacencia) for indice in range( len(listaPaquetes) ): #Enviamos la lista de paquetes al nodo verde que se aba de conectar. self.tcplService.sendPackage(listaPaquetes[indice], ipFuente, puertoFuente) else: print("No hay nodos disponibles") self.tcplService.sendPackage( self.assemblePackage.assemblePackage( numeroDeRequest, 0, 201, 0, 50, 0, 0, bytearray(0)), ipFuente, puertoFuente) #Tenemos que buscar ID #Hacemos request pos para los demas def extractPortAndIp(self, datos): ''' Subrutina que extrae el puerto e ip de un bytearray :param datos: datos de los que se va a extraer la informacion :return: retorna el numero de puerto y la ip. ''' ip = "" ip += str(datos[0]) + "." ip += str(datos[1]) + "." ip += str(datos[2]) + "." ip += str(datos[3]) port = str(int.from_bytes(datos[4:6], byteorder='big')) return port, ip def assignAddressToNode(self, id, ip, puerto): pass def loadOrangeNeighboring(self, orangeNodes): listaDeIpsYpuertos = orangeNodes.split() #print("El tamaño de la lista es", len(listaDeIpsYpuertos)) for i in range(0, len(listaDeIpsYpuertos), 2): #print(i, "\n") #print("Hola ",listaDeIpsYpuertos[i], "\n") if self.localIp != listaDeIpsYpuertos[i]: self.orangeNodesList.append( [listaDeIpsYpuertos[i], listaDeIpsYpuertos[i + 1]]) ''' Genera un numero de nodo verde entre los disponibles Si hay nodos disponibles retorna uno generado aleatoriamente, De lo contrario retorna 0 ''' def getAvailableGreenNum(self): #print(len(self.freeNodeList)) while len(self.freeNodeList): nodeNumIndex = random.randint(0, len(self.freeNodeList) - 1) #print("Numero de index ", nodeNumIndex) if not self.freeNodeList[nodeNumIndex] in self.instantiatingList: #print("Generé el nombre de nodo: ", self.freeNodeList[nodeNumIndex], " aleatoriamiente") return self.freeNodeList[nodeNumIndex] return 0 # Pregunta si un nodo ha sido instanciado a los de más nodos naranjas # retorna la posición instanciada def requestPos(self, ipPort): """ Pregunta si un nodo ha sido instanciado a los de más nodos naranjas @:param ipPort Ip broadcast @:return: La posición instanciada para el nodo verde """ requested = False while not requested: # Crear paquete para REQUEST_POS # Generar un numero aleatorio entre los disponibles position = self.getAvailableGreenNum() # Si se generó un 0, no hay nodos disponibles, retornamos fallo if position == 0: return 0 # Agregar paquete a lista de nodos instanciandose self.instantiatingList.append(position) #print("Instanciando nodos:", self.instantiatingList) #print(self.instantiatingList) #print(ipPort) # Se ensambla el paquete requestPosPacket = self.assemblePackage.assemblePackageRequestPos( position, self.id) requestNum = int.from_bytes(requestPosPacket[0:4], byteorder='big') self.confirmationCounters[requestNum] = 0 # Enviamos la solicitud a todos los naranjas for node in self.orangeNodesList: ip, puerto = node self.tcplService.sendPackage(requestPosPacket, ip, puerto) # hacer broadcast timeout = time.time() + self.WAITFORACKTIMEOUT # en segundo # esperar confirmación de todos (si tardan mas de determinado tiempo) while requestNum in self.confirmationCounters\ and position in self.instantiatingList\ and self.confirmationCounters[requestNum] < len(self.orangeNodesList)\ and time.time() < timeout: ''' Acá se espera a que el hilo que recibe request aumente el contador respectivo para saber si todos confirmaron, pero si se eliminó la entrada, es porque otro naranja no aceptó el request pos ''' # Utilizamos un pequeño delay para el ciclo time.sleep(self.WAITFORACKDELAY) ''' Si se aceptó el request pos, se puede proseguir notar que si no se obtuvo respuesta de todos pero tampoco una denegación, se instancia. ''' if requestNum in self.confirmationCounters and position in self.instantiatingList: requested = True self.confirmationCounters.pop( requestNum) #Se saca de el diccionario y se instancia. else: print("Se me denegó request pos para: ", position) #return 0 # Si se instanció la posición, lo sacamos de la lista de disponible y retornamos if self.confirmPos(position, ipPort): self.freeNodeList.remove(position) self.instantiatingList.remove(position) self.instantiateNode(position, ipPort[0], ipPort[1]) print("Logré instanciar a nodo:", position) #print("Lista de adyacencias:") #self.printAdyacencyList(self.adyacentNodes) return position def listAdyacentGenerator(self, numeroNodo): listAdyacent = [] for indice in self.adyacentNodes[numeroNodo]: #print(type(indice.id)) #print("Añadiendo: ", self.adyacentNodes.get(indice.id)[0].id, "del nodo: ", numeroNodo) listAdyacent.append(self.adyacentNodes.get(indice.id)[0]) return listAdyacent def requestPosACK(self, position, ipPort, packageRequest): """ Envia un ACK indicando si una posición ya está instanciada o no @:param position posición que se determinará si esta usada o no @:param ipPort Ip y peurto al que se devuelve el ACK instantiated = True @:param packageRequest paquete request sobre el cuál se devuelve el ACK """ priority = int.from_bytes( packageRequest[7:9], byteorder='big' ) #Estaba de 7 a 9 que si corresponden a las posiciones si se empiza a contar desde 1. ''' Revisamos si no está instanciado, no se está intentando instanciar y el request tiene mayor prioridad. ''' if (position in self.freeNodeList) and not (position in self.instantiatingList): instantiated = 1 #El id no esta instaciado else: instantiated = 0 #El id esta instaciado print("Recibí un request pos para: ", position, "con prioridad", priority) ''' Si este nodo tiene menor prioridad y se está instanciando esa misma posición, debe sacar el nodo de la lista de instanciamiento. ''' if priority < self.id and position in self.instantiatingList: self.instantiatingList.remove(position) instantiated = 1 #print("Responderé al request pos con un:", instantiated) # Ensamblamos y enviamos el paquete según el estado de esa posición ackPacket = self.assemblePackage.assemblePackageRequestACK( packageRequest, instantiated) self.tcplService.sendPackage(ackPacket, ipPort[0], ipPort[1]) def popPackage(self): while 1: #print("Pop package...") package, address = self.tcplService.receivePackage() hiloDeAtencionRequest = threading.Thread( target=self.attendRequests, args=(package, address)) hiloDeAtencionRequest.start() pass # Anuncia a lo demás nodos naranajs la instanciación de un nodo verde # Retornta True si todos confirmaron def confirmPos(self, position, ipPort): """ Anuncia a lo demás nodos naranjas la instanciación de un nodo verde @:param position posición a confirmar @param ipPort ip broadcast @:return: True si se confirmó la posición por parte de los demás naranjas """ confirmed = False while not confirmed: # Crear paquete para CONFIRM_POS # Se ensambla el paquete requestPosPacket = self.assemblePackage.assemblePackageConfirmPos( position, ipPort) requestNum = int.from_bytes(requestPosPacket[0:4], byteorder='big') self.confirmationCounters[requestNum] = 0 # Enviamos la solicitud a todos los naranjas for node in self.orangeNodesList: ip, puerto = node self.tcplService.sendPackage(requestPosPacket, ip, puerto) # hacer broadcast #print("Envié un confirm pos para: ", position) timeout = time.time() + self.WAITFORACKTIMEOUT # en segundo # esperar confirmación de todos (si tardan mas de determinado tiempo) while requestNum in self.confirmationCounters\ and self.confirmationCounters[requestNum] < len(self.orangeNodesList)\ and time.time() < timeout: ''' Acá se espera a que el hilo que recibe request aumente el contador respectivo para saber si todos confirmaron, pero si se eliminó la entrada, es porque otro naranja no aceptó el request pos ''' # Utilizamos un pequeño delay para el ciclo time.sleep(self.WAITFORACKDELAY) # Si los demás recibieron el confirm pos, se puede proseguir if requestNum in self.confirmationCounters: confirmed = True #print("Estoy popeando el numero de request (estoy en request pos)", requestNum) self.confirmationCounters.pop(requestNum) #print("confirm pos para: ", position, "entregado") return True def confirmPosACK(self, position, ipPort): """ Envia un ACK confirmando la instanciación de una posición @:param position posición a confirmar @:param ipPort ip del nodo que hizo el request """ print("Recibí un confirm pos para: ", position) # Ensamblamos y enviamos el ACK (ya se debio agregar la posicion de instanciado) ackPacket = self.assemblePackage.assemblePackageConfirmPos( position, ipPort) self.tcplService.sendPackage(ackPacket, ipPort[0], ipPort[1]) def printAdyacencyList(self, lista): # La lista contiene: #nodo, ip, puerto, ¿instanciado? for node in lista: #for data in lista[node]: nodeinfo = lista[node][0] print(nodeinfo.id, nodeinfo.ip, nodeinfo.port, nodeinfo.state) print("-----------")