first
This commit is contained in:
commit
83d031600e
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
# created by virtualenv automatically
|
||||
.idea
|
||||
config/main_local.py
|
||||
bin
|
||||
lib
|
0
config/__init__.py
Normal file
0
config/__init__.py
Normal file
BIN
config/__pycache__/__init__.cpython-310.pyc
Normal file
BIN
config/__pycache__/__init__.cpython-310.pyc
Normal file
Binary file not shown.
BIN
config/__pycache__/main_local.cpython-310.pyc
Normal file
BIN
config/__pycache__/main_local.cpython-310.pyc
Normal file
Binary file not shown.
4
config/main_example.py
Normal file
4
config/main_example.py
Normal file
@ -0,0 +1,4 @@
|
||||
pool = [
|
||||
("demo-23", "3000"),
|
||||
("demo-22", "3001")
|
||||
]
|
194
daemon/SimpleDaemon.py
Normal file
194
daemon/SimpleDaemon.py
Normal file
@ -0,0 +1,194 @@
|
||||
"""Generic linux daemon base class for python 3.x."""
|
||||
|
||||
import sys, os, time, atexit, signal, logging
|
||||
|
||||
|
||||
class Daemon:
|
||||
"""A generic daemon class.
|
||||
|
||||
Usage: subclass the daemon class and override the run() method."""
|
||||
|
||||
def __init__(self, pidfile, error_log_file='/dev/null'):
|
||||
self.logging = logging
|
||||
self.logging.basicConfig(filename=error_log_file, filemode='w',
|
||||
format='%(name)s - %(levelname)s - %(message)s\n')
|
||||
self.logging.getLogger().addHandler(logging.StreamHandler(sys.stdout))
|
||||
self.error_log_file = error_log_file
|
||||
self.pidfile = pidfile
|
||||
self.commands = {}
|
||||
|
||||
def __enter__(self):
|
||||
self.base_commands()
|
||||
self.reg_command()
|
||||
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
pass
|
||||
|
||||
def daemonize(self):
|
||||
"""Deamonize class. UNIX double fork mechanism."""
|
||||
|
||||
try:
|
||||
pid = os.fork()
|
||||
if pid > 0:
|
||||
# exit first parent
|
||||
sys.exit(0)
|
||||
except OSError as err:
|
||||
sys.stderr.write('fork #1 failed: {0}\n'.format(err))
|
||||
self.logging.error('fork #1 failed: {0}\n'.format(err))
|
||||
sys.exit(1)
|
||||
|
||||
# decouple from parent environment
|
||||
os.chdir('/')
|
||||
os.setsid()
|
||||
os.umask(0)
|
||||
|
||||
# do second fork
|
||||
try:
|
||||
pid = os.fork()
|
||||
if pid > 0:
|
||||
# exit from second parent
|
||||
sys.exit(0)
|
||||
except OSError as err:
|
||||
sys.stderr.write('fork #2 failed: {0}\n'.format(err))
|
||||
self.logging.error('fork #2 failed: {0}\n'.format(err))
|
||||
sys.exit(1)
|
||||
|
||||
# redirect standard file descriptors
|
||||
sys.stdout.flush()
|
||||
sys.stderr.flush()
|
||||
si = open(os.devnull, 'r')
|
||||
so = open(os.devnull, 'a+')
|
||||
se = open(os.devnull, 'a+')
|
||||
|
||||
os.dup2(si.fileno(), sys.stdin.fileno())
|
||||
os.dup2(so.fileno(), sys.stdout.fileno())
|
||||
os.dup2(se.fileno(), sys.stderr.fileno())
|
||||
|
||||
# write pidfile
|
||||
atexit.register(self.delpid)
|
||||
|
||||
pid = str(os.getpid())
|
||||
with open(self.pidfile, 'w+') as f:
|
||||
f.write(pid + '\n')
|
||||
|
||||
def delpid(self):
|
||||
os.remove(self.pidfile)
|
||||
|
||||
def start(self):
|
||||
"""Start the daemon."""
|
||||
self.logging.info("Start")
|
||||
|
||||
# Check for a pidfile to see if the daemon already runs
|
||||
try:
|
||||
with open(self.pidfile, 'r') as pf:
|
||||
|
||||
pid = int(pf.read().strip())
|
||||
except IOError:
|
||||
pid = None
|
||||
|
||||
if pid:
|
||||
message = "pidfile {0} already exist. " + \
|
||||
"Daemon already running?\n"
|
||||
sys.stderr.write(message.format(self.pidfile))
|
||||
self.logging.error(message.format(self.pidfile))
|
||||
sys.exit(1)
|
||||
|
||||
# Start the daemon
|
||||
self.daemonize()
|
||||
self.run()
|
||||
|
||||
def stop(self):
|
||||
"""Stop the daemon."""
|
||||
self.logging.info("Stop")
|
||||
|
||||
# Get the pid from the pidfile
|
||||
try:
|
||||
with open(self.pidfile, 'r') as pf:
|
||||
pid = int(pf.read().strip())
|
||||
except IOError:
|
||||
pid = None
|
||||
|
||||
if not pid:
|
||||
message = "pidfile {0} does not exist. " + \
|
||||
"Daemon not running?\n"
|
||||
sys.stderr.write(message.format(self.pidfile))
|
||||
self.logging.error(message.format(self.pidfile))
|
||||
return # not an error in a restart
|
||||
|
||||
# Try killing the daemon process
|
||||
try:
|
||||
while 1:
|
||||
os.kill(pid, signal.SIGTERM)
|
||||
time.sleep(0.1)
|
||||
except OSError as err:
|
||||
e = str(err.args)
|
||||
if e.find("No such process") > 0:
|
||||
if os.path.exists(self.pidfile):
|
||||
os.remove(self.pidfile)
|
||||
else:
|
||||
print(str(err.args))
|
||||
sys.exit(1)
|
||||
|
||||
def restart(self):
|
||||
"""Restart the daemon."""
|
||||
self.logging.info("Restart")
|
||||
self.stop()
|
||||
self.start()
|
||||
|
||||
def status(self):
|
||||
print("Status")
|
||||
try:
|
||||
with open(self.pidfile, 'r') as pf:
|
||||
|
||||
pid = int(pf.read().strip())
|
||||
except IOError:
|
||||
pid = None
|
||||
|
||||
if pid:
|
||||
print("Process started, pid %d" % pid)
|
||||
else:
|
||||
print("Process is not running")
|
||||
|
||||
def console_stdout(self):
|
||||
sys.stdout = sys.__stdout__
|
||||
print(123)
|
||||
|
||||
def process_command(self):
|
||||
if len(sys.argv) > 1:
|
||||
command = sys.argv[1]
|
||||
handler = self.get_command_handler(command)
|
||||
if handler:
|
||||
handler()
|
||||
else:
|
||||
print("Unknown command: %s" % command)
|
||||
else:
|
||||
print("usage: %s start|stop|restart|status" % sys.argv[0])
|
||||
sys.exit(2)
|
||||
|
||||
def base_commands(self):
|
||||
self.add_command('start', self.start)
|
||||
self.add_command('stop', self.stop)
|
||||
self.add_command('restart', self.restart)
|
||||
self.add_command('status', self.status)
|
||||
self.add_command('console_stdout', self.console_stdout)
|
||||
|
||||
def add_command(self, command, handler):
|
||||
if command not in self.commands:
|
||||
self.commands[command] = handler
|
||||
|
||||
def get_command_handler(self, command):
|
||||
if command in self.commands:
|
||||
return self.commands[command]
|
||||
|
||||
return None
|
||||
|
||||
def reg_command(self):
|
||||
pass
|
||||
|
||||
def run(self):
|
||||
"""You should override this method when you subclass Daemon.
|
||||
|
||||
It will be called after the process has been daemonized by
|
||||
start() or restart()."""
|
0
daemon/__init__.py
Normal file
0
daemon/__init__.py
Normal file
BIN
daemon/__pycache__/SimpleDaemon.cpython-310.pyc
Normal file
BIN
daemon/__pycache__/SimpleDaemon.cpython-310.pyc
Normal file
Binary file not shown.
BIN
daemon/__pycache__/__init__.cpython-310.pyc
Normal file
BIN
daemon/__pycache__/__init__.cpython-310.pyc
Normal file
Binary file not shown.
0
front-demo-daemon.txt
Normal file
0
front-demo-daemon.txt
Normal file
63
main.py
Normal file
63
main.py
Normal file
@ -0,0 +1,63 @@
|
||||
from http.server import BaseHTTPRequestHandler, HTTPServer
|
||||
from http.client import HTTPMessage
|
||||
import requests
|
||||
from config.main_local import pool
|
||||
from daemon.SimpleDaemon import Daemon
|
||||
|
||||
hostName = "localhost"
|
||||
serverPort = 9000
|
||||
|
||||
class FrontDemoDaemon(Daemon):
|
||||
def run(self):
|
||||
webServer = HTTPServer((hostName, serverPort), MyServer)
|
||||
print(f"Server started http://{hostName}:{serverPort}")
|
||||
|
||||
try:
|
||||
webServer.serve_forever()
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
|
||||
webServer.server_close()
|
||||
print("Http server stopped.")
|
||||
|
||||
|
||||
class MyServer(BaseHTTPRequestHandler):
|
||||
def do_GET(self):
|
||||
host = get_host_from_headers(self.headers)
|
||||
subdomain = get_subdomain(host)
|
||||
port = get_port(subdomain)
|
||||
|
||||
url = 'http://localhost:{port}{path}'.format(path=self.path, port=port)
|
||||
response = requests.get(url)
|
||||
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", response.headers.get("Content-type"))
|
||||
self.end_headers()
|
||||
self.wfile.write(bytes(response.text, "utf-8"))
|
||||
|
||||
|
||||
def get_host_from_headers(headers: HTTPMessage):
|
||||
for item in headers.items():
|
||||
if item[0] == "Host":
|
||||
return item[1]
|
||||
|
||||
return None
|
||||
|
||||
def get_subdomain(host: str):
|
||||
res = host.split(sep=".", maxsplit=-1)
|
||||
if res[0]:
|
||||
return res[0]
|
||||
|
||||
return None
|
||||
|
||||
def get_port(subdomain: str):
|
||||
for item in pool:
|
||||
if item[0] == subdomain:
|
||||
return item[1]
|
||||
|
||||
return None
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
with FrontDemoDaemon('/tmp/front-demo-daemon.pid', error_log_file='/var/log/front-demo-daemon.txt') as front:
|
||||
front.process_command()
|
8
pyvenv.cfg
Normal file
8
pyvenv.cfg
Normal file
@ -0,0 +1,8 @@
|
||||
home = /usr/bin
|
||||
implementation = CPython
|
||||
version_info = 3.10.12.final.0
|
||||
virtualenv = 20.24.5
|
||||
include-system-site-packages = false
|
||||
base-prefix = /usr
|
||||
base-exec-prefix = /usr
|
||||
base-executable = /usr/bin/python3.10
|
14
requirements.txt
Normal file
14
requirements.txt
Normal file
@ -0,0 +1,14 @@
|
||||
blinker==1.9.0
|
||||
certifi==2024.12.14
|
||||
charset-normalizer==3.4.1
|
||||
click==8.1.8
|
||||
filelock==3.16.1
|
||||
idna==3.10
|
||||
itsdangerous==2.2.0
|
||||
MarkupSafe==3.0.2
|
||||
python-magic==0.4.27
|
||||
requests==2.32.3
|
||||
requests-file==2.1.0
|
||||
tldextract==5.1.3
|
||||
urllib3==2.3.0
|
||||
Werkzeug==3.1.3
|
Loading…
x
Reference in New Issue
Block a user