diff --git a/node-daemon/pvcd.sample.yaml b/node-daemon/pvcd.sample.yaml index c143b090..66469105 100644 --- a/node-daemon/pvcd.sample.yaml +++ b/node-daemon/pvcd.sample.yaml @@ -122,6 +122,10 @@ pvc: file_logging: True # stdout_logging: Enable or disable logging to stdout (i.e. journald) stdout_logging: True + # log_colours: Enable or disable ANSI colours in log output + log_colours: True + # log_dates: Enable or disable date strings in log output + log_dates: True # log_keepalives: Enable or disable keepalive logging log_keepalives: True # log_keepalive_cluster_details: Enable or disable node status logging during keepalive diff --git a/node-daemon/pvcd/Daemon.py b/node-daemon/pvcd/Daemon.py index 5354f4ba..55419118 100644 --- a/node-daemon/pvcd/Daemon.py +++ b/node-daemon/pvcd/Daemon.py @@ -143,6 +143,8 @@ def readConfig(pvcd_config_file, myhostname): 'console_log_directory': o_config['pvc']['system']['configuration']['directories']['console_log_directory'], 'file_logging': o_config['pvc']['system']['configuration']['logging']['file_logging'], 'stdout_logging': o_config['pvc']['system']['configuration']['logging']['stdout_logging'], + 'log_colours': o_config['pvc']['system']['configuration']['logging']['log_colours'], + 'log_dates': o_config['pvc']['system']['configuration']['logging']['log_dates'], 'log_keepalives': o_config['pvc']['system']['configuration']['logging']['log_keepalives'], 'log_keepalive_cluster_details': o_config['pvc']['system']['configuration']['logging']['log_keepalive_cluster_details'], 'log_keepalive_storage_details': o_config['pvc']['system']['configuration']['logging']['log_keepalive_storage_details'], @@ -323,6 +325,24 @@ logger.out(' OS: {}'.format(staticdata[2])) logger.out(' Kernel: {}'.format(staticdata[1])) logger.out('Starting pvcd on host {}'.format(myfqdn), state='s') +# Define some colours for future messages if applicable +if config['log_colours']: + fmt_end = logger.fmt_end + fmt_bold = logger.fmt_bold + fmt_blue = logger.fmt_blue + fmt_green = logger.fmt_green + fmt_yellow = logger.fmt_yellow + fmt_red = logger.fmt_red + fmt_purple = logger.fmt_purple +else: + fmt_end = '' + fmt_bold = '' + fmt_blue = '' + fmt_green = '' + fmt_yellow = '' + fmt_red = '' + fmt_purple = '' + ############################################################################### # PHASE 2a - Create local IP addresses for static networks ############################################################################### @@ -405,7 +425,7 @@ if myhostname in coordinator_nodes: # We are indeed a coordinator host config['daemon_mode'] = 'coordinator' # Start the zookeeper service using systemctl - logger.out('Node is a ' + logger.fmt_blue + 'coordinator' + logger.fmt_end, state='i') + logger.out('Node is a ' + fmt_blue + 'coordinator' + fmt_end, state='i') else: config['daemon_mode'] = 'hypervisor' @@ -558,7 +578,7 @@ signal.signal(signal.SIGQUIT, term) # Check if our node exists in Zookeeper, and create it if not if zk_conn.exists('/nodes/{}'.format(myhostname)): - logger.out("Node is " + logger.fmt_green + "present" + logger.fmt_end + " in Zookeeper", state='i') + logger.out("Node is " + fmt_green + "present" + fmt_end + " in Zookeeper", state='i') # Update static data just in case it's changed zkhandler.writedata(zk_conn, { '/nodes/{}/daemonstate'.format(myhostname): 'init', @@ -569,7 +589,7 @@ if zk_conn.exists('/nodes/{}'.format(myhostname)): '/nodes/{}/ipmipassword'.format(myhostname): config['ipmi_password'] }) else: - logger.out("Node is " + logger.fmt_red + "absent" + logger.fmt_end + " in Zookeeper; adding new node", state='i') + logger.out("Node is " + fmt_red + "absent" + fmt_end + " in Zookeeper; adding new node", state='i') keepalive_time = int(time.time()) zkhandler.writedata(zk_conn, { '/nodes/{}'.format(myhostname): config['daemon_mode'], @@ -602,7 +622,7 @@ except kazoo.exceptions.NoNodeError: current_primary = 'none' if current_primary and current_primary != 'none': - logger.out('Current primary node is {}{}{}.'.format(logger.fmt_blue, current_primary, logger.fmt_end), state='i') + logger.out('Current primary node is {}{}{}.'.format(fmt_blue, current_primary, fmt_end), state='i') else: if config['daemon_mode'] == 'coordinator': logger.out('No primary node found; creating with us as primary.', state='i') @@ -719,7 +739,7 @@ def update_nodes(new_node_list): # Update and print new list node_list = new_node_list - logger.out('{}Node list:{} {}'.format(logger.fmt_blue, logger.fmt_end, ' '.join(node_list)), state='i') + logger.out('{}Node list:{} {}'.format(fmt_blue, fmt_end, ' '.join(node_list)), state='i') # Update node objects' list for node in d_node: @@ -787,7 +807,7 @@ if enable_networking: # Update and print new list network_list = new_network_list - logger.out('{}Network list:{} {}'.format(logger.fmt_blue, logger.fmt_end, ' '.join(network_list)), state='i') + logger.out('{}Network list:{} {}'.format(fmt_blue, fmt_end, ' '.join(network_list)), state='i') # Update node objects' list for node in d_node: @@ -812,7 +832,7 @@ if enable_hypervisor: # Update and print new list domain_list = new_domain_list - logger.out('{}Domain list:{} {}'.format(logger.fmt_blue, logger.fmt_end, ' '.join(domain_list)), state='i') + logger.out('{}Domain list:{} {}'.format(fmt_blue, fmt_end, ' '.join(domain_list)), state='i') # Update node objects' list for node in d_node: @@ -843,7 +863,7 @@ if enable_storage: # Update and print new list osd_list = new_osd_list - logger.out('{}OSD list:{} {}'.format(logger.fmt_blue, logger.fmt_end, ' '.join(osd_list)), state='i') + logger.out('{}OSD list:{} {}'.format(fmt_blue, fmt_end, ' '.join(osd_list)), state='i') # Pool objects @zk_conn.ChildrenWatch('/ceph/pools') @@ -865,7 +885,7 @@ if enable_storage: # Update and print new list pool_list = new_pool_list - logger.out('{}Pool list:{} {}'.format(logger.fmt_blue, logger.fmt_end, ' '.join(pool_list)), state='i') + logger.out('{}Pool list:{} {}'.format(fmt_blue, fmt_end, ' '.join(pool_list)), state='i') # Volume objects in each pool for pool in pool_list: @@ -886,7 +906,7 @@ if enable_storage: # Update and print new list volume_list[pool] = new_volume_list - logger.out('{}Volume list [{pool}]:{} {plist}'.format(logger.fmt_blue, logger.fmt_end, pool=pool, plist=' '.join(volume_list[pool])), state='i') + logger.out('{}Volume list [{pool}]:{} {plist}'.format(fmt_blue, fmt_end, pool=pool, plist=' '.join(volume_list[pool])), state='i') ############################################################################### # PHASE 9 - Run the daemon @@ -918,11 +938,11 @@ def update_zookeeper(): retcode, stdout, stderr = common.run_os_command('ceph health', timeout=1) ceph_health = stdout.rstrip() if 'HEALTH_OK' in ceph_health: - ceph_health_colour = logger.fmt_green + ceph_health_colour = fmt_green elif 'HEALTH_WARN' in ceph_health: - ceph_health_colour = logger.fmt_yellow + ceph_health_colour = fmt_yellow else: - ceph_health_colour = logger.fmt_red + ceph_health_colour = fmt_red # Set ceph health information in zookeeper (primary only) if this_node.router_state == 'primary': @@ -1218,9 +1238,9 @@ def update_zookeeper(): if config['log_keepalives']: logger.out( '{}{} keepalive{}'.format( - logger.fmt_purple, + fmt_purple, myhostname, - logger.fmt_end + fmt_end ), state='t' ) @@ -1232,8 +1252,8 @@ def update_zookeeper(): '{bold}Free memory [MiB]:{nofmt} {freemem} ' '{bold}Used memory [MiB]:{nofmt} {usedmem} ' '{bold}Load:{nofmt} {load}'.format( - bold=logger.fmt_bold, - nofmt=logger.fmt_end, + bold=fmt_bold, + nofmt=fmt_end, domcount=this_node.domains_count, freemem=this_node.memfree, usedmem=this_node.memused, @@ -1248,9 +1268,9 @@ def update_zookeeper(): '{bold}Total OSDs:{nofmt} {total_osds} ' '{bold}Node OSDs:{nofmt} {node_osds} ' '{bold}Pools:{nofmt} {total_pools} '.format( - bold=logger.fmt_bold, + bold=fmt_bold, health_colour=ceph_health_colour, - nofmt=logger.fmt_end, + nofmt=fmt_end, health=ceph_health, total_osds=len(osd_list), node_osds=osds_this_node, diff --git a/node-daemon/pvcd/log.py b/node-daemon/pvcd/log.py index 9e575deb..c8bc242e 100644 --- a/node-daemon/pvcd/log.py +++ b/node-daemon/pvcd/log.py @@ -37,16 +37,42 @@ class Logger(object): fmt_bold = '\033[1m' fmt_end = '\033[0m' + last_colour = '' + last_prompt = '' + + # Format maps + format_map_colourized = { + # Colourized formatting with chevron prompts (log_colours = True) + 'o': { 'colour': fmt_green, 'prompt': '>>> ' }, + 'e': { 'colour': fmt_red, 'prompt': '>>> ' }, + 'w': { 'colour': fmt_yellow, 'prompt': '>>> ' }, + 't': { 'colour': fmt_purple, 'prompt': '>>> ' }, + 'i': { 'colour': fmt_blue, 'prompt': '>>> ' }, + 's': { 'colour': fmt_cyan, 'prompt': '>>> ' }, + 'x': { 'colour': last_colour, 'prompt': last_prompt } + } + format_map_textual = { + # Uncolourized formatting with text prompts (log_colours = False) + 'o': { 'colour': '', 'prompt': 'ok: ' }, + 'e': { 'colour': '', 'prompt': 'failed: ' }, + 'w': { 'colour': '', 'prompt': 'warning: ' }, + 't': { 'colour': '', 'prompt': 'tick: ' }, + 'i': { 'colour': '', 'prompt': 'info: ' }, + 's': { 'colour': '', 'prompt': 'system: ' }, + 'x': { 'colour': '', 'prompt': last_prompt } + } + # Initialization of instance def __init__(self, config): self.config = config + if self.config['file_logging'] == 'True': self.logfile = self.config['log_directory'] + '/pvc.log' # We open the logfile for the duration of our session, but have a hup function self.writer = open(self.logfile, 'a', buffering=1) - self.last_colour = self.fmt_cyan - else: - self.last_colour = "" + + self.last_colour = '' + self.last_prompt = '' # Provide a hup function to close and reopen the writer def hup(self): @@ -54,49 +80,46 @@ class Logger(object): self.writer = open(self.logfile, 'a', buffering=0) # Output function - def out(self, message, state='', prefix=''): + def out(self, message, state=None, prefix=''): # Get the date - date = '{} - '.format(datetime.datetime.now().strftime('%Y/%m/%d %H:%M:%S.%f')) - endc = Logger.fmt_end - - # Determine the formatting - # OK - if state == 'o': - colour = Logger.fmt_green - prompt = '>>> ' - # Error - elif state == 'e': - colour = Logger.fmt_red - prompt = '>>> ' - # Warning - elif state == 'w': - colour = Logger.fmt_yellow - prompt = '>>> ' - # Tick - elif state == 't': - colour = Logger.fmt_purple - prompt = '>>> ' - # Information - elif state == 'i': - colour = Logger.fmt_blue - prompt = '>>> ' - # Startup - elif state == 's': - colour = Logger.fmt_cyan - prompt = '>>> ' - # Continuation + if self.config['log_dates']: + date = '{} - '.format(datetime.datetime.now().strftime('%Y/%m/%d %H:%M:%S.%f')) else: date = '' - colour = self.last_colour - prompt = '>>> ' - # Append space to prefix + # Get the format map + if self.config['log_colours']: + format_map = self.format_map_colourized + endc = Logger.fmt_end + else: + format_map = self.format_map_textual + endc = '' + + # Define an undefined state as 'x'; no date in these prompts + if not state: + state = 'x' + date = '' + + # Get colour and prompt from the map + colour = format_map[state]['colour'] + prompt = format_map[state]['prompt'] + + # Append space and separator to prefix if prefix != '': prefix = prefix + ' - ' + # Assemble message string message = colour + prompt + endc + date + prefix + message - print(message) - if self.config['file_logging'] == 'True': + + # Log to stdout + if self.config['stdout_logging']: + print(message) + + # Log to file + if self.config['file_logging']: self.writer.write(message + '\n') + + # Set last message variables self.last_colour = colour + self.last_prompt = prompt