def test_handle_nack_on_await_data(self): """Test handle nack on the name of awaiting data""" computation_name = Name("/func/f1") computation_name += "_(/test/data,/data/test)" computation_name += "NFN" computation_interest = Interest(computation_name) computation_entry = NFNComputationTableEntry(computation_name) computation_str, prepended = self.nfn_layer.parser.network_name_to_nfn_str( computation_name) computation_entry.ast = self.nfn_layer.parser.parse(computation_str) computation_entry.interest = computation_interest self.nfn_layer.computation_table.append_computation(computation_entry) self.assertEqual(self.nfn_layer.computation_table.get_container_size(), 1) self.nfn_layer.computation_table.add_awaiting_data( computation_name, Name("/test/data")) self.assertEqual( len( self.nfn_layer.computation_table.get_computation( computation_name).awaiting_data), 1) self.nfn_layer.handleNack( 1, Nack(Name("/test/data"), NackReason.NO_CONTENT, interest=Interest(Name("/test/data")))) res = self.nfn_layer.queue_to_lower.get(timeout=2.0) self.assertEqual( res[1], Nack(computation_name, NackReason.NO_CONTENT, interest=computation_interest)) self.assertEqual(self.nfn_layer.computation_table.get_container(), [])
def test_handle_nack_on_computation_name(self): """Test handle nack on the name of the original computation""" computation_name = Name("/func/f1") computation_name += "_(/test/data,/data/test)" computation_name += "NFN" computation_interest = Interest(computation_name) computation_entry = NFNComputationTableEntry(computation_name) computation_str, prepended = self.nfn_layer.parser.network_name_to_nfn_str( computation_name) computation_entry.ast = self.nfn_layer.parser.parse(computation_str) computation_entry.interest = computation_interest self.nfn_layer.computation_table.append_computation(computation_entry) self.assertEqual(self.nfn_layer.computation_table.get_container_size(), 1) self.nfn_layer.handleNack( 1, Nack(computation_name, NackReason.COMP_PARAM_UNAVAILABLE, interest=computation_interest)) res = self.nfn_layer.queue_to_lower.get(timeout=2.0) self.assertEqual( res[1], Nack(computation_name, NackReason.COMP_PARAM_UNAVAILABLE, interest=computation_interest)) self.assertEqual(self.nfn_layer.computation_table.get_container(), [])
def compute(self, interest: Interest): """Compute a result, when all data are available :param interest: The original interest message to be handled (can be taken from computation table) """ self.logger.info("Start computation: " + str(interest.name)) params = [] entry = self.computation_table.get_computation(interest.name) if entry is None: return self.computation_table.remove_computation(interest.name) if entry.comp_state == NFNComputationState.WRITEBACK: name = self.parser.nfn_str_to_network_name(entry.rewrite_list[0]) res = entry.available_data[name] data = Content(entry.original_name, res) #self.queue_to_lower.put([entry.id, data]) self.handleContent(entry.id, data) return function_name = Name(entry.ast._element) function_code = entry.available_data.get(function_name) if function_code is None: self.queue_to_lower.put([entry.id, Nack(entry.original_name, NackReason.COMP_PARAM_UNAVAILABLE, interest=entry.interest)]) return executor: BaseNFNExecutor = self.executors.get(self.get_nf_code_language(function_code)) if executor is None: self.queue_to_lower.put([entry.id, Nack(entry.original_name, NackReason.COMP_EXCEPTION, interest=entry.interest)]) for e in entry.ast.params: if isinstance(e, AST_Name): param = entry.available_data.get(Name(e._element)) if param is None: self.queue_to_lower.put([entry.id, Nack(entry.original_name, NackReason.COMP_PARAM_UNAVAILABLE, interest=entry.interest)]) return params.append(param) elif isinstance(e, AST_FuncCall): search_name = Name() search_name += str(e) search_name += "NFN" params.append(entry.available_data[search_name]) elif not isinstance(e.type, AST): params.append(e.type(e._element)) res = executor.execute(function_code=function_code, params=params) if res is None: self.queue_to_lower.put([entry.id, Nack(entry.original_name, NackReason.COMP_EXCEPTION, interest=entry.interest)]) content_res: Content = Content(entry.original_name, str(res)) #TODO typed results self.logger.info("Finish Computation: " + str(content_res.name)) #self.computation_table.push_data(content_res) #self.queue_to_lower.put([entry.id, content_res]) self.handleContent(entry.id, content_res)
def data_from_lower(self, to_lower: multiprocessing.Queue, to_higher: multiprocessing.Queue, data): packet_id = data[0] packet = data[1] if 'THUNK' in str(packet.name): self.queue_to_higher.put(data) return if isinstance(packet, Interest): self.logger.info("Reveived Interest from lower... " + str(packet.name)) if len(packet.name.components) > 2 and packet.name.string_components[-2] == 'KEEPALIVE': self.logger.info("Interest is keep alive") if self.computation_table is None: return nfn_name = self.remove_keep_alive_from_name(packet.name) self.logger.info("NFN name is: " + str(nfn_name)) self.logger.info("#Running Computations: " + str(self.computation_table.is_comp_running(nfn_name))) comp = self.computation_table.get_computation(nfn_name) if comp is not None or self.computation_table.is_comp_running(nfn_name) or nfn_name in self.running_computations: to_lower.put([packet_id, Content(packet.name)]) else: to_lower.put([packet_id, Nack(packet.name, NackReason.COMP_NOT_RUNNING, interest=packet)]) #todo is it working with a nack? return elif len(packet.name.components) > 0 and packet.name.components[-1] == b'NFN': self.running_computations.append(packet.name) to_higher.put(data) else: to_higher.put(data) elif (isinstance(packet, Content) or isinstance(packet, Nack)) and len(packet.name.components) > 2 and packet.name.string_components[-2] == 'KEEPALIVE': if isinstance(packet, Content): self.logger.info("Received KEEP ALIVE reply, updating timestamps") entry = self.message_dict.get_entry(packet.name) if entry: self.logger.info("Old Timestamp was: " + str(entry.timestamp)) self.message_dict.update_timestamp(packet.name) #update timestamp for the R2C message self.logger.info("Timestamp is now: " + str(self.message_dict.get_entry(packet.name).timestamp)) return if isinstance(packet, Nack): original_name = self.remove_keep_alive_from_name(packet.name) self.message_dict.remove_entry(packet.name) self.message_dict.remove_entry(original_name) self.queue_to_higher.put([packet_id, Nack(original_name, reason=NackReason.COMP_NOT_RUNNING, interest=Interest(original_name))]) elif isinstance(packet, Content) or isinstance(packet, Nack): #R2C Content or Nack, remove entry and give to higher layer entry = self.message_dict.get_entry(packet.name) if entry is not None: self.logger.info("Removing entry from Keep Alive Dict") self.message_dict.remove_entry(packet.name) keepalive_name = self.add_keep_alive_from_name(packet.name) self.message_dict.remove_entry(keepalive_name) to_higher.put(data) #TODO R2C nack not handled correctly?
def handle_interest_from_lower(self, face_id: int, interest: Interest, to_lower: multiprocessing.Queue): self.logger.info("Incoming interest: " + interest.name.to_string()) # incoming interest is nfn expression if interest.name.string_components[-1] == "NFN": try: parser = DefaultNFNParser() nfn_str, prepended_name = parser.network_name_to_nfn_str(interest.name) ast = parser.parse(nfn_str) # assert that valid publish expression if is_publish_expression(ast): # store to database data_name = ast.params[0]._element payload = ast.params[1]._element try: payload = base64.b64decode(payload[7:]) self.logger.info("Payload is base64 encoded. Decoded.") except: self.logger.info("Invalid publish expression. The payload could not be decoded.") nack = Nack(interest.name, reason=NackReason.COMP_NOT_PARSED, interest=interest) self.queue_to_lower.put([face_id, nack]) self.cs.add_content_object(Content(data_name, payload)) self.logger.info("Add to database: " + data_name) # reply confirmation confirmation = Content(interest.name, "ok") to_lower.put([face_id, confirmation]) else: self.logger.info("Invalid publish expression. Wrong format.") nack = Nack(interest.name, reason=NackReason.COMP_NOT_PARSED, interest=interest) self.queue_to_lower.put([face_id, nack]) except: self.logger.info("Invalid publish expression.") nack = Nack(interest.name, reason=NackReason.COMP_NOT_PARSED, interest=interest) self.queue_to_lower.put([face_id, nack]) # incoming interest is data request else: db_entry = self.cs.find_content_object(interest.name) if db_entry is not None: self.logger.info("Found in database") to_lower.put([face_id, db_entry.content]) return else: self.logger.info("Not found in database") nack = Nack(interest.name, NackReason.NO_CONTENT, interest) to_lower.put([face_id, nack]) return
def test_handle_nack_on_rewritten_computation_further_rewrite(self): """Test if a Nack message is handled correctly for a rewritten computation, when there is a further rewrite""" self.nfn_layer.fib.add_fib_entry(Name('/test'), 1, True) self.nfn_layer.fib.add_fib_entry(Name('/data'), 1, True) computation_name = Name("/func/f1") computation_name += "_(/test/data,/data/test)" computation_name += "NFN" computation_interest = Interest(computation_name) computation_entry = NFNComputationTableEntry(computation_name) computation_str, prepended = self.nfn_layer.parser.network_name_to_nfn_str( computation_name) computation_entry.ast = self.nfn_layer.parser.parse(computation_str) computation_entry.interest = computation_interest self.nfn_layer.computation_table.append_computation(computation_entry) nack_name = Name("/test/data") nack_name += "/func/f1(_,/data/test)" nack_name += "NFN" self.nfn_layer.forwarding_descision(computation_interest) res = self.nfn_layer.queue_to_lower.get(timeout=2.0) self.assertEqual(res[1], Interest(nack_name)) self.assertEqual( self.nfn_layer.computation_table.get_computation( computation_name).comp_state, NFNComputationState.REWRITE) self.assertEqual( len( self.nfn_layer.computation_table.get_computation( computation_name).rewrite_list), 2) self.assertEqual( self.nfn_layer.computation_table.get_computation( computation_name).rewrite_list, [ "/func/f1(%/test/data%,/data/test)", "/func/f1(/test/data,%/data/test%)" ]) self.nfn_layer.handleNack( res[1], Nack(nack_name, NackReason.COMP_PARAM_UNAVAILABLE, interest=Interest(nack_name))) second_request_name = Name("/data/test") second_request_name += "/func/f1(/test/data,_)" second_request_name += "NFN" res = self.nfn_layer.queue_to_lower.get(timeout=2.0) self.assertEqual(res[1], Interest(second_request_name)) self.assertEqual(self.nfn_layer.computation_table.get_container_size(), 1) self.assertEqual( len( self.nfn_layer.computation_table.get_computation( computation_name).rewrite_list), 1) self.assertEqual( self.nfn_layer.computation_table.get_computation( computation_name).rewrite_list, ["/func/f1(/test/data,%/data/test%)"])
def _send_forwarder_solicitation(self, retry: int): autoconf: Interest = Interest(_AUTOCONFIG_FORWARDERS_PREFIX) for i in self._bc_interfaces: interface = self._linklayer.interfaces[i] if not isinstance(interface, UDP4Interface): # Autoconfig currently only supported for UDP over IPv4 continue interface: UDP4Interface = interface bcaddr: str = interface.get_broadcast_address() if bcaddr is not None: addr_info = AddressInfo((bcaddr, self._bc_port), i) autoconf_fid = self._linklayer.faceidtable.get_or_create_faceid( addr_info) self.queue_to_lower.put([autoconf_fid, autoconf]) # Schedule re-broadcast of the forwarder solicitation interest, which will recursively call this function. if self._solicitation_timeout is not None and retry > 1: self._solicitation_timer = threading.Timer( self._solicitation_timeout, self._send_forwarder_solicitation, kwargs={'retry': retry - 1}) self._solicitation_timer.start() elif retry <= 1: # If all forwarder solicitations timed out, send a Nack packet upwards for each held interest. for interest in self._held_interests: nack = Nack(interest.name, NackReason.NO_ROUTE, interest) self.queue_to_higher.put([None, nack]) self._held_interests = []
def test_Encoder_encode_decode_nack(self): """Test the nack decoding of Encoder""" interest = Interest("/data/test") n = Nack("/data/test", NackReason.NO_CONTENT, interest=interest) en = self.encoder1.encode(n) dn = self.encoder1.decode(en) self.assertTrue(n == dn)
def get_chunks_available(self, packet: Packet): """ Check if chunks are available for a given name. Return a content object containing the names of the available chunks, or NACK """ chunks_available = [] name = self.unpack(packet.name) request_entry = self.get_request_entry(name) cs_entry = self.cs.find_content_object(name) if request_entry is not None: chunks_available = [ str(chunk.name) for chunk in request_entry.chunks ] elif cs_entry: chunks_available.append("complete;") if chunks_available: chunks_available = Content(packet.name, ";".join(chunks_available)) if len(chunks_available.content) > self.chunk_size: meta_data, chunks = self.chunkifyer.chunk_data( chunks_available) meta_data.extend(chunks) for data in meta_data: self.cs.remove_content_object(data.name) self.cs.add_content_object(data) return meta_data[0] else: return chunks_available return Nack(packet.name, NackReason.NO_CONTENT, packet)
def return_nack(self, packet_id, interest: Interest): self.queue_to_lower.put([ packet_id, Nack(interest.name, reason=NackReason.COMP_NOT_RUNNING, interest=interest) ]) # TODO -- choose an appropriate NACK reason
def test_keep_alive_ageing_no_reply(self): """test ageing with keepalive with no keep alive reply""" self.timeoutPreventionLayer.ageing() interest = Interest("/test/func/_()/NFN") keepalive = Interest("/test/func/_()/KEEPALIVE/NFN") self.timeoutPreventionLayer.queue_from_higher.put([1, interest]) res1 = self.timeoutPreventionLayer.queue_to_lower.get(timeout=2.0) self.assertEqual(res1, [1, interest]) e1 = self.timeoutPreventionLayer.message_dict.get_entry(interest.name) self.assertTrue(e1 is not None) e2 = self.timeoutPreventionLayer.message_dict.get_entry(keepalive.name) self.assertTrue(e2 is not None) res2 = self.timeoutPreventionLayer.queue_to_lower.get(timeout=2.0) self.assertEqual(res2, [1, interest]) res3 = self.timeoutPreventionLayer.queue_to_lower.get(timeout=2.0) self.assertEqual(res3, [1, keepalive]) res4 = self.timeoutPreventionLayer.queue_to_lower.get(timeout=2.0) self.assertEqual(res4, [1, interest]) res5 = self.timeoutPreventionLayer.queue_to_lower.get(timeout=2.0) self.assertEqual(res5, [1, keepalive]) self.assertTrue(self.timeoutPreventionLayer.queue_to_lower.empty()) res7 = self.timeoutPreventionLayer.queue_to_higher.get(timeout=2.0) self.assertEqual(res7, [1, Nack(name=interest.name, reason=NackReason.COMP_NOT_RUNNING, interest=interest)])
def test_multicast_and_nack_handling_with_retransmit(self): """Test if a multicast works, and if the nack counter for the multicast works""" i1 = Interest("/test/data") n1 = Nack(i1.name, NackReason.NO_CONTENT, i1) self.icn_layer.start_process() self.icn_layer.fib.add_fib_entry(i1.name, [2, 3]) self.icn_layer.queue_from_lower.put([1, i1]) d1 = self.icn_layer.queue_to_lower.get(timeout=2.0) d2 = self.icn_layer.queue_to_lower.get(timeout=2.0) self.assertEqual([2, i1], d1) self.assertEqual([3, i1], d2) self.icn_layer.queue_from_lower.put([3, n1]) self.assertTrue(self.icn_layer.queue_to_lower.empty()) self.icn_layer.ageing() d3 = self.icn_layer.queue_to_lower.get(timeout=2.0) self.assertEqual([2, i1], d3) self.icn_layer.queue_from_lower.put([2, n1]) d4 = self.icn_layer.queue_to_lower.get(timeout=2.0) self.assertEqual([1, n1], d4)
def handle_interest_from_higher(self, face_id: int, interest: Interest, to_lower: multiprocessing.Queue, to_higher: multiprocessing.Queue): self.logger.info("Handling Interest (from higher): " + str(interest.name) + "; Face ID: " + str(face_id)) cs_entry = self.cs.find_content_object(interest.name) if cs_entry is not None: self.queue_to_higher.put([face_id, cs_entry.content]) return pit_entry = self.pit.find_pit_entry(interest.name) self.pit.add_pit_entry(interest.name, face_id, interest, local_app=True) if pit_entry: fib_entry = self.fib.find_fib_entry( interest.name, incoming_faceids=pit_entry.faceids) else: fib_entry = self.fib.find_fib_entry(interest.name) if fib_entry is not None: self.pit.add_used_fib_entry(interest.name, fib_entry) to_lower.put([fib_entry.faceid, interest]) else: self.logger.info("No FIB entry, sending Nack: " + str(interest.name)) nack = Nack(interest.name, NackReason.NO_ROUTE, interest=interest) if pit_entry is not None: # if pit entry is available, consider it, otherwise assume interest came from higher for i in range(0, len(pit_entry.faceids)): if pit_entry._local_app[i]: to_higher.put([face_id, nack]) # else: # to_lower.put([pit_entry._faceids[i], nack]) else: to_higher.put([face_id, nack])
def handle_interest(self, face_id: int, interest: Interest, to_lower: multiprocessing.Queue): """ Handle incoming interest :param face_id: ID of incoming face :param interest: Interest :param to_lower: Queue to lower layer :return: None """ cache_entry = self.cache.find_content_object(interest.name) if cache_entry is not None: self.logger.info("Found in cache") to_lower.put([face_id, cache_entry.content]) return else: if self.generate_data(interest.name) is False: nack = Nack(interest.name, NackReason.NO_CONTENT, interest=interest) to_lower.put([face_id, nack]) self.logger.info("Object not in repo") return else: self.logger.info("Not found in cache, successfully generated") manifest = self.cache.find_content_object( interest.name).content to_lower.put([face_id, manifest]) return
def test_multicast_and_nack_handling(self): """Test if a multicast works, and if the nack counter for the multicast works""" i1 = Interest("/test/data") n1 = Nack(i1.name, NackReason.NO_CONTENT, i1) self.icn_layer.start_process() self.icn_layer.fib.add_fib_entry(i1.name, [2,3,4]) self.icn_layer.queue_from_lower.put([1, i1]) d1 = self.icn_layer.queue_to_lower.get(timeout=2.0) self.assertEqual([2, i1], d1) self.assertTrue(self.icn_layer.queue_to_lower.empty()) self.icn_layer.queue_from_lower.put([2, n1]) d2 = self.icn_layer.queue_to_lower.get(timeout=4.0) print(d2) self.assertEqual([3, i1], d2) self.assertTrue(self.icn_layer.queue_to_lower.empty()) self.icn_layer.queue_from_lower.put([3, n1]) try: d3 = self.icn_layer.queue_to_lower.get(timeout=4.0) except: self.fail() print(d3) self.assertEqual([4, i1], d3)
def decode(self, wire_data) -> Packet: """ NDN TLV wire format packet to python object (PiCN's internal representation) :param wire_data: Packet in wire format (NDN TLV representation) :return: Packet in PiCN's internal representation """ # print("got %d bytes to decode" % len(wire_data)) if(self.is_content(wire_data)): self.logger.info("Decode content object") try: (name, payload) = self.decode_data(wire_data) return Content(name, payload, wire_data) except: self.logger.info("Decoding failed (malformed packet)") return UnknownPacket(wire_format=wire_data) if(self.is_interest(wire_data)): self.logger.info("Decode interest") try: name = self.decode_interest(wire_data) return Interest(name, wire_data) except: self.logger.info("Decoding failed (malformed packet)") return UnknownPacket(wire_format=wire_data) if(self.is_nack(wire_data)): self.logger.info("Decode NACK") try: (name, reason) = self.decode_nack(wire_data) return Nack(name, reason, None, wire_format=wire_data) except: self.logger.info("Decoding failed (malformed packet)") return UnknownPacket(wire_format=wire_data) else: self.logger.info("Decode failed (unknown packet type)") return UnknownPacket(wire_format=wire_data)
def handleNack(self, packet_id: int, nack: Nack): """Handles a Nack :param packet_id: id of the computation :param nack: nack that arrived """ remove_list = [] for e in self.computation_table.get_container(): self.computation_table.remove_computation(e.original_name) #check next rewrite if current is nack-ed TODO this is a code duplication with ageing in ComputationTableEntry if e.comp_state == NFNComputationState.REWRITE and\ e.rewrite_list != [] and\ nack.name == self.parser.nfn_str_to_network_name(e.rewrite_list[0]): e.rewrite_list.pop(0) if e.rewrite_list == []: remove_list.append(e.original_name) else: request = Interest(self.parser.nfn_str_to_network_name(e.rewrite_list[0])) self.queue_to_lower.put([e.id, request]) #check if nack-ed data were required. elif nack.name == e.original_name: remove_list.append(e.original_name) else: for a in e.awaiting_data: if nack.name == a.name: remove_list.append(e.original_name) self.computation_table.append_computation(e) #remove all computation that are nack-ed and forward nack for r in remove_list: e = self.computation_table.get_computation(r) self.computation_table.remove_computation(r) new_nack = Nack(e.original_name, nack.reason, interest=e.interest) self.queue_to_lower.put([e.id, new_nack]) self.handleNack(e.id, new_nack)
def data_from_lower(self, to_lower: multiprocessing.Queue, to_higher: multiprocessing.Queue, data: Packet): self.logger.info("Got Data from lower") if self._repository is None: return faceid = data[0] packet = data[1] if isinstance( packet, Interest ): # TODO: Can I check here for connector interest and return session key? if self._repository.is_content_available(packet.name): c = self._repository.get_content(packet.name) self.queue_to_lower.put([faceid, c]) self.logger.info("Found content object, sending down") return elif self._propagate_interest is True: # TODO: What does this do? --> Used for NDN. self.queue_to_lower.put([faceid, packet]) return else: self.logger.info( "No matching data, dropping interest, sending nack") nack = Nack(packet.name, NackReason.NO_CONTENT, interest=packet) to_lower.put([faceid, nack]) return if isinstance(packet, Content): pass
def ageing(self): """Ageing the data structs""" try: self.logger.debug("Ageing") #PIT ageing retransmits, removed_pit_entries = self.pit.ageing() for pit_entry in retransmits: fib_entry = self.fib.find_fib_entry( pit_entry.name, pit_entry.fib_faces_already_used, pit_entry.faceids) if not fib_entry: continue for fid in fib_entry.faceid: if not self.pit.test_faceid_was_nacked( pit_entry.name, fid): self.queue_to_lower.put([fid, pit_entry.interest]) for pit_entry in removed_pit_entries: if not pit_entry: continue for fid, local in zip(pit_entry.faceids, pit_entry.local_app): if local is True: self.queue_to_higher.put([ fid, Nack(pit_entry.name, NackReason.PIT_TIMEOUT, pit_entry.interest) ]) #CS ageing self.cs.ageing() except Exception as e: self.logger.warning("Exception during ageing: " + str(e)) pass finally: t = threading.Timer(self._ageing_interval, self.ageing) t.setDaemon(True) t.start()
def test_none_thunk_nack_from_higher(self): """test that a normal nack is forwared from higher to lower""" nack = Nack("/test/data", NackReason.NO_CONTENT, interest=Interest("/test/data")) self.thunklayer.queue_from_higher.put([1, nack]) res = self.thunklayer.queue_to_lower.get(timeout=2) self.assertEqual(res, [1, nack])
def get_matching_content_from_packed_name(self, packet: Packet): """Return either the content matching the unpacked name or NACK""" name_in = self.unpack(packet.name) if name_in in self._chunk_table: matching_content = self._chunk_table.get(name_in)[0] matching_content.name = packet.name return matching_content else: return Nack(packet.name, NackReason.NO_CONTENT, packet)
def test_keep_alive_request_no_comp_running(self): """test replying a incoming keep alive request if no comp running""" keep_alive = Interest("/test/func/_()/KEEPALIVE/NFN") nack = Nack(keep_alive.name, reason=NackReason.COMP_NOT_RUNNING, interest=keep_alive) self.timeoutPreventionLayer.queue_from_lower.put([3, keep_alive]) self.assertTrue(self.timeoutPreventionLayer.queue_to_higher.empty()) res = self.timeoutPreventionLayer.queue_to_lower.get(timeout=2.0) self.assertEqual(res, [3, nack])
def test_nack_from_lower(self): """Test nack from lower""" self.chunkLayer.start_process() nack1 = Nack("/test/data", NackReason.NO_CONTENT, None) self.chunkLayer.queue_from_lower.put([1, nack1]) try: data = self.chunkLayer.queue_to_higher.get(timeout=2.0) except: self.fail() self.assertEqual(data[0], 1) self.assertEqual(data[1], nack1)
def test_nack_from_lower_message_dict_entry(self): """test nack from lower WITH message dict entry""" nack = Nack("/test/data", interest=Interest("/test/data"), reason=NackReason.CONGESTION) self.timeoutPreventionLayer.message_dict.create_entry(nack.name, 1) e = self.timeoutPreventionLayer.message_dict.get_entry(nack.name) self.assertTrue(e is not None) self.timeoutPreventionLayer.queue_from_lower.put([1, nack]) res = self.timeoutPreventionLayer.queue_to_higher.get(timeout=2.0) self.assertEqual([1, nack], res) e = self.timeoutPreventionLayer.message_dict.get_entry(nack.name) self.assertTrue(e is None)
def test_nack_prefix(self): """Test if repo returns Nack if nprefix not matching""" self.repositoryLayer.start_process() i1 = Interest("/data/test/f1") n1 = Nack(i1.name, NackReason.NO_CONTENT, interest=i1) self.repositoryLayer.queue_from_lower.put([0, i1]) try: data = self.repositoryLayer.queue_to_lower.get(timeout=2.0) except: self.fail() self.assertEqual(n1, data[1])
def ageing(self): if self.queue_to_lower._closed or self.queue_to_higher._closed: return timestamp = time.time() try: removes = [] container = self.message_dict.get_container() for name in container: entry = self.message_dict.get_entry(name) if len(name.components) > 2 and name.string_components[-2] == "KEEPALIVE": if entry.timestamp + self.timeout_interval < timestamp: self.logger.info("Remove Keep Alvie Job because of timeout. Timestamp is: " + str(entry.timestamp) + " Now is: " + str(timestamp)) removes.append(name) if name in self.running_computations: self.running_computations.remove(name) original_name = self.remove_keep_alive_from_name(name) removes.append(original_name) if original_name in self.running_computations: self.running_computations.remove(original_name) nack = Nack(name=original_name, reason=NackReason.COMP_NOT_RUNNING, interest=Interest(name)) self.queue_to_higher.put([entry.packetid, nack]) else: self.queue_to_lower.put([entry.packetid, Interest(name=name)]) else: if name.components[-1] != b'NFN' and entry.timestamp + self.timeout_interval < time.time(): removes.append(name) nack = Nack(name=name, reason=NackReason.COMP_PARAM_UNAVAILABLE, interest=Interest(name)) self.queue_to_higher.put([entry.packetid, nack]) else: self.queue_to_lower.put([entry.packetid, Interest(name=name)]) for n in removes: self.message_dict.remove_entry(n) if self.pit is not None: self.pit.remove_pit_entry(n) except Exception as e: self.logger.warning("Exception during ageing: " + str(e)) return t = threading.Timer(self.ageing_interval, self.ageing) t.setDaemon(True) t.start()
def handle_interest_from_higher(self, face_id: int, interest: Interest, to_lower: multiprocessing.Queue, to_higher: multiprocessing.Queue): self.logger.info("Handling Interest (from higher): " + str(interest.name) + "; Face ID: " + str(face_id)) cs_entry = self.cs.find_content_object(interest.name) if cs_entry is not None: self.queue_to_higher.put([face_id, cs_entry.content]) return pit_entry = self.pit.find_pit_entry(interest.name) if pit_entry: self.pit.add_interested_face(interest.name, face_id) fib_entry = self.fib.find_fib_entry( interest.name, incoming_faceids=pit_entry.faceids) else: fib_entry = self.fib.find_fib_entry(interest.name) if fib_entry is not None: self.pit.set_number_of_forwards(interest.name, 0) pit_occupancy = self.pit.occupancy_available_faces_per_name( fib_entry) sorted_pit_occupancy = sorted(pit_occupancy.items(), key=lambda kv: kv[1]) faces_sorted_by_occupancy = list( map(lambda kv: kv[0], sorted_pit_occupancy)) for fid in faces_sorted_by_occupancy: try: if not self.pit.test_faceid_was_nacked(interest.name, fid): self.pit.add_pit_entry(interest.name, face_id, fid, interest, local_app=True) self.pit.increase_number_of_forwards(interest.name) self.pit.add_used_fib_face(interest.name, [fid]) to_lower.put([fid, interest]) break except: pass else: self.logger.info("No FIB entry, sending Nack: " + str(interest.name)) nack = Nack(interest.name, NackReason.NO_ROUTE, interest=interest) if pit_entry is not None: # if pit entry is available, consider it, otherwise assume interest came from higher for i in range(0, len(pit_entry.faceids)): if pit_entry._local_app[i]: to_higher.put([face_id, nack]) else: to_lower.put([pit_entry._faceids[i], nack]) else: to_higher.put([face_id, nack])
def test_Nack_Creation_no_wireformat(self): """Test the creation of a nack object message with no wireformat given""" name: Name = Name("/test/data") i1: Interest = Interest(name) n1: Nack = Nack(name, NackReason.NO_ROUTE, interest=i1) enc_n1 = self.encoder.encode(n1) self.assertEqual(enc_n1[0], 0x64) self.assertEqual(enc_n1[3], 0x03) self.assertEqual(enc_n1[4], 0x20) self.assertFalse(self.encoder.is_interest(enc_n1)) self.assertFalse(self.encoder.is_content(enc_n1)) self.assertTrue(self.encoder.is_nack(enc_n1)) dec_n1 = self.encoder.decode(enc_n1) self.assertEqual(dec_n1, n1)
def get_matching_content(self, packet: Packet): """Return either the content matching the packet name or NACK""" name_in = self.unpack(packet.name) cs_entry = self.cs.find_content_object(name_in) if name_in in self._chunk_table: matching_content = self._chunk_table.get(name_in)[0] matching_content.name = packet.name return matching_content elif cs_entry: matching_content = cs_entry.content matching_content.name = packet.name return matching_content else: return Nack(packet.name, NackReason.NO_CONTENT, packet)
def test_ICNLayer_issue_nack_no_content_no_fib_from_lower(self): """Test if ICN Layer issues Nack if no content and no fib entry is available from lower""" self.icn_layer.start_process() interest = Interest("/test/data") nack = Nack(interest.name, NackReason.NO_ROUTE, interest=interest) self.icn_layer.queue_from_lower.put([1, interest]) try: data = self.icn_layer.queue_to_lower.get(timeout=2.0) except: self.fail() fid = data[0] packet = data[1] self.assertEqual(fid, 1) self.assertEqual(packet, nack)