def __init__(self, configs, hosts, services, contacts, hostgroups, servicegroups, contactgroups, timeperiods, commands, schedulers, pollers, reactionners, brokers, db, use_aggressive_sql, pnp_path, return_queue, counters): # Runtime data form the global LiveStatus object self.configs = configs self.hosts = hosts self.services = services self.contacts = contacts self.hostgroups = hostgroups self.servicegroups = servicegroups self.contactgroups = contactgroups self.timeperiods = timeperiods self.commands = commands self.schedulers = schedulers self.pollers = pollers self.reactionners = reactionners self.brokers = brokers self.db = db self.use_aggressive_sql = use_aggressive_sql self.pnp_path = pnp_path self.return_queue = return_queue self.counters = counters # Private attributes for this specific request self.response = LiveStatusResponse(responseheader = 'off', outputformat = 'csv', keepalive = 'off', columnheaders = 'undef', separators = LiveStatusResponse.separators) self.table = None self.columns = [] self.filtercolumns = [] self.prefiltercolumns = [] self.stats_group_by = [] self.stats_columns = [] self.aliases = [] self.limit = None self.extcmd = False self.out_map = self.copy_out_map_hooks() # Initialize the stacks which are needed for the Filter: and Stats: # filter- and count-operations self.filter_stack = LiveStatusStack() # This stack is used to create a full-blown select-statement self.sql_filter_stack = LiveStatusStack() self.sql_filter_stack.type = 'sql' # This stack is used to create a minimal select-statement which # selects only by time >= and time <= self.sql_time_filter_stack = LiveStatusStack() self.sql_time_filter_stack.type = 'sql' self.stats_filter_stack = LiveStatusStack() self.stats_postprocess_stack = LiveStatusStack() self.stats_request = False # When was this query launched? self.tic = time.time() # Clients can also send their local time with the request self.client_localtime = self.tic
def __init__(self, datamgr, query_cache, db, pnp_path, return_queue, counters): # Runtime data form the global LiveStatus object self.datamgr = datamgr self.query_cache = query_cache self.db = db self.pnp_path = pnp_path self.return_queue = return_queue self.counters = counters # Private attributes for this specific request self.response = LiveStatusResponse() self.raw_data = '' self.authuser = None self.table = None self.columns = [] self.filtercolumns = [] self.prefiltercolumns = [] self.outputcolumns = [] self.stats_group_by = [] self.stats_columns = [] self.aliases = [] self.limit = None self.extcmd = False # Initialize the stacks which are needed for the Filter: and Stats: # filter- and count-operations self.filter_stack = LiveStatusStack() self.stats_filter_stack = LiveStatusStack() self.stats_postprocess_stack = LiveStatusStack() self.stats_query = False # When was this query launched? self.tic = time.time() # Clients can also send their local time with the request self.client_localtime = self.tic # This is mostly used in the Response.format... which needs to know # the class behind a queries table self.table_class_map = table_class_map
def __init__(self, datamgr, query_cache, db, pnp_path, return_queue, counters): # Runtime data form the global LiveStatus object self.datamgr = datamgr self.query_cache = query_cache self.db = db self.pnp_path = pnp_path self.return_queue = return_queue self.counters = counters # Private attributes for this specific request self.response = LiveStatusResponse() self.authuser = None self.table = None self.columns = [] self.filtercolumns = [] self.prefiltercolumns = [] self.outputcolumns = [] self.stats_group_by = [] self.stats_columns = [] self.aliases = [] self.limit = None self.extcmd = False # Initialize the stacks which are needed for the Filter: and Stats: # filter- and count-operations self.filter_stack = LiveStatusStack() self.stats_filter_stack = LiveStatusStack() self.stats_postprocess_stack = LiveStatusStack() self.stats_query = False # When was this query launched? self.tic = time.time() # Clients can also send their local time with the request self.client_localtime = self.tic # This is mostly used in the Response.format... which needs to know # the class behind a queries table self.table_class_map = table_class_map
class LiveStatusQuery(object): my_type = 'query' def __init__(self, datamgr, query_cache, db, pnp_path, return_queue, counters): # Runtime data form the global LiveStatus object self.datamgr = datamgr self.query_cache = query_cache self.db = db self.pnp_path = pnp_path self.return_queue = return_queue self.counters = counters # Private attributes for this specific request self.response = LiveStatusResponse() self.authuser = None self.table = None self.columns = [] self.filtercolumns = [] self.prefiltercolumns = [] self.outputcolumns = [] self.stats_group_by = [] self.stats_columns = [] self.aliases = [] self.limit = None self.extcmd = False # Initialize the stacks which are needed for the Filter: and Stats: # filter- and count-operations self.filter_stack = LiveStatusStack() self.stats_filter_stack = LiveStatusStack() self.stats_postprocess_stack = LiveStatusStack() self.stats_query = False # When was this query launched? self.tic = time.time() # Clients can also send their local time with the request self.client_localtime = self.tic # This is mostly used in the Response.format... which needs to know # the class behind a queries table self.table_class_map = table_class_map def __str__(self): output = "LiveStatusRequest:\n" for attr in ["table", "columns", "filtercolumns", "prefiltercolumns", "aliases", "stats_group_by", "stats_query"]: output += "request %s: %s\n" % (attr, getattr(self, attr)) return output def split_command(self, line, splits=1): """Create a list from the words of a line""" return line.split(' ', splits) def split_option(self, line, splits=1): """Like split_commands, but converts numbers to int data type""" x = map(lambda i: (i.isdigit() and int(i)) or i, [token.strip() for token in re.split(r"[\s]+", line, splits)]) return x def split_option_with_columns(self, line): """Split a line in a command and a list of words""" cmd, columns = self.split_option(line) return cmd, [self.strip_table_from_column(c) for c in re.compile(r'\s+').split(columns)] def strip_table_from_column(self, column): """Cut off the table name, because it is possible to say service_state instead of state""" bygroupmatch = re.compile('(\w+)by.*group').search(self.table) if bygroupmatch: return re.sub(re.sub('s$', '', bygroupmatch.group(1)) + '_', '', column, 1) else: return re.sub(re.sub('s$', '', self.table) + '_', '', column, 1) def parse_input(self, data): """Parse the lines of a livestatus request. This function looks for keywords in input lines and sets the attributes of the request object """ for line in data.splitlines(): line = line.strip() # Tools like NagVis send KEYWORK:option, and we prefer to have # a space following the: if ':' in line and not ' ' in line: line = line.replace(':', ': ') keyword = line.split(' ')[0].rstrip(':') if keyword == 'GET': # Get the name of the base table _, self.table = self.split_command(line) if self.table not in table_class_map.keys(): raise LiveStatusQueryError(404, self.table) elif keyword == 'Columns': # Get the names of the desired columns _, self.columns = self.split_option_with_columns(line) self.response.columnheaders = 'off' elif keyword == 'ResponseHeader': _, responseheader = self.split_option(line) self.response.responseheader = responseheader elif keyword == 'OutputFormat': _, outputformat = self.split_option(line) self.response.outputformat = outputformat elif keyword == 'KeepAlive': _, keepalive = self.split_option(line) self.response.keepalive = keepalive elif keyword == 'ColumnHeaders': _, columnheaders = self.split_option(line) self.response.columnheaders = columnheaders elif keyword == 'Limit': _, self.limit = self.split_option(line) elif keyword == 'AuthUser': if self.table in ['hosts', 'hostgroups', 'services', 'servicegroups', 'hostsbygroup', 'servicesbygroup', 'servicesbyhostgroup']: _, self.authuser = self.split_option(line) # else self.authuser stays None and will be ignored elif keyword == 'Filter': try: _, attribute, operator, reference = self.split_option(line, 3) except: _, attribute, operator, reference = self.split_option(line, 2) + [''] if operator in ['=', '>', '>=', '<', '<=', '=~', '~', '~~', '!=', '!>', '!>=', '!<', '!<=']: # Cut off the table name attribute = self.strip_table_from_column(attribute) # Some operators can simply be negated if operator in ['!>', '!>=', '!<', '!<=']: operator = {'!>': '<=', '!>=': '<', '!<': '>=', '!<=': '>'}[operator] # Put a function on top of the filter_stack which implements # the desired operation self.filtercolumns.append(attribute) self.prefiltercolumns.append(attribute) self.filter_stack.put_stack(self.make_filter(operator, attribute, reference)) if self.table == 'log': self.db.add_filter(operator, attribute, reference) else: print "illegal operation", operator pass # illegal operation elif keyword == 'And': _, andnum = self.split_option(line) # Take the last andnum functions from the stack # Construct a new function which makes a logical and # Put the function back onto the stack self.filter_stack.and_elements(andnum) if self.table == 'log': self.db.add_filter_and(andnum) elif keyword == 'Or': _, ornum = self.split_option(line) # Take the last ornum functions from the stack # Construct a new function which makes a logical or # Put the function back onto the stack self.filter_stack.or_elements(ornum) if self.table == 'log': self.db.add_filter_or(ornum) elif keyword == 'Negate': self.filter_stack.not_elements() if self.table == 'log': self.db.add_filter_not() elif keyword == 'StatsGroupBy': _, stats_group_by = self.split_option_with_columns(line) self.filtercolumns.extend(stats_group_by) self.stats_group_by.extend(stats_group_by) # Deprecated. If your query contains at least one Stats:-header # then Columns: has the meaning of the old StatsGroupBy: header elif keyword == 'Stats': self.stats_query = True try: _, attribute, operator, reference = self.split_option(line, 3) if attribute in ['sum', 'min', 'max', 'avg', 'std'] and reference.startswith('as '): attribute, operator = operator, attribute _, alias = reference.split(' ') self.aliases.append(alias) elif attribute in ['sum', 'min', 'max', 'avg', 'std'] and reference == '=': # Workaround for thruk-cmds like: Stats: sum latency = attribute, operator = operator, attribute reference = '' except: _, attribute, operator = self.split_option(line, 3) if attribute in ['sum', 'min', 'max', 'avg', 'std']: attribute, operator = operator, attribute reference = '' attribute = self.strip_table_from_column(attribute) if operator in ['=', '>', '>=', '<', '<=', '=~', '~', '~~', '!=', '!>', '!>=', '!<', '!<=']: if operator in ['!>', '!>=', '!<', '!<=']: operator = {'!>': '<=', '!>=': '<', '!<': '>=', '!<=': '>'}[operator] self.filtercolumns.append(attribute) self.stats_columns.append(attribute) self.stats_filter_stack.put_stack(self.make_filter(operator, attribute, reference)) self.stats_postprocess_stack.put_stack(self.make_filter('count', attribute, None)) elif operator in ['sum', 'min', 'max', 'avg', 'std']: self.stats_columns.append(attribute) self.stats_filter_stack.put_stack(self.make_filter('dummy', attribute, None)) self.stats_postprocess_stack.put_stack(self.make_filter(operator, attribute, None)) else: print "illegal operation", operator pass # illegal operation elif keyword == 'StatsAnd': _, andnum = self.split_option(line) self.stats_filter_stack.and_elements(andnum) elif keyword == 'StatsOr': _, ornum = self.split_option(line) self.stats_filter_stack.or_elements(ornum) elif keyword == 'Separators': _, sep1, sep2, sep3, sep4 = line.split(' ', 5) self.response.separators = map(lambda x: chr(int(x)), [sep1, sep2, sep3, sep4]) elif keyword == 'Localtime': _, self.client_localtime = self.split_option(line) elif keyword == 'COMMAND': _, self.extcmd = line.split(' ', 1) else: # This line is not valid or not implemented print "Received a line of input which i can't handle: '%s'" % line pass self.metainfo = LiveStatusQueryMetainfo(data) def launch_query(self): """ Prepare the request object's filter stacks """ # The Response object needs to access the Query self.response.load(self) # A minimal integrity check if not self.table: return [] # Ask the cache if this request was already answered under the same # circumstances. (And f not, whether this query is cacheable at all) cacheable, cache_hit, cached_response = self.query_cache.get_cached_query(self.metainfo) if cache_hit: self.columns = cached_response['columns'] self.response.columnheaders = cached_response['columnheaders'] return cached_response['result'] # Make columns unique self.filtercolumns = list(set(self.filtercolumns)) self.prefiltercolumns = list(set(self.prefiltercolumns)) self.stats_columns = list(set(self.stats_columns)) if self.stats_query: if len(self.columns) > 0: # StatsGroupBy is deprecated. Columns: can be used instead self.stats_group_by = self.columns elif len(self.stats_group_by) > 0: self.columns = self.stats_group_by + self.stats_columns #if len(self.stats_columns) > 0 and len(self.columns) == 0: if len(self.stats_columns) > 0: self.columns = self.stats_columns + self.columns else: if len(self.columns) == 0: self.outputcolumns = list_livestatus_attributes(self.table) else: self.outputcolumns = self.columns # Make one big filter where the single filters are anded self.filter_stack.and_elements(self.filter_stack.qsize()) # Get the function which implements the Filter: statements filter_func = self.filter_stack.get_stack() without_filter = len(self.filtercolumns) == 0 cs = LiveStatusConstraints(filter_func, without_filter, self.authuser) try: # Remember the number of stats filters. We need these numbers as columns later. # But we need to ask now, because get_live_data() will empty the stack num_stats_filters = self.stats_filter_stack.qsize() if self.table == 'log': result = self.get_live_data_log(cs) else: # If the pnpgraph_present column is involved, then check # with each request if the pnp perfdata path exists if 'pnpgraph_present' in self.columns + self.filtercolumns + self.prefiltercolumns and self.pnp_path and os.access(self.pnp_path, os.R_OK): self.pnp_path_readable = True else: self.pnp_path_readable = False # Apply the filters on the broker's host/service/etc elements result = self.get_live_data(cs) if self.stats_query: self.columns = range(num_stats_filters) if self.stats_group_by: self.columns = tuple(list(self.stats_group_by) + list(self.columns)) if len(self.aliases) == 0: # If there were Stats: staments without "as", show no column headers at all self.response.columnheaders = 'off' else: self.response.columnheaders = 'on' if self.stats_query: result = self.statsify_result(result) # statsify_result returns a dict with column numers as keys elif self.table == 'columns': # With stats_request set to True, format_output expects result # to be a list of dicts instead a list of objects self.stats_query = True except Exception, e: import traceback print "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" print e traceback.print_exc(32) print "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" result = [] if cacheable and not cache_hit: # We cannot cache generators, so we must first read them into a list result = [r for r in result] # Especially for stats requests also the columns and headers # are modified, so we need to save them too. self.query_cache.cache_query(self.metainfo, { 'result': result, 'columns': self.columns, 'columnheaders': self.response.columnheaders, }) return result
class LiveStatusQuery(object): my_type = 'query' def __init__(self, datamgr, query_cache, db, pnp_path, return_queue, counters): # Runtime data form the global LiveStatus object self.datamgr = datamgr self.query_cache = query_cache self.db = db self.pnp_path = pnp_path self.return_queue = return_queue self.counters = counters # Private attributes for this specific request self.response = LiveStatusResponse() self.authuser = None self.table = None self.columns = [] self.filtercolumns = [] self.prefiltercolumns = [] self.outputcolumns = [] self.stats_group_by = [] self.stats_columns = [] self.aliases = [] self.limit = None self.extcmd = False # Initialize the stacks which are needed for the Filter: and Stats: # filter- and count-operations self.filter_stack = LiveStatusStack() self.stats_filter_stack = LiveStatusStack() self.stats_postprocess_stack = LiveStatusStack() self.stats_query = False # When was this query launched? self.tic = time.time() # Clients can also send their local time with the request self.client_localtime = self.tic # This is mostly used in the Response.format... which needs to know # the class behind a queries table self.table_class_map = table_class_map def __str__(self): output = "LiveStatusRequest:\n" for attr in [ "table", "columns", "filtercolumns", "prefiltercolumns", "aliases", "stats_group_by", "stats_query" ]: output += "request %s: %s\n" % (attr, getattr(self, attr)) return output def split_command(self, line, splits=1): """Create a list from the words of a line""" return line.split(' ', splits) def split_option(self, line, splits=1): """Like split_commands, but converts numbers to int data type""" x = map(lambda i: (i.isdigit() and int(i)) or i, [token.strip() for token in re.split(r"[\s]+", line, splits)]) return x def split_option_with_columns(self, line): """Split a line in a command and a list of words""" cmd, columns = self.split_option(line) return cmd, [ self.strip_table_from_column(c) for c in re.compile(r'\s+').split(columns) ] def strip_table_from_column(self, column): """Cut off the table name, because it is possible to say service_state instead of state""" bygroupmatch = re.compile('(\w+)by.*group').search(self.table) if bygroupmatch: return re.sub( re.sub('s$', '', bygroupmatch.group(1)) + '_', '', column, 1) else: return re.sub(re.sub('s$', '', self.table) + '_', '', column, 1) def parse_input(self, data): """Parse the lines of a livestatus request. This function looks for keywords in input lines and sets the attributes of the request object """ for line in data.splitlines(): line = line.strip() # Tools like NagVis send KEYWORK:option, and we prefer to have # a space following the: if ':' in line and not ' ' in line: line = line.replace(':', ': ') keyword = line.split(' ')[0].rstrip(':') if keyword == 'GET': # Get the name of the base table _, self.table = self.split_command(line) if self.table not in table_class_map.keys(): raise LiveStatusQueryError(404, self.table) elif keyword == 'Columns': # Get the names of the desired columns _, self.columns = self.split_option_with_columns(line) self.response.columnheaders = 'off' elif keyword == 'ResponseHeader': _, responseheader = self.split_option(line) self.response.responseheader = responseheader elif keyword == 'OutputFormat': _, outputformat = self.split_option(line) self.response.outputformat = outputformat elif keyword == 'KeepAlive': _, keepalive = self.split_option(line) self.response.keepalive = keepalive elif keyword == 'ColumnHeaders': _, columnheaders = self.split_option(line) self.response.columnheaders = columnheaders elif keyword == 'Limit': _, self.limit = self.split_option(line) elif keyword == 'AuthUser': if self.table in [ 'hosts', 'hostgroups', 'services', 'servicegroups', 'hostsbygroup', 'servicesbygroup', 'servicesbyhostgroup' ]: _, self.authuser = self.split_option(line) # else self.authuser stays None and will be ignored elif keyword == 'Filter': try: _, attribute, operator, reference = self.split_option( line, 3) except: _, attribute, operator, reference = self.split_option( line, 2) + [''] if operator in [ '=', '>', '>=', '<', '<=', '=~', '~', '~~', '!=', '!>', '!>=', '!<', '!<=' ]: # Cut off the table name attribute = self.strip_table_from_column(attribute) # Some operators can simply be negated if operator in ['!>', '!>=', '!<', '!<=']: operator = { '!>': '<=', '!>=': '<', '!<': '>=', '!<=': '>' }[operator] # Put a function on top of the filter_stack which implements # the desired operation self.filtercolumns.append(attribute) self.prefiltercolumns.append(attribute) self.filter_stack.put_stack( self.make_filter(operator, attribute, reference)) if self.table == 'log': self.db.add_filter(operator, attribute, reference) else: print "illegal operation", operator pass # illegal operation elif keyword == 'And': _, andnum = self.split_option(line) # Take the last andnum functions from the stack # Construct a new function which makes a logical and # Put the function back onto the stack self.filter_stack.and_elements(andnum) if self.table == 'log': self.db.add_filter_and(andnum) elif keyword == 'Or': _, ornum = self.split_option(line) # Take the last ornum functions from the stack # Construct a new function which makes a logical or # Put the function back onto the stack self.filter_stack.or_elements(ornum) if self.table == 'log': self.db.add_filter_or(ornum) elif keyword == 'Negate': self.filter_stack.not_elements() if self.table == 'log': self.db.add_filter_not() elif keyword == 'StatsGroupBy': _, stats_group_by = self.split_option_with_columns(line) self.filtercolumns.extend(stats_group_by) self.stats_group_by.extend(stats_group_by) # Deprecated. If your query contains at least one Stats:-header # then Columns: has the meaning of the old StatsGroupBy: header elif keyword == 'Stats': self.stats_query = True try: _, attribute, operator, reference = self.split_option( line, 3) if attribute in ['sum', 'min', 'max', 'avg', 'std' ] and reference.startswith('as '): attribute, operator = operator, attribute _, alias = reference.split(' ') self.aliases.append(alias) elif attribute in ['sum', 'min', 'max', 'avg', 'std' ] and reference == '=': # Workaround for thruk-cmds like: Stats: sum latency = attribute, operator = operator, attribute reference = '' except: _, attribute, operator = self.split_option(line, 3) if attribute in ['sum', 'min', 'max', 'avg', 'std']: attribute, operator = operator, attribute reference = '' attribute = self.strip_table_from_column(attribute) if operator in [ '=', '>', '>=', '<', '<=', '=~', '~', '~~', '!=', '!>', '!>=', '!<', '!<=' ]: if operator in ['!>', '!>=', '!<', '!<=']: operator = { '!>': '<=', '!>=': '<', '!<': '>=', '!<=': '>' }[operator] self.filtercolumns.append(attribute) self.stats_columns.append(attribute) self.stats_filter_stack.put_stack( self.make_filter(operator, attribute, reference)) self.stats_postprocess_stack.put_stack( self.make_filter('count', attribute, None)) elif operator in ['sum', 'min', 'max', 'avg', 'std']: self.stats_columns.append(attribute) self.stats_filter_stack.put_stack( self.make_filter('dummy', attribute, None)) self.stats_postprocess_stack.put_stack( self.make_filter(operator, attribute, None)) else: print "illegal operation", operator pass # illegal operation elif keyword == 'StatsAnd': _, andnum = self.split_option(line) self.stats_filter_stack.and_elements(andnum) elif keyword == 'StatsOr': _, ornum = self.split_option(line) self.stats_filter_stack.or_elements(ornum) elif keyword == 'Separators': _, sep1, sep2, sep3, sep4 = line.split(' ', 5) self.response.separators = map(lambda x: chr(int(x)), [sep1, sep2, sep3, sep4]) elif keyword == 'Localtime': _, self.client_localtime = self.split_option(line) elif keyword == 'COMMAND': _, self.extcmd = line.split(' ', 1) else: # This line is not valid or not implemented print "Received a line of input which i can't handle: '%s'" % line pass self.metainfo = LiveStatusQueryMetainfo(data) def launch_query(self): """ Prepare the request object's filter stacks """ # The Response object needs to access the Query self.response.load(self) # A minimal integrity check if not self.table: return [] # Ask the cache if this request was already answered under the same # circumstances. (And f not, whether this query is cacheable at all) cacheable, cache_hit, cached_response = self.query_cache.get_cached_query( self.metainfo) if cache_hit: self.columns = cached_response['columns'] self.response.columnheaders = cached_response['columnheaders'] return cached_response['result'] # Make columns unique self.filtercolumns = list(set(self.filtercolumns)) self.prefiltercolumns = list(set(self.prefiltercolumns)) self.stats_columns = list(set(self.stats_columns)) if self.stats_query: if len(self.columns) > 0: # StatsGroupBy is deprecated. Columns: can be used instead self.stats_group_by = self.columns elif len(self.stats_group_by) > 0: self.columns = self.stats_group_by + self.stats_columns #if len(self.stats_columns) > 0 and len(self.columns) == 0: if len(self.stats_columns) > 0: self.columns = self.stats_columns + self.columns else: if len(self.columns) == 0: self.outputcolumns = list_livestatus_attributes(self.table) else: self.outputcolumns = self.columns # Make one big filter where the single filters are anded self.filter_stack.and_elements(self.filter_stack.qsize()) # Get the function which implements the Filter: statements filter_func = self.filter_stack.get_stack() without_filter = len(self.filtercolumns) == 0 cs = LiveStatusConstraints(filter_func, without_filter, self.authuser) try: # Remember the number of stats filters. We need these numbers as columns later. # But we need to ask now, because get_live_data() will empty the stack num_stats_filters = self.stats_filter_stack.qsize() if self.table == 'log': result = self.get_live_data_log(cs) else: # If the pnpgraph_present column is involved, then check # with each request if the pnp perfdata path exists if 'pnpgraph_present' in self.columns + self.filtercolumns + self.prefiltercolumns and self.pnp_path and os.access( self.pnp_path, os.R_OK): self.pnp_path_readable = True else: self.pnp_path_readable = False # Apply the filters on the broker's host/service/etc elements result = self.get_live_data(cs) if self.stats_query: self.columns = range(num_stats_filters) if self.stats_group_by: self.columns = tuple( list(self.stats_group_by) + list(self.columns)) if len(self.aliases) == 0: # If there were Stats: staments without "as", show no column headers at all self.response.columnheaders = 'off' else: self.response.columnheaders = 'on' if self.stats_query: result = self.statsify_result(result) # statsify_result returns a dict with column numers as keys elif self.table == 'columns': # With stats_request set to True, format_output expects result # to be a list of dicts instead a list of objects self.stats_query = True except Exception, e: import traceback print "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" print e traceback.print_exc(32) print "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" result = [] if cacheable and not cache_hit: # We cannot cache generators, so we must first read them into a list result = [r for r in result] # Especially for stats requests also the columns and headers # are modified, so we need to save them too. self.query_cache.cache_query( self.metainfo, { 'result': result, 'columns': self.columns, 'columnheaders': self.response.columnheaders, }) return result
def __init__( self, configs, hosts, services, contacts, hostgroups, servicegroups, contactgroups, timeperiods, commands, schedulers, pollers, reactionners, brokers, dbconn, pnp_path, return_queue, counters, ): # Runtime data form the global LiveStatus object self.configs = configs self.hosts = hosts self.services = services self.contacts = contacts self.hostgroups = hostgroups self.servicegroups = servicegroups self.contactgroups = contactgroups self.timeperiods = timeperiods self.commands = commands self.schedulers = schedulers self.pollers = pollers self.reactionners = reactionners self.brokers = brokers self.dbconn = dbconn self.pnp_path = pnp_path self.return_queue = return_queue self.counters = counters # Private attributes for this specific request self.response = LiveStatusResponse( responseheader="off", outputformat="csv", keepalive="off", columnheaders="undef", separators=LiveStatusResponse.separators, ) self.table = None self.columns = [] self.filtercolumns = [] self.prefiltercolumns = [] self.stats_group_by = [] self.stats_columns = [] self.aliases = [] self.limit = None self.extcmd = False self.out_map = self.copy_out_map_hooks() # Initialize the stacks which are needed for the Filter: and Stats: # filter- and count-operations self.filter_stack = LiveStatusStack() self.sql_filter_stack = LiveStatusStack() self.sql_filter_stack.type = "sql" self.stats_filter_stack = LiveStatusStack() self.stats_postprocess_stack = LiveStatusStack() self.stats_request = False # When was this query launched? self.tic = time.time() # Clients can also send their local time with the request self.client_localtime = self.tic
class LiveStatusQuery(Hooker): my_type = "query" def __init__( self, configs, hosts, services, contacts, hostgroups, servicegroups, contactgroups, timeperiods, commands, schedulers, pollers, reactionners, brokers, dbconn, pnp_path, return_queue, counters, ): # Runtime data form the global LiveStatus object self.configs = configs self.hosts = hosts self.services = services self.contacts = contacts self.hostgroups = hostgroups self.servicegroups = servicegroups self.contactgroups = contactgroups self.timeperiods = timeperiods self.commands = commands self.schedulers = schedulers self.pollers = pollers self.reactionners = reactionners self.brokers = brokers self.dbconn = dbconn self.pnp_path = pnp_path self.return_queue = return_queue self.counters = counters # Private attributes for this specific request self.response = LiveStatusResponse( responseheader="off", outputformat="csv", keepalive="off", columnheaders="undef", separators=LiveStatusResponse.separators, ) self.table = None self.columns = [] self.filtercolumns = [] self.prefiltercolumns = [] self.stats_group_by = [] self.stats_columns = [] self.aliases = [] self.limit = None self.extcmd = False self.out_map = self.copy_out_map_hooks() # Initialize the stacks which are needed for the Filter: and Stats: # filter- and count-operations self.filter_stack = LiveStatusStack() self.sql_filter_stack = LiveStatusStack() self.sql_filter_stack.type = "sql" self.stats_filter_stack = LiveStatusStack() self.stats_postprocess_stack = LiveStatusStack() self.stats_request = False # When was this query launched? self.tic = time.time() # Clients can also send their local time with the request self.client_localtime = self.tic def find_converter(self, attribute): """Return a function that converts textual numbers in the request to the correct data type""" out_map = LSout_map[self.out_map_name] if attribute in out_map and "type" in out_map[attribute]: if out_map[attribute]["type"] == "int": return int elif out_map[attribute]["type"] == "float": return float return None def set_default_out_map_name(self): """Translate the table name to the corresponding out_map key.""" try: self.out_map_name = { "hosts": "Host", "services": "Service", "hostgroups": "Hostgroup", "servicegroups": "Servicegroup", "contacts": "Contact", "contactgroups": "Contactgroup", "comments": "Comment", "downtimes": "Downtime", "commands": "Command", "timeperiods": "Timeperiod", "hostsbygroup": "Hostsbygroup", "servicesbygroup": "Servicesbygroup", "servicesbyhostgroup": "Servicesbyhostgroup", "status": "Config", "log": "Logline", "schedulers": "SchedulerLink", "pollers": "PollerLink", "reactionners": "ReactionnerLink", "brokers": "BrokerLink", "problems": "Problem", "columns": "Config", # just a dummy }[self.table] except: self.out_map_name = "hosts" def copy_out_map_hooks(self): """Update the hooks for some out_map entries. Livestatus columns which have a fulldepythonize postprocessor need an updated argument list. The third argument needs to be the request object. (When the out_map is first supplied with hooks, the third argument is the Livestatus object.) """ new_map = {} for objtype in LSout_map: new_map[objtype] = {} for attribute in LSout_map[objtype]: new_map[objtype][attribute] = {} entry = LSout_map[objtype][attribute] if "hooktype" in entry: if "prop" not in entry or entry["prop"] is None: prop = attribute else: prop = entry["prop"] if "default" in entry: default = entry["default"] else: if entry["type"] == "int" or entry["type"] == "float": default = 0 else: default = "" func = entry["fulldepythonize"] new_map[objtype][attribute]["hook"] = self.make_hook( "get_prop_full_depythonize", prop, default, func, None ) else: new_map[objtype][attribute]["hook"] = entry["hook"] return new_map def __str__(self): output = "LiveStatusRequest:\n" for attr in [ "table", "columns", "filtercolumns", "prefiltercolumns", "aliases", "stats_group_by", "stats_request", ]: output += "request %s: %s\n" % (attr, getattr(self, attr)) return output def split_command(self, line, splits=1): """Create a list from the words of a line""" return line.split(" ", splits) def split_option(self, line, splits=1): """Like split_commands, but converts numbers to int data type""" # x = [int(i) if i.isdigit() else i for i in [token.strip() for token in re.split(r"[\s]+", line, splits)]] x = map(lambda i: (i.isdigit() and int(i)) or i, [token.strip() for token in re.split(r"[\s]+", line, splits)]) return x def split_option_with_columns(self, line): """Split a line in a command and a list of words""" cmd, columns = self.split_option(line) return cmd, [self.strip_table_from_column(c) for c in re.compile(r"\s+").split(columns)] def strip_table_from_column(self, column): """Cut off the table name, because it is possible to say service_state instead of state""" bygroupmatch = re.compile("(\w+)by.*group").search(self.table) if bygroupmatch: return re.sub(re.sub("s$", "", bygroupmatch.group(1)) + "_", "", column, 1) else: return re.sub(re.sub("s$", "", self.table) + "_", "", column, 1) def parse_input(self, data): """Parse the lines of a livestatus request. This function looks for keywords in input lines and sets the attributes of the request object """ for line in data.splitlines(): line = line.strip() # Tools like NagVis send KEYWORK:option, and we prefer to have # a space folowing the : if ":" in line and not " " in line: line = line.replace(":", ": ") keyword = line.split(" ")[0].rstrip(":") if keyword == "GET": # Get the name of the base table cmd, self.table = self.split_command(line) self.set_default_out_map_name() elif keyword == "Columns": # Get the names of the desired columns cmd, self.columns = self.split_option_with_columns(line) self.response.columnheaders = "off" elif keyword == "ResponseHeader": cmd, responseheader = self.split_option(line) self.response.responseheader = responseheader elif keyword == "OutputFormat": cmd, outputformat = self.split_option(line) self.response.outputformat = outputformat elif keyword == "KeepAlive": cmd, keepalive = self.split_option(line) self.response.keepalive = keepalive elif keyword == "ColumnHeaders": cmd, columnheaders = self.split_option(line) self.response.columnheaders = columnheaders elif keyword == "Limit": cmd, self.limit = self.split_option(line) elif keyword == "Filter": try: cmd, attribute, operator, reference = self.split_option(line, 3) except: cmd, attribute, operator, reference = self.split_option(line, 2) + [""] if operator in ["=", ">", ">=", "<", "<=", "=~", "~", "~~", "!=", "!>", "!>=", "!<", "!<="]: # Cut off the table name attribute = self.strip_table_from_column(attribute) # Some operators can simply be negated if operator in ["!>", "!>=", "!<", "!<="]: operator = {"!>": "<=", "!>=": "<", "!<": ">=", "!<=": ">"}[operator] # Put a function on top of the filter_stack which implements # the desired operation self.filtercolumns.append(attribute) self.prefiltercolumns.append(attribute) self.filter_stack.put(self.make_filter(operator, attribute, reference)) if self.table == "log": if attribute == "time": self.sql_filter_stack.put(self.make_sql_filter(operator, attribute, reference)) else: print "illegal operation", operator pass # illegal operation elif keyword == "And": cmd, andnum = self.split_option(line) # Take the last andnum functions from the stack # Construct a new function which makes a logical and # Put the function back onto the stack self.filter_stack.and_elements(andnum) elif keyword == "Or": cmd, ornum = self.split_option(line) # Take the last ornum functions from the stack # Construct a new function which makes a logical or # Put the function back onto the stack self.filter_stack.or_elements(ornum) elif keyword == "StatsGroupBy": cmd, stats_group_by = self.split_option_with_columns(line) self.filtercolumns.extend(stats_group_by) self.stats_group_by.extend(stats_group_by) # Deprecated. If your query contains at least one Stats:-header # then Columns: has the meaning of the old StatsGroupBy: header elif keyword == "Stats": self.stats_request = True try: cmd, attribute, operator, reference = self.split_option(line, 3) if attribute in ["sum", "min", "max", "avg", "std"] and reference.find("as ", 3) != -1: attribute, operator = operator, attribute asas, alias = reference.split(" ") self.aliases.append(alias) elif attribute in ["sum", "min", "max", "avg", "std"] and reference == "=": # Workaround for thruk-cmds like: Stats: sum latency = attribute, operator = operator, attribute reference = "" except: cmd, attribute, operator = self.split_option(line, 3) if attribute in ["sum", "min", "max", "avg", "std"]: attribute, operator = operator, attribute reference = "" attribute = self.strip_table_from_column(attribute) if operator in ["=", ">", ">=", "<", "<=", "=~", "~", "~~", "!=", "!>", "!>=", "!<", "!<="]: if operator in ["!>", "!>=", "!<", "!<="]: operator = {"!>": "<=", "!>=": "<", "!<": ">=", "!<=": ">"}[operator] self.filtercolumns.append(attribute) self.stats_columns.append(attribute) self.stats_filter_stack.put(self.make_filter(operator, attribute, reference)) self.stats_postprocess_stack.put(self.make_filter("count", attribute, None)) elif operator in ["sum", "min", "max", "avg", "std"]: self.stats_columns.append(attribute) self.stats_filter_stack.put(self.make_filter("dummy", attribute, None)) self.stats_postprocess_stack.put(self.make_filter(operator, attribute, None)) else: print "illegal operation", operator pass # illegal operation elif keyword == "StatsAnd": cmd, andnum = self.split_option(line) self.stats_filter_stack.and_elements(andnum) elif keyword == "StatsOr": cmd, ornum = self.split_option(line) self.stats_filter_stack.or_elements(ornum) elif keyword == "Separators": cmd, sep1, sep2, sep3, sep4 = line.split(" ", 5) self.response.separators = map(lambda x: chr(int(x)), [sep1, sep2, sep3, sep4]) elif keyword == "Localtime": cmd, self.client_localtime = self.split_option(line) elif keyword == "COMMAND": cmd, self.extcmd = line.split(" ", 1) else: # This line is not valid or not implemented print "Received a line of input which i can't handle : '%s'" % line pass def launch_query(self): """ Prepare the request object's filter stacks """ # A minimal integrity check if not self.table: return [] # Make columns unique self.filtercolumns = list(set(self.filtercolumns)) self.prefiltercolumns = list(set(self.prefiltercolumns)) self.stats_columns = list(set(self.stats_columns)) if self.stats_request: if len(self.columns) > 0: # StatsGroupBy is deprecated. Columns: can be used instead self.stats_group_by = self.columns elif len(self.stats_group_by) > 0: self.columns = self.stats_group_by + self.stats_columns # if len(self.stats_columns) > 0 and len(self.columns) == 0: if len(self.stats_columns) > 0: self.columns = self.stats_columns + self.columns # Make one big filter where the single filters are anded self.filter_stack.and_elements(self.filter_stack.qsize()) try: # Remember the number of stats filters. We need these numbers as columns later. # But we need to ask now, because get_live_data() will empty the stack num_stats_filters = self.stats_filter_stack.qsize() if self.table == "log": self.sql_filter_stack.and_elements(self.sql_filter_stack.qsize()) result = self.get_live_data_log() else: # If the pnpgraph_present column is involved, then check # with each request if the pnp perfdata path exists if ( "pnpgraph_present" in self.columns + self.filtercolumns + self.prefiltercolumns and self.pnp_path and os.access(self.pnp_path, os.R_OK) ): self.pnp_path_readable = True else: self.pnp_path_readable = False # Apply the filters on the broker's host/service/etc elements result = self.get_live_data() if self.stats_request: self.columns = range(num_stats_filters) if self.stats_group_by: self.columns = tuple(list(self.stats_group_by) + list(self.columns)) if len(self.aliases) == 0: # If there were Stats: staments without "as", show no column headers at all self.response.columnheaders = "off" else: self.response.columnheaders = "on" except Exception, e: import traceback print "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" print e traceback.print_exc(32) print "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" result = [] return result
class LiveStatusQuery(Hooker): my_type = 'query' def __init__(self, configs, hosts, services, contacts, hostgroups, servicegroups, contactgroups, timeperiods, commands, schedulers, pollers, reactionners, brokers, db, use_aggressive_sql, pnp_path, return_queue, counters): # Runtime data form the global LiveStatus object self.configs = configs self.hosts = hosts self.services = services self.contacts = contacts self.hostgroups = hostgroups self.servicegroups = servicegroups self.contactgroups = contactgroups self.timeperiods = timeperiods self.commands = commands self.schedulers = schedulers self.pollers = pollers self.reactionners = reactionners self.brokers = brokers self.db = db self.use_aggressive_sql = use_aggressive_sql self.pnp_path = pnp_path self.return_queue = return_queue self.counters = counters # Private attributes for this specific request self.response = LiveStatusResponse(responseheader = 'off', outputformat = 'csv', keepalive = 'off', columnheaders = 'undef', separators = LiveStatusResponse.separators) self.table = None self.columns = [] self.filtercolumns = [] self.prefiltercolumns = [] self.stats_group_by = [] self.stats_columns = [] self.aliases = [] self.limit = None self.extcmd = False self.out_map = self.copy_out_map_hooks() # Initialize the stacks which are needed for the Filter: and Stats: # filter- and count-operations self.filter_stack = LiveStatusStack() # This stack is used to create a full-blown select-statement self.sql_filter_stack = LiveStatusStack() self.sql_filter_stack.type = 'sql' # This stack is used to create a minimal select-statement which # selects only by time >= and time <= self.sql_time_filter_stack = LiveStatusStack() self.sql_time_filter_stack.type = 'sql' self.stats_filter_stack = LiveStatusStack() self.stats_postprocess_stack = LiveStatusStack() self.stats_request = False # When was this query launched? self.tic = time.time() # Clients can also send their local time with the request self.client_localtime = self.tic def find_converter(self, attribute): """Return a function that converts textual numbers in the request to the correct data type""" out_map = LSout_map[self.out_map_name] if attribute in out_map and 'type' in out_map[attribute]: if out_map[attribute]['type'] == 'int': return int elif out_map[attribute]['type'] == 'float': return float return None def set_default_out_map_name(self): """Translate the table name to the corresponding out_map key.""" try: self.out_map_name = { 'hosts' : 'Host', 'services' : 'Service', 'hostgroups' : 'Hostgroup', 'servicegroups' : 'Servicegroup', 'contacts' : 'Contact', 'contactgroups' : 'Contactgroup', 'comments' : 'Comment', 'downtimes' : 'Downtime', 'commands' : 'Command', 'timeperiods' : 'Timeperiod', 'hostsbygroup' : 'Hostsbygroup', 'servicesbygroup' : 'Servicesbygroup', 'servicesbyhostgroup' : 'Servicesbyhostgroup', 'status' : 'Config', 'log' : 'Logline', 'schedulers' : 'SchedulerLink', 'pollers' : 'PollerLink', 'reactionners' : 'ReactionnerLink', 'brokers' : 'BrokerLink', 'problems' : 'Problem', 'columns' : 'Config', # just a dummy }[self.table] except: self.out_map_name = 'hosts' def copy_out_map_hooks(self): """Update the hooks for some out_map entries. Livestatus columns which have a fulldepythonize postprocessor need an updated argument list. The third argument needs to be the request object. (When the out_map is first supplied with hooks, the third argument is the Livestatus object.) """ new_map = {} for objtype in LSout_map: new_map[objtype] = {} for attribute in LSout_map[objtype]: new_map[objtype][attribute] = {} entry = LSout_map[objtype][attribute] if 'hooktype' in entry: if 'prop' not in entry or entry['prop'] is None: prop = attribute else: prop = entry['prop'] if 'default' in entry: default = entry['default'] else: if entry['type'] == 'int' or entry['type'] == 'float': default = 0 else: default = '' func = entry['fulldepythonize'] new_map[objtype][attribute]['hook'] = self.make_hook('get_prop_full_depythonize', prop, default, func, None) else: new_map[objtype][attribute]['hook'] = entry['hook'] return new_map def __str__(self): output = "LiveStatusRequest:\n" for attr in ["table", "columns", "filtercolumns", "prefiltercolumns", "aliases", "stats_group_by", "stats_request"]: output += "request %s: %s\n" % (attr, getattr(self, attr)) return output def split_command(self, line, splits=1): """Create a list from the words of a line""" return line.split(' ', splits) def split_option(self, line, splits=1): """Like split_commands, but converts numbers to int data type""" #x = [int(i) if i.isdigit() else i for i in [token.strip() for token in re.split(r"[\s]+", line, splits)]] x = map (lambda i: (i.isdigit() and int(i)) or i, [token.strip() for token in re.split(r"[\s]+", line, splits)]) return x def split_option_with_columns(self, line): """Split a line in a command and a list of words""" cmd, columns = self.split_option(line) return cmd, [self.strip_table_from_column(c) for c in re.compile(r'\s+').split(columns)] def strip_table_from_column(self, column): """Cut off the table name, because it is possible to say service_state instead of state""" bygroupmatch = re.compile('(\w+)by.*group').search(self.table) if bygroupmatch: return re.sub(re.sub('s$', '', bygroupmatch.group(1)) + '_', '', column, 1) else: return re.sub(re.sub('s$', '', self.table) + '_', '', column, 1) def parse_input(self, data): """Parse the lines of a livestatus request. This function looks for keywords in input lines and sets the attributes of the request object """ for line in data.splitlines(): line = line.strip() # Tools like NagVis send KEYWORK:option, and we prefer to have # a space following the : if ':' in line and not ' ' in line: line = line.replace(':', ': ') keyword = line.split(' ')[0].rstrip(':') if keyword == 'GET': # Get the name of the base table cmd, self.table = self.split_command(line) self.set_default_out_map_name() elif keyword == 'Columns': # Get the names of the desired columns cmd, self.columns = self.split_option_with_columns(line) self.response.columnheaders = 'off' elif keyword == 'ResponseHeader': cmd, responseheader = self.split_option(line) self.response.responseheader = responseheader elif keyword == 'OutputFormat': cmd, outputformat = self.split_option(line) self.response.outputformat = outputformat elif keyword == 'KeepAlive': cmd, keepalive = self.split_option(line) self.response.keepalive = keepalive elif keyword == 'ColumnHeaders': cmd, columnheaders = self.split_option(line) self.response.columnheaders = columnheaders elif keyword == 'Limit': cmd, self.limit = self.split_option(line) elif keyword == 'Filter': try: cmd, attribute, operator, reference = self.split_option(line, 3) except: cmd, attribute, operator, reference = self.split_option(line, 2) + [''] if operator in ['=', '>', '>=', '<', '<=', '=~', '~', '~~', '!=', '!>', '!>=', '!<', '!<=']: # Cut off the table name attribute = self.strip_table_from_column(attribute) # Some operators can simply be negated if operator in ['!>', '!>=', '!<', '!<=']: operator = { '!>' : '<=', '!>=' : '<', '!<' : '>=', '!<=' : '>' }[operator] # Put a function on top of the filter_stack which implements # the desired operation self.filtercolumns.append(attribute) self.prefiltercolumns.append(attribute) self.filter_stack.put(self.make_filter(operator, attribute, reference)) if self.table == 'log': if attribute == 'time': self.sql_time_filter_stack.put(self.make_sql_filter(operator, attribute, reference)) self.sql_filter_stack.put(self.make_sql_filter(operator, attribute, reference)) else: print "illegal operation", operator pass # illegal operation elif keyword == 'And': cmd, andnum = self.split_option(line) # Take the last andnum functions from the stack # Construct a new function which makes a logical and # Put the function back onto the stack self.filter_stack.and_elements(andnum) if self.table == 'log': #self.sql_time_filter_stack.and_elements(andnum) self.sql_filter_stack.and_elements(andnum) elif keyword == 'Or': cmd, ornum = self.split_option(line) # Take the last ornum functions from the stack # Construct a new function which makes a logical or # Put the function back onto the stack self.filter_stack.or_elements(ornum) if self.table == 'log': #self.sql_time_filter_stack.or_elements(ornum) self.sql_filter_stack.or_elements(ornum) elif keyword == 'StatsGroupBy': cmd, stats_group_by = self.split_option_with_columns(line) self.filtercolumns.extend(stats_group_by) self.stats_group_by.extend(stats_group_by) # Deprecated. If your query contains at least one Stats:-header # then Columns: has the meaning of the old StatsGroupBy: header elif keyword == 'Stats': self.stats_request = True try: cmd, attribute, operator, reference = self.split_option(line, 3) if attribute in ['sum', 'min', 'max', 'avg', 'std'] and reference.find('as ', 3) != -1: attribute, operator = operator, attribute asas, alias = reference.split(' ') self.aliases.append(alias) elif attribute in ['sum', 'min', 'max', 'avg', 'std'] and reference == '=': # Workaround for thruk-cmds like: Stats: sum latency = attribute, operator = operator, attribute reference = '' except: cmd, attribute, operator = self.split_option(line, 3) if attribute in ['sum', 'min', 'max', 'avg', 'std']: attribute, operator = operator, attribute reference = '' attribute = self.strip_table_from_column(attribute) if operator in ['=', '>', '>=', '<', '<=', '=~', '~', '~~', '!=', '!>', '!>=', '!<', '!<=']: if operator in ['!>', '!>=', '!<', '!<=']: operator = { '!>' : '<=', '!>=' : '<', '!<' : '>=', '!<=' : '>' }[operator] self.filtercolumns.append(attribute) self.stats_columns.append(attribute) self.stats_filter_stack.put(self.make_filter(operator, attribute, reference)) self.stats_postprocess_stack.put(self.make_filter('count', attribute, None)) elif operator in ['sum', 'min', 'max', 'avg', 'std']: self.stats_columns.append(attribute) self.stats_filter_stack.put(self.make_filter('dummy', attribute, None)) self.stats_postprocess_stack.put(self.make_filter(operator, attribute, None)) else: print "illegal operation", operator pass # illegal operation elif keyword == 'StatsAnd': cmd, andnum = self.split_option(line) self.stats_filter_stack.and_elements(andnum) elif keyword == 'StatsOr': cmd, ornum = self.split_option(line) self.stats_filter_stack.or_elements(ornum) elif keyword == 'Separators': cmd, sep1, sep2, sep3, sep4 = line.split(' ', 5) self.response.separators = map(lambda x: chr(int(x)), [sep1, sep2, sep3, sep4]) elif keyword == 'Localtime': cmd, self.client_localtime = self.split_option(line) elif keyword == 'COMMAND': cmd, self.extcmd = line.split(' ', 1) else: # This line is not valid or not implemented print "Received a line of input which i can't handle : '%s'" % line pass def launch_query(self): """ Prepare the request object's filter stacks """ # A minimal integrity check if not self.table: return [] # Make columns unique self.filtercolumns = list(set(self.filtercolumns)) self.prefiltercolumns = list(set(self.prefiltercolumns)) self.stats_columns = list(set(self.stats_columns)) if self.stats_request: if len(self.columns) > 0: # StatsGroupBy is deprecated. Columns: can be used instead self.stats_group_by = self.columns elif len(self.stats_group_by) > 0: self.columns = self.stats_group_by + self.stats_columns #if len(self.stats_columns) > 0 and len(self.columns) == 0: if len(self.stats_columns) > 0: self.columns = self.stats_columns + self.columns # Make one big filter where the single filters are anded self.filter_stack.and_elements(self.filter_stack.qsize()) try: # Remember the number of stats filters. We need these numbers as columns later. # But we need to ask now, because get_live_data() will empty the stack num_stats_filters = self.stats_filter_stack.qsize() if self.table == 'log': self.sql_time_filter_stack.and_elements(self.sql_time_filter_stack.qsize()) self.sql_filter_stack.and_elements(self.sql_filter_stack.qsize()) result = self.get_live_data_log() else: # If the pnpgraph_present column is involved, then check # with each request if the pnp perfdata path exists if 'pnpgraph_present' in self.columns + self.filtercolumns + self.prefiltercolumns and self.pnp_path and os.access(self.pnp_path, os.R_OK): self.pnp_path_readable = True else: self.pnp_path_readable = False # Apply the filters on the broker's host/service/etc elements result = self.get_live_data() if self.stats_request: self.columns = range(num_stats_filters) if self.stats_group_by: self.columns = tuple(list(self.stats_group_by) + list(self.columns)) if len(self.aliases) == 0: #If there were Stats: staments without "as", show no column headers at all self.response.columnheaders = 'off' else: self.response.columnheaders = 'on' except Exception, e: import traceback print "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" print e traceback.print_exc(32) print "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" result = [] return result