init
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
.vscode/
|
||||
venv/
|
||||
__pycache__/
|
||||
3
collect.py
Executable file
3
collect.py
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from collector import *
|
||||
27
collector.json
Normal file
27
collector.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"graphite": {
|
||||
"server": "graphite.ducoterra.net",
|
||||
"port": 2003
|
||||
},
|
||||
"os": "osx",
|
||||
"data": {
|
||||
"storage": [
|
||||
{
|
||||
"path": "/Users/ducoterra",
|
||||
"name": "home"
|
||||
},
|
||||
{
|
||||
"path": ".",
|
||||
"name": "curdir"
|
||||
},
|
||||
{
|
||||
"path": "..",
|
||||
"name": "outdir"
|
||||
},
|
||||
{
|
||||
"path": "/Users",
|
||||
"name": "users"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
71
collector/__init__.py
Normal file
71
collector/__init__.py
Normal file
@@ -0,0 +1,71 @@
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import functools
|
||||
from collector.sender import send
|
||||
from collector.collector import *
|
||||
from collector.helper import *
|
||||
|
||||
CONFIG_FILE = os.getenv("COLLECTOR_CONF", "collector.json")
|
||||
REQUIRED = ["graphite","os","data"]
|
||||
VALID_DATAPOINTS = ["storage"]
|
||||
VALID_OS = ["linux", "osx"]
|
||||
REQUIRED_STORAGE = ["path", "name"]
|
||||
CONFIG = {}
|
||||
HOSTNAME = get_hostname()
|
||||
|
||||
with open(CONFIG_FILE) as f:
|
||||
CONFIG = json.loads(f.read())
|
||||
|
||||
# Check top level
|
||||
missing = get_missing(REQUIRED, CONFIG.keys())
|
||||
if len(missing) > 0:
|
||||
print(f"Missing: {', '.join(missing)}")
|
||||
sys.exit(1)
|
||||
|
||||
# Now that we know we have a graphite server
|
||||
sender = functools.partial(
|
||||
send,
|
||||
server = CONFIG["graphite"]["server"],
|
||||
port = CONFIG["graphite"]["port"]
|
||||
)
|
||||
|
||||
# Check data exists
|
||||
data = CONFIG["data"]
|
||||
if type(data) != dict:
|
||||
print("Config at 'data' missing values.")
|
||||
sys.exit(1)
|
||||
data_points = data.keys()
|
||||
if not len(data_points) > 0:
|
||||
print("No data points found in config.")
|
||||
sys.exit(1)
|
||||
unknown = get_missing(data_points, VALID_DATAPOINTS)
|
||||
if len(unknown) > 0:
|
||||
print(f"Unknown data: {', '.join(unknown)}")
|
||||
sys.exit(1)
|
||||
|
||||
# Check storage
|
||||
if "storage" in data_points:
|
||||
storage_data = data["storage"]
|
||||
# Check if storage is a list
|
||||
if type(storage_data) != list:
|
||||
print("storage data must be a list")
|
||||
sys.exit(1)
|
||||
# Check if storage is not empty
|
||||
if not len(storage_data) > 0:
|
||||
print("Storage data cannot be empty")
|
||||
sys.exit(1)
|
||||
for item in storage_data:
|
||||
# Check if the storage item is a dict
|
||||
if type(item) != dict:
|
||||
print(f"Storage data at {item} is not an object.")
|
||||
# Make sure each storage item has the required fields
|
||||
missing = get_missing(REQUIRED_STORAGE, item.keys())
|
||||
if len(missing) > 0:
|
||||
print(f"Storage data at {item} missing: {', '.join(missing)}")
|
||||
# Collect data
|
||||
else:
|
||||
name = item["name"]
|
||||
path = item["path"]
|
||||
storage_sender = functools.partial(sender, f"{HOSTNAME}.storage.{name}")
|
||||
thread(collect_storage, storage_sender, path)
|
||||
41
collector/collector.py
Normal file
41
collector/collector.py
Normal file
@@ -0,0 +1,41 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import threading
|
||||
import subprocess
|
||||
import re
|
||||
import functools
|
||||
import random
|
||||
import time
|
||||
|
||||
from .sender import send
|
||||
|
||||
# Disk Space
|
||||
# CPU
|
||||
# MEM
|
||||
# Temperatures
|
||||
# Network in/out
|
||||
# Backups (Plus 5 day forecast)
|
||||
|
||||
def handle_stdout(stdout):
|
||||
return stdout.decode("utf-8").strip()
|
||||
|
||||
def nice_run(*args, **kwargs):
|
||||
run = subprocess.run(*args, capture_output=True, **kwargs)
|
||||
return handle_stdout(run.stdout)
|
||||
|
||||
def get_hostname():
|
||||
return nice_run(["hostname"])
|
||||
|
||||
def clean_storage(storage):
|
||||
group = re.search(r"([0-9]*.?[0-9]+[\w]+)", storage)
|
||||
return group.group(1)
|
||||
|
||||
def collect_temp():
|
||||
pass
|
||||
|
||||
def collect_compute():
|
||||
pass
|
||||
|
||||
def collect_storage(path):
|
||||
storage = nice_run(["du", "-sk", path])
|
||||
return clean_storage(storage)
|
||||
14
collector/helper.py
Normal file
14
collector/helper.py
Normal file
@@ -0,0 +1,14 @@
|
||||
import functools
|
||||
import threading
|
||||
|
||||
def thread(func, after, *args, **kwargs):
|
||||
func = functools.partial(func, *args, **kwargs)
|
||||
call = lambda after, func: after(func())
|
||||
t = threading.Thread(
|
||||
target = call,
|
||||
args = (after, func)
|
||||
)
|
||||
t.start()
|
||||
|
||||
def get_missing(required, provided):
|
||||
return list(filter(lambda item: item not in provided, required))
|
||||
14
collector/sender.py
Normal file
14
collector/sender.py
Normal file
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import socket
|
||||
import time
|
||||
|
||||
def send(metric, data, server = None, port = None):
|
||||
received = int(time.time())
|
||||
|
||||
with socket.socket() as sock:
|
||||
msg = f"{metric} {data} {received}\n"
|
||||
# msg = f"mainframe.temp 40 {received}\n"
|
||||
msg = msg.encode("utf-8")
|
||||
sock.connect((server, port))
|
||||
sock.sendall(msg)
|
||||
Reference in New Issue
Block a user