Skip to content
Permalink
Browse files

Added type annotations related to cmk.ec.history.

Change-Id: I291b67c011b49ae13f55b1aa80193966d343d01b
  • Loading branch information
svenpanne committed Jan 8, 2020
1 parent be3bafb commit a89789e0eb19c4d68e5375e2e90a2174e2a213e4
@@ -31,12 +31,13 @@
import time
from logging import Logger # pylint: disable=unused-import
from pathlib import Path # pylint: disable=unused-import
from typing import Any, AnyStr, Dict, List, Optional, Tuple # pylint: disable=unused-import
from typing import Any, AnyStr, Dict, List, Optional, Tuple, Union # pylint: disable=unused-import

from cmk.ec.actions import quote_shell_string
from cmk.ec.query import QueryGET
from cmk.ec.settings import Settings # pylint: disable=unused-import
import cmk.ec.actions
from cmk.utils.log import VERBOSE
import cmk.utils.render
from cmk.utils.render import date_and_time

# TODO: As one can see clearly below, we should really have a class hierarchy here...

@@ -56,7 +57,7 @@ def __init__(self, settings, config, logger, event_columns, history_columns):
self.reload_configuration(config)

def reload_configuration(self, config):
# type: (Any) -> None
# type: (Dict[str, Any]) -> None
self._config = config
if self._config['archive_mode'] == 'mongodb':
_reload_configuration_mongodb(self)
@@ -79,7 +80,7 @@ def add(self, event, what, who="", addinfo=""):

# TODO: We can't use Query in the type because of cyclic imports... :-/
def get(self, query):
# type: (Any) -> List[Any]
# type: (QueryGET) -> List[Any]
if self._config['archive_mode'] == 'mongodb':
return _get_mongodb(self, query)
return _get_files(self, self._logger, query)
@@ -239,7 +240,7 @@ def _log_event(config, logger, event, what, who, addinfo):


def _get_mongodb(history, query):
# type: (History, Any) -> List[Any]
# type: (History, QueryGET) -> List[Any]
filters, limit = query.filters, query.limit

history_entries = []
@@ -249,11 +250,11 @@ def _get_mongodb(history, query):

# Construct the mongodb filtering specification. We could fetch all information
# and do filtering on this data, but this would be way too inefficient.
query = {}
mongo_query = {}
for column_name, operator_name, _predicate, argument in filters:

if operator_name == '=':
mongo_filter = argument
mongo_filter = argument # type: Union[str, Dict[str, str]]
elif operator_name == '>':
mongo_filter = {'$gt': argument}
elif operator_name == '<':
@@ -275,16 +276,16 @@ def _get_mongodb(history, query):
column_name)

if column_name[:6] == 'event_':
query['event.' + column_name[6:]] = mongo_filter
mongo_query['event.' + column_name[6:]] = mongo_filter
elif column_name[:8] == 'history_':
key = column_name[8:]
if key == 'line':
key = '_id'
query[key] = mongo_filter
mongo_query[key] = mongo_filter
else:
raise Exception('Filter %s not implemented for MongoDB' % column_name)

result = history._mongodb.db.ec_archive.find(query).sort('time', -1)
result = history._mongodb.db.ec_archive.find(mongo_query).sort('time', -1)

# Might be used for debugging / profiling
#open(cmk.utils.paths.omd_root + '/var/log/check_mk/ec_history_debug.log', 'a').write(
@@ -444,11 +445,11 @@ def _expire_logfiles(settings, config, logger, lock_history, flush):
days = config["history_lifetime"]
min_mtime = time.time() - days * 86400
logger.log(VERBOSE, "Expiring logfiles (Horizon: %d days -> %s)", days,
cmk.utils.render.date_and_time(min_mtime))
date_and_time(min_mtime))
for path in settings.paths.history_dir.value.glob('*.log'):
if flush or path.stat().st_mtime < min_mtime:
logger.info("Deleting log file %s (age %s)" %
(path, cmk.utils.render.date_and_time(path.stat().st_mtime)))
(path, date_and_time(path.stat().st_mtime)))
path.unlink()
except Exception as e:
if settings.options.debug:
@@ -457,7 +458,7 @@ def _expire_logfiles(settings, config, logger, lock_history, flush):


def _get_files(history, logger, query):
# type: (History, Logger, Any) -> List[Any]
# type: (History, Logger, QueryGET) -> List[Any]
filters, limit = query.filters, query.limit
history_entries = [] # type: List[Any]
if not history._settings.paths.history_dir.value.exists():
@@ -557,9 +558,9 @@ def _parse_history_file(history, path, query, greptexts, limit, logger):
# If we have greptexts we pre-filter the file using the extremely
# fast GNU Grep
# Revert lines from the log file to have the newer lines processed first
cmd = 'tac %s' % cmk.ec.actions.quote_shell_string(str(path))
cmd = 'tac %s' % quote_shell_string(str(path))
if greptexts:
cmd += " | egrep -i -e %s" % cmk.ec.actions.quote_shell_string(".*".join(greptexts))
cmd += " | egrep -i -e %s" % quote_shell_string(".*".join(greptexts))
grep = subprocess.Popen(cmd, shell=True, close_fds=True, stdout=subprocess.PIPE) # nosec

for line in grep.stdout:
@@ -48,7 +48,7 @@ def make(status_server, raw_query, logger):
raise MKClientError("Invalid query. Need GET/COMMAND plus argument(s)")
method = parts[0]
if method == "GET":
return _QueryGET(status_server, raw_query, logger)
return QueryGET(status_server, raw_query, logger)
if method == "REPLICATE":
return _QueryREPLICATE(status_server, raw_query, logger)
if method == "COMMAND":
@@ -79,7 +79,7 @@ def __repr__(self):
return repr("\n".join(self._raw_query))


class _QueryGET(Query):
class QueryGET(Query):
_filter_operators = {
"=": (lambda a, b: a == b),
">": (lambda a, b: a > b),
@@ -241,7 +241,7 @@ def _valuespec_inventory_if_rules():
Transform(
DropdownChoice(
title=_("Select interface port type"),
choices=defines.interface_port_types(),
choices=ListChoice.dict_choices(defines.interface_port_types()),
help=_("Only interfaces with the given port type are put into this group. "
"For example 53 (propVirtual)."),
),
@@ -2261,14 +2261,10 @@ def __init__( # pylint: disable=redefined-builtin

def choices(self):
# type: () -> List[TypingTuple[Any, Text]]
result = [] # type: List[TypingTuple[Any, Text]]
if isinstance(self._choices, list):
result = self._choices
elif isinstance(self._choices, dict):
result = ListChoice.dict_choices(self._choices)
else:
result = self._choices()

if self._no_preselect:
return [(self._no_preselect_value, self._no_preselect_title)] + result
return result
@@ -26,6 +26,8 @@
"""This module serves constants which are needed in several components
of Check_MK."""

from typing import Dict, List, Text, Tuple # pylint: disable=unused-import

from cmk.utils.i18n import _

# TODO: Investigate Check_MK code for more defines and other places
@@ -35,6 +37,7 @@

# TODO: Rename to service_state_names()
def core_state_names():
# type: () -> Dict[int, Text]
return {
-1: _("NODATA"),
0: _("OK"),
@@ -44,11 +47,13 @@ def core_state_names():
}


def service_state_name(state_num, deflt=""):
def service_state_name(state_num, deflt=u""):
# type: (int, Text) -> Text
return core_state_names().get(state_num, deflt)


def short_service_state_names():
# type: () -> Dict[int, Text]
return {
-1: _("PEND"),
0: _("OK"),
@@ -58,11 +63,13 @@ def short_service_state_names():
}


def short_service_state_name(state_num, deflt=""):
def short_service_state_name(state_num, deflt=u""):
# type: (int, Text) -> Text
return short_service_state_names().get(state_num, deflt)


def host_state_name(state_num, deflt=""):
def host_state_name(state_num, deflt=u""):
# type: (int, Text) -> Text
states = {
0: _("UP"),
1: _("DOWN"),
@@ -71,22 +78,26 @@ def host_state_name(state_num, deflt=""):
return states.get(state_num, deflt)


def short_host_state_name(state_num, deflt=""):
def short_host_state_name(state_num, deflt=u""):
# type: (int, Text) -> Text
states = {0: _("UP"), 1: _("DOWN"), 2: _("UNREACH")}
return states.get(state_num, deflt)


def weekday_name(day_num):
# type: (int) -> Text
"""Returns the human readable day name of a given weekday number (starting with 0 at Monday)"""
return weekdays()[day_num]


def weekday_ids():
# type: () -> List[str]
"""Returns a list of the internal week day names"""
return [d[0] for d in weekdays_by_name()]


def weekdays():
# type: () -> Dict[int, Text]
"""Returns a map of weekday number (starting with 0 at Monday) to the human readable day name"""
return {
0: _("Monday"),
@@ -100,6 +111,7 @@ def weekdays():


def weekdays_by_name():
# type: () -> List[Tuple[str, Text]]
"""Returns a list of two element tuples containing the weekday ID and the human readable day name"""
return [
("monday", _("Monday")),
@@ -113,6 +125,7 @@ def weekdays_by_name():


def month_name(month_num):
# type: (int) -> Text
"""Returns the human readable month name of a given month number
(starting with 0 = January)"""
return [
@@ -131,11 +144,13 @@ def month_name(month_num):
][month_num]


def interface_oper_state_name(state_num, deflt=""):
def interface_oper_state_name(state_num, deflt=u""):
# type: (int, Text) -> Text
return interface_oper_states().get(state_num, deflt)


def interface_oper_states():
# type: () -> Dict[int, Text]
return {
1: _("up"),
2: _("down"),
@@ -150,6 +165,7 @@ def interface_oper_states():


def interface_port_types():
# type: () -> Dict[int, str]
return {
1: "other",
2: "regular1822",
@@ -86,6 +86,7 @@ def get_formatter(format_str="%(asctime)s [%(levelno)s] [%(name)s %(process)d] %


def clear_console_logging():
# type: () -> None
logger.handlers[:] = []
logger.addHandler(logging.NullHandler())
logger.setLevel(logging.INFO)
@@ -34,7 +34,7 @@
import time
import math
from datetime import timedelta
from typing import Tuple, Union # pylint: disable=unused-import
from typing import Optional, Tuple, Union # pylint: disable=unused-import

from cmk.utils.i18n import _

@@ -51,15 +51,18 @@

# NOTE: strftime's format *must* be of type str, both in Python 2 and 3.
def date(timestamp):
# type: (Optional[float]) -> str
return time.strftime(str(_("%Y-%m-%d")), time.localtime(timestamp))


def date_and_time(timestamp):
# type: (Optional[float]) -> str
return "%s %s" % (date(timestamp), time_of_day(timestamp))


# NOTE: strftime's format *must* be of type str, both in Python 2 and 3.
def time_of_day(timestamp):
# type: (Optional[float]) -> str
return time.strftime(str(_("%H:%M:%S")), time.localtime(timestamp))


@@ -76,10 +79,13 @@ def time_since(timestamp):
class Age(object):
"""Format time difference seconds into approximated human readable text"""
def __init__(self, secs):
# type: (float) -> None
super(Age, self).__init__()
self.__secs = secs

# NOTE: In Python 2 the return type is WRONG, we should return str.
def __str__(self):
# not-yet-a-type: () -> Text
secs = self.__secs

if secs < 0:
@@ -115,11 +121,13 @@ def __str__(self):
return "%.0f %s" % (years, _("y"))

def __float__(self):
# type: () -> float
return float(self.__secs)


# TODO: Make call sites use Age() directly?
def approx_age(secs):
# type: (float) -> str
return "%s" % Age(secs)


@@ -35,7 +35,6 @@
from typing import ( # pylint: disable=unused-import
NewType, AnyStr, Any, Type, List, Text, cast, Tuple, Union, Dict, Pattern, Optional,
)
import six

UserId = NewType("UserId", Text)
SiteId = NewType("SiteId", str)
@@ -67,7 +66,7 @@

def _ensure_unicode(value):
# type: (Union[Text, bytes]) -> Text
if isinstance(value, six.text_type):
if isinstance(value, Text):
return value
return value.decode("utf-8")

@@ -116,6 +115,7 @@ def lqencode(s):


def quote_dict(s):
# type: (Text) -> Text
"""Apply the quoting used for dict-valued columns (See #6972)"""
return "'%s'" % s.replace(u"'", u"''")

@@ -758,7 +758,7 @@ def __init__(self, sites, disabled_sites=None):
# Fetch all the states of status hosts of this local site in one query
query = u"GET hosts\nColumns: name state has_been_checked last_time_up\n"
for host in hosts:
query += u"Filter: name = %s\n" % six.text_type(host)
query += u"Filter: name = %s\n" % Text(host)
query += u"Or: %d\n" % len(hosts)
self.set_only_sites([sitename]) # only connect one site
try:
@@ -1035,6 +1035,7 @@ def get_connection(self, site_id):

class LocalConnection(SingleSiteConnection):
def __init__(self, *args, **kwargs):
# type: (*Any, **Any) -> None
omd_root = os.getenv("OMD_ROOT")
if not omd_root:
raise MKLivestatusConfigError(

0 comments on commit a89789e

Please sign in to comment.
You can’t perform that action at this time.