forked from guydavis/chiadog
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
33 changed files
with
1,128 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
# secrets | ||
config.yaml | ||
|
||
# dev files | ||
.idea | ||
venv | ||
|
||
# Byte-compiled / optimized / DLL files | ||
__pycache__/ | ||
*.py[cod] | ||
*$py.class |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
<p align="center"> | ||
<img src="https://raw.githubusercontent.com/martomi/chiadog/main/logo.jpg" /> | ||
</p> | ||
<p align="center"> | ||
Photo by <a href="https://unsplash.com/@tukacszoltan1984?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Zoltan Tukacs</a> on <a href="https://unsplash.com/s/photos/dog-grass?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a> | ||
</p> | ||
|
||
-------------------------------------------------------------------- | ||
|
||
# Watchdog for your Chia farm | ||
|
||
So you've become a [Chia](https://www.chia.net) farmer and are now looking to improve the uptime of your farm? How about | ||
some automated monitoring that sends notifications directly to your phone? | ||
|
||
This tool parses the `debug.log` generated by the chia process and runs various checks to determine the health of your | ||
farming operations. It can detect if your node has lost sync and the farmer is no longer participating in challenges, or | ||
if one of your external HDDs disconnected and your harvester doesn't have access to the full amount of plots you have. | ||
|
||
## List of checked conditions: | ||
|
||
- Time since last eligible farming event | ||
- Non-decreasing number of total plots farmed | ||
- Quick plot seeking time when responding to a challenge | ||
- Whether a challenge proof was found | ||
|
||
## Smartphone Notifications | ||
|
||
In case of deviations from the expected values, notifications with low, normal, or high priority can be triggered. | ||
Currently the tool supports integration with [Pushover](https://pushover.net/) | ||
which is available for both Android and iOS. High priority notifications can be configured from the Pushover app to | ||
overwrite any Silence or Do-Not-Disturb modes on your phone and sound a loud alarm at any time of the day to make you | ||
aware of any issues in a timely manner. | ||
|
||
# Getting started | ||
|
||
**So far only tested on Ubuntu 20.04. Requires Python 3.7+.** | ||
|
||
1. Clone the repository | ||
|
||
``` | ||
git clone https://github.com/martomi/chiadog.git | ||
cd chiadog | ||
``` | ||
|
||
2. Create virtual env (Recommended) | ||
|
||
``` | ||
python3 -m venv venv | ||
. ./venv/bin/activate | ||
``` | ||
|
||
3. Install the dependencies: | ||
|
||
``` | ||
pip3 install -r requirements.txt | ||
``` | ||
|
||
4. Copy the example config file | ||
|
||
``` | ||
cp config-example.yaml config.yaml | ||
``` | ||
|
||
5. Edit the configuration file | ||
- Enable Pushover Notifier | ||
- Set your Pushover API token | ||
- Set your Pushover user key | ||
- Double-check the path to your chia logs | ||
|
||
6. Start the watchdog | ||
|
||
``` | ||
python3 main.py --config config.yaml | ||
``` | ||
|
||
7. Verify that your plots are detected. Within a few seconds you should see INFO log: | ||
|
||
``` | ||
Detected new plots. Farming with 42 plots. | ||
``` | ||
|
||
## Troubleshooting | ||
|
||
The best way to check that everything works on your system is to run the unit tests: | ||
|
||
``` | ||
PUSHOVER_API_TOKEN=<your_token> PUSHOVER_USER_KEY=<your_key> python3 -m unittest | ||
``` | ||
|
||
If your Pushover API token and user key are correctly set in the snippet above, you should receive notifications on your | ||
phone and the tests should complete with an `OK` status: | ||
|
||
You can also enable more verbose logging from `config.yaml` by changing `INFO` to `DEBUG`. Then you'll be able to see | ||
logs for every keep-alive event from the harvester. | ||
|
||
# Extending & Contributing | ||
|
||
Contributions are welcome, I've added a good amount of doxygen style comments and structured the code in a way that | ||
should make this easier. Happy to provide feedback on PRs. | ||
|
||
## Modular and extendable design | ||
|
||
Here are some jump-off points where you can start adding extensions: | ||
|
||
- additional [condition checks](src/chia_log/handlers/harvester_activity_handler.py) e.g. time since last eligible plot | ||
- additional [parsers](src/chia_log/parsers) to monitor other parts of the log output (e.g. timelord) | ||
- additional [log consumers](src/chia_log/log_consumer.py) to support watchdog over the network (NetworkLogConsumer) | ||
- additional [notifiers](src/notifier) to support notifications over Slack, WhatsApp, E-mail, etc. | ||
|
||
## Further feature wishes | ||
|
||
- Handlers for automated recovery | ||
- auto-restart chia processes if node cannot re-sync | ||
- auto-restart xHCI devices to recover external HDDs |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
log_level: INFO | ||
chia_logs: | ||
file_log_consumer: | ||
enable: true | ||
file_path: '~/.chia/mainnet/log/debug.log' | ||
notifier: | ||
pushover: | ||
enable: false | ||
api_token: 'dummy_token' | ||
user_key: 'dummy_key' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
# std | ||
import argparse | ||
import logging | ||
import signal | ||
from pathlib import Path | ||
|
||
# project | ||
from src.chia_log.log_consumer import FileLogConsumer | ||
from src.chia_log.log_handler import LogHandler | ||
from src.config import Config | ||
from src.notifier.keep_alive_monitor import KeepAliveMonitor | ||
from src.notifier.notify_manager import NotifyManager | ||
|
||
|
||
def parse_arguments(): | ||
parser = argparse.ArgumentParser(description='ChiaFarmWatch: Watch your crops ' | ||
'with a piece in mind for the yield.') | ||
parser.add_argument('--config', required=True, type=str, help='path to config.yaml') | ||
return parser.parse_args() | ||
|
||
|
||
def get_log_level(log_level: str) -> int: | ||
if log_level == "CRITICAL": | ||
return logging.CRITICAL | ||
if log_level == "ERROR": | ||
return logging.ERROR | ||
if log_level == "WARNING": | ||
return logging.WARNING | ||
if log_level == "INFO": | ||
return logging.INFO | ||
if log_level == "DEBUG": | ||
return logging.DEBUG | ||
|
||
logging.warning(f"Unsupported log level: {log_level}. Fallback to INFO level.") | ||
return logging.INFO | ||
|
||
|
||
if __name__ == '__main__': | ||
# Parse config and configure logger | ||
args = parse_arguments() | ||
config = Config(Path(args.config)) | ||
log_level = get_log_level(config.get_log_level_config()) | ||
logging.basicConfig( | ||
format='[%(asctime)s] [%(levelname)8s] --- %(message)s (%(filename)s:%(lineno)s)', | ||
level=log_level, datefmt='%Y-%m-%d %H:%M:%S') | ||
|
||
# Using file log consumer by default TODO: make configurable | ||
chia_logs_config = config.get_chia_logs_config() | ||
if not chia_logs_config["file_log_consumer"]["enable"]: | ||
logging.warning("Currently only file log consumer is supported. Cannot be disabled.") | ||
log_path = Path(chia_logs_config["file_log_consumer"]["file_path"]) | ||
log_consumer = FileLogConsumer(log_path=log_path) | ||
|
||
# Keep a reference here so we can stop the thread | ||
# TODO: read keep-alive thresholds from config | ||
keep_alive_monitor = KeepAliveMonitor() | ||
|
||
# Notify manager is responsible for the lifecycle of all notifiers | ||
notify_manager = NotifyManager( | ||
config=config.get_notifier_config(), | ||
keep_alive_monitor=keep_alive_monitor | ||
) | ||
|
||
# Link stuff up in the log handler | ||
# Pipeline: Consume -> Handle -> Notify | ||
log_handler = LogHandler( | ||
log_consumer=log_consumer, | ||
notify_manager=notify_manager | ||
) | ||
|
||
|
||
def interrupt(signal_number, frame): | ||
if signal_number == signal.SIGINT: | ||
logging.info("Received interrupt. Stopping...") | ||
log_consumer.stop() | ||
keep_alive_monitor.stop() | ||
exit(0) | ||
|
||
|
||
signal.signal(signal.SIGINT, interrupt) | ||
signal.pause() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
python-dateutil~=2.8.1 | ||
PyYAML==5.3.1 |
Empty file.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
"""A LogHandler is a single responsibility | ||
class that analyses a specific part of the log | ||
stream and generates noteworthy events. | ||
A single log handler could check for multiple | ||
conditions. It delegates the task to ConditionCheckers. | ||
""" | ||
|
||
# std | ||
from abc import ABC, abstractmethod | ||
from typing import List | ||
|
||
# project | ||
from src.notifier import Event | ||
|
||
|
||
class LogHandler(ABC): | ||
"""Common interface for log handlers | ||
""" | ||
|
||
@abstractmethod | ||
def handle(self, logs: str) -> List[Event]: | ||
pass | ||
|
||
|
||
class ConditionChecker(ABC): | ||
"""Common interface for condition checkers""" | ||
|
||
@abstractmethod | ||
def check(self, obj: object) -> Event: | ||
pass |
Oops, something went wrong.