def _validate_mapped_segment_file(filename): """Callable used by MappedSegmentInfo, validating a mapped segment file. In this initial version, we just check the existence of the file. """ validated = filename is not None and os.path.exists(filename) logger.info(LIBMEMMGR_MAPPED_SEGMENT_VALIDATE, filename if filename is not None else '<unknown>', 'ok' if validated else 'failed') return validated
def rmfile(path): """Trivial internal helper to remove a specific file. Failure of removing these files is not expected but not critical, so we only log any failure and move on. """ if os.path.exists(path): try: os.unlink(path) except Exception as ex: logger.info(LIBMEMMGR_MAPPED_SEGMENT_RMFILE_FAIL, ex)
def __do_load_zone(self, writer, zname, rrclass, dsrc_name, dsrc_info): """A short helper for handle_load(). Try incremental load until the load is completed or any cancel event happens. The number of load items per iteration is arbitrarily chosen, and may have to be adjusted or customizable in future. It returns True if the load is completed and False if it's canceled. """ while not writer.load(1000): if self.__cmd_canceled(dsrc_info): logger.info(LIBMEMMGR_BUILDER_SEGMENT_LOAD_CANCELED, zname, rrclass, dsrc_name, dsrc_info.gen_id) writer.cleanup() return False return True
def _remove(self): def rmfile(path): """Trivial internal helper to remove a specific file. Failure of removing these files is not expected but not critical, so we only log any failure and move on. """ if os.path.exists(path): try: os.unlink(path) except Exception as ex: logger.info(LIBMEMMGR_MAPPED_SEGMENT_RMFILE_FAIL, ex) rmfile(self.__map_versions_file) for vers in (0, 1): mapped_file = '%s.%d' % (self.__mapped_file_base, vers) rmfile(mapped_file) logger.info(LIBMEMMGR_MAPPED_SEGMENT_REMOVED, self.get_generation_id())
def __reset_segment(self, clist, dsrc_name, rrclass, params): try: clist.reset_memory_segment(dsrc_name, ConfigurableClientList.READ_WRITE, params) logger.debug(logger.DBGLVL_TRACE_BASIC, LIBMEMMGR_BUILDER_SEGMENT_RESET, dsrc_name, rrclass) return self.__RESET_SEGMENT_OK except Exception as ex: logger.error(LIBMEMMGR_BUILDER_RESET_SEGMENT_ERROR, dsrc_name, rrclass, ex) try: clist.reset_memory_segment(dsrc_name, ConfigurableClientList.CREATE, params) logger.info(LIBMEMMGR_BUILDER_SEGMENT_CREATED, dsrc_name, rrclass) return self.__RESET_SEGMENT_CREATED except Exception as ex: logger.error(LIBMEMMGR_BUILDER_SEGMENT_CREATE_ERROR, dsrc_name, rrclass, ex) return self.__RESET_SEGMENT_FAILED
def _handle_load(self, zone_name, dsrc_info, rrclass, dsrc_name): # This method is called when handling the 'load' command. The # following tuple is passed: # # ('load', zone_name, dsrc_info, rrclass, dsrc_name) # # where: # # * zone_name is None or bundy.dns.Name, specifying the zone name # to load. If it's None, it means all zones to be cached in # the specified data source (used for initialization). # # * dsrc_info is a DataSrcInfo object corresponding to the # generation ID of the set of data sources for this loading. # # * rrclass is an bundy.dns.RRClass object, the RR class of the # data source. # # * dsrc_name is a string, specifying a data source name. # # This is essentially a 'private' method, but allows tests to call it # directly; for other purposes shouldn't be called outside of the class. clist = dsrc_info.clients_map[rrclass] sgmt_info = dsrc_info.segment_info_map[(rrclass, dsrc_name)] params = json.dumps(sgmt_info.get_reset_param(SegmentInfo.WRITER)) result = self.__reset_segment(clist, dsrc_name, rrclass, params) if result == self.__RESET_SEGMENT_FAILED: self.__send_response(('load-completed', dsrc_info, rrclass, dsrc_name, False)) return # If we were told to load a single zone but had to create a new # segment, we'll need to load all zones, not just this one. if result == self.__RESET_SEGMENT_CREATED and zone_name is not None: logger.info(LIBMEMMGR_BUILDER_SEGMENT_LOAD_ALL, zone_name, rrclass, dsrc_name) zone_name = None if zone_name is not None: zones = [(None, zone_name)] else: zones = clist.get_zone_table_accessor(dsrc_name, True) errors = 0 for _, zname in zones: # note: don't override zone_name here # install empty zone initially catch_load_error = (zname is None) try: result, writer = clist.get_cached_zone_writer(zname, catch_load_error, dsrc_name) if result != ConfigurableClientList.CACHE_STATUS_ZONE_SUCCESS: # handle this with other genuine exceptions below raise bundy.datasrc.Error('result=%d' % result) except bundy.datasrc.Error as ex: logger.error(LIBMEMMGR_BUILDER_GET_ZONE_WRITER_ERROR, zname, dsrc_name, ex) errors += 1 continue try: error = writer.load() if error is not None: logger.error(LIBMEMMGR_BUILDER_ZONE_WRITER_LOAD_1_ERROR, zname, dsrc_name, error) errors += 1 continue writer.install() except Exception as e: logger.error(LIBMEMMGR_BUILDER_ZONE_WRITER_LOAD_2_ERROR, zname, dsrc_name, e) errors += 1 # fall through to cleanup writer.cleanup() # need to reset the segment so readers can read it (note: memmgr # itself doesn't have to keep it open, but there's currently no # public API to just clear the segment). This 'reset' should succeed, # so we'll let any exception be propagated. clist.reset_memory_segment(dsrc_name, ConfigurableClientList.READ_ONLY, params) # At this point, we consider the load a failure only if loading a # specific zone has failed. succeeded = True if (zone_name is None or errors == 0) else False self.__send_response(('load-completed', dsrc_info, rrclass, dsrc_name, succeeded))
def _handle_load(self, zone_name, dsrc_info, rrclass, dsrc_name): # This method is called when handling the 'load' command. The # following tuple is passed: # # ('load', zone_name, dsrc_info, rrclass, dsrc_name) # # where: # # * zone_name is None or bundy.dns.Name, specifying the zone name # to load. If it's None, it means all zones to be cached in # the specified data source (used for initialization). # # * dsrc_info is a DataSrcInfo object corresponding to the # generation ID of the set of data sources for this loading. # # * rrclass is an bundy.dns.RRClass object, the RR class of the # data source. # # * dsrc_name is a string, specifying a data source name. # # This is essentially a 'private' method, but allows tests to call it # directly; for other purposes shouldn't be called outside of the class. clist = dsrc_info.clients_map[rrclass] sgmt_info = dsrc_info.segment_info_map[(rrclass, dsrc_name)] params = json.dumps(sgmt_info.get_reset_param(SegmentInfo.WRITER)) result = self.__reset_segment(clist, dsrc_name, rrclass, params) if result == self.__RESET_SEGMENT_FAILED: self.__send_response(('load-completed', dsrc_info, rrclass, dsrc_name, False)) return # If we were told to load a single zone but had to create a new # segment, we'll need to load all zones, not just this one. if result == self.__RESET_SEGMENT_CREATED and zone_name is not None: logger.info(LIBMEMMGR_BUILDER_SEGMENT_LOAD_ALL, zone_name, rrclass, dsrc_name) zone_name = None if zone_name is not None: zones = [(None, zone_name)] else: zones = clist.get_zone_table_accessor(dsrc_name, True) errors = 0 canceled = False for _, zname in zones: # note: don't override zone_name here # install empty zone initially catch_load_error = (zone_name is None) try: result, writer = clist.get_cached_zone_writer(zname, catch_load_error, dsrc_name) if result != ConfigurableClientList.CACHE_STATUS_ZONE_SUCCESS: # handle this with other genuine exceptions below raise bundy.datasrc.Error('result=%d' % result) except bundy.datasrc.Error as ex: logger.error(LIBMEMMGR_BUILDER_GET_ZONE_WRITER_ERROR, zname, dsrc_name, ex) errors += 1 continue try: try: if not self.__do_load_zone(writer, zname, rrclass, dsrc_name, dsrc_info): canceled = True break except bundy.datasrc.Error as error: logger.error(LIBMEMMGR_BUILDER_ZONE_WRITER_LOAD_1_ERROR, zname, dsrc_name, error) errors += 1 # If this is initial full load, we'll add an empty zone # for failed zones. if catch_load_error: writer.install() continue writer.install() except Exception as e: logger.error(LIBMEMMGR_BUILDER_ZONE_WRITER_LOAD_2_ERROR, zname, dsrc_name, e) errors += 1 # fall through to cleanup writer.cleanup() # Make sure the writer is destroyed no matter how we reach here # befoe resetting the segment; otherwise the temporary resource # maintained in the writer could cause a disruption. writer = None # need to reset the segment so readers can read it (note: memmgr # itself doesn't have to keep it open, but there's currently no # public API to just clear the segment). This 'reset' should succeed, # so we'll let any exception be propagated. clist.reset_memory_segment(dsrc_name, ConfigurableClientList.READ_ONLY, params) # If the load has been canceled, we are not expected to return a # response. We should return after all cleanups are completed. if canceled: return # At this point, we consider the load a failure only if loading a # specific zone has failed. succeeded = (zone_name is None or errors == 0) self.__send_response(('load-completed', dsrc_info, rrclass, dsrc_name, succeeded))