From a3559391e493bdb9c2074fc7d6b5e292dd758d34 Mon Sep 17 00:00:00 2001 From: Akashdeep Dhar Date: Jan 29 2021 14:46:51 +0000 Subject: Added monitoring endpoints for Docker management Signed-off-by: Akashdeep Dhar --- diff --git a/base/frnt.py b/base/frnt.py index a0af333..cfdf0d0 100644 --- a/base/frnt.py +++ b/base/frnt.py @@ -20,18 +20,11 @@ """ import json -from secrets import choice import falcon from base.back import DeadUpdatingElements, LiveUpdatingElements, ProcessHandler -class ConnectionManager: - def passphrase_generator(self, lent=16): - retndata = "".join(choice("ABCDEF0123456789") for i in range(lent)) - return retndata - - class StatisticalEndpoint(object): def __init__(self, passcode): self.passcode = passcode @@ -39,12 +32,13 @@ class StatisticalEndpoint(object): def on_get(self, rqst, resp): passcode = rqst.get_param("passcode") opername = rqst.get_param("opername") - retnjson = {} 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) @@ -75,7 +69,6 @@ class ProcessControllingEndpoint(object): passcode = rqst.get_param("passcode") opername = rqst.get_param("opername") prociden = int(rqst.get_param("prociden")) - retnjson = {} if passcode == self.passcode: if opername == "KILL": retnjson = ProcessHandler(prociden).process_killer() @@ -85,6 +78,8 @@ class ProcessControllingEndpoint(object): 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) diff --git a/dish/back.py b/dish/back.py new file mode 100644 index 0000000..91aed70 --- /dev/null +++ b/dish/back.py @@ -0,0 +1,210 @@ +""" +########################################################################## +* +* 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): + return self.clinobjc.info() + + def get_docker_version(self): + return self.clinobjc.version() + + +class DockerContainerInformation: + def __init__(self, unixsock): + self.clinobjc = DockerClient(base_url=unixsock) + + def get_container_list(self): + 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): + 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.name, + "short_id": contobjc.image.short_id + } + } + except: + dispdict = { + "retnmesg": "deny" + } + return dispdict + + def get_per_container_logs_data(self, contiden): + 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): + try: + contobjc = self.clinobjc.containers.get(contiden) + dispdict = { + "top": contobjc.top() + } + except: + dispdict = { + "retnmesg": "deny" + } + return dispdict + + def get_per_container_statistics(self, contiden): + 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): + imejlist = self.clinobjc.images.list(all=True) + dispdict = {} + for indx in imejlist: + dispdict[indx.short_id] = { + "id": indx.id, + "name": indx.name, + } + return dispdict + + def get_per_image_static_information(self, imejiden): + try: + imejobjc = self.clinobjc.images.get(imejiden) + dispdict = { + "short_id": imejobjc.short_id, + "id": imejobjc.id, + "name": imejobjc.name, + "attrs": imejobjc.attrs, + "labels": imejobjc.labels, + "tags": imejobjc.tags + } + except: + dispdict = { + "retnmesg": "deny" + } + return dispdict + + def get_per_image_revision_history(self, imejiden): + 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): + 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): + 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): + 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): + 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..92b63d0 --- /dev/null +++ b/dish/frnt.py @@ -0,0 +1,157 @@ +""" +########################################################################## +* +* 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): + 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): + 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): + 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): + 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): + 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/falc.py b/falc.py index 8f69f7c..9c519c4 100644 --- a/falc.py +++ b/falc.py @@ -19,32 +19,56 @@ ########################################################################## """ +from secrets import choice + import click import falcon from base.frnt import ( - ConnectionManager, 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 docker import __version__ as dockvers from werkzeug import serving main = falcon.API() +class ConnectionManager: + def passphrase_generator(self, lent=16): + retndata = "".join(choice("ABCDEF0123456789") for indx in range(lent)) + return retndata + + @click.command() @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.0.2", prog_name=click.style("SuperVisor Driver Service", fg="magenta")) -def mainfunc(portdata, netprotc): +@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": @@ -53,19 +77,32 @@ def mainfunc(portdata, netprotc): elif netprotc == "ipprotv4": 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") + ": " + "DockerPy v" + dockvers + "\n" + - " * " + click.style("Container 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) - statsync = StatisticalEndpoint(passcode) - procinfo = ProcessHandlingEndpoint(passcode) - proctool = ProcessControllingEndpoint(passcode) - main.add_route("/statsync", statsync) - main.add_route("/procinfo", procinfo) - main.add_route("/proctool", proctool) + 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) + 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) serving.run_simple(netpdata, int(portdata), main) except Exception as expt: click.echo(" * " + click.style("Error occurred : " + str(expt), fg="red"))