This repository has been archived by the owner on Jun 10, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 141
/
Copy pathserver.py
131 lines (107 loc) · 4.37 KB
/
server.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import Pyro4
import logging
Pyro4.config.REQUIRE_EXPOSE = True
log = logging.getLogger(__name__)
def main():
opts = getopts()
from . import register_deserialization_handlers
register_deserialization_handlers()
log_forwarder = setup_logging()
server = Server(opts['--listen'], log_forwarder)
server.start()
def setup_logging():
root = logging.getLogger()
root.setLevel(logging.NOTSET)
from .log import LogForwarder
log_forwarder = LogForwarder()
root.addHandler(log_forwarder)
from datetime import datetime
import os.path
from bootstrapvz.base.log import get_file_handler
timestamp = datetime.now().strftime('%Y%m%d%H%M%S')
filename = '{timestamp}_remote.log'.format(timestamp=timestamp)
logfile_path = os.path.join('/var/log/bootstrap-vz', filename)
file_handler = get_file_handler(logfile_path, True)
root.addHandler(file_handler)
return log_forwarder
def getopts():
from docopt import docopt
usage = """bootstrap-vz-server
Usage: bootstrap-vz-server [options]
Options:
--listen <port> Serve on specified port [default: 46675]
-h, --help show this help
"""
return docopt(usage)
class Server(object):
def __init__(self, listen_port, log_forwarder):
self.stop_serving = False
self.log_forwarder = log_forwarder
self.listen_port = listen_port
def start(self):
Pyro4.config.COMMTIMEOUT = 0.5
daemon = Pyro4.Daemon('localhost', port=int(self.listen_port), unixsocket=None)
daemon.register(self, 'server')
daemon.requestLoop(loopCondition=lambda: not self.stop_serving)
@Pyro4.expose
def set_callback_server(self, server):
log.debug('Forwarding logs to the callback server')
self.log_forwarder.set_server(server)
@Pyro4.expose
def ping(self):
if hasattr(self, 'connection_timeout'):
# pylint: disable=no-member
self.connection_timeout.cancel()
del self.connection_timeout
return 'pong'
@Pyro4.expose
def stop(self):
if hasattr(self, 'bootstrap_process'):
log.warn('Sending SIGINT to bootstrapping process')
import os
import signal
os.killpg(self.bootstrap_process.pid, signal.SIGINT)
self.bootstrap_process.join()
# We can't send a SIGINT to the server,
# for some reason the Pyro4 shutdowns are rather unclean,
# throwing exceptions and such.
self.stop_serving = True
@Pyro4.expose
def run(self, manifest, debug=False, dry_run=False):
def bootstrap(queue):
# setsid() creates a new session, making this process the group leader.
# We do that, so when the server calls killpg (kill process group)
# on us, it won't kill itself (this process was spawned from a
# thread under the server, meaning it's part of the same group).
# The process hierarchy looks like this:
# Pyro server (process - listening on a port)
# +- pool thread
# +- pool thread
# +- pool thread
# +- started thread (the one that got the "run()" call)
# L bootstrap() process (us)
# Calling setsid() also fixes another problem:
# SIGINTs sent to this process seem to be redirected
# to the process leader. Since there is a thread between
# us and the process leader, the signal will not be propagated
# (signals are not propagated to threads), this means that any
# subprocess we start (i.e. debootstrap) will not get a SIGINT.
import os
os.setsid()
from bootstrapvz.base.main import run
try:
bootstrap_info = run(manifest, debug=debug, dry_run=dry_run)
queue.put(bootstrap_info)
except (Exception, KeyboardInterrupt) as e: # pylint: disable=broad-except
queue.put(e)
from multiprocessing import Queue
from multiprocessing import Process
queue = Queue()
self.bootstrap_process = Process(target=bootstrap, args=(queue,))
self.bootstrap_process.start()
self.bootstrap_process.join()
del self.bootstrap_process
result = queue.get()
if isinstance(result, Exception):
raise result
return result