def __buildDefaults(self, section): '''! Helper API to include missing objects in a configuration section and validate their values. Below are the objects that are validated and added if not present: \n - ignore-result\n - reboot-on-success\n - reboot-on-failure\n - halt-on-failure\n - timestamp\n Below are the objects whose value is validated:\n - status - suspend-exit-code If the specified value is invalid of if the object is not specified, its value is read from ztp_cfg.json @param section (dict) Configuration Section input data read from JSON file. ''' default_objs = [ 'ignore-result', 'reboot-on-success', 'reboot-on-failure', 'halt-on-failure' ] # Loop through objects and update them with default values for key in default_objs: _val = getField(section, key, bool, getCfg(key)) section[key] = _val # set status if section.get('status') is None: section['status'] = 'BOOT' section['timestamp'] = getTimestamp() elif isString(section.get('status')) is False or \ section.get('status') not in ['BOOT', 'IN-PROGRESS', 'SUSPEND', 'DISABLED', 'FAILED', 'SUCCESS']: logger.warning( 'Invalid value (%s) used for configuration section status. Setting it to DISABLED.' % section.get('status')) section['status'] = 'DISABLED' section['timestamp'] = getTimestamp() # Validate suspend-exit code if section.get('suspend-exit-code') is not None: suspend_exit_code = getField(section, 'suspend-exit-code', int, None) if suspend_exit_code is None or suspend_exit_code < 0: del section['suspend-exit-code'] # Add timestamp if missing if section.get('timestamp') is None: section['timestamp'] = getTimestamp()
def getRuntime(status, startTimeStamp, endTimeStamp): if status == 'IN-PROGRESS': return timeDiff(startTimeStamp, getTimestamp()) elif status == 'SUCCESS' or status == 'FAILED': return timeDiff(startTimeStamp, endTimeStamp) else: return None
def getTimeString(msg): split_msg = msg.split('|', 1) if len(split_msg) == 2: time_str = split_msg[0] try: return "({})".format(timeDiff(time_str.strip(), getTimestamp())) + split_msg[1] except: ret = msg else: return msg
def updateStatus(self, obj, status): '''! Update status of configuration section. Also update the timestamp indicating date/time when it is updated. The changes are also saved to the JSON file on disk which corresponds to the configuration section. @param status (str) Value to be stored as status ''' if isinstance(obj, dict) and isString(status): self.objJson.set(obj, 'status', status) self.objJson.set(obj, 'timestamp', getTimestamp(), True) else: logger.error('Invalid argument type.') raise TypeError('Invalid argument type')
def test_logging_output(self): '''! Test logging on both stdout and console ''' if os.path.isfile("/tmp/test_Logger.txt"): os.remove("/tmp/test_Logger.txt") os.remove("/etc/rsyslog.d/10-ztp-log-file.conf") log = Logger(log_file="/tmp/test_Logger.txt") assert (log != None) log.setLevel("warning") msg = "Test 1 " + getTimestamp() log.warning(msg) assert (self.__search_file('/var/log/syslog', msg) == True) assert (self.__search_file('/tmp/test_Logger.txt', msg) == True) msg = "Test 2 " + getTimestamp() log.debug(msg) assert (self.__search_file('/var/log/syslog', msg) != True) assert (self.__search_file('/tmp/test_Logger.txt', msg) != True) log.setLevel("error") msg = "Test 3 " + getTimestamp() log.debug(msg) assert (self.__search_file('/var/log/syslog', msg) != True) assert (self.__search_file('/tmp/test_Logger.txt', msg) != True) log.setLevel("critical") msg = "Test 4 " + getTimestamp() log.info(msg) assert (self.__search_file('/var/log/syslog', msg) != True) assert (self.__search_file('/tmp/test_Logger.txt', msg) != True) log.setLevel("warning") msg = "Test 5 " + getTimestamp() log.warning(msg) assert (self.__search_file('/var/log/syslog', msg) == True) assert (self.__search_file('/tmp/test_Logger.txt', msg) == True) msg = "Test 6 " + getTimestamp() log.error(msg) assert (self.__search_file('/var/log/syslog', msg) == True) assert (self.__search_file('/tmp/test_Logger.txt', msg) == True) log.setLevel("critical") msg = "Test 7 " + getTimestamp() log.debug(msg) assert (self.__search_file('/var/log/syslog', msg) != True) assert (self.__search_file('/tmp/test_Logger.txt', msg) != True) msg = "Test 8 " + getTimestamp() log.info(msg) assert (self.__search_file('/var/log/syslog', msg) != True) assert (self.__search_file('/tmp/test_Logger.txt', msg) != True) msg = "Test 9 " + getTimestamp() log.warning(msg) assert (self.__search_file('/var/log/syslog', msg) != True) assert (self.__search_file('/tmp/test_Logger.txt', msg) != True) msg = "Test 10 " + getTimestamp() log.error(msg) assert (self.__search_file('/var/log/syslog', msg) != True) assert (self.__search_file('/tmp/test_Logger.txt', msg) != True) msg = "Test 11 " + getTimestamp() log.critical(msg) assert (self.__search_file('/var/log/syslog', msg) == True) assert (self.__search_file('/tmp/test_Logger.txt', msg) == True)
def executeLoop(self, test_mode=False): '''! ZTP service loop which peforms provisioning data discovery and initiates processing. ''' updateActivity('Initializing') # Set testing mode self.test_mode = test_mode # Check if ZTP is disabled administratively, bail out if disabled if getCfg('admin-mode') is False: logger.info('ZTP is administratively disabled.') self.__removeZTPProfile() return # Check if ZTP data restart flag is set if os.path.isfile(getCfg('ztp-restart-flag')): self.__ztp_restart = True os.remove(getCfg('ztp-restart-flag')) if self.test_mode: logger.warning( 'ZTP service started in test mode with restricted functionality.' ) else: logger.info('ZTP service started.') self.__ztp_engine_start_time = getTimestamp() _start_time = None self.ztp_mode = 'DISCOVERY' # Main provisioning data discovery loop while self.ztp_mode == 'DISCOVERY': updateActivity('Discovering provisioning data', overwrite=False) try: result = self.__discover() except Exception as e: logger.error( "Exception [%s] encountered while running the discovery logic." % (str(e))) _exc_type, _exc_value, _exc_traceback = sys.exc_info() __tb = traceback.extract_tb(_exc_traceback) for l in __tb: logger.debug(' File ' + l[0] + ', line ' + str(l[1]) + ', in ' + str(l[2])) logger.debug(' ' + str(l[3])) self.__forceRestartDiscovery( "Invalid provisioning data received") continue if result: if self.ztp_mode == 'MANUAL_CONFIG': logger.info( "Configuration file '%s' detected. Shutting down ZTP service." % (getCfg('config-db-json'))) break elif self.ztp_mode != 'DISCOVERY': (rv, msg) = self.__processZTPJson() if rv == "retry": self.ztp_mode = 'DISCOVERY' elif rv == "restart": self.__forceRestartDiscovery(msg) else: break # Initialize in-band interfaces to establish connectivity if not done already self.__loadZTPProfile("discovery") logger.debug('Provisioning data not found.') # Scan for inband interfaces to link up and restart interface connectivity if self.__link_scan(): updateActivity('Restarting network discovery after link scan') logger.info('Restarting network discovery after link scan.') runCommand('systemctl restart interfaces-config', capture_stdout=False) logger.info('Restarted network discovery after link scan.') _start_time = time.time() continue # Start keeping time of last time restart networking was done if _start_time is None: _start_time = time.time() # Check if we have to restart networking if (time.time() - _start_time > getCfg('restart-ztp-interval')): updateActivity('Restarting network discovery') if self.test_mode is False: # Remove existing leases to source new provisioning data self.__cleanup_dhcp_leases() logger.info('Restarting network discovery.') runCommand('systemctl restart interfaces-config', capture_stdout=False) logger.info('Restarted network discovery.') _start_time = time.time() continue # Try after sometime time.sleep(getCfg('discovery-interval')) # Cleanup installed ZTP configuration profile self.__removeZTPProfile() if self.reboot_on_completion and self.test_mode == False: updateActivity('System reboot requested') systemReboot() updateActivity('Exiting ZTP server')