├── sysmon ├── __init__.py ├── tests │ ├── __init__.py │ ├── test_bytes2human.py │ └── test_sysmon_tags.py ├── templatetags │ ├── __init__.py │ └── sysmon_tags.py ├── admin.py └── templates │ └── sysmon │ └── index.html ├── testproject ├── __init__.py ├── testproject │ ├── __init__.py │ ├── urls.py │ ├── wsgi.py │ └── settings.py └── manage.py ├── requirements ├── base.txt └── testing.txt ├── MANIFEST.in ├── docs └── screen.png ├── .gitignore ├── pytest.ini ├── Makefile ├── setup.py └── README.md /sysmon/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /testproject/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sysmon/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sysmon/templatetags/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /testproject/testproject/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /requirements/base.txt: -------------------------------------------------------------------------------- 1 | psutil==1.2.1 2 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md 2 | include MANIFEST.in 3 | recursive-include sysmon/templates * -------------------------------------------------------------------------------- /docs/screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hakanzy/django-system-monitor/HEAD/docs/screen.png -------------------------------------------------------------------------------- /sysmon/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | admin.site.index_template = 'sysmon/index.html' 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | .cache 3 | .coverage 4 | django_system_monitor.egg-info 5 | htmlcov 6 | venv 7 | dist 8 | *.sqlite3 9 | -------------------------------------------------------------------------------- /requirements/testing.txt: -------------------------------------------------------------------------------- 1 | -r base.txt 2 | django 3 | pytest==2.3.4 4 | pytest-cov==1.6 5 | pytest-flakes==0.1 6 | pytest-pep8==1.0.4 7 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | 3 | python_files = 4 | test_*.py 5 | tests.py 6 | 7 | DJANGO_SETTINGS_MODULE = testproject.testproject.settings 8 | -------------------------------------------------------------------------------- /testproject/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "testproject.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /testproject/testproject/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, include, url 2 | 3 | from django.contrib import admin 4 | admin.autodiscover() 5 | 6 | urlpatterns = patterns('', 7 | # Examples: 8 | # url(r'^$', 'testproject.views.home', name='home'), 9 | # url(r'^blog/', include('blog.urls')), 10 | 11 | url(r'^admin/', include(admin.site.urls)), 12 | ) 13 | -------------------------------------------------------------------------------- /testproject/testproject/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for testproject project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.6/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "testproject.settings") 12 | 13 | from django.core.wsgi import get_wsgi_application 14 | application = get_wsgi_application() 15 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile 2 | venv/bin/activate: 3 | test -d venv || virtualenv venv 4 | . venv/bin/activate; pip install -r requirements/testing.txt 5 | touch venv/bin/activate 6 | 7 | venv: venv/bin/activate 8 | 9 | test: venv 10 | . venv/bin/activate; py.test sysmon/ 11 | 12 | pep8: venv 13 | . venv/bin/activate; py.test --pep8 --flakes sysmon/ 14 | 15 | cov: venv 16 | . venv/bin/activate; py.test --cov=sysmon/ --cov-report=term-missing --cov-report=html sysmon/ 17 | 18 | all: venv 19 | . venv/bin/activate; py.test --pep8 --flakes --cov=sysmon/ --cov-report=term-missing --cov-report=html sysmon/ 20 | 21 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | import os 3 | 4 | setup( 5 | name='django-system-monitor', 6 | version='0.5', 7 | author='Hakan OZAY', 8 | author_email='hakanzy@gmail.com', 9 | url='https://github.com/hakanzy/django-system-monitor', 10 | description='View simple system statistics in django admin panel', 11 | long_description=os.path.join(os.path.dirname(__file__), 'README.md'), 12 | packages=find_packages(exclude=[]), 13 | install_requires=[ 14 | 'psutil==1.2.1', 15 | ], 16 | include_package_data=True, 17 | classifiers=[ 18 | 'Development Status :: 4 - Beta', 19 | 'Environment :: Web Environment', 20 | 'Intended Audience :: Developers', 21 | 'License :: OSI Approved :: BSD License', 22 | 'Operating System :: OS Independent', 23 | 'Programming Language :: Python', 24 | 'Framework :: Django', 25 | ], 26 | ) 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | django-system-monitor 2 | ========================= 3 | 4 | Simple System Monitoring in Django Admin Panel 5 | 6 | Features 7 | ========================= 8 | 9 | - CPU Usage 10 | - Memory Usage 11 | - Disk Usage (with partitions) 12 | - Network Usage 13 | - TOP 10 memory used processes 14 | - Viewing only with django superuser 15 | - It currently supports Linux, Windows, OSX and FreeBSD (psutil supported) 16 | 17 | Requirements 18 | ========================= 19 | - psutil (https://pypi.python.org/pypi/psutil) 20 | 21 | Screenshots 22 | ========================= 23 |  24 | 25 | 26 | Installation/Usage 27 | ========================= 28 | 29 | - pip install django-system-monitor 30 | 31 | - After setup, add 'sysmon' to your INSTALLED_APPS, that's all. 32 | 33 | 34 | Authors 35 | ========================= 36 | 37 | Hakan OZAY, https://github.com/hakanzy/ 38 | 39 | 40 | Contributors 41 | ========================= 42 | 43 | Rafael Carício, https://github.com/rafaelcaricio 44 | 45 | Peter Heise, https://github.com/degenhard 46 | 47 | Cihan Okyay, https://github.com/cihann 48 | 49 | Fatih Erikli, https://github.com/fatiherikli 50 | 51 | John Sykora, https://github.com/jsykora 52 | 53 | Tuna VARGI, https://github.com/vargi 54 | -------------------------------------------------------------------------------- /sysmon/tests/test_bytes2human.py: -------------------------------------------------------------------------------- 1 | from sysmon.templatetags.sysmon_tags import bytes2human 2 | 3 | 4 | def kbyte(quantity): 5 | return 1024. * quantity 6 | 7 | 8 | def mega(quantity): 9 | return kbyte(1024.) * quantity 10 | 11 | 12 | def giga(quantity): 13 | return mega(1024) * quantity 14 | 15 | 16 | def tera(quantity): 17 | return giga(1024) * quantity 18 | 19 | 20 | def test_bytes(): 21 | assert bytes2human(0.) == '0.0bytes' 22 | assert bytes2human(0.1) == '0.1bytes' 23 | assert bytes2human(1.) == '1.0bytes' 24 | assert bytes2human(100.) == '100.0bytes' 25 | assert bytes2human(1023.9) == '1023.9bytes' 26 | 27 | 28 | def test_kb(): 29 | assert bytes2human(kbyte(1)) == '1.0KB' 30 | assert bytes2human(kbyte(2)) == '2.0KB' 31 | assert bytes2human(kbyte(1023.9)) == '1023.9KB' 32 | 33 | 34 | def test_mb(): 35 | assert bytes2human(mega(1.)) == '1.0MB' 36 | assert bytes2human(mega(123.)) == '123.0MB' 37 | assert bytes2human(mega(1023.9)) == '1023.9MB' 38 | 39 | 40 | def test_giga(): 41 | assert bytes2human(giga(1.)) == '1.0GB' 42 | assert bytes2human(giga(999.2)) == '999.2GB' 43 | assert bytes2human(giga(1023.9)) == '1023.9GB' 44 | 45 | 46 | def test_tera(): 47 | assert bytes2human(tera(1.)) == '1.0TB' 48 | assert bytes2human(tera(999.)) == '999.0TB' 49 | -------------------------------------------------------------------------------- /testproject/testproject/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for testproject project. 3 | 4 | For more information on this file, see 5 | https://docs.djangoproject.com/en/1.6/topics/settings/ 6 | 7 | For the full list of settings and their values, see 8 | https://docs.djangoproject.com/en/1.6/ref/settings/ 9 | """ 10 | 11 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 12 | import os 13 | BASE_DIR = os.path.dirname(os.path.dirname(__file__)) 14 | 15 | 16 | # Quick-start development settings - unsuitable for production 17 | # See https://docs.djangoproject.com/en/1.6/howto/deployment/checklist/ 18 | 19 | # SECURITY WARNING: keep the secret key used in production secret! 20 | SECRET_KEY = 'q@x05k=(lyob=mox$vpl!3t_&rld9utdmu##=5_nh=pj3u&7dw' 21 | 22 | # SECURITY WARNING: don't run with debug turned on in production! 23 | DEBUG = True 24 | 25 | TEMPLATE_DEBUG = True 26 | 27 | ALLOWED_HOSTS = [] 28 | 29 | 30 | # Application definition 31 | 32 | INSTALLED_APPS = ( 33 | 'django.contrib.admin', 34 | 'django.contrib.auth', 35 | 'django.contrib.contenttypes', 36 | 'django.contrib.sessions', 37 | 'django.contrib.messages', 38 | 'django.contrib.staticfiles', 39 | 'sysmon', 40 | ) 41 | 42 | MIDDLEWARE_CLASSES = ( 43 | 'django.contrib.sessions.middleware.SessionMiddleware', 44 | 'django.middleware.common.CommonMiddleware', 45 | 'django.middleware.csrf.CsrfViewMiddleware', 46 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 47 | 'django.contrib.messages.middleware.MessageMiddleware', 48 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 49 | ) 50 | 51 | ROOT_URLCONF = 'testproject.urls' 52 | 53 | WSGI_APPLICATION = 'testproject.wsgi.application' 54 | 55 | 56 | # Database 57 | # https://docs.djangoproject.com/en/1.6/ref/settings/#databases 58 | 59 | DATABASES = { 60 | 'default': { 61 | 'ENGINE': 'django.db.backends.sqlite3', 62 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 63 | } 64 | } 65 | 66 | # Internationalization 67 | # https://docs.djangoproject.com/en/1.6/topics/i18n/ 68 | 69 | LANGUAGE_CODE = 'en-us' 70 | 71 | TIME_ZONE = 'UTC' 72 | 73 | USE_I18N = True 74 | 75 | USE_L10N = True 76 | 77 | USE_TZ = True 78 | 79 | 80 | # Static files (CSS, JavaScript, Images) 81 | # https://docs.djangoproject.com/en/1.6/howto/static-files/ 82 | 83 | STATIC_URL = '/static/' 84 | -------------------------------------------------------------------------------- /sysmon/tests/test_sysmon_tags.py: -------------------------------------------------------------------------------- 1 | from sysmon.templatetags.sysmon_tags import SysMon 2 | 3 | 4 | def test_cpu_info_in_context(): 5 | context = {} 6 | SysMon().render(context) 7 | assert context.get('cpu_info', None) 8 | cpu_info = context['cpu_info'] 9 | assert hasattr(cpu_info, 'core') 10 | assert hasattr(cpu_info, 'used') 11 | 12 | 13 | def test_cpu_info_in_context_with_fr_language_code(settings): 14 | settings.LANGUAGE_CODE = 'fr-FR' 15 | context = {} 16 | SysMon().render(context) 17 | assert context.get('cpu_info', None) 18 | cpu_info = context['cpu_info'] 19 | assert ',' not in str(cpu_info.used) 20 | 21 | 22 | def test_mem_info_in_context(): 23 | context = {} 24 | SysMon().render(context) 25 | assert context.get('mem_info', None) 26 | mem_info = context['mem_info'] 27 | assert hasattr(mem_info, 'total') 28 | assert hasattr(mem_info, 'used') 29 | 30 | 31 | def test_mem_info_in_context_with_fr_language_code(settings): 32 | settings.LANGUAGE_CODE = 'fr-FR' 33 | context = {} 34 | SysMon().render(context) 35 | assert context.get('mem_info', None) 36 | mem_info = context['mem_info'] 37 | assert ',' not in str(mem_info.total) 38 | assert ',' not in str(mem_info.used) 39 | 40 | 41 | def test_disk_partitions_in_context(): 42 | context = {} 43 | SysMon().render(context) 44 | assert context.get('partitions', None) 45 | partitions = context['partitions'] 46 | assert type(partitions) == list 47 | first_partition = partitions[0] 48 | assert hasattr(first_partition, 'device') 49 | assert hasattr(first_partition, 'mountpoint') 50 | assert hasattr(first_partition, 'fstype') 51 | assert hasattr(first_partition, 'total') 52 | assert hasattr(first_partition, 'percent') 53 | 54 | 55 | def test_disk_partitions_in_context_with_fr_language_code(settings): 56 | settings.LANGUAGE_CODE = 'fr-FR' 57 | context = {} 58 | SysMon().render(context) 59 | assert context.get('partitions', None) 60 | partitions = context['partitions'] 61 | assert type(partitions) == list 62 | first_partition = partitions[0] 63 | assert ',' not in str(first_partition.total) 64 | assert ',' not in str(first_partition.percent) 65 | 66 | 67 | def test_networks_in_context(): 68 | context = {} 69 | SysMon().render(context) 70 | assert context.get('networks', None) 71 | networks = context['networks'] 72 | assert type(networks) == list 73 | first_network = networks[0] 74 | assert hasattr(first_network, 'device') 75 | assert hasattr(first_network, 'sent') 76 | assert hasattr(first_network, 'recv') 77 | assert hasattr(first_network, 'pkg_sent') 78 | assert hasattr(first_network, 'pkg_recv') 79 | 80 | 81 | def test_processes_in_context(): 82 | context = {} 83 | SysMon().render(context) 84 | assert context.get('processes', None) 85 | processes = context['processes'] 86 | assert type(processes) == list 87 | first_process = processes[0] 88 | assert hasattr(first_process, 'pid') 89 | assert hasattr(first_process, 'name') 90 | assert hasattr(first_process, 'status') 91 | assert hasattr(first_process, 'user') 92 | assert hasattr(first_process, 'memory') 93 | -------------------------------------------------------------------------------- /sysmon/templatetags/sysmon_tags.py: -------------------------------------------------------------------------------- 1 | from collections import namedtuple 2 | from django.contrib.humanize.templatetags.humanize import intcomma 3 | 4 | from django import template 5 | 6 | from psutil import AccessDenied 7 | 8 | 9 | cpuTuple = namedtuple('cpuTuple', 10 | 'core, used') 11 | 12 | memTuple = namedtuple('memTuple', 13 | 'total, used') 14 | 15 | diskPartTuple = namedtuple('diskPartTuple', 16 | 'device, mountpoint, fstype, total, percent') 17 | 18 | networkTuple = namedtuple('networkTuple', 19 | 'device, sent, recv, pkg_sent, pkg_recv') 20 | 21 | processTuple = namedtuple('processTuple', 22 | 'pid, name, status, user, memory') 23 | 24 | 25 | def bytes2human(num): 26 | for x in ['bytes', 'KB', 'MB', 'GB']: 27 | if num < 1024.0: 28 | return "%3.1f%s" % (num, x) 29 | num /= 1024.0 30 | return "%3.1f%s" % (num, 'TB') 31 | 32 | 33 | register = template.Library() 34 | 35 | 36 | def tofloat(value): 37 | try: 38 | return float(value) 39 | except ValueError: 40 | return 0 41 | 42 | register.filter(name='tofloat', filter_func=tofloat) 43 | 44 | 45 | class SysMon(template.Node): 46 | def render(self, context): 47 | try: 48 | import psutil as pu 49 | except: 50 | context['error_psutil'] = 'not_found' 51 | return '' 52 | 53 | # cpu 54 | cpu_info = cpuTuple( 55 | core=pu.NUM_CPUS, 56 | used=intcomma(pu.cpu_percent(), use_l10n=False)) 57 | 58 | # memory 59 | mem_info = memTuple( 60 | total=bytes2human(pu.TOTAL_PHYMEM), 61 | used=intcomma(pu.virtual_memory().percent, use_l10n=False)) 62 | 63 | # disk 64 | partitions = list() 65 | for part in pu.disk_partitions(): 66 | partitions.append( 67 | diskPartTuple( 68 | device=part.device, 69 | mountpoint=part.mountpoint, 70 | fstype=part.fstype, 71 | total=bytes2human( 72 | pu.disk_usage(part.mountpoint).total), 73 | percent=intcomma(pu.disk_usage(part.mountpoint).percent, 74 | use_l10n=False) 75 | ) 76 | ) 77 | 78 | # network 79 | networks = list() 80 | for k, v in pu.net_io_counters(pernic=True).items(): 81 | # Skip loopback interface 82 | if k == 'lo': 83 | continue 84 | 85 | networks.append( 86 | networkTuple( 87 | device=k, 88 | sent=bytes2human(v.bytes_sent), 89 | recv=bytes2human(v.bytes_recv), 90 | pkg_sent=v.packets_sent, 91 | pkg_recv=v.packets_recv)) 92 | 93 | # processes 94 | processes = list() 95 | for process in pu.process_iter(): 96 | 97 | try: 98 | percent = process.get_memory_percent() 99 | except AccessDenied: 100 | percent = "Access Denied" 101 | else: 102 | percent = int(percent) 103 | 104 | processes.append(processTuple( 105 | pid=process.pid, 106 | name=process.name, 107 | status=process.status, 108 | user=process.username, 109 | memory=percent)) 110 | 111 | processes_sorted = sorted( 112 | processes, key=lambda p: p.memory, reverse=True) 113 | 114 | all_stats = { 115 | 'cpu_info': cpu_info, 116 | 'mem_info': mem_info, 117 | 'partitions': partitions, 118 | 'networks': networks, 119 | 'processes': processes_sorted[:10], 120 | } 121 | 122 | context.update(all_stats) 123 | 124 | return '' 125 | 126 | 127 | @register.tag 128 | def get_system_stats(parser, token): 129 | return SysMon() 130 | -------------------------------------------------------------------------------- /sysmon/templates/sysmon/index.html: -------------------------------------------------------------------------------- 1 | {% extends "admin/index.html" %} 2 | {% load sysmon_tags %} 3 | {% block content %} 4 | {% get_system_stats %} 5 | 30 | 31 | {% if user.is_superuser %} 32 |
|
39 | - Please install psutil for view system statistics on this page
40 | 41 | $ pip install psutil 42 | |
43 |
44 |
|---|
| CPU | 64 |||||
|---|---|---|---|---|
|
67 |
68 | {{cpu_info.used}}% | Core(s): {{cpu_info.core}}
69 |
70 | |
71 | ||||
| Memory usage | 75 |||||
|
78 |
79 | {{mem_info.used}}% | Total: {{mem_info.total}}
80 |
81 | |
82 | ||||
| Disks | 86 |||||
|
90 |
91 | {{part.percent}}% | {{part.mountpoint}} | ({{part.total}})
92 |
93 | |
94 | ||||
| Networking | 99 |||||
| Device | 102 |Recv | 103 |Sent | 104 |Pkg Sent | 105 |Pkg Recv | 106 |
| {{network.device}} | 110 |{{network.recv}} | 111 |{{network.sent}} | 112 |{{network.pkg_sent}} | 113 |{{network.pkg_recv}} | 114 |
| Processes by memory usage | 119 |||||
| Name | 122 |PID | 123 |Mem | 124 |Status | 125 |User | 126 |
| {{process.name}} | 130 |{{process.pid}} | 131 |{{process.memory}}% | 132 |{{process.status}} | 133 |{{process.user}} | 134 |