Implemented collector utility.
This commit is contained in:
		
							
								
								
									
										145
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,145 @@
 | 
				
			|||||||
 | 
					# Byte-compiled / optimized / DLL files
 | 
				
			||||||
 | 
					__pycache__/
 | 
				
			||||||
 | 
					*.py[cod]
 | 
				
			||||||
 | 
					*$py.class
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# C extensions
 | 
				
			||||||
 | 
					*.so
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Distribution / packaging
 | 
				
			||||||
 | 
					.Python
 | 
				
			||||||
 | 
					build/
 | 
				
			||||||
 | 
					develop-eggs/
 | 
				
			||||||
 | 
					dist/
 | 
				
			||||||
 | 
					downloads/
 | 
				
			||||||
 | 
					eggs/
 | 
				
			||||||
 | 
					.eggs/
 | 
				
			||||||
 | 
					lib/
 | 
				
			||||||
 | 
					lib64/
 | 
				
			||||||
 | 
					parts/
 | 
				
			||||||
 | 
					sdist/
 | 
				
			||||||
 | 
					var/
 | 
				
			||||||
 | 
					wheels/
 | 
				
			||||||
 | 
					share/python-wheels/
 | 
				
			||||||
 | 
					*.egg-info/
 | 
				
			||||||
 | 
					.installed.cfg
 | 
				
			||||||
 | 
					*.egg
 | 
				
			||||||
 | 
					MANIFEST
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# PyInstaller
 | 
				
			||||||
 | 
					#  Usually these files are written by a python script from a template
 | 
				
			||||||
 | 
					#  before PyInstaller builds the exe, so as to inject date/other infos into it.
 | 
				
			||||||
 | 
					*.manifest
 | 
				
			||||||
 | 
					*.spec
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Installer logs
 | 
				
			||||||
 | 
					pip-log.txt
 | 
				
			||||||
 | 
					pip-delete-this-directory.txt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Unit test / coverage reports
 | 
				
			||||||
 | 
					htmlcov/
 | 
				
			||||||
 | 
					.tox/
 | 
				
			||||||
 | 
					.nox/
 | 
				
			||||||
 | 
					.coverage
 | 
				
			||||||
 | 
					.coverage.*
 | 
				
			||||||
 | 
					.cache
 | 
				
			||||||
 | 
					nosetests.xml
 | 
				
			||||||
 | 
					coverage.xml
 | 
				
			||||||
 | 
					*.cover
 | 
				
			||||||
 | 
					*.py,cover
 | 
				
			||||||
 | 
					.hypothesis/
 | 
				
			||||||
 | 
					.pytest_cache/
 | 
				
			||||||
 | 
					cover/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Translations
 | 
				
			||||||
 | 
					*.mo
 | 
				
			||||||
 | 
					*.pot
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Django stuff:
 | 
				
			||||||
 | 
					*.log
 | 
				
			||||||
 | 
					local_settings.py
 | 
				
			||||||
 | 
					db.sqlite3
 | 
				
			||||||
 | 
					db.sqlite3-journal
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Flask stuff:
 | 
				
			||||||
 | 
					instance/
 | 
				
			||||||
 | 
					.webassets-cache
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Scrapy stuff:
 | 
				
			||||||
 | 
					.scrapy
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Sphinx documentation
 | 
				
			||||||
 | 
					docs/_build/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# PyBuilder
 | 
				
			||||||
 | 
					.pybuilder/
 | 
				
			||||||
 | 
					target/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Jupyter Notebook
 | 
				
			||||||
 | 
					.ipynb_checkpoints
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# IPython
 | 
				
			||||||
 | 
					profile_default/
 | 
				
			||||||
 | 
					ipython_config.py
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# pyenv
 | 
				
			||||||
 | 
					#   For a library or package, you might want to ignore these files since the code is
 | 
				
			||||||
 | 
					#   intended to run in multiple environments; otherwise, check them in:
 | 
				
			||||||
 | 
					# .python-version
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# pipenv
 | 
				
			||||||
 | 
					#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
 | 
				
			||||||
 | 
					#   However, in case of collaboration, if having platform-specific dependencies or dependencies
 | 
				
			||||||
 | 
					#   having no cross-platform support, pipenv may install dependencies that don't work, or not
 | 
				
			||||||
 | 
					#   install all needed dependencies.
 | 
				
			||||||
 | 
					#Pipfile.lock
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# PEP 582; used by e.g. github.com/David-OConnor/pyflow
 | 
				
			||||||
 | 
					__pypackages__/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Celery stuff
 | 
				
			||||||
 | 
					celerybeat-schedule
 | 
				
			||||||
 | 
					celerybeat.pid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# SageMath parsed files
 | 
				
			||||||
 | 
					*.sage.py
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Environments
 | 
				
			||||||
 | 
					.env
 | 
				
			||||||
 | 
					.venv
 | 
				
			||||||
 | 
					env/
 | 
				
			||||||
 | 
					venv/
 | 
				
			||||||
 | 
					ENV/
 | 
				
			||||||
 | 
					env.bak/
 | 
				
			||||||
 | 
					venv.bak/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Spyder project settings
 | 
				
			||||||
 | 
					.spyderproject
 | 
				
			||||||
 | 
					.spyproject
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Rope project settings
 | 
				
			||||||
 | 
					.ropeproject
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# mkdocs documentation
 | 
				
			||||||
 | 
					/site
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# mypy
 | 
				
			||||||
 | 
					.mypy_cache/
 | 
				
			||||||
 | 
					.dmypy.json
 | 
				
			||||||
 | 
					dmypy.json
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Pyre type checker
 | 
				
			||||||
 | 
					.pyre/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# pytype static type analyzer
 | 
				
			||||||
 | 
					.pytype/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Cython debug symbols
 | 
				
			||||||
 | 
					cython_debug/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.vscode
 | 
				
			||||||
 | 
					*.code-workspace
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Local History for Visual Studio Code
 | 
				
			||||||
 | 
					.history/
 | 
				
			||||||
							
								
								
									
										62
									
								
								collector.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								collector.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,62 @@
 | 
				
			|||||||
 | 
					import config
 | 
				
			||||||
 | 
					import database
 | 
				
			||||||
 | 
					import signal
 | 
				
			||||||
 | 
					from threading import Event
 | 
				
			||||||
 | 
					from plugins.cpu_plugin import CpuPlugin
 | 
				
			||||||
 | 
					from plugins.memory_plugin import MemoryPlugin
 | 
				
			||||||
 | 
					from plugins.disk_plugin import DiskPlugin
 | 
				
			||||||
 | 
					from plugins.network_plugin import NetworkPlugin
 | 
				
			||||||
 | 
					from plugins.temperatures_plugin import TemperaturesPlugin
 | 
				
			||||||
 | 
					from plugins.ping_plugin import PingPlugin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Collector(object):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self):
 | 
				
			||||||
 | 
					        self.plugins = [
 | 
				
			||||||
 | 
					            CpuPlugin(),
 | 
				
			||||||
 | 
					            MemoryPlugin(),
 | 
				
			||||||
 | 
					            DiskPlugin(),
 | 
				
			||||||
 | 
					            NetworkPlugin(),
 | 
				
			||||||
 | 
					            TemperaturesPlugin(),
 | 
				
			||||||
 | 
					            PingPlugin()
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					        self.event = Event()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def collect_models(self):
 | 
				
			||||||
 | 
					        models = []
 | 
				
			||||||
 | 
					        for plugin in self.plugins:
 | 
				
			||||||
 | 
					            models.extend(plugin.models)
 | 
				
			||||||
 | 
					        return models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def initialize(self):
 | 
				
			||||||
 | 
					        models = self.collect_models()
 | 
				
			||||||
 | 
					        database.initialize_db()
 | 
				
			||||||
 | 
					        database.DB.create_tables(models)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        signal.signal(signal.SIGHUP, self.abort)
 | 
				
			||||||
 | 
					        signal.signal(signal.SIGTERM, self.abort)
 | 
				
			||||||
 | 
					        signal.signal(signal.SIGINT, self.abort)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def run(self):
 | 
				
			||||||
 | 
					        self.initialize()
 | 
				
			||||||
 | 
					        print(f'Started.')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        while not self.event.is_set():
 | 
				
			||||||
 | 
					            for plugin in self.plugins:
 | 
				
			||||||
 | 
					                # try:
 | 
				
			||||||
 | 
					                plugin.execute()
 | 
				
			||||||
 | 
					                # except BaseException as ex:
 | 
				
			||||||
 | 
					                    # print(ex)
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            self.event.wait(config.INTERVAL)
 | 
				
			||||||
 | 
					            # TODO: calculate wait time based on execution time
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        print(f'Stopped.')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def abort(self, signum, frame):
 | 
				
			||||||
 | 
					        print(f'Received signal {signum}, aborting...')
 | 
				
			||||||
 | 
					        self.event.set()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == "__main__":
 | 
				
			||||||
 | 
					    Collector().run()
 | 
				
			||||||
							
								
								
									
										51
									
								
								config.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								config.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,51 @@
 | 
				
			|||||||
 | 
					# Collect interval in seconds
 | 
				
			||||||
 | 
					INTERVAL=30
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Database URL, which defines connection settings.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# sqlite:///my_database.db 
 | 
				
			||||||
 | 
					#   will create a SqliteDatabase instance for the file my_database.db in the current directory.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# sqlite:///:memory:
 | 
				
			||||||
 | 
					#   will create an in-memory SqliteDatabase instance.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# postgresql://postgres:my_password@localhost:5432/my_database 
 | 
				
			||||||
 | 
					#   will create a PostgresqlDatabase instance. A username and password are provided, as well as the host and port to connect to.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# mysql://user:passwd@ip:port/my_db 
 | 
				
			||||||
 | 
					#   will create a MySQLDatabase instance for the local MySQL database my_db.
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# mysql+pool://user:passwd@ip:port/my_db?max_connections=20&stale_timeout=300 
 | 
				
			||||||
 | 
					#   will create a PooledMySQLDatabase instance for the local MySQL database my_db with max_connections set to 20 and a
 | 
				
			||||||
 | 
					#   stale_timeout setting of 300 seconds.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DATABASE_URL = 'sqlite:///data.db'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Plugin configuration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### CPU
 | 
				
			||||||
 | 
					# Store statistics per CPU, not only combined
 | 
				
			||||||
 | 
					CPU_PER_CPU = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Disk
 | 
				
			||||||
 | 
					# How often to poll space information, as a number of INTERVALs
 | 
				
			||||||
 | 
					DISK_SPACE_FREQUENCY = 10
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Temperatures
 | 
				
			||||||
 | 
					# If true, fahrenheit is used, otherwise celsius
 | 
				
			||||||
 | 
					TEMPERATURE_USE_FAHRENHEIT = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Ping
 | 
				
			||||||
 | 
					# Ping hosts
 | 
				
			||||||
 | 
					PING_HOSTS = [
 | 
				
			||||||
 | 
					    '10.0.0.1',
 | 
				
			||||||
 | 
					    '192.168.0.1',
 | 
				
			||||||
 | 
					    '1.1.1.1',
 | 
				
			||||||
 | 
					    'google.com',
 | 
				
			||||||
 | 
					    'bing.com'
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# How often to send pings, as a number of INTERVALs
 | 
				
			||||||
 | 
					PING_FREQUENCY = 10
 | 
				
			||||||
							
								
								
									
										13
									
								
								database.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								database.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
				
			|||||||
 | 
					from peewee import DatabaseProxy, Model
 | 
				
			||||||
 | 
					from playhouse.db_url import connect
 | 
				
			||||||
 | 
					import config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DB = DatabaseProxy()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class BaseModel(Model):
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        database = DB
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def initialize_db():
 | 
				
			||||||
 | 
					    db = connect(config.DATABASE_URL)
 | 
				
			||||||
 | 
					    DB.initialize(db)
 | 
				
			||||||
							
								
								
									
										53
									
								
								plugins/cpu_plugin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								plugins/cpu_plugin.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,53 @@
 | 
				
			|||||||
 | 
					from datetime import datetime
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import psutil
 | 
				
			||||||
 | 
					from peewee import *
 | 
				
			||||||
 | 
					from playhouse.shortcuts import model_to_dict
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import config
 | 
				
			||||||
 | 
					from database import BaseModel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .plugin import Plugin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Cpu(BaseModel):
 | 
				
			||||||
 | 
					    time = DateTimeField(index=True, default=datetime.now)
 | 
				
			||||||
 | 
					    cpu = SmallIntegerField(null=True)
 | 
				
			||||||
 | 
					    idle_pct = FloatField(null=False)
 | 
				
			||||||
 | 
					    user_pct = FloatField(null=False)
 | 
				
			||||||
 | 
					    system_pct = FloatField(null=False)
 | 
				
			||||||
 | 
					    nice_pct = FloatField(null=True)
 | 
				
			||||||
 | 
					    iowait_pct = FloatField(null=True)
 | 
				
			||||||
 | 
					    irq_pct = FloatField(null=True)
 | 
				
			||||||
 | 
					    softirq_pct = FloatField(null=True)
 | 
				
			||||||
 | 
					    freq_min = FloatField(null=True)
 | 
				
			||||||
 | 
					    freq_current = FloatField(null=True)
 | 
				
			||||||
 | 
					    freq_max = FloatField(null=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CpuPlugin(Plugin):
 | 
				
			||||||
 | 
					    models = [Cpu]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def store(self, cpu, times, freq):
 | 
				
			||||||
 | 
					        entry = Cpu()
 | 
				
			||||||
 | 
					        entry.cpu = cpu
 | 
				
			||||||
 | 
					        entry.idle_pct = times.idle
 | 
				
			||||||
 | 
					        entry.user_pct = times.user
 | 
				
			||||||
 | 
					        entry.system_pct = times.system
 | 
				
			||||||
 | 
					        entry.nice_pct = getattr(times, 'nice', None)
 | 
				
			||||||
 | 
					        entry.iowait_pct = getattr(times, 'iowait', None)
 | 
				
			||||||
 | 
					        entry.irq_pct = getattr(times, 'irq', getattr(times, 'interrupt', None))
 | 
				
			||||||
 | 
					        entry.softirq_pct = getattr(times, 'softirq', None)
 | 
				
			||||||
 | 
					        entry.freq_min = getattr(freq, 'min', None)
 | 
				
			||||||
 | 
					        entry.freq_current = getattr(freq, 'current', None)
 | 
				
			||||||
 | 
					        entry.freq_max = getattr(freq, 'max', None)
 | 
				
			||||||
 | 
					        entry.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def execute(self):
 | 
				
			||||||
 | 
					        self.store(None, psutil.cpu_times_percent(percpu=False), psutil.cpu_freq(percpu=False))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if config.CPU_PER_CPU:
 | 
				
			||||||
 | 
					            times = psutil.cpu_times_percent(percpu=True)
 | 
				
			||||||
 | 
					            freqs = psutil.cpu_freq(percpu=True)
 | 
				
			||||||
 | 
					            for i in range(len(times)):
 | 
				
			||||||
 | 
					                self.store(i, times[i], freqs[i])
 | 
				
			||||||
							
								
								
									
										74
									
								
								plugins/disk_plugin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								plugins/disk_plugin.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,74 @@
 | 
				
			|||||||
 | 
					from collections import namedtuple
 | 
				
			||||||
 | 
					from datetime import datetime
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import psutil
 | 
				
			||||||
 | 
					from peewee import *
 | 
				
			||||||
 | 
					from playhouse.shortcuts import model_to_dict
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import config
 | 
				
			||||||
 | 
					from database import BaseModel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .plugin import Plugin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DiskUsage(BaseModel):
 | 
				
			||||||
 | 
					    time = DateTimeField(index=True, default=datetime.now)
 | 
				
			||||||
 | 
					    partition = TextField(null=False)
 | 
				
			||||||
 | 
					    mountpoint = TextField(null=False)
 | 
				
			||||||
 | 
					    total = BigIntegerField(null=False)
 | 
				
			||||||
 | 
					    used = BigIntegerField(null=False)
 | 
				
			||||||
 | 
					    free = BigIntegerField(null=False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DiskIO(BaseModel):
 | 
				
			||||||
 | 
					    time = DateTimeField(index=True, default=datetime.now)
 | 
				
			||||||
 | 
					    disk = TextField(null=True)
 | 
				
			||||||
 | 
					    read_count = FloatField(null=False)     # all values are per second
 | 
				
			||||||
 | 
					    write_count = FloatField(null=False)
 | 
				
			||||||
 | 
					    read_speed = FloatField(null=False)
 | 
				
			||||||
 | 
					    write_speed = FloatField(null=False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DiskPlugin(Plugin):
 | 
				
			||||||
 | 
					    models = [DiskUsage, DiskIO]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self):
 | 
				
			||||||
 | 
					        self.__i = 0
 | 
				
			||||||
 | 
					        self.__previous_io = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def store_io(self, disk, current):
 | 
				
			||||||
 | 
					        previous = self.__previous_io.get(disk, current)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        entry = DiskIO()
 | 
				
			||||||
 | 
					        entry.disk = disk
 | 
				
			||||||
 | 
					        entry.read_count = (current.read_count - previous.read_count) / config.INTERVAL
 | 
				
			||||||
 | 
					        entry.write_count = (current.write_count - previous.write_count) / config.INTERVAL
 | 
				
			||||||
 | 
					        entry.read_speed = (current.read_bytes - previous.read_bytes) / config.INTERVAL
 | 
				
			||||||
 | 
					        entry.write_speed = (current.write_bytes - previous.write_bytes) / config.INTERVAL
 | 
				
			||||||
 | 
					        entry.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.__previous_io[disk] = current
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def execute(self):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Collect disk usage
 | 
				
			||||||
 | 
					        if (self.__i % config.DISK_SPACE_FREQUENCY) == 0:
 | 
				
			||||||
 | 
					            for partition in psutil.disk_partitions():
 | 
				
			||||||
 | 
					                usage = psutil.disk_usage(partition.mountpoint)
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                entry = DiskUsage()
 | 
				
			||||||
 | 
					                entry.partition = partition.device
 | 
				
			||||||
 | 
					                entry.mountpoint = partition.mountpoint
 | 
				
			||||||
 | 
					                entry.total = usage.total
 | 
				
			||||||
 | 
					                entry.used = usage.used
 | 
				
			||||||
 | 
					                entry.free = usage.free
 | 
				
			||||||
 | 
					                entry.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Collect IO
 | 
				
			||||||
 | 
					        self.store_io(None, psutil.disk_io_counters(perdisk=False))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        io_reads = psutil.disk_io_counters(perdisk=True)
 | 
				
			||||||
 | 
					        for disk, current in io_reads.items():
 | 
				
			||||||
 | 
					            self.store_io(disk, current)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.__i += 1
 | 
				
			||||||
							
								
								
									
										0
									
								
								plugins/finance_plugin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								plugins/finance_plugin.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										46
									
								
								plugins/memory_plugin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								plugins/memory_plugin.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
				
			|||||||
 | 
					from datetime import datetime
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import psutil
 | 
				
			||||||
 | 
					from peewee import *
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import config
 | 
				
			||||||
 | 
					from database import BaseModel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .plugin import Plugin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Memory(BaseModel):
 | 
				
			||||||
 | 
					    time = DateTimeField(index=True, default=datetime.now)
 | 
				
			||||||
 | 
					    total = BigIntegerField(null=False)
 | 
				
			||||||
 | 
					    available = BigIntegerField(null=False)
 | 
				
			||||||
 | 
					    used = BigIntegerField(null=False)
 | 
				
			||||||
 | 
					    free = BigIntegerField(null=False)
 | 
				
			||||||
 | 
					    active = BigIntegerField(null=True)
 | 
				
			||||||
 | 
					    inactive = BigIntegerField(null=True)
 | 
				
			||||||
 | 
					    buffers = BigIntegerField(null=True)
 | 
				
			||||||
 | 
					    cached = BigIntegerField(null=True)
 | 
				
			||||||
 | 
					    swap_total = BigIntegerField(null=False)
 | 
				
			||||||
 | 
					    swap_used = BigIntegerField(null=False)
 | 
				
			||||||
 | 
					    swap_free = BigIntegerField(null=False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MemoryPlugin(Plugin):
 | 
				
			||||||
 | 
					    models = [Memory]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def execute(self):
 | 
				
			||||||
 | 
					        vmem = psutil.virtual_memory()
 | 
				
			||||||
 | 
					        swap = psutil.swap_memory()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        entry = Memory()
 | 
				
			||||||
 | 
					        entry.total = vmem.total
 | 
				
			||||||
 | 
					        entry.available = vmem.available
 | 
				
			||||||
 | 
					        entry.used = vmem.used
 | 
				
			||||||
 | 
					        entry.free = vmem.free
 | 
				
			||||||
 | 
					        entry.active = getattr(vmem, 'active', None)
 | 
				
			||||||
 | 
					        entry.inactive = getattr(vmem, 'inactive', None)
 | 
				
			||||||
 | 
					        entry.buffers = getattr(vmem, 'buffers', None)
 | 
				
			||||||
 | 
					        entry.cached = getattr(vmem, 'cached', None)
 | 
				
			||||||
 | 
					        entry.swap_total = swap.total
 | 
				
			||||||
 | 
					        entry.swap_free = swap.free
 | 
				
			||||||
 | 
					        entry.swap_used = swap.used
 | 
				
			||||||
 | 
					        entry.save()
 | 
				
			||||||
							
								
								
									
										48
									
								
								plugins/network_plugin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								plugins/network_plugin.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,48 @@
 | 
				
			|||||||
 | 
					from collections import namedtuple
 | 
				
			||||||
 | 
					from datetime import datetime
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import psutil
 | 
				
			||||||
 | 
					from peewee import *
 | 
				
			||||||
 | 
					from playhouse.shortcuts import model_to_dict
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import config
 | 
				
			||||||
 | 
					from database import BaseModel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .plugin import Plugin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class NetworkIO(BaseModel):
 | 
				
			||||||
 | 
					    time = DateTimeField(index=True, default=datetime.now)
 | 
				
			||||||
 | 
					    nic = TextField(null=True)
 | 
				
			||||||
 | 
					    packets_sent = FloatField(null=False)     # all values are per second
 | 
				
			||||||
 | 
					    packets_recv = FloatField(null=False)
 | 
				
			||||||
 | 
					    bytes_sent = FloatField(null=False)
 | 
				
			||||||
 | 
					    bytes_recv = FloatField(null=False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class NetworkPlugin(Plugin):
 | 
				
			||||||
 | 
					    models = [NetworkIO]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self):
 | 
				
			||||||
 | 
					        self.__previous_io = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def store_io(self, nic, current):
 | 
				
			||||||
 | 
					        previous = self.__previous_io.get(nic, current)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        entry = NetworkIO()
 | 
				
			||||||
 | 
					        entry.nic = nic
 | 
				
			||||||
 | 
					        entry.packets_sent = (current.packets_sent - previous.packets_sent) / config.INTERVAL
 | 
				
			||||||
 | 
					        entry.packets_recv = (current.packets_recv - previous.packets_recv) / config.INTERVAL
 | 
				
			||||||
 | 
					        entry.bytes_sent = (current.bytes_sent - previous.bytes_sent) / config.INTERVAL
 | 
				
			||||||
 | 
					        entry.bytes_recv = (current.bytes_recv - previous.bytes_recv) / config.INTERVAL
 | 
				
			||||||
 | 
					        entry.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.__previous_io[nic] = current
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def execute(self):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.store_io(None, psutil.net_io_counters(pernic=False))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        io_reads = psutil.net_io_counters(pernic=True)
 | 
				
			||||||
 | 
					        for nic, current in io_reads.items():
 | 
				
			||||||
 | 
					            self.store_io(nic, current)
 | 
				
			||||||
							
								
								
									
										61
									
								
								plugins/ping_plugin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								plugins/ping_plugin.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,61 @@
 | 
				
			|||||||
 | 
					import asyncio
 | 
				
			||||||
 | 
					import re
 | 
				
			||||||
 | 
					import subprocess
 | 
				
			||||||
 | 
					from datetime import datetime
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import psutil
 | 
				
			||||||
 | 
					from peewee import *
 | 
				
			||||||
 | 
					from playhouse.shortcuts import model_to_dict
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import config
 | 
				
			||||||
 | 
					from database import BaseModel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .plugin import Plugin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Ping(BaseModel):
 | 
				
			||||||
 | 
					    time = DateTimeField(index=True, default=datetime.now)
 | 
				
			||||||
 | 
					    host = TextField(null=False)
 | 
				
			||||||
 | 
					    ping = FloatField(null=True) # null = timeout or error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PingPlugin(Plugin):
 | 
				
			||||||
 | 
					    models = [Ping]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self):
 | 
				
			||||||
 | 
					        self.__timeout = config.INTERVAL // 3
 | 
				
			||||||
 | 
					        self.__i = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def do_ping(self, host):
 | 
				
			||||||
 | 
					        command = ['ping', '-c', '1', '-W', str(self.__timeout), host]
 | 
				
			||||||
 | 
					        proc = await asyncio.create_subprocess_shell(' '.join(command), 
 | 
				
			||||||
 | 
					                                                     stdout=asyncio.subprocess.PIPE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        stdout,_ = await proc.communicate()
 | 
				
			||||||
 | 
					        stdout = stdout.decode()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        entry = Ping()
 | 
				
			||||||
 | 
					        entry.host = host
 | 
				
			||||||
 | 
					        entry.ping = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        match = re.search(r'time=([\d\.]+) ms', stdout)
 | 
				
			||||||
 | 
					        if match is not None:
 | 
				
			||||||
 | 
					            entry.ping = float(match.group(1))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        entry.save()
 | 
				
			||||||
 | 
					        print(model_to_dict(entry))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def execute_internal(self):
 | 
				
			||||||
 | 
					        await asyncio.gather(*[self.do_ping(host) for host in config.PING_HOSTS])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def execute(self):
 | 
				
			||||||
 | 
					        if (self.__i % config.PING_FREQUENCY) == 0:
 | 
				
			||||||
 | 
					            if getattr(asyncio, 'run', None) is not None:
 | 
				
			||||||
 | 
					                # Python 3.7+
 | 
				
			||||||
 | 
					                asyncio.run(self.execute_internal())
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                loop = asyncio.get_event_loop()
 | 
				
			||||||
 | 
					                loop.run_until_complete(self.execute_internal())
 | 
				
			||||||
 | 
					                loop.close()
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        self.__i += 1
 | 
				
			||||||
							
								
								
									
										10
									
								
								plugins/plugin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								plugins/plugin.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					from abc import ABC, abstractmethod
 | 
				
			||||||
 | 
					from typing import Tuple
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Plugin(ABC):
 | 
				
			||||||
 | 
					    models = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @abstractmethod
 | 
				
			||||||
 | 
					    def execute(self) -> None:
 | 
				
			||||||
 | 
					        pass
 | 
				
			||||||
							
								
								
									
										35
									
								
								plugins/temperatures_plugin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								plugins/temperatures_plugin.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
				
			|||||||
 | 
					from collections import namedtuple
 | 
				
			||||||
 | 
					from datetime import datetime
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import psutil
 | 
				
			||||||
 | 
					from peewee import *
 | 
				
			||||||
 | 
					from playhouse.shortcuts import model_to_dict
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import config
 | 
				
			||||||
 | 
					from database import BaseModel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .plugin import Plugin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Temperatures(BaseModel):
 | 
				
			||||||
 | 
					    time = DateTimeField(index=True, default=datetime.now)
 | 
				
			||||||
 | 
					    sensor = TextField(null=False)
 | 
				
			||||||
 | 
					    sensor_label = TextField(null=False)
 | 
				
			||||||
 | 
					    current = FloatField(null=False)     # all values are per second
 | 
				
			||||||
 | 
					    high = FloatField(null=False)
 | 
				
			||||||
 | 
					    critical = FloatField(null=False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TemperaturesPlugin(Plugin):
 | 
				
			||||||
 | 
					    models = [Temperatures]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def execute(self):
 | 
				
			||||||
 | 
					        for sensor, temps in psutil.sensors_temperatures(config.TEMPERATURE_USE_FAHRENHEIT).items():
 | 
				
			||||||
 | 
					            for temp in temps:
 | 
				
			||||||
 | 
					                entry = Temperatures()
 | 
				
			||||||
 | 
					                entry.sensor = sensor
 | 
				
			||||||
 | 
					                entry.sensor_label = temp.label
 | 
				
			||||||
 | 
					                entry.current = temp.current
 | 
				
			||||||
 | 
					                entry.high = temp.high
 | 
				
			||||||
 | 
					                entry.critical = temp.critical
 | 
				
			||||||
 | 
					                entry.save()
 | 
				
			||||||
							
								
								
									
										1
									
								
								requirements.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								requirements.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					peewee
 | 
				
			||||||
		Reference in New Issue
	
	Block a user