예제 #1
0
def clear_item_state(user_key: _UserKey) -> None:
    """Deletes a stored matching the given key. This needs to be
    the same key as used with set_item_state().

    In case the given item does not exist, the function returns
    without modification."""
    get_value_store().pop(user_key, None)
예제 #2
0
def get_average(
    itemname: _UserKey,
    this_time: float,
    this_val: float,
    backlog_minutes: float,
    initialize_zero: bool = True,
) -> float:
    """Return new average based on current value and last average

    itemname        : unique ID for storing this average until the next check
    this_time       : timestamp of new value
    backlog         : averaging horizon in minutes
    initialize_zero : assume average of 0.0 when now previous average is stored

    This function returns the new average value aₙ as the weighted sum of the
    current value xₙ and the last average:

        aₙ = (1 - w)xₙ + waₙ₋₁

           = (1-w) ∑ᵢ₌₀ⁿ wⁱxₙ₋ᵢ

    This results in a so called "exponential moving average".

    The weight is chosen such that for long running timeseries the "backlog"
    (all recorded values in the last n minutes) will make up 50% of the
    weighted average.

    Assuming k values in the backlog, compute their combined weight such that
    they sum up to the backlog weight b (0.5 in our case):

       b = (1-w) ∑ᵢ₌₀ᵏ⁻¹  wⁱ  =>  w = (1 - b) ** (1/k)    ("geometric sum")
    """
    value_store = get_value_store()
    last_time, last_average = value_store.get(itemname, (this_time, None))
    # first call: take current value as average or assume 0.0
    if last_average is None:
        average = 0.0 if initialize_zero else this_val
        value_store[itemname] = (this_time, average)
        return average

    # at the current rate, how many values are in the backlog?
    time_diff = this_time - last_time
    if time_diff <= 0:
        # Gracefully handle time-anomaly of target systems
        return last_average
    backlog_count = (backlog_minutes * 60.0) / time_diff

    backlog_weight = 0.50
    # TODO: For the version in the new Check API change the above line to
    # backlog_weight = 0.5 ** min(1, (time - start_time) / (2 * backlog_minutes * 60.)
    # And add to doc string:
    #  For shorter timeseries we give the backlog more than those 50% weight
    #  with the advantage that a) the initial value becomes irrelevant, and
    #  b) for beginning timeseries we reach a meaningful value more quickly.

    weight = (1 - backlog_weight)**(1.0 / backlog_count)

    average = (1.0 - weight) * this_val + weight * last_average
    value_store[itemname] = (this_time, average)
    return average
예제 #3
0
def test_value_store():

    store = value_store.get_value_store()

    with pytest.raises(MKGeneralException):
        store["foo"] = 42

    saved_prefix = value_store.get_item_state_prefix()

    with value_store.context(CheckPluginName("plugin"), "item"):

        assert len(store) == 0
        assert not store
        assert "foo" not in store
        assert store.get("foo") is None
        with pytest.raises(KeyError):
            _ = store["foo"]
        with pytest.raises(TypeError):
            store[2] = "key must be string"  # type: ignore[index]

        store["foo"] = 42
        store["bar"] = 23

        assert set(store) == {"foo", "bar"}
        del store["bar"]
        assert "foo" in store
        assert len(store) == 1
        assert bool(store)
        assert store["foo"] == 42

    assert value_store.get_item_state_prefix() == saved_prefix
예제 #4
0
def test_value_store():

    store = value_store.get_value_store()

    assert len(store) == 0
    assert not store
    assert "foo" not in store
    assert store.get("foo") is None
    with pytest.raises(KeyError):
        _ = store["foo"]
    with pytest.raises(TypeError):
        store[2] = "key must be string"  # type: ignore[index]

    store["foo"] = 42
    store["bar"] = 23

    assert set(store) == {"foo", "bar"}
    del store["bar"]
    assert "foo" in store
    assert len(store) == 1
    assert bool(store)
    assert store["foo"] == 42
예제 #5
0
def get_item_state(user_key: object, default: Any = None) -> Any:
    """Returns the currently stored item with the user_key.

    Returns None or the given default value in case there
    is currently no such item stored."""
    return get_value_store().get(_stringify(user_key), default)
예제 #6
0
def set_item_state(user_key: object, state: Any) -> None:
    """Store arbitrary values until the next execution of a check.

    The user_key is the identifier of the stored value and needs
    to be unique per service."""
    get_value_store()[_stringify(user_key)] = state