Esempio n. 1
0
def test_triggers_good_temp(report):
    t = Triggers(
        TriggerConfig(
            global_commands=Actions(
                panic=AlertCommands(enter_cmd=None, leave_cmd=None),
                threshold=AlertCommands(enter_cmd=None, leave_cmd=None),
            ),
            temp_commands={
                TempName("mobo"): Actions(
                    panic=AlertCommands(enter_cmd=None, leave_cmd=None),
                    threshold=AlertCommands(enter_cmd=None, leave_cmd=None),
                )
            },
        ),
        report=report,
    )
    with t:
        assert not t.is_alerting
        t.check(
            {
                TempName("mobo"): TempStatus(
                    temp=TempCelsius(34.0),
                    min=TempCelsius(40.0),
                    max=TempCelsius(50.0),
                    panic=TempCelsius(60.0),
                    threshold=None,
                    is_panic=False,
                    is_threshold=False,
                )
            }
        )
        assert not t.is_alerting
Esempio n. 2
0
def make_temp_status(temp):
    return TempStatus(
        min=TempCelsius(30),
        max=TempCelsius(50),
        temp=TempCelsius(temp),
        panic=None,
        threshold=None,
        is_panic=False,
        is_threshold=False,
    )
Esempio n. 3
0
def test_hddtemp_exec_failed():
    t = HDDTemp(
        disk_path="/dev/sd?",
        min=TempCelsius(38.0),
        max=TempCelsius(45.0),
        panic=TempCelsius(50.0),
        threshold=None,
        hddtemp_bin="false",
    )
    with pytest.raises(subprocess.CalledProcessError):
        t._call_hddtemp()
Esempio n. 4
0
def test_bad_temp(cls, report, sense_exec_shell_command):
    t = cls(
        global_commands=AlertCommands(
            enter_cmd="printf '@%s' enter", leave_cmd="printf '@%s' leave"
        ),
        temp_commands=dict(
            mobo=AlertCommands(
                enter_cmd="printf '@%s' mobo enter", leave_cmd="printf '@%s' mobo leave"
            )
        ),
        report=report,
    )
    with sense_exec_shell_command(trigger) as (mock_exec_shell_command, get_stdout):
        with t:
            assert not t.is_alerting
            t.check(
                dict(
                    mobo=TempStatus(
                        temp=TempCelsius(70.0),
                        min=TempCelsius(40.0),
                        max=TempCelsius(50.0),
                        panic=TempCelsius(60.0),
                        threshold=TempCelsius(55.0),
                        is_panic=True,
                        is_threshold=True,
                    )
                )
            )
            assert t.is_alerting
            assert mock_exec_shell_command.call_args_list == [
                call("printf '@%s' mobo enter"),
                call("printf '@%s' enter"),
            ]
            assert ["@mobo@enter", "@enter"] == get_stdout()
            mock_exec_shell_command.reset_mock()

            t.check(
                dict(
                    mobo=TempStatus(
                        temp=TempCelsius(34.0),
                        min=TempCelsius(40.0),
                        max=TempCelsius(50.0),
                        panic=TempCelsius(60.0),
                        threshold=None,
                        is_panic=False,
                        is_threshold=False,
                    )
                )
            )
            assert not t.is_alerting
            assert mock_exec_shell_command.call_args_list == [
                call("printf '@%s' mobo leave"),
                call("printf '@%s' leave"),
            ]
            assert ["@mobo@leave", "@leave"] == get_stdout()
            mock_exec_shell_command.reset_mock()
        assert 0 == mock_exec_shell_command.call_count
Esempio n. 5
0
def test_hddtemp_bad(hddtemp_output_bad):
    with patch.object(HDDTemp, "_call_hddtemp") as mock_call_hddtemp:
        mock_call_hddtemp.return_value = hddtemp_output_bad
        t = HDDTemp(
            disk_path="/dev/sda",
            min=TempCelsius(38.0),
            max=TempCelsius(45.0),
            panic=TempCelsius(50.0),
            threshold=None,
            hddtemp_bin="testbin",
        )
        with pytest.raises(RuntimeError):
            t.get()
Esempio n. 6
0
def test_hddtemp_exec_successful(temp_path):
    (temp_path / "sda").write_text("")
    (temp_path / "sdz").write_text("")
    t = HDDTemp(
        disk_path=str(temp_path / "sd") + "?",
        min=TempCelsius(38.0),
        max=TempCelsius(45.0),
        panic=TempCelsius(50.0),
        threshold=None,
        hddtemp_bin="printf '@%s'",
    )
    expected_out = "@-n@-u@C@--@{0}/sda@{0}/sdz".format(temp_path)
    assert expected_out == t._call_hddtemp()
Esempio n. 7
0
def test_command_temp_without_minmax():
    t = CommandTemp(
        shell_command=r"printf '%s\n' 35 30 40",
        min=None,
        max=None,
        panic=TempCelsius(50.0),
        threshold=None,
    )
    assert t.get() == TempStatus(
        temp=TempCelsius(35.0),
        min=TempCelsius(30.0),
        max=TempCelsius(40.0),
        panic=TempCelsius(50.0),
        threshold=None,
        is_panic=False,
        is_threshold=False,
    )
Esempio n. 8
0
def test_file_temp_min_max_files(temp_path, file_temp_path):
    with pytest.raises(RuntimeError):
        # min == max is an error
        FileTemp(
            temp_path=str(file_temp_path),
            min=None,
            max=None,
            panic=TempCelsius(60.0),
            threshold=None,
        ).get()

    temp = FileTemp(
        temp_path=str(file_temp_path),
        min=TempCelsius(50.0),
        max=None,
        panic=TempCelsius(60.0),
        threshold=None,
    )
    assert temp.get() == TempStatus(
        temp=TempCelsius(34.0),
        min=TempCelsius(50.0),
        max=TempCelsius(127.0),
        panic=TempCelsius(60.0),
        threshold=None,
        is_panic=False,
        is_threshold=False,
    )
Esempio n. 9
0
def test_file_temp_glob(file_temp_path):
    temp = FileTemp(
        temp_path=str(file_temp_path).replace("/temp1", "/temp?"),
        min=TempCelsius(40.0),
        max=None,
        panic=None,
        threshold=None,
    )
    assert temp.get() == TempStatus(
        temp=TempCelsius(34.0),
        min=TempCelsius(40.0),
        max=TempCelsius(127.0),
        panic=None,
        threshold=None,
        is_panic=False,
        is_threshold=False,
    )
    print(repr(temp))
Esempio n. 10
0
def test_good_temp(cls, report):
    t = cls(
        global_commands=AlertCommands(enter_cmd=None, leave_cmd=None),
        temp_commands=dict(mobo=AlertCommands(enter_cmd=None, leave_cmd=None)),
        report=report,
    )
    with t:
        assert not t.is_alerting
        t.check(
            dict(
                mobo=TempStatus(
                    temp=TempCelsius(34.0),
                    min=TempCelsius(40.0),
                    max=TempCelsius(50.0),
                    panic=TempCelsius(60.0),
                    threshold=None,
                    is_panic=False,
                    is_threshold=False,
                )
            )
        )
        assert not t.is_alerting
Esempio n. 11
0
def test_temp(
    temp: TempCelsius,
    threshold: Optional[TempCelsius],
    panic: TempCelsius,
    is_threshold,
    is_panic,
):
    min = TempCelsius(40.0)
    max = TempCelsius(50.0)

    with patch.object(DummyTemp, "_get_temp") as mock_get_temp:
        t = DummyTemp(panic=panic, threshold=threshold)
        mock_get_temp.return_value = [temp, min, max]

        assert t.get() == TempStatus(
            temp=temp,
            min=min,
            max=max,
            panic=panic,
            threshold=threshold,
            is_panic=is_panic,
            is_threshold=is_threshold,
        )
Esempio n. 12
0
def test_file_temp_min_max_numbers(file_temp_path):
    temp = FileTemp(
        temp_path=str(file_temp_path),
        min=TempCelsius(40.0),
        max=TempCelsius(50.0),
        panic=TempCelsius(60.0),
        threshold=None,
    )
    assert temp.get() == TempStatus(
        temp=TempCelsius(34.0),
        min=TempCelsius(40.0),
        max=TempCelsius(50.0),
        panic=TempCelsius(60.0),
        threshold=None,
        is_panic=False,
        is_threshold=False,
    )
    print(repr(temp))
Esempio n. 13
0
def test_hddtemp_many(hddtemp_output_many):
    with patch.object(HDDTemp, "_call_hddtemp") as mock_call_hddtemp:
        mock_call_hddtemp.return_value = hddtemp_output_many
        t = HDDTemp(
            disk_path="/dev/sd?",
            min=TempCelsius(38.0),
            max=TempCelsius(45.0),
            panic=TempCelsius(50.0),
            threshold=None,
            hddtemp_bin="testbin",
        )

        assert t.get() == TempStatus(
            temp=TempCelsius(39.0),
            min=TempCelsius(38.0),
            max=TempCelsius(45.0),
            panic=TempCelsius(50.0),
            threshold=None,
            is_panic=False,
            is_threshold=False,
        )
        print(repr(t))
Esempio n. 14
0
def test_example_conf(example_conf: Path):
    daemon_cli_config = DaemonCLIConfig(pidfile=None,
                                        logfile=None,
                                        exporter_listen_host=None)

    parsed = parse_config(example_conf, daemon_cli_config)
    assert parsed == ParsedConfig(
        daemon=DaemonConfig(
            pidfile="/run/afancontrol.pid",
            logfile="/var/log/afancontrol.log",
            exporter_listen_host="127.0.0.1:8083",
            interval=5,
        ),
        report_cmd=
        ('printf "Subject: %s\nTo: %s\n\n%b" '
         '"afancontrol daemon report: %REASON%" root "%MESSAGE%" | sendmail -t'
         ),
        triggers=TriggerConfig(
            global_commands=Actions(
                panic=AlertCommands(enter_cmd=None, leave_cmd=None),
                threshold=AlertCommands(enter_cmd=None, leave_cmd=None),
            ),
            temp_commands={
                TempName("hdds"):
                Actions(
                    panic=AlertCommands(enter_cmd=None, leave_cmd=None),
                    threshold=AlertCommands(enter_cmd=None, leave_cmd=None),
                ),
                TempName("mobo"):
                Actions(
                    panic=AlertCommands(enter_cmd=None, leave_cmd=None),
                    threshold=AlertCommands(enter_cmd=None, leave_cmd=None),
                ),
            },
        ),
        fans={
            FanName("cpu"):
            PWMFanNorm(
                LinuxPWMFan(
                    PWMDevice("/sys/class/hwmon/hwmon0/device/pwm1"),
                    FanInputDevice(
                        "/sys/class/hwmon/hwmon0/device/fan1_input"),
                ),
                pwm_line_start=PWMValue(100),
                pwm_line_end=PWMValue(240),
                never_stop=True,
            ),
            FanName("hdd"):
            PWMFanNorm(
                LinuxPWMFan(
                    PWMDevice("/sys/class/hwmon/hwmon0/device/pwm2"),
                    FanInputDevice(
                        "/sys/class/hwmon/hwmon0/device/fan2_input"),
                ),
                pwm_line_start=PWMValue(100),
                pwm_line_end=PWMValue(240),
                never_stop=False,
            ),
            FanName("my_arduino_fan"):
            PWMFanNorm(
                ArduinoPWMFan(
                    ArduinoConnection(
                        ArduinoName("mymicro"),
                        "/dev/ttyACM0",  # linux
                        # "/dev/cu.usbmodem14201",  # macos
                        baudrate=115200,
                        status_ttl=5,
                    ),
                    pwm_pin=ArduinoPin(9),
                    tacho_pin=ArduinoPin(3),
                ),
                pwm_line_start=PWMValue(100),
                pwm_line_end=PWMValue(240),
                never_stop=True,
            ),
        },
        temps={
            TempName("hdds"):
            HDDTemp(
                "/dev/sd?",
                min=TempCelsius(35.0),
                max=TempCelsius(48.0),
                panic=TempCelsius(55.0),
                threshold=None,
                hddtemp_bin="hddtemp",
            ),
            TempName("mobo"):
            FileTemp(
                "/sys/class/hwmon/hwmon0/device/temp1_input",
                min=TempCelsius(30.0),
                max=TempCelsius(40.0),
                panic=None,
                threshold=None,
            ),
        },
        mappings={
            MappingName("1"):
            FansTempsRelation(
                temps=[TempName("mobo"), TempName("hdds")],
                fans=[
                    FanSpeedModifier(fan=FanName("cpu"), modifier=1.0),
                    FanSpeedModifier(fan=FanName("hdd"), modifier=0.6),
                    FanSpeedModifier(fan=FanName("my_arduino_fan"),
                                     modifier=0.222),
                ],
            ),
            MappingName("2"):
            FansTempsRelation(
                temps=[TempName("hdds")],
                fans=[FanSpeedModifier(fan=FanName("hdd"), modifier=1.0)],
            ),
        },
    )
Esempio n. 15
0
def test_prometheus_metrics(requests_session):
    mocked_fan = MagicMock(spec=PWMFanNorm)()
    mocked_triggers = MagicMock(spec=Triggers)()
    mocked_report = MagicMock(spec=Report)()

    port = random.randint(20000, 50000)
    metrics = PrometheusMetrics("127.0.0.1:%s" % port)
    with metrics:
        resp = requests_session.get("http://127.0.0.1:%s/metrics" % port)
        assert resp.status_code == 200
        assert "is_threshold 0.0" in resp.text

        with metrics.measure_tick():
            sleep(0.01)

        resp = requests_session.get("http://127.0.0.1:%s/metrics" % port)
        assert resp.status_code == 200
        assert "tick_duration_count 1.0" in resp.text
        assert "tick_duration_sum 0." in resp.text

        mocked_triggers.panic_trigger.is_alerting = True
        mocked_triggers.threshold_trigger.is_alerting = False

        mocked_fan.pwm_line_start = 100
        mocked_fan.pwm_line_end = 240
        mocked_fan.get_speed.return_value = 999
        mocked_fan.get_raw.return_value = 142
        mocked_fan.get = types.MethodType(PWMFanNorm.get, mocked_fan)
        mocked_fan.pwmfan.max_pwm = 255

        metrics.tick(
            temps={
                TempName("goodtemp"):
                TempStatus(
                    temp=TempCelsius(74.0),
                    min=TempCelsius(40.0),
                    max=TempCelsius(50.0),
                    panic=TempCelsius(60.0),
                    threshold=None,
                    is_panic=True,
                    is_threshold=False,
                ),
                TempName("failingtemp"):
                None,
            },
            fans=Fans(fans={FanName("test"): mocked_fan},
                      report=mocked_report),
            triggers=mocked_triggers,
        )

        resp = requests_session.get("http://127.0.0.1:%s/metrics" % port)
        assert resp.status_code == 200
        print(resp.text)
        assert 'temperature_current{temp_name="failingtemp"} NaN' in resp.text
        assert 'temperature_current{temp_name="goodtemp"} 74.0' in resp.text
        assert 'temperature_is_failing{temp_name="failingtemp"} 1.0' in resp.text
        assert 'temperature_is_failing{temp_name="goodtemp"} 0.0' in resp.text
        assert 'fan_rpm{fan_name="test"} 999.0' in resp.text
        assert 'fan_pwm{fan_name="test"} 142.0' in resp.text
        assert 'fan_pwm_normalized{fan_name="test"} 0.556' in resp.text
        assert 'fan_is_failing{fan_name="test"} 0.0' in resp.text
        assert "is_panic 1.0" in resp.text
        assert "is_threshold 0.0" in resp.text
        assert "last_metrics_tick_seconds_ago 0." in resp.text

    with pytest.raises(IOError):
        requests_session.get("http://127.0.0.1:%s/metrics" % port)
Esempio n. 16
0
def test_pkg_conf(pkg_conf: Path):
    daemon_cli_config = DaemonCLIConfig(pidfile=None,
                                        logfile=None,
                                        exporter_listen_host=None)

    parsed = parse_config(pkg_conf, daemon_cli_config)
    assert parsed == ParsedConfig(
        daemon=DaemonConfig(
            pidfile="/run/afancontrol.pid",
            logfile="/var/log/afancontrol.log",
            interval=5,
            exporter_listen_host=None,
        ),
        report_cmd=
        ('printf "Subject: %s\nTo: %s\n\n%b" '
         '"afancontrol daemon report: %REASON%" root "%MESSAGE%" | sendmail -t'
         ),
        triggers=TriggerConfig(
            global_commands=Actions(
                panic=AlertCommands(enter_cmd=None, leave_cmd=None),
                threshold=AlertCommands(enter_cmd=None, leave_cmd=None),
            ),
            temp_commands={
                TempName("mobo"):
                Actions(
                    panic=AlertCommands(enter_cmd=None, leave_cmd=None),
                    threshold=AlertCommands(enter_cmd=None, leave_cmd=None),
                )
            },
        ),
        fans={
            FanName("hdd"):
            PWMFanNorm(
                LinuxPWMFan(
                    PWMDevice("/sys/class/hwmon/hwmon0/device/pwm2"),
                    FanInputDevice(
                        "/sys/class/hwmon/hwmon0/device/fan2_input"),
                ),
                pwm_line_start=PWMValue(100),
                pwm_line_end=PWMValue(240),
                never_stop=False,
            )
        },
        temps={
            TempName("mobo"):
            FileTemp(
                "/sys/class/hwmon/hwmon0/device/temp1_input",
                min=TempCelsius(30.0),
                max=TempCelsius(40.0),
                panic=None,
                threshold=None,
            )
        },
        mappings={
            MappingName("1"):
            FansTempsRelation(
                temps=[TempName("mobo")],
                fans=[FanSpeedModifier(fan=FanName("hdd"), modifier=0.6)],
            )
        },
    )
Esempio n. 17
0
        assert mocked_triggers.check.call_count == 1
        assert mocked_case_fan.__enter__.call_count == 1
        assert mocked_metrics.__enter__.call_count == 1
        assert mocked_metrics.tick.call_count == 1
    assert mocked_case_fan.__exit__.call_count == 1
    assert mocked_metrics.__exit__.call_count == 1


@pytest.mark.parametrize(
    "temps, mappings, expected_fan_speeds",
    [
        (
            {
                TempName("cpu"):
                TempStatus(
                    min=TempCelsius(30),
                    max=TempCelsius(50),
                    temp=TempCelsius((50 - 30) * 0.42 + 30),
                    panic=None,
                    threshold=None,
                    is_panic=False,
                    is_threshold=False,
                ),
                TempName("hdd"):
                None,  # a failing sensor
            },
            {
                MappingName("all"):
                FansTempsRelation(
                    temps=[TempName("cpu"), TempName("hdd")],
                    fans=[FanSpeedModifier(fan=FanName("rear"), modifier=1.0)],
Esempio n. 18
0
def _parse_temps(
    config: configparser.ConfigParser, hddtemp: str
) -> Tuple[Mapping[TempName, Temp], Mapping[TempName, Actions]]:
    temps = {}  # type: Dict[TempName, Temp]
    temp_commands = {}  # type: Dict[TempName, Actions]
    for section_name in config.sections():
        section_name_parts = section_name.split(":", 1)

        if section_name_parts[0].strip().lower() != "temp":
            continue

        temp_name = TempName(section_name_parts[1].strip())
        temp = config[section_name]
        keys = set(temp.keys())

        actions_panic = AlertCommands(
            enter_cmd=first_not_none(temp.get("panic_enter_cmd")),
            leave_cmd=first_not_none(temp.get("panic_leave_cmd")),
        )
        keys.discard("panic_enter_cmd")
        keys.discard("panic_leave_cmd")

        actions_threshold = AlertCommands(
            enter_cmd=first_not_none(temp.get("threshold_enter_cmd")),
            leave_cmd=first_not_none(temp.get("threshold_leave_cmd")),
        )
        keys.discard("threshold_enter_cmd")
        keys.discard("threshold_leave_cmd")

        panic = TempCelsius(temp.getfloat("panic"))
        threshold = TempCelsius(temp.getfloat("threshold"))
        min = TempCelsius(temp.getfloat("min"))
        max = TempCelsius(temp.getfloat("max"))
        keys.discard("panic")
        keys.discard("threshold")
        keys.discard("min")
        keys.discard("max")

        type = temp["type"]
        keys.discard("type")

        if type == "file":
            t = FileTemp(temp["path"],
                         min=min,
                         max=max,
                         panic=panic,
                         threshold=threshold)  # type: Temp
            keys.discard("path")
        elif type == "hdd":
            if min is None or max is None:
                raise RuntimeError(
                    "hdd temp '%s' doesn't define the mandatory `min` and `max` temps"
                    % temp_name)
            t = HDDTemp(
                temp["path"],
                min=min,
                max=max,
                panic=panic,
                threshold=threshold,
                hddtemp_bin=hddtemp,
            )
            keys.discard("path")
        elif type == "exec":
            t = CommandTemp(temp["command"],
                            min=min,
                            max=max,
                            panic=panic,
                            threshold=threshold)
            keys.discard("command")
        else:
            raise RuntimeError("Unsupported temp type '%s' for temp '%s'" %
                               (type, temp_name))

        if keys:
            raise RuntimeError("Unknown options in the [%s] section: %s" %
                               (section_name, keys))

        if temp_name in temps:
            raise RuntimeError("Duplicate temp section declaration for '%s'" %
                               temp_name)
        temps[temp_name] = t
        temp_commands[temp_name] = Actions(panic=actions_panic,
                                           threshold=actions_threshold)

    if not temps:
        raise RuntimeError(
            "No temps found in the config, at least 1 must be specified")
    return temps, temp_commands