From 6ed5c5f80f30b74645398ea8685720c8a4acd0d5 Mon Sep 17 00:00:00 2001 From: Akashdeep Dhar Date: Feb 04 2021 08:58:34 +0000 Subject: Merge pull request #2 from t0xic0der/container-monitoring Added container monitoring and statistics conveyance --- diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index eab4511..0000000 --- a/Dockerfile +++ /dev/null @@ -1,6 +0,0 @@ -FROM python:3.8-alpine -COPY . . -RUN apk add --no-cache gcc musl-dev linux-headers -RUN pip3 install -r requirements.txt -EXPOSE 6969 -CMD ["python3", "falc.py", "-p", "6969", "-6"] diff --git a/README.md b/README.md index 52e5827..54b4f70 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@

- +

SuperVisor

Driver Service

-

Reference driver endpoint service written in Falcon, Werkzeug and Psutil

+

Reference driver endpoint service written in Falcon, Werkzeug, Psutil and Docker

@@ -15,14 +15,21 @@

## Note -This project works as an intuitive remotely accessible system performance monitoring and task management service for servers and headless Raspberry Pi setups, with secure passcode-protected endpoints so that clients can connect via the [SuperVisor Frontend Service](https://github.com/t0xic0der/supervisor-frontend-service/) and manage their devices. +This project works as an intuitive remotely accessible system performance monitoring and task management service for +container station servers and headless Raspberry Pi setups where multiple containers are concurrently running and +require observation, with secure passcode-protected endpoints so that clients can connect via the [SuperVisor Frontend +Service](https://github.com/t0xic0der/supervisor-frontend-service/) and manage their devices. ## Features - Zero dependence on pre-rendered template with the use of **DeadSync** to render DOM elements on connection - Efficient JSON data structure for 100% faster updating with the use of **LiveSync** to refresh DOM contents +- Intuitive endpoints for fetching preliminaries, statistics, logs and process listing for containers +- Intuitive endpoints for fetching preliminaries and revision histories for images +- Intuitive endpoints for fetching preliminaries for both networks and volumes - Relatively low overhead from the server during live stat (approx. 2MB over Python 3 runtime usage) - Live polling time period is left at client-side decision to stick to a simple client-server model -- Monitoring information is provided by the awesome **`psutil`** library +- Monitoring station information is provided by the awesome **`psutil`** library +- Monitoring and management Docker information is provided by the effective **`docker-py`** library - Added protection using passcode verification parameter check and server-side authentication of data requests - Decoupled structure allows for connection to frontend service ([Check here](https://github.com/t0xic0der/supervisor-frontend-service)) - Authenticated process management endpoints - **`TERMINATE`**, **`KILL`**, **`SUSPEND`** and **`RESUME`** ops @@ -31,10 +38,11 @@ This project works as an intuitive remotely accessible system performance monito ## Table of contents 1. [Home](https://github.com/t0xic0der/supervisor-driver-service/wiki) 2. [Notice](https://github.com/t0xic0der/supervisor-driver-service/wiki/Notice) -3. [Installing on Raspberry Pi](https://github.com/t0xic0der/supervisor-driver-service/wiki/Installing-on-Raspberry-Pi) -4. [Installing on a Generic PC](https://github.com/t0xic0der/supervisor-driver-service/wiki/Installing-on-a-Generic-PC) -5. [Download releases](https://github.com/t0xic0der/supervisor-driver-service/releases) -6. [SuperVisor Frontend Service](https://github.com/t0xic0der/supervisor-frontend-service) +3. [Installation](https://github.com/t0xic0der/supervisor-driver-service/wiki/Installation) +4. [Download releases](https://github.com/t0xic0der/supervisor-driver-service/releases) +5. [SuperVisor Frontend Service](https://github.com/t0xic0der/supervisor-frontend-service) ## Contribute -You may request for the addition of new features in the [issues](https://github.com/t0xic0der/supervisor-driver-service/issues) page but as the project is singlehandedly maintained - it might take time to develop on them. Please consider forking the repository and contributing to its development. :heart: +You may request for the addition of new features in the [issues](https://github.com/t0xic0der/supervisor-driver-service/issues) +page but as the project is singlehandedly maintained - it might take time to develop on them. Please consider forking +the repository and contributing to its development. :heart: diff --git a/base/back.py b/base/back.py new file mode 100644 index 0000000..8d6956a --- /dev/null +++ b/base/back.py @@ -0,0 +1,468 @@ +""" +########################################################################## +* +* Copyright © 2019-2021 Akashdeep Dhar +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +########################################################################## +""" + +import getpass +import os +import time + +import psutil + + +class ProcessHandler: + def __init__(self, prociden): + self.prociden = prociden + + def return_process_info(self): + """ + Returns process information + """ + procstmp = psutil.Process(self.prociden).as_dict() + retndata = { + "pid": procstmp["pid"], + "username": procstmp["username"], + "uids": { + "real": procstmp["uids"].real, + "effective": procstmp["uids"].effective, + "saved": procstmp["uids"].saved + }, + "memory_percent": procstmp["memory_percent"], + "name": procstmp["name"], + "create_time": time.ctime(procstmp["create_time"]), + "num_ctx_switches": { + "voluntary": procstmp["num_ctx_switches"].voluntary, + "involuntary": procstmp["num_ctx_switches"].involuntary, + }, + "cpu_percent": procstmp["cpu_percent"], + "cpu_times": { + "user": procstmp["cpu_times"].user, + "system": procstmp["cpu_times"].system, + "children_user": procstmp["cpu_times"].children_user, + "children_system": procstmp["cpu_times"].children_system, + "iowait": procstmp["cpu_times"].iowait, + }, + "memory_info": { + "rss": procstmp["memory_info"].rss, + "vms": procstmp["memory_info"].vms, + "shared": procstmp["memory_info"].shared, + "text": procstmp["memory_info"].text, + "lib": procstmp["memory_info"].lib, + "data": procstmp["memory_info"].data, + "dirty": procstmp["memory_info"].dirty, + }, + "status": procstmp["status"], + "num_threads": procstmp["num_threads"], + "gids": { + "real": procstmp["gids"].real, + "effective": procstmp["gids"].effective, + "saved": procstmp["gids"].saved, + }, + "terminal": procstmp["terminal"] + } + return retndata + + def get_single_process(self): + """ + Returns information about a single process + """ + try: + return psutil.Process(int(self.prociden)) + except Exception as e: + return str(e) + + def process_killer(self): + """ + Kills process with the requested process ID + """ + singproc = self.get_single_process() + try: + if type(singproc) == psutil.Process: + singproc.kill() + return {"retnmesg": True} + except: + return {"retnmesg": False} + + def process_terminator(self): + """ + Terminates process with the requested process ID + """ + singproc = self.get_single_process() + try: + if type(singproc) == psutil.Process: + singproc.terminate() + return {"retnmesg": True} + except: + return {"retnmesg": False} + + def process_suspender(self): + """ + Suspends process with the requested process ID + """ + singproc = self.get_single_process() + try: + if type(singproc) == psutil.Process: + singproc.suspend() + return {"retnmesg": True} + except: + return {"retnmesg": False} + + def process_resumer(self): + """ + Resumes process with the requested process ID + """ + singproc = self.get_single_process() + try: + if type(singproc) == psutil.Process: + singproc.resume() + return {"retnmesg": True} + except: + return {"retnmesg": False} + + +class LiveUpdatingElements: + def get_virtual_memory_data(self): + """ + Returns physical memory data + """ + bruhdata = psutil.virtual_memory() + retndata = { + "total": bruhdata.total, + "available": bruhdata.available, + "percent": bruhdata.percent, + "used": bruhdata.used, + "active": bruhdata.active, + "inactive": bruhdata.inactive, + "buffers": bruhdata.buffers, + "cached": bruhdata.cached, + "shared": bruhdata.shared, + "slab": bruhdata.slab, + } + return retndata + + def get_swap_memory_info(self): + """ + Returns virtual memory data + """ + swapinfo = psutil.swap_memory() + retndata = { + "total": swapinfo.total, + "used": swapinfo.used, + "free": swapinfo.free, + "percent": swapinfo.percent, + "sin": swapinfo.sin, + "sout": swapinfo.sout, + } + return retndata + + def get_cpu_state_times(self): + """ + Returns CPU state time information + """ + timedata = psutil.cpu_times(percpu=True) + retndata = {} + for indx in range(len(timedata)): + elemobjc = { + "user": timedata[indx].user, + "nice": timedata[indx].nice, + "system": timedata[indx].system, + "idle": timedata[indx].idle, + "iowait": timedata[indx].iowait, + "irq": timedata[indx].irq, + "softirq": timedata[indx].softirq, + "steal": timedata[indx].steal, + "guest": timedata[indx].guest, + "guest_nice": timedata[indx].guest_nice, + } + retndata[indx] = elemobjc + return retndata + + def get_cpu_usage_percent(self): + """ + Returns CPU usage percentage per-core + """ + cpuprcnt = psutil.cpu_percent(percpu=True) + retndata = {} + for indx in range(len(cpuprcnt)): + retndata[indx] = cpuprcnt[indx] + return retndata + + def get_cpu_statistics(self): + """ + Returns CPU statistical information + """ + cpustats = psutil.cpu_stats() + retndata = { + "ctx_switches": cpustats.ctx_switches, + "interrupts": cpustats.interrupts, + "soft_interrupts": cpustats.soft_interrupts, + "syscalls": cpustats.syscalls, + } + return retndata + + def get_cpu_clock_speed(self): + """ + Returns CPU clock speed information per-core + """ + cpuclock = psutil.cpu_freq(percpu=True) + retndata = {} + for indx in range(len(cpuclock)): + singlist = { + "current": cpuclock[indx].current, + "min": cpuclock[indx].min, + "max": cpuclock[indx].max, + } + retndata[indx] = singlist + return retndata + + def get_disk_io_usage(self): + """ + Returns disk IO usage + """ + diousage = psutil.disk_io_counters(perdisk=True) + retndata = {} + for indx in diousage.keys(): + singlist = { + "read_count": diousage[indx].read_count, + "write_count": diousage[indx].write_count, + "read_bytes": diousage[indx].read_bytes, + "write_bytes": diousage[indx].write_bytes, + "read_time": diousage[indx].read_time, + "write_time": diousage[indx].write_time, + "read_merged_count": diousage[indx].read_merged_count, + "write_merged_count": diousage[indx].write_merged_count, + "busy_time": diousage[indx].busy_time, + } + retndata[indx] = singlist + return retndata + + def get_network_io_usage(self): + """ + Returns network IO usage + """ + netusage = psutil.net_io_counters(pernic=True) + retndata = {} + for indx in netusage.keys(): + singlist = { + "bytes_sent": netusage[indx].bytes_sent, + "bytes_recv": netusage[indx].bytes_recv, + "packets_sent": netusage[indx].packets_sent, + "packets_recv": netusage[indx].packets_recv, + "errin": netusage[indx].errin, + "errout": netusage[indx].errout, + "dropin": netusage[indx].dropin, + "dropout": netusage[indx].dropout, + } + retndata[indx] = singlist + return retndata + + def get_process_listing_info(self): + """ + Returns process listing information + """ + procstmp = psutil.process_iter(["pid", "name", "username", "memory_percent", "cpu_percent"]) + retndata = {} + for indx in procstmp: + singlist = { + "pid": indx.info["pid"], + "name": indx.info["name"], + "username": indx.info["username"], + "memory_percent": indx.info["memory_percent"], + "cpu_percent": indx.info["cpu_percent"], + } + retndata[indx.info["pid"]] = singlist + return retndata + + def get_sensors_temperature(self): + """ + Returns thermal statistics + """ + senstemp = psutil.sensors_temperatures(fahrenheit=False) + retndata = {} + for indx in senstemp.keys(): + retndata[indx] = [] + for jndx in senstemp[indx]: + singdict = { + "label": jndx.label, + "current": str(jndx.current), + "high": str(jndx.high), + "critical": str(jndx.critical), + } + retndata[indx].append(singdict) + return retndata + + def get_sensors_fan_speed(self): + """ + Returns fan speed information + """ + senstemp = psutil.sensors_fans() + retndata = {} + for indx in senstemp.keys(): + retndata[indx] = [] + for jndx in senstemp[indx]: + singdict = { + "label": jndx.label, + "current": jndx.current + } + retndata[indx].append(singdict) + return retndata + + def get_sensors_battery_status(self): + """ + Returns battery statistics + """ + retndata = {} + try: + battstat = psutil.sensors_battery() + retndata = { + "percent": battstat.percent, + "secsleft": battstat.secsleft, + "power_plugged": battstat.power_plugged, + } + except: + retndata = { + "percent": 0, + "secsleft": 0, + "power_plugged": True, + } + return retndata + + def return_live_data(self): + """ + Returns combined information as a dictionary + """ + jsonobjc = { + "virtdata": self.get_virtual_memory_data(), + "swapinfo": self.get_swap_memory_info(), + "cpustats": self.get_cpu_statistics(), + "cputimes": self.get_cpu_state_times(), + "cpuprcnt": self.get_cpu_usage_percent(), + "cpuclock": self.get_cpu_clock_speed(), + "diousage": self.get_disk_io_usage(), + "netusage": self.get_network_io_usage(), + "procinfo": self.get_process_listing_info(), + "sensread": { + "senstemp": self.get_sensors_temperature(), + "fanspeed": self.get_sensors_fan_speed(), + "battstat": self.get_sensors_battery_status(), + } + } + return jsonobjc + + +class DeadUpdatingElements(LiveUpdatingElements): + def get_os_uname_data(self): + """ + Returns static OS information + """ + unamdata = os.uname() + retndata = { + "System name": unamdata.sysname + " " + unamdata.release, + "Host name": unamdata.nodename + " [" + unamdata.machine + "] ", + "Version": unamdata.version, + "Username": getpass.getuser(), + } + return retndata + + def get_cpu_logical_count(self): + """ + Returns CPU core count + """ + cpuquant = psutil.cpu_count(logical=True) + return str(cpuquant) + + def get_all_disk_partitions(self): + """ + Returns disk partitions information + """ + diskpart = psutil.disk_partitions(all=True) + retndata = [] + for indx in diskpart: + singinfo = { + "device": indx.device, + "mountpoint": indx.mountpoint, + "fstype": indx.fstype, + "opts": indx.opts, + } + retndata.append(singinfo) + return retndata + + def get_network_statistics(self): + """ + Returns network statistics + """ + netstats = psutil.net_if_stats() + retndata = {} + for indx in netstats.keys(): + singinfo = { + "isup": netstats[indx].isup, + "duplex": netstats[indx].duplex, + "speed": netstats[indx].speed, + "mtu": netstats[indx].mtu, + } + retndata[indx] = singinfo + return retndata + + def get_network_if_addresses(self): + """ + Returns network addresses + """ + netaddrs = psutil.net_if_addrs() + retndata = {} + for indx in netaddrs.keys(): + retndata[indx] = {} + for jndx in netaddrs[indx]: + addrobjc = { + "address": jndx.address, + "netmask": jndx.netmask, + "broadcast": jndx.broadcast, + "ptp": jndx.ptp, + } + retndata[indx][jndx.family] = addrobjc + return retndata + + def get_boot_time(self): + """ + Returns boot time information + """ + boottime = time.ctime(psutil.boot_time()) + return boottime + + def return_dead_data(self): + """ + Returns combined information as a dictionary + """ + jsonobjc = { + "osnmdata": self.get_os_uname_data(), + "cpuquant": self.get_cpu_logical_count(), + "cpuclock": self.get_cpu_clock_speed(), + "diskpart": self.get_all_disk_partitions(), + "diousage": self.get_disk_io_usage(), + "netusage": self.get_network_io_usage(), + "netaddrs": self.get_network_if_addresses(), + "netstats": self.get_network_statistics(), + "boottime": self.get_boot_time(), + "procinfo": self.get_process_listing_info(), + "sensread": { + "senstemp": self.get_sensors_temperature(), + "fanspeed": self.get_sensors_fan_speed(), + "battstat": self.get_sensors_battery_status() + } + } + return jsonobjc diff --git a/base/frnt.py b/base/frnt.py new file mode 100644 index 0000000..332b836 --- /dev/null +++ b/base/frnt.py @@ -0,0 +1,99 @@ +""" +########################################################################## +* +* Copyright © 2019-2021 Akashdeep Dhar +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +########################################################################## +""" + +import json + +import falcon +from base.back import DeadUpdatingElements, LiveUpdatingElements, ProcessHandler + + +class StatisticalEndpoint(object): + def __init__(self, passcode): + self.passcode = passcode + + def on_get(self, rqst, resp): + """ + Endpoint for fetching host station information + Method: GET + """ + passcode = rqst.get_param("passcode") + opername = rqst.get_param("opername") + if passcode == self.passcode: + if opername == "livesync": + retnjson = LiveUpdatingElements().return_live_data() + elif opername == "deadsync": + retnjson = DeadUpdatingElements().return_dead_data() + else: + retnjson = {"retnmesg": "deny"} + else: + retnjson = {"retnmesg": "deny"} + resp.body = json.dumps(retnjson, ensure_ascii=False) + resp.set_header("Access-Control-Allow-Origin", "*") + resp.status = falcon.HTTP_200 + + +class ProcessHandlingEndpoint(object): + def __init__(self, passcode): + self.passcode = passcode + + def on_get(self, rqst, resp): + """ + Endpoint for fetching information about a specific process + Method: GET + """ + passcode = rqst.get_param("passcode") + if passcode == self.passcode: + retnjson = ProcessHandler(int(rqst.get_param("prociden"))).return_process_info() + else: + retnjson = {"retnmesg": "deny"} + resp.body = json.dumps(retnjson, ensure_ascii=False) + resp.set_header("Access-Control-Allow-Origin", "*") + resp.status = falcon.HTTP_200 + + +class ProcessControllingEndpoint(object): + def __init__(self, passcode): + self.passcode = passcode + + def on_get(self, rqst, resp): + """ + Endpoint for controlling specific processes + Method: GET + """ + passcode = rqst.get_param("passcode") + opername = rqst.get_param("opername") + prociden = int(rqst.get_param("prociden")) + if passcode == self.passcode: + if opername == "KILL": + retnjson = ProcessHandler(prociden).process_killer() + elif opername == "TERM": + retnjson = ProcessHandler(prociden).process_terminator() + elif opername == "HANG": + retnjson = ProcessHandler(prociden).process_suspender() + elif opername == "CONT": + retnjson = ProcessHandler(prociden).process_resumer() + else: + retnjson = {"retnmesg": "deny"} + else: + retnjson = {"retnmesg": "deny"} + resp.body = json.dumps(retnjson, ensure_ascii=False) + resp.set_header("Access-Control-Allow-Origin", "*") + resp.status = falcon.HTTP_200 diff --git a/dish/back.py b/dish/back.py new file mode 100644 index 0000000..ef1efc0 --- /dev/null +++ b/dish/back.py @@ -0,0 +1,252 @@ +""" +########################################################################## +* +* Copyright © 2019-2021 Akashdeep Dhar +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +########################################################################## +""" + +from docker import DockerClient + + +class DockerPreliminaryInformation: + def __init__(self, unixsock): + self.clinobjc = DockerClient(base_url=unixsock) + + def get_docker_info(self): + """ + Returns container station information + """ + return self.clinobjc.info() + + def get_docker_version(self): + """ + Returns container station versioning + """ + return self.clinobjc.version() + + +class DockerContainerInformation: + def __init__(self, unixsock): + self.clinobjc = DockerClient(base_url=unixsock) + + def get_container_list(self): + """ + Returns list of containers + """ + contlist = self.clinobjc.containers.list(all=True) + dispdict = {} + for indx in contlist: + dispdict[indx.short_id] = { + "id": indx.id, + "name": indx.name, + } + return dispdict + + def get_per_container_static_information(self, contiden): + """ + Returns preliminary information of a selected container + """ + try: + contobjc = self.clinobjc.containers.get(contiden) + dispdict = { + "short_id": contobjc.short_id, + "id": contobjc.id, + "name": contobjc.name, + "attrs": contobjc.attrs, + "labels": contobjc.labels, + "ports": contobjc.ports, + "status": contobjc.status, + "image": { + "name": contobjc.image.tags[0], + "short_id": contobjc.image.short_id + } + } + except: + dispdict = { + "retnmesg": "deny" + } + return dispdict + + def get_per_container_logs_data(self, contiden): + """ + Returns logging information of a selected container + """ + try: + contobjc = self.clinobjc.containers.get(contiden) + dispdict = { + "logs": contobjc.logs(stream=False).decode() + } + except: + dispdict = { + "retnmesg": "deny" + } + return dispdict + + def get_per_container_top_data(self, contiden): + """ + Returns list of processes running in a selected container + """ + try: + contobjc = self.clinobjc.containers.get(contiden) + dispdict = { + "top": contobjc.top() + } + except: + dispdict = { + "retnmesg": "deny" + } + return dispdict + + def get_per_container_statistics(self, contiden): + """ + Returns hardware statistics of a selected container + """ + try: + contobjc = self.clinobjc.containers.get(contiden) + dispdict = { + "stats": contobjc.stats(stream=False) + } + except: + dispdict = { + "retnmesg": "deny" + } + return dispdict + + +class DockerImageInformation: + def __init__(self, unixsock): + self.clinobjc = DockerClient(base_url=unixsock) + + def get_image_list(self): + """ + Returns list of images + """ + imejlist = self.clinobjc.images.list(all=True) + dispdict = {} + for indx in imejlist: + dispdict[indx.short_id] = { + "id": indx.id, + "name": indx.tags[0], + } + return dispdict + + def get_per_image_static_information(self, imejiden): + """ + Returns preliminary information of a selected image + """ + try: + imejobjc = self.clinobjc.images.get(imejiden) + dispdict = { + "short_id": imejobjc.short_id, + "id": imejobjc.id, + "name": imejobjc.tags[0], + "attrs": imejobjc.attrs, + "labels": imejobjc.labels, + "tags": imejobjc.tags + } + except: + dispdict = { + "retnmesg": "deny" + } + return dispdict + + def get_per_image_revision_history(self, imejiden): + """ + Returns revision history of a selected container + """ + try: + imejobjc = self.clinobjc.images.get(imejiden) + dispdict = { + "history": imejobjc.history() + } + except: + dispdict = { + "retnmesg": "deny" + } + return dispdict + + +class DockerNetworkInformation: + def __init__(self, unixsock): + self.clinobjc = DockerClient(base_url=unixsock) + + def get_network_list(self): + """ + Returns list of networks + """ + ntwklist = self.clinobjc.networks.list() + dispdict = {} + for indx in ntwklist: + dispdict[indx.short_id] = { + "id": indx.id, + "name": indx.name, + } + return dispdict + + def get_per_network_static_information(self, ntwkiden): + """ + Returns preliminary information of a selected network + """ + try: + ntwkobjc = self.clinobjc.networks.get(ntwkiden) + dispdict = { + "short_id": ntwkobjc.short_id, + "id": ntwkobjc.id, + "name": ntwkobjc.name, + "attrs": ntwkobjc.attrs + } + except: + dispdict = { + "retnmesg": "deny" + } + return dispdict + + +class DockerVolumeInformation: + def __init__(self, unixsock): + self.clinobjc = DockerClient(base_url=unixsock) + + def get_volume_list(self): + """ + Returns list of volumes + """ + volmlist = self.clinobjc.volumes.list() + dispdict = {} + for indx in volmlist: + dispdict[indx.short_id] = { + "id": indx.id, + "name": indx.name, + } + return dispdict + + def get_per_volume_static_information(self, volmiden): + """ + Returns preliminary information of a selected volume + """ + try: + volmobjc = self.clinobjc.volumes.get(volmiden) + dispdict = { + "short_id": volmobjc.short_id, + "id": volmobjc.id, + "name": volmobjc.name, + "attrs": volmobjc.attrs + } + except: + dispdict = { + "retnmesg": "deny" + } + return dispdict diff --git a/dish/frnt.py b/dish/frnt.py new file mode 100644 index 0000000..935e9e7 --- /dev/null +++ b/dish/frnt.py @@ -0,0 +1,177 @@ +""" +########################################################################## +* +* Copyright © 2019-2021 Akashdeep Dhar +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +########################################################################## +""" + +import json + +import falcon +from dish.back import ( + DockerPreliminaryInformation, + DockerContainerInformation, + DockerImageInformation, + DockerNetworkInformation, + DockerVolumeInformation +) + + +class PreliminaryInformationEndpoint(object): + def __init__(self, passcode, unixsock): + self.passcode = passcode + self.unixsock = unixsock + + def on_get(self, rqst, resp): + """ + Endpoint for fetching container station information + Method: GET + """ + passcode = rqst.get_param("passcode") + opername = rqst.get_param("opername") + if passcode == self.passcode: + if opername == "INFO": + retnjson = DockerPreliminaryInformation(self.unixsock).get_docker_info() + elif opername == "VERS": + retnjson = DockerPreliminaryInformation(self.unixsock).get_docker_version() + else: + retnjson = {"retnmesg": "deny"} + else: + retnjson = {"retnmesg": "deny"} + resp.body = json.dumps(retnjson, ensure_ascii=False) + resp.set_header("Access-Control-Allow-Origin", "*") + resp.status = falcon.HTTP_200 + + +class ContainerInformationEndpoint(object): + def __init__(self, passcode, unixsock): + self.passcode = passcode + self.unixsock = unixsock + + def on_get(self, rqst, resp): + """ + Endpoint for fetching container information + Method: GET + """ + passcode = rqst.get_param("passcode") + opername = rqst.get_param("opername") + if passcode == self.passcode: + if opername == "LIST": + retnjson = DockerContainerInformation(self.unixsock).get_container_list() + elif opername == "IDEN": + contiden = rqst.get_param("contiden") + retnjson = DockerContainerInformation(self.unixsock).get_per_container_static_information(contiden) + elif opername == "LOGS": + contiden = rqst.get_param("contiden") + retnjson = DockerContainerInformation(self.unixsock).get_per_container_logs_data(contiden) + elif opername == "HTOP": + contiden = rqst.get_param("contiden") + retnjson = DockerContainerInformation(self.unixsock).get_per_container_top_data(contiden) + elif opername == "STAT": + contiden = rqst.get_param("contiden") + retnjson = DockerContainerInformation(self.unixsock).get_per_container_statistics(contiden) + else: + retnjson = {"retnmesg": "deny"} + else: + retnjson = {"retnmesg": "deny"} + resp.body = json.dumps(retnjson, ensure_ascii=False) + resp.set_header("Access-Control-Allow-Origin", "*") + resp.status = falcon.HTTP_200 + + +class ImageInformationEndpoint(object): + def __init__(self, passcode, unixsock): + self.passcode = passcode + self.unixsock = unixsock + + def on_get(self, rqst, resp): + """ + Endpoint for fetching image information + Method: GET + """ + passcode = rqst.get_param("passcode") + opername = rqst.get_param("opername") + if passcode == self.passcode: + if opername == "LIST": + retnjson = DockerImageInformation(self.unixsock).get_image_list() + elif opername == "IDEN": + imejiden = rqst.get_param("imejiden") + retnjson = DockerImageInformation(self.unixsock).get_per_image_static_information(imejiden) + elif opername == "REVS": + imejiden = rqst.get_param("imejiden") + retnjson = DockerImageInformation(self.unixsock).get_per_image_revision_history(imejiden) + else: + retnjson = {"retnmesg": "deny"} + else: + retnjson = {"retnmesg": "deny"} + resp.body = json.dumps(retnjson, ensure_ascii=False) + resp.set_header("Access-Control-Allow-Origin", "*") + resp.status = falcon.HTTP_200 + + +class NetworkInformationEndpoint(object): + def __init__(self, passcode, unixsock): + self.passcode = passcode + self.unixsock = unixsock + + def on_get(self, rqst, resp): + """ + Endpoint for fetching network information + Method: GET + """ + passcode = rqst.get_param("passcode") + opername = rqst.get_param("opername") + if passcode == self.passcode: + if opername == "LIST": + retnjson = DockerNetworkInformation(self.unixsock).get_network_list() + elif opername == "IDEN": + ntwkiden = rqst.get_param("ntwkiden") + retnjson = DockerNetworkInformation(self.unixsock).get_per_network_static_information(ntwkiden) + else: + retnjson = {"retnmesg": "deny"} + else: + retnjson = {"retnmesg": "deny"} + resp.body = json.dumps(retnjson, ensure_ascii=False) + resp.set_header("Access-Control-Allow-Origin", "*") + resp.status = falcon.HTTP_200 + + +class VolumeInformationEndpoint(object): + def __init__(self, passcode, unixsock): + self.passcode = passcode + self.unixsock = unixsock + + def on_get(self, rqst, resp): + """ + Endpoint for fetching volume information + Method: GET + """ + passcode = rqst.get_param("passcode") + opername = rqst.get_param("opername") + if passcode == self.passcode: + if opername == "LIST": + retnjson = DockerVolumeInformation(self.unixsock).get_volume_list() + elif opername == "IDEN": + volmiden = rqst.get_param("volmiden") + retnjson = DockerVolumeInformation(self.unixsock).get_per_volume_static_information(volmiden) + else: + retnjson = {"retnmesg": "deny"} + else: + retnjson = {"retnmesg": "deny"} + resp.body = json.dumps(retnjson, ensure_ascii=False) + resp.set_header("Access-Control-Allow-Origin", "*") + resp.status = falcon.HTTP_200 diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 6783396..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,5 +0,0 @@ -services: - svdriver: - build: . - ports: - - "6969:6969" diff --git a/falc.py b/falc.py index 53da13d..d57ef45 100644 --- a/falc.py +++ b/falc.py @@ -1,7 +1,7 @@ """ ########################################################################## * -* Copyright © 2019-2020 Akashdeep Dhar +* Copyright © 2019-2021 Akashdeep Dhar * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,119 +20,46 @@ """ import json +from secrets import choice import click import falcon -from falcon import __version__ as flcnvers -from hard import ( - ConnectionManager, - DeadUpdatingElements, - LiveUpdatingElements, - ProcessHandler, +from base.frnt import ( + ProcessControllingEndpoint, + ProcessHandlingEndpoint, + StatisticalEndpoint, +) +from dish.frnt import ( + ContainerInformationEndpoint, + ImageInformationEndpoint, + NetworkInformationEndpoint, + PreliminaryInformationEndpoint, + VolumeInformationEndpoint, ) +from docker import __version__ as dockvers +from falcon import __version__ as flcnvers from psutil import __version__ as psutvers from werkzeug import __version__ as wkzgvers from werkzeug import serving -class LiveUpdatingEndpoint(object): - def __init__(self, passcode): - self.passcode = passcode - - def on_get(self, rqst, resp): - passcode = rqst.get_param("passcode") - if passcode == self.passcode: - retnjson = LiveUpdatingElements().return_live_data() - else: - retnjson = {"retnmesg": "deny"} - resp.body = json.dumps(retnjson, ensure_ascii=False) - resp.set_header("Access-Control-Allow-Origin", "*") - resp.status = falcon.HTTP_200 - - -class DeadUpdatingEndpoint(object): - def __init__(self, passcode): - self.passcode = passcode - - def on_get(self, rqst, resp): - passcode = rqst.get_param("passcode") - if passcode == self.passcode: - retnjson = DeadUpdatingElements().return_dead_data() - else: - retnjson = {"retnmesg": "deny"} - resp.body = json.dumps(retnjson, ensure_ascii=False) - resp.set_header("Access-Control-Allow-Origin", "*") - resp.status = falcon.HTTP_200 - - -class ProcessHandlingEndpoint(object): - def __init__(self, passcode): - self.passcode = passcode - - def on_get(self, rqst, resp): - passcode = rqst.get_param("passcode") - if passcode == self.passcode: - retnjson = ProcessHandler(int(rqst.get_param("prociden"))).return_process_info() - else: - retnjson = {"retnmesg": "deny"} - resp.body = json.dumps(retnjson, ensure_ascii=False) - resp.set_header("Access-Control-Allow-Origin", "*") - resp.status = falcon.HTTP_200 - - -class ProcessKillingEndpoint(object): - def __init__(self, passcode): - self.passcode = passcode - - def on_get(self, rqst, resp): - passcode = rqst.get_param("passcode") - if passcode == self.passcode: - retnjson = ProcessHandler(int(rqst.get_param("prociden"))).process_killer() - else: - retnjson = {"retnmesg": "deny"} - resp.body = json.dumps(retnjson, ensure_ascii=False) - resp.set_header("Access-Control-Allow-Origin", "*") - resp.status = falcon.HTTP_200 - - -class ProcessTerminatingEndpoint(object): - def __init__(self, passcode): - self.passcode = passcode - - def on_get(self, rqst, resp): - passcode = rqst.get_param("passcode") - if passcode == self.passcode: - retnjson = ProcessHandler(int(rqst.get_param("prociden"))).process_terminator() - else: - retnjson = {"retnmesg": "deny"} - resp.body = json.dumps(retnjson, ensure_ascii=False) - resp.set_header("Access-Control-Allow-Origin", "*") - resp.status = falcon.HTTP_200 +main = falcon.API() -class ProcessSuspendingEndpoint(object): - def __init__(self, passcode): - self.passcode = passcode - - def on_get(self, rqst, resp): - passcode = rqst.get_param("passcode") - if passcode == self.passcode: - retnjson = ProcessHandler(int(rqst.get_param("prociden"))).process_suspender() - else: - retnjson = {"retnmesg": "deny"} - resp.body = json.dumps(retnjson, ensure_ascii=False) - resp.set_header("Access-Control-Allow-Origin", "*") - resp.status = falcon.HTTP_200 +class ConnectionManager: + def passphrase_generator(self, lent=16): + retndata = "".join(choice("ABCDEF0123456789") for indx in range(lent)) + return retndata -class ProcessResumingEndpoint(object): +class ConnectionExaminationEndpoint(object): def __init__(self, passcode): self.passcode = passcode def on_get(self, rqst, resp): passcode = rqst.get_param("passcode") if passcode == self.passcode: - retnjson = ProcessHandler(int(rqst.get_param("prociden"))).process_resumer() + retnjson = {"retnmesg": "allow"} else: retnjson = {"retnmesg": "deny"} resp.body = json.dumps(retnjson, ensure_ascii=False) @@ -140,48 +67,89 @@ class ProcessResumingEndpoint(object): resp.status = falcon.HTTP_200 -main = falcon.API() - - @click.command() -@click.option("-p", "--portdata", "portdata", help="Set the port value [0-65536].", default="6969") -@click.option("-6", "--ipprotv6", "netprotc", flag_value="ipprotv6", help="Start the server on an IPv6 address.") -@click.option("-4", "--ipprotv4", "netprotc", flag_value="ipprotv4", help="Start the server on an IPv4 address.") -@click.version_option(version="1.0.2", prog_name=click.style("SuperVisor Driver Service", fg="magenta")) -def mainfunc(portdata, netprotc): +@click.option( + "-p", + "--portdata", + "portdata", + help="Set the port value [0-65536].", + default="6969" +) +@click.option( + "-u", + "--unixsock", + "unixsock", + help="Set the UNIX socket for Docker.", + default="unix://var/run/docker.sock" +) +@click.option( + "-6", + "--ipprotv6", + "netprotc", + flag_value="ipprotv6", + help="Start the server on an IPv6 address." +) +@click.option( + "-4", + "--ipprotv4", + "netprotc", + flag_value="ipprotv4", + help="Start the server on an IPv4 address." +) +@click.version_option( + version="1.1.0-beta", + prog_name=click.style("SuperVisor Driver Service", fg="magenta") +) +def mainfunc(portdata, netprotc, unixsock): try: - click.echo(" * " + click.style("SuperVisor Driver Service v1.0.2", fg="green")) + click.echo( + click.style( + " ,---. . ,o \n" + + " `---.. .,---.,---.,---.| |.,---.,---.,---.\n" + + " || || ||---'| \ / |`---.| || \n" + + " `---'`---'|---'`---'` `' ``---'`---'` \n" + + " |", bold=True + ) + ) + click.echo(" * " + click.style("Driver Service v1.1.0-beta", fg="green")) netpdata = "" passcode = ConnectionManager().passphrase_generator() if netprotc == "ipprotv6": - click.echo(" * " + click.style("IP version ", fg="magenta") + ": " + "6") + click.echo(" * " + click.style("IP version ", fg="magenta") + ": " + "6") netpdata = "::" elif netprotc == "ipprotv4": - click.echo(" * " + click.style("IP version ", fg="magenta") + ": " + "4") + click.echo(" * " + click.style("IP version ", fg="magenta") + ": " + "4") netpdata = "0.0.0.0" - click.echo(" * " + click.style("Passcode ", fg="magenta") + ": " + passcode + "\n" + - " * " + click.style("Reference URI ", fg="magenta") + ": " + "http://" + netpdata + ":" + portdata + - "/" + "\n" + - " * " + click.style("Monitor service ", fg="magenta") + ": " + "Psutil v" + psutvers + "\n" + - " * " + click.style("Endpoint service ", fg="magenta") + ": " + "Falcon v" + flcnvers + "\n" + - " * " + click.style("HTTP server ", fg="magenta") + ": " + "Werkzeug v" + wkzgvers) - livesync = LiveUpdatingEndpoint(passcode) - deadsync = DeadUpdatingEndpoint(passcode) - procinfo = ProcessHandlingEndpoint(passcode) - killproc = ProcessKillingEndpoint(passcode) - termproc = ProcessTerminatingEndpoint(passcode) - suspproc = ProcessSuspendingEndpoint(passcode) - resmproc = ProcessResumingEndpoint(passcode) - main.add_route("/livesync", livesync) - main.add_route("/deadsync", deadsync) - main.add_route("/procinfo", procinfo) - main.add_route("/killproc", killproc) - main.add_route("/termproc", termproc) - main.add_route("/suspproc", suspproc) - main.add_route("/resmproc", resmproc) + click.echo( + " * " + click.style("Passcode ", bold=True) + ": " + passcode + "\n" + + " * " + click.style("Reference URI ", bold=True) + ": " + "http://" + netpdata + ":" + portdata + + "/" + "\n" + + " * " + click.style("Monitor service ", bold=True) + ": " + "Psutil v" + psutvers + "\n" + + " * " + click.style("Container service ", bold=True) + ": " + "DockerPy v" + dockvers + "\n" + + " * " + click.style("Endpoint service ", bold=True) + ": " + "Falcon v" + flcnvers + "\n" + + " * " + click.style("HTTP server ", bold=True) + ": " + "Werkzeug v" + wkzgvers + ) + basestat = StatisticalEndpoint(passcode) + basepsin = ProcessHandlingEndpoint(passcode) + basetool = ProcessControllingEndpoint(passcode) + dishplim = PreliminaryInformationEndpoint(passcode, unixsock) + dishcont = ContainerInformationEndpoint(passcode, unixsock) + dishimej = ImageInformationEndpoint(passcode, unixsock) + dishntwk = NetworkInformationEndpoint(passcode, unixsock) + dishvolm = VolumeInformationEndpoint(passcode, unixsock) + testconn = ConnectionExaminationEndpoint(passcode) + main.add_route("/basestat", basestat) + main.add_route("/basepsin", basepsin) + main.add_route("/basetool", basetool) + main.add_route("/dishplim", dishplim) + main.add_route("/dishcont", dishcont) + main.add_route("/dishimej", dishimej) + main.add_route("/dishntwk", dishntwk) + main.add_route("/dishvolm", dishvolm) + main.add_route("/testconn", testconn) serving.run_simple(netpdata, int(portdata), main) except Exception as expt: - click.echo(" * " + click.style("Error occurred : " + str(expt), fg="red")) + click.echo(" * " + click.style("Error occurred : " + str(expt), fg="red")) if __name__ == "__main__": diff --git a/hard.py b/hard.py deleted file mode 100644 index 33ec848..0000000 --- a/hard.py +++ /dev/null @@ -1,385 +0,0 @@ -""" -########################################################################## -* -* Copyright © 2019-2020 Akashdeep Dhar -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -* -########################################################################## -""" - -import getpass -import os -import time -from secrets import choice - -import psutil - - -class ConnectionManager: - def passphrase_generator(self, lent=16): - retndata = "".join(choice("ABCDEF0123456789") for i in range(lent)) - return retndata - - -class ProcessHandler: - def __init__(self, prociden): - self.prociden = prociden - - def return_process_info(self): - procstmp = psutil.Process(self.prociden).as_dict() - retndata = { - "pid": procstmp["pid"], - "username": procstmp["username"], - "uids": { - "real": procstmp["uids"].real, - "effective": procstmp["uids"].effective, - "saved": procstmp["uids"].saved - }, - "memory_percent": procstmp["memory_percent"], - "name": procstmp["name"], - "create_time": time.ctime(procstmp["create_time"]), - "num_ctx_switches": { - "voluntary": procstmp["num_ctx_switches"].voluntary, - "involuntary": procstmp["num_ctx_switches"].involuntary, - }, - "cpu_percent": procstmp["cpu_percent"], - "cpu_times": { - "user": procstmp["cpu_times"].user, - "system": procstmp["cpu_times"].system, - "children_user": procstmp["cpu_times"].children_user, - "children_system": procstmp["cpu_times"].children_system, - "iowait": procstmp["cpu_times"].iowait, - }, - "memory_info": { - "rss": procstmp["memory_info"].rss, - "vms": procstmp["memory_info"].vms, - "shared": procstmp["memory_info"].shared, - "text": procstmp["memory_info"].text, - "lib": procstmp["memory_info"].lib, - "data": procstmp["memory_info"].data, - "dirty": procstmp["memory_info"].dirty, - }, - "status": procstmp["status"], - "num_threads": procstmp["num_threads"], - "gids": { - "real": procstmp["gids"].real, - "effective": procstmp["gids"].effective, - "saved": procstmp["gids"].saved, - }, - "terminal": procstmp["terminal"] - } - return retndata - - def get_single_process(self): - try: - return psutil.Process(int(self.prociden)) - except Exception as e: - return str(e) - - def process_killer(self): - singproc = self.get_single_process() - if type(singproc) == psutil.Process: - singproc.kill() - return {"retnmesg": True} - - def process_terminator(self): - singproc = self.get_single_process() - if type(singproc) == psutil.Process: - singproc.terminate() - return {"retnmesg": True} - - def process_suspender(self): - singproc = self.get_single_process() - if type(singproc) == psutil.Process: - singproc.suspend() - return {"retnmesg": True} - - def process_resumer(self): - singproc = self.get_single_process() - if type(singproc) == psutil.Process: - singproc.resume() - return {"retnmesg": True} - - -class LiveUpdatingElements: - def get_virtual_memory_data(self): - bruhdata = psutil.virtual_memory() - retndata = { - "total": bruhdata.total, - "available": bruhdata.available, - "percent": bruhdata.percent, - "used": bruhdata.used, - "active": bruhdata.active, - "inactive": bruhdata.inactive, - "buffers": bruhdata.buffers, - "cached": bruhdata.cached, - "shared": bruhdata.shared, - "slab": bruhdata.slab, - } - return retndata - - def get_swap_memory_info(self): - swapinfo = psutil.swap_memory() - retndata = { - "total": swapinfo.total, - "used": swapinfo.used, - "free": swapinfo.free, - "percent": swapinfo.percent, - "sin": swapinfo.sin, - "sout": swapinfo.sout, - } - return retndata - - def get_cpu_state_times(self): - timedata = psutil.cpu_times(percpu=True) - retndata = {} - for indx in range(len(timedata)): - elemobjc = { - "user": timedata[indx].user, - "nice": timedata[indx].nice, - "system": timedata[indx].system, - "idle": timedata[indx].idle, - "iowait": timedata[indx].iowait, - "irq": timedata[indx].irq, - "softirq": timedata[indx].softirq, - "steal": timedata[indx].steal, - "guest": timedata[indx].guest, - "guest_nice": timedata[indx].guest_nice, - } - retndata[indx] = elemobjc - return retndata - - def get_cpu_usage_percent(self): - cpuprcnt = psutil.cpu_percent(percpu=True) - retndata = {} - for indx in range(len(cpuprcnt)): - retndata[indx] = cpuprcnt[indx] - return retndata - - def get_cpu_statistics(self): - cpustats = psutil.cpu_stats() - retndata = { - "ctx_switches": cpustats.ctx_switches, - "interrupts": cpustats.interrupts, - "soft_interrupts": cpustats.soft_interrupts, - "syscalls": cpustats.syscalls, - } - return retndata - - def get_cpu_clock_speed(self): - cpuclock = psutil.cpu_freq(percpu=True) - retndata = {} - for indx in range(len(cpuclock)): - singlist = { - "current": cpuclock[indx].current, - "min": cpuclock[indx].min, - "max": cpuclock[indx].max, - } - retndata[indx] = singlist - return retndata - - def get_disk_io_usage(self): - diousage = psutil.disk_io_counters(perdisk=True) - retndata = {} - for indx in diousage.keys(): - singlist = { - "read_count": diousage[indx].read_count, - "write_count": diousage[indx].write_count, - "read_bytes": diousage[indx].read_bytes, - "write_bytes": diousage[indx].write_bytes, - "read_time": diousage[indx].read_time, - "write_time": diousage[indx].write_time, - "read_merged_count": diousage[indx].read_merged_count, - "write_merged_count": diousage[indx].write_merged_count, - "busy_time": diousage[indx].busy_time, - } - retndata[indx] = singlist - return retndata - - def get_network_io_usage(self): - netusage = psutil.net_io_counters(pernic=True) - retndata = {} - for indx in netusage.keys(): - singlist = { - "bytes_sent": netusage[indx].bytes_sent, - "bytes_recv": netusage[indx].bytes_recv, - "packets_sent": netusage[indx].packets_sent, - "packets_recv": netusage[indx].packets_recv, - "errin": netusage[indx].errin, - "errout": netusage[indx].errout, - "dropin": netusage[indx].dropin, - "dropout": netusage[indx].dropout, - } - retndata[indx] = singlist - return retndata - - def get_process_listing_info(self): - procstmp = psutil.process_iter(["pid", "name", "username", "memory_percent", "cpu_percent"]) - retndata = {} - for indx in procstmp: - singlist = { - "pid": indx.info["pid"], - "name": indx.info["name"], - "username": indx.info["username"], - "memory_percent": indx.info["memory_percent"], - "cpu_percent": indx.info["cpu_percent"], - } - retndata[indx.info["pid"]] = singlist - return retndata - - def get_sensors_temperature(self): - senstemp = psutil.sensors_temperatures(fahrenheit=False) - retndata = {} - for indx in senstemp.keys(): - retndata[indx] = [] - for jndx in senstemp[indx]: - singdict = { - "label": jndx.label, - "current": str(jndx.current), - "high": str(jndx.high), - "critical": str(jndx.critical), - } - retndata[indx].append(singdict) - return retndata - - def get_sensors_fan_speed(self): - senstemp = psutil.sensors_fans() - retndata = {} - for indx in senstemp.keys(): - retndata[indx] = [] - for jndx in senstemp[indx]: - singdict = { - "label": jndx.label, - "current": jndx.current - } - retndata[indx].append(singdict) - return retndata - - def get_sensors_battery_status(self): - retndata = {} - try: - battstat = psutil.sensors_battery() - retndata = { - "percent": battstat.percent, - "secsleft": battstat.secsleft, - "power_plugged": battstat.power_plugged, - } - except: - retndata = { - "percent": 0, - "secsleft": 0, - "power_plugged": True, - } - return retndata - - def return_live_data(self): - jsonobjc = { - "virtdata": self.get_virtual_memory_data(), - "swapinfo": self.get_swap_memory_info(), - "cpustats": self.get_cpu_statistics(), - "cputimes": self.get_cpu_state_times(), - "cpuprcnt": self.get_cpu_usage_percent(), - "cpuclock": self.get_cpu_clock_speed(), - "diousage": self.get_disk_io_usage(), - "netusage": self.get_network_io_usage(), - "procinfo": self.get_process_listing_info(), - "sensread": { - "senstemp": self.get_sensors_temperature(), - "fanspeed": self.get_sensors_fan_speed(), - "battstat": self.get_sensors_battery_status(), - } - } - return jsonobjc - - -class DeadUpdatingElements(LiveUpdatingElements): - def get_os_uname_data(self): - unamdata = os.uname() - retndata = { - "System name": unamdata.sysname + " " + unamdata.release, - "Host name": unamdata.nodename + " [" + unamdata.machine + "] ", - "Version": unamdata.version, - "Username": getpass.getuser(), - } - return retndata - - def get_cpu_logical_count(self): - cpuquant = psutil.cpu_count(logical=True) - return str(cpuquant) - - def get_all_disk_partitions(self): - diskpart = psutil.disk_partitions(all=True) - retndata = [] - for indx in diskpart: - singinfo = { - "device": indx.device, - "mountpoint": indx.mountpoint, - "fstype": indx.fstype, - "opts": indx.opts, - } - retndata.append(singinfo) - return retndata - - def get_network_statistics(self): - netstats = psutil.net_if_stats() - retndata = {} - for indx in netstats.keys(): - singinfo = { - "isup": netstats[indx].isup, - "duplex": netstats[indx].duplex, - "speed": netstats[indx].speed, - "mtu": netstats[indx].mtu, - } - retndata[indx] = singinfo - return retndata - - def get_network_if_addresses(self): - netaddrs = psutil.net_if_addrs() - retndata = {} - for indx in netaddrs.keys(): - retndata[indx] = {} - for jndx in netaddrs[indx]: - addrobjc = { - "address": jndx.address, - "netmask": jndx.netmask, - "broadcast": jndx.broadcast, - "ptp": jndx.ptp, - } - retndata[indx][jndx.family] = addrobjc - return retndata - - def get_boot_time(self): - boottime = time.ctime(psutil.boot_time()) - return boottime - - def return_dead_data(self): - jsonobjc = { - "osnmdata": self.get_os_uname_data(), - "cpuquant": self.get_cpu_logical_count(), - "cpuclock": self.get_cpu_clock_speed(), - "diskpart": self.get_all_disk_partitions(), - "diousage": self.get_disk_io_usage(), - "netusage": self.get_network_io_usage(), - "netaddrs": self.get_network_if_addresses(), - "netstats": self.get_network_statistics(), - "boottime": self.get_boot_time(), - "procinfo": self.get_process_listing_info(), - "sensread": { - "senstemp": self.get_sensors_temperature(), - "fanspeed": self.get_sensors_fan_speed(), - "battstat": self.get_sensors_battery_status() - } - } - return jsonobjc \ No newline at end of file diff --git a/pictures/falchelp.png b/pictures/falchelp.png deleted file mode 100644 index 5a701b6..0000000 Binary files a/pictures/falchelp.png and /dev/null differ diff --git a/pictures/falcmode.png b/pictures/falcmode.png deleted file mode 100644 index 224c1ed..0000000 Binary files a/pictures/falcmode.png and /dev/null differ diff --git a/pictures/legacymd/falchelp.png b/pictures/legacymd/falchelp.png new file mode 100644 index 0000000..5a701b6 Binary files /dev/null and b/pictures/legacymd/falchelp.png differ diff --git a/pictures/legacymd/falcmode.png b/pictures/legacymd/falcmode.png new file mode 100644 index 0000000..224c1ed Binary files /dev/null and b/pictures/legacymd/falcmode.png differ diff --git a/pictures/legacymd/loginmod.png b/pictures/legacymd/loginmod.png new file mode 100644 index 0000000..2bd5bf4 Binary files /dev/null and b/pictures/legacymd/loginmod.png differ diff --git a/pictures/loginmod.png b/pictures/loginmod.png deleted file mode 100644 index 2bd5bf4..0000000 Binary files a/pictures/loginmod.png and /dev/null differ diff --git a/pictures/mainicon.svg b/pictures/mainicon.svg new file mode 100644 index 0000000..5a145d6 --- /dev/null +++ b/pictures/mainicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/pictures/modernmd/falcmode.png b/pictures/modernmd/falcmode.png new file mode 100644 index 0000000..8d9a456 Binary files /dev/null and b/pictures/modernmd/falcmode.png differ diff --git a/requirements.txt b/requirements.txt index d7a6c7d..bd71068 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,12 @@ +certifi==2020.12.5 +chardet==4.0.0 click==7.1.2 +docker==4.4.1 falcon==2.0.0 -psutil==5.7.3 +idna==2.10 +psutil==5.8.0 +requests==2.25.1 +six==1.15.0 +urllib3==1.26.3 +websocket-client==0.57.0 Werkzeug==1.0.1