#!/usr/bin/python3 import json import os import pathlib import platform import pprint import subprocess as sp import sys from shutil import copyfile import psutil from src.backend.lib.display import TerminalDisplay log_file = "installer.log" messages = [] class Configuration: def __init__(self): self._cp = pathlib.Path("config.json") self._data = self.open_file() self.system = platform.system() def open_file(self): """ Try to open and then backup the configuration file. Fail and return false if initial configuration is not found. # TODO: More specific error handling """ try: with open(str(self._cp), "r") as read_file: data = json.load(read_file) with open("config.backup.json", "w") as backup_file: json.dump(data, backup_file) return data except Exception as e: print(e) return False def write_file(self, data): """ Write the provided data to the new configuration file """ with open(str(self._cp), "w") as write_file: json.dump(data, write_file) return True class RequiredServices: def check_ps(self, service_list): """ Check service_list against running processes by calling self.process_list, remove found services from the list and return """ # Get the matched processes _matches = self.process_list().intersection(set(service_list)) for r in _matches: service_list.remove(r) return service_list @staticmethod def process_list(): """ Iterate running processes returning the name of each make it a set and return """ _processes = [] for p in psutil.process_iter(): _processes.append(p.name()) return set(_processes) @staticmethod def web_server_found(service_list): # Determine whether or not both possible webservers are missing _c = 0 for r in service_list: if r == "nginx" or r == "httpd": _c = _c + 1 if _c > 1: return False # Return false if neither are found else: return True # Return true if one is found @staticmethod def db_server_found(service_list): _c = 0 for r in service_list: if r == "postgres": _c = _c + 1 if _c > 0: return False else: return True class SystemInstaller: def __init__(self): self.bin = self.get() self.site_dirs = ["/etc/nginx/sites-available", "/etc/nginx/sites-enabled"] self.nginx_conf = "pyshelf_nginx.conf" def get(self): platfrm = platform.platform().split("-") if platfrm[0].lower() == "linux": installers = [ {"bin": "apt", "options": [], "search": "search", "install": "install"}, {"bin": "pacman", "options": [], "search": "-Ss", "install": "-S"}, {"bin": "yum", "options": [], "search": "search", "install": "install"}, {"bin": "docker", "options": []}, ] _paths = os.environ["PATH"].split(":") for p in _paths: for _installer in installers: _fp = p + "/" + str(_installer["bin"]) if os.path.isfile(_fp): global messages messages = messages + [ "Found system installer binary " + str(_installer["bin"]) ] return _installer def copy_config(self, _file=None, _dirs=None): if _file is None: _file = self.nginx_conf if _dirs is None: _dirs = self.site_dirs outfile = "/%s" % _file.__str__() if os.path.isdir(_dirs[0]): os.system("sudo cp %s %s" % (_file, _dirs[0] + outfile)) else: os.system("sudo mkdir %s" % _dirs[0]) os.system("sudo cp %s %s" % (_file, _dirs[0] + outfile)) try: if os.path.isdir(_dirs[1]): ln_string = str(_dirs[0] + outfile + " " + _dirs[1] + outfile) os.system("sudo ln -s %s" % ln_string) except Exception as e: pass return True def make_nginx_config(self, answers): root = os.path.abspath(".") _fp = "pyshelf_nginx.conf" for r in answers: if r["name"] == "hostname": hostname = r["answer"] elif r["name"] == "webport": port = r["answer"] elif r["name"] == "wsgiport": wsgiport = r["answer"] nginx_conf_str = """ # pyshelf_nginx.conf upstream django {server unix://%s/pyshelf_wsgi.sock;} server { listen %s; server_name %s; access_log /var/log/nginx/pyshelf.access.log; error_log /var/log/nginx/pyshelf.error.log; charset utf-8; client_max_body_size 75M; location /media {root %s/src/interface;} location /static {root %s/src/interface;} location /books {internal; alias %s;} location / {uwsgi_pass django; include %s/uwsgi_params;} } """ % ( root, port, hostname, root, root, root, root, ) with open(_fp, "w") as write_file: write_file.write(nginx_conf_str) global messages messages = messages + ["Generated new pyshelf_nginx.conf", nginx_conf_str] def make_wsgi_config(self, answers): root = os.path.abspath(".") _fp = "uwsgi.ini" for r in answers: if r["name"] == "hostname": hostname = r["answer"] elif r["name"] == "wsgiport": wsgiport = r["answer"] wsgi_conf_str = """ [uwsgi] chdir=%s/src module=frontend.wsgi master=True pidfile=/tmp/pyShelf.pid vacuum=True socket=%s/pyshelf_wsgi.sock chmod-socket=666 """ % ( root, root ) with open(_fp, "w") as write_file: write_file.write(wsgi_conf_str) global messages messages = messages + ["Generated uwsgi.ini", wsgi_conf_str] def log(self): global log_file global messages with open(log_file, "w") as write_file: write_file.write(TerminalDisplay().banner_render()) for message in messages: write_file.write(message + "\n") messages = messages + ["Log file written to " + log_file.__str__()] config = Configuration().open_file() sysinstall = SystemInstaller() installer = sysinstall.bin # Get user configuration options install_answers = TerminalDisplay().installer() for key in install_answers: config[key["name"]] = key["answer"] config["USER"] = os.environ["USER"] # Write configuration Configuration().write_file(config) # Start checking for our list of required services service_list = ["postgres", "nginx", "httpd", "test"] req = RequiredServices().check_ps(service_list) # Does user have either nginx || apache? if RequiredServices().web_server_found(req) is False: web_prompt = [ { "message": " You must have either apache or nginx\n would you like us to try and install nginx now? > ", "options": "nginx", "name": "NGINX", "answer": None, "default": "no", } ] install_prompt = TerminalDisplay().prompt(web_prompt) if install_prompt[0]["answer"] == "yes": if installer is None: installer = SystemInstaller().bin if installer["bin"] == "pacman": package = "nginx-mainline" else: package = "nginx" options = "" for o in installer["options"]: options = options + " " + o cmd = ( "sudo " + installer["bin"] + " " + installer["install"] + " " + options + package ) install_status = os.system(cmd) os.system("sudo systemctl start nginx") messages = messages + [ "Nginx installed and started", "To enable autostart you must run", " sudo systemctl enable nginx", "\n", ] # Does user have postgreSQL? if RequiredServices().db_server_found(req) is False: db_prompt = [ { "message": " You must have PostgreSQL\n would you like us to try and install it now? > ", "options": "postgres", "name": "postgresql", "answer": None, "default": "no", } ] install_prompt = TerminalDisplay().prompt(db_prompt) if install_prompt[0]["answer"] == "yes": if installer is None: installer = SystemInstaller().bin options = "" for o in installer["options"]: options = options + " " + o package = "postgresql postgresql-contrib" cmd = ( "sudo " + installer["bin"] + " " + installer["install"] + " " + options + package ) install_status = os.system(cmd) for r in install_answers: if r["name"] == "PASSWORD": sql_pass = r["answer"] sql_user = config["USER"] db_name = "pyshelf" psql_cmd = """ CREATE DATABASE %s; CREATE USER %s WITH PASSWORD \'%s\'; GRANT ALL PRIVILEGES ON DATABASE %s TO %s; """ % (db_name, sql_user, sql_pass, db_name, sql_user) _sql_file = "create_db.sql" with open(_sql_file, "w") as sql_file_open: sql_file_open.write(psql_cmd) sql_file_open.close() os.system("sudo systemctl start postgresql") os.system("sudo -u postgres initdb --locale=en_US.UTF-8 -E UTF8 -D /var/lib/postgres/data") os.system("sudo -u postgres psql -f %s"%_sql_file) messages = messages + [ "PostgreSQL installed and started", "To enable autostart you must run", " sudo systemctl enable nginx", "\n", "Database cluster initialized at /var/lib/postgres", "pyShelf database and user created", psql_cmd ] # Post install configurations sysinstall.make_nginx_config(install_answers) try: os.chdir("src/") os.system("python manage.py makemigrations") os.system("python manage.py makemigrations interface") os.system("python manage.py migrate") os.system("python manage.py migrate interface") os.chdir("../") except Exception as e: print("-"*80) print(" E:"+e) try: copy_config = sysinstall.copy_config() if copy_config: messages = messages + ["pyShelf site config copied to sites-available, and symlinked to sites-enabled"] except Exception as e: messages = messages + ["nginx site config not copied", "A nginx config for your install has been created \"pyshelf_nginx.conf\""] sysinstall.make_wsgi_config(install_answers) messages = messages + ["You should now import your books by running importBooks", "You can then start the interface with uwsgi --ini uwsgi.ini"] # Display end screen sysinstall.log() TerminalDisplay().clear() TerminalDisplay().banner() for message in messages: print(" "+message) print() TerminalDisplay().h_rule()