From f4e7273735733cb417adb694cd53816e4d15e806 Mon Sep 17 00:00:00 2001 From: Jonathan Branan Date: Mon, 17 Oct 2022 17:18:17 -0500 Subject: [PATCH] pushing existing work --- .gitignore | 164 ++++++++++++++++++++++++++++++++++++++++++++ cclient.py | 14 ++++ clogging.py | 33 +++++++++ config.json.example | 13 ++++ cprocess.py | 8 +++ crane.py | 57 +++++++++++++++ test_crane.py | 41 +++++++++++ 7 files changed, 330 insertions(+) create mode 100644 .gitignore create mode 100644 cclient.py create mode 100644 clogging.py create mode 100644 config.json.example create mode 100644 cprocess.py create mode 100644 crane.py create mode 100644 test_crane.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..da47f49 --- /dev/null +++ b/.gitignore @@ -0,0 +1,164 @@ +*.log +*.json +*.csv + +# 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 + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__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/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ \ No newline at end of file diff --git a/cclient.py b/cclient.py new file mode 100644 index 0000000..7386342 --- /dev/null +++ b/cclient.py @@ -0,0 +1,14 @@ +def c_auth(req_obj, host, port, username, password): + url = f'https://{host}:{port}/api/auth' + c_auth_response = req_obj.post(url, json={"Username":username,"Password":password}, verify=False) + return c_auth_response.json()["jwt"] + +def c_get_containers(req_obj, host, port, jwt, endpoint): + url = f'https://{host}:{port}/api/endpoints/{endpoint}/docker/containers/json?all=true' + c_get_containers_response = req_obj.get(url, headers={"Authorization": f"Bearer {jwt}"},verify=False) + return c_get_containers_response.json() + +def c_start_container(req_obj, host, port, jwt, endpoint, cid): + url = f'https://{host}:{port}/api/endpoints/{endpoint}/docker/containers/{cid}/start' + c_start_container_response = req_obj.post(url, headers={"Authorization": f"Bearer {jwt}"},verify=False) + return c_start_container_response.status_code \ No newline at end of file diff --git a/clogging.py b/clogging.py new file mode 100644 index 0000000..9b4cfa2 --- /dev/null +++ b/clogging.py @@ -0,0 +1,33 @@ +def cont_log(self): + """Setting up the log file, if self.use_log is set to true and self.loglevel is DEBUG OR INFO""" + if self.use_log: + if self.log_level == 'DEBUG': + self.tl.basicConfig(filename=self.log_path, format='%(asctime)s:%(levelname)s:%(message)s', encoding='utf-8', datefmt='%m/%d/%Y %I:%M:%S %p',level=self.tl.DEBUG) + elif self.log_level == 'INFO': + self.tl.basicConfig(filename=self.log_path, format='%(asctime)s:%(levelname)s:%(message)s', encoding='utf-8', datefmt='%m/%d/%Y %I:%M:%S %p',level=self.tl.INFO) + +def cont_notify(self): + """Seting up to use pushover, if self.use_pushover is set to true and + if valid self.po_key and self.po_token is provided in the config file""" + if self.use_pushover: + self.poc = self.po.Client(self.po_key, api_token=self.po_token) + +def cont_notify_summary(self): + """Main notification method when the app is used in an automated fashion""" + self.poc.send_message(f" \ + {self.extm}", title="--- qbit-maid summary ---") + +def list_first_cont(self, index=0): + """Only lists the first torrent""" + self.tl.debug('First torrent in the list:') + torrent = self.torrent_list[index] + for k,v in torrent.items(): + self.tl.debug(f'{k}: {v}') + self.tl.debug('\n') + +def get_script_runtime(self): + elapsed_time = self.et - self.st + if self.use_log: + self.tl.info(f'Execution time: [{elapsed_time}]') + if self.use_pushover: + self.extm = f"Execution time: [{elapsed_time}]" \ No newline at end of file diff --git a/config.json.example b/config.json.example new file mode 100644 index 0000000..417a259 --- /dev/null +++ b/config.json.example @@ -0,0 +1,13 @@ +{ + "host": "192.168.1.1", + "port": 9443, + "username": "admin", + "password": "admin", + "endpoint": 1, + "log_level": "INFO", + "log_path": "./crn.log", + "use_pushover": false, + "use_log": true, + "po_key": "", + "po_token": "" +} \ No newline at end of file diff --git a/cprocess.py b/cprocess.py new file mode 100644 index 0000000..5be4a18 --- /dev/null +++ b/cprocess.py @@ -0,0 +1,8 @@ +def build_cont_list(obj): + cont_list = [] + for i, c in enumerate(obj): + if c["State"].lower() != "running": + print(f'index: {i} container: {c["Names"][0].lstrip("/")} State: {c["State"]} ID: {c["Id"]}') + cont_list.append(c) + print(len(cont_list)) + return cont_list \ No newline at end of file diff --git a/crane.py b/crane.py new file mode 100644 index 0000000..e97fa6c --- /dev/null +++ b/crane.py @@ -0,0 +1,57 @@ +import pushover +from json import load +from cclient import c_auth, c_get_containers +from clogging import * +import time +import datetime +import logging +import requests + +class Crn: + def __init__(self): + """Main object, should be calling functions from qlist.py, qlogging.py and qprocess.py""" + # Open the config. Needs a json file with the data in config.json.example + self.st = datetime.datetime.now() + with open('./config.json') as c: + self.config = load(c) + # Create the api object + self.cc = requests + # Create the logging and pushover objects + self.tl = logging + self.po = pushover + + # Init config.json + self.use_pushover = self.config["use_pushover"] + self.use_log = self.config["use_log"] + self.po_key = self.config["po_key"] + self.po_token = self.config["po_token"] + self.log_path = self.config["log_path"] + self.log_level = self.config["log_level"] + self.host = self.config["host"] + self.port = self.config["port"] + self.username = self.config["username"] + self.password = self.config["password"] + self.endpoint = self.config["endpoint"] + cont_log(self) + cont_notify(self) + self.t = time + + #logging in + try: + self.tl.info('Authenticating.') + self.jwt = c_auth(self.cc, self.host, self.port, self.username, self.password) + self.tl.info('Authenticated successfully.') + self.cont_obj = c_get_containers(self.cc, self.host, self.port, self.jwt) + self.tl.info('Collected container list.') + except requests.exceptions.RequestException as e: + self.tl.exception(e) + self.po.send_message(e, title="crane API ERROR") + + #Main process block + self.et = datetime.datetime.now() + get_script_runtime(self) + if self.use_pushover: + cont_notify_summary(self) +# Run +if __name__== "__main__": + Crn() \ No newline at end of file diff --git a/test_crane.py b/test_crane.py new file mode 100644 index 0000000..1651ffe --- /dev/null +++ b/test_crane.py @@ -0,0 +1,41 @@ +import unittest +import requests +from json import load +from cclient import c_auth, c_get_containers, c_start_container +from cprocess import build_cont_list + +class TestCrane(unittest.TestCase): + def setUp(self): + with open('./config.json') as c: + self.config = load(c) + self.host = self.config["host"] + self.port = self.config["port"] + self.username = self.config["username"] + self.password = self.config["password"] + self.endpoint = self.config["endpoint"] + self.cid = 'ef8fee86e02b2b82acbddf6f0da1ff023f60bfe52c0b4087cac29c1686ccbac4' + self.req_obj = requests + def test_c_auth(self): + self.jwt = c_auth(self.req_obj, self.host, self.port, self.username, self.password) + self.assertTrue(self.jwt, "No JWT returned by cauth.") + + def test_c_get_containers(self): + self.jwt = c_auth(self.req_obj, self.host, self.port, self.username, self.password) + self.cont_obj = c_get_containers(self.req_obj, self.host, self.port, self.jwt, self.endpoint) + self.assertTrue(self.cont_obj, "No cont object returned by c_get_containers.") + + def test_build_cont_list(self): + self.jwt = c_auth(self.req_obj, self.host, self.port, self.username, self.password) + self.cont_obj = c_get_containers(self.req_obj, self.host, self.port, self.jwt, self.endpoint) + self.cont_list = build_cont_list(self.cont_obj) + self.assertTrue(self.cont_list, "No cont_list returned by build_cont_list.") + + def test_c_start_container(self): + self.jwt = c_auth(self.req_obj, self.host, self.port, self.username, self.password) + self.c_start_container_response = c_start_container(self.req_obj, self.host, self.port, self.jwt, self.endpoint, self.cid) + print(self.c_start_container_response) + self.assertTrue(self.c_start_container_response, "No c_start_container_resonse returned by c_start_container.") + # 204 success 304 already on + +if __name__ == '__main__': + unittest.main() \ No newline at end of file