Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagecell
Path: blob/master/web_server.py
447 views
1
#! /usr/bin/env python
2
3
import fcntl
4
import os
5
import signal
6
import socket
7
import struct
8
9
import asyncio
10
asyncio.set_event_loop(asyncio.new_event_loop())
11
12
import paramiko
13
import psutil
14
import tornado.ioloop
15
import tornado.web
16
17
import handlers
18
from log import logger
19
from kernel_dealer import KernelDealer
20
import misc
21
import permalink
22
23
24
config = misc.Config()
25
26
27
def start_providers(port, providers, dir):
28
r"""
29
Start kernel providers.
30
31
INPUT:
32
33
- ``port`` -- port for providers to connect to
34
35
- ``providers`` -- list of dictionaries
36
37
- ``dir`` -- directory name for user files saved by kernels
38
"""
39
for config in providers:
40
client = paramiko.SSHClient()
41
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
42
client.connect(config["host"], username=config["username"])
43
command = "{} '{}/kernel_provider.py' {} '{}'".format(
44
config["python"], config["location"], port, dir)
45
logger.debug("starting kernel provider: %s", command)
46
client.exec_command(command)
47
client.close()
48
49
50
class SageCellServer(tornado.web.Application):
51
def __init__(self, baseurl, dir):
52
# This matches a kernel id (uuid4 format) from a url
53
_kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
54
baseurl = baseurl.rstrip('/')
55
handlers_list = [
56
(r"/", handlers.RootHandler),
57
(r"/embedded_sagecell.js",
58
tornado.web.RedirectHandler,
59
{"url":baseurl+"/static/embedded_sagecell.js"}),
60
(r"/help.html", handlers.HelpHandler),
61
(r"/kernel", handlers.KernelHandler),
62
(r"/kernel/%s" % _kernel_id_regex, handlers.KernelHandler),
63
(r"/kernel/%s/channels" % _kernel_id_regex,
64
handlers.WebChannelsHandler),
65
(r"/kernel/%s/files/(?P<file_path>.*)" % _kernel_id_regex,
66
handlers.FileHandler, {"path": dir}),
67
(r"/permalink", permalink.PermalinkHandler),
68
(r"/service", handlers.ServiceHandler),
69
(r"/tos.html", handlers.TOSHandler),
70
] + handlers.KernelRouter.urls
71
handlers_list = [[baseurl+i[0]]+list(i[1:]) for i in handlers_list]
72
settings = dict(
73
compress_response = True,
74
template_path = os.path.join(
75
os.path.dirname(os.path.abspath(__file__)), "templates"),
76
static_path = os.path.join(
77
os.path.dirname(os.path.abspath(__file__)), "static"),
78
static_url_prefix = baseurl + "/static/",
79
static_handler_class = handlers.StaticHandler
80
)
81
self.kernel_dealer = KernelDealer(config.get("provider_settings"))
82
start_providers(self.kernel_dealer.port, config.get("providers"), dir)
83
self.completer = handlers.Completer(self.kernel_dealer)
84
db = __import__('db_' + config.get('db'))
85
self.db = db.DB(config.get('db_config')['uri'])
86
self.ioloop = tornado.ioloop.IOLoop.current()
87
super(SageCellServer, self).__init__(handlers_list, **settings)
88
logger.info('SageCell server started')
89
try:
90
from systemd.daemon import notify
91
logger.debug('notifying systemd that we are ready')
92
notify('READY=1\nMAINPID={}'.format(os.getpid()), True)
93
except ImportError:
94
pass
95
96
97
def get_ip_address(ifname):
98
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
99
return socket.inet_ntoa(fcntl.ioctl(
100
s.fileno(),
101
0x8915, # SIOCGIFADDR
102
struct.pack('256s', ifname[:15])
103
)[20:24])
104
105
106
if __name__ == "__main__":
107
import argparse
108
parser = argparse.ArgumentParser(description='Launch a SageCell web server',
109
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
110
parser.add_argument('-p', '--port', type=int, default=8888,
111
help='port to launch the server')
112
parser.add_argument('-b', '--baseurl', default="", help="base url")
113
parser.add_argument('--interface', default=None, help="interface to listen on (default all)")
114
parser.add_argument('--dir', default=config.get("dir"), help="directory for user files")
115
args = parser.parse_args()
116
117
logger.info("starting tornado web server")
118
from lockfile.pidlockfile import PIDLockFile
119
pidfile_path = config.get('pid_file')
120
pidlock = PIDLockFile(pidfile_path)
121
if pidlock.is_locked():
122
old_pid = pidlock.read_pid()
123
logger.info("Lock file exists for PID %d." % old_pid)
124
if os.getpid() == old_pid:
125
logger.info("Stale lock since we have the same PID.")
126
else:
127
try:
128
old = psutil.Process(old_pid)
129
if os.path.basename(__file__) in old.cmdline():
130
try:
131
logger.info("Trying to terminate old instance...")
132
old.terminate()
133
try:
134
old.wait(10)
135
except psutil.TimeoutExpired:
136
logger.info("Trying to kill old instance.")
137
old.kill()
138
except psutil.AccessDenied:
139
logger.error("The process seems to be SageCell, but "
140
"can not be stopped. Its command line: %s"
141
% old.cmdline())
142
else:
143
logger.info("Process does not seem to be SageCell.")
144
except psutil.NoSuchProcess:
145
logger.info("No such process exist anymore.")
146
logger.info("Breaking old lock.")
147
pidlock.break_lock()
148
149
pidlock.acquire(timeout=10)
150
app = SageCellServer(args.baseurl, args.dir)
151
listen = {'port': args.port, 'xheaders': True}
152
if args.interface is not None:
153
listen['address'] = get_ip_address(args.interface)
154
logger.info("Listening configuration: %s", listen)
155
156
def handler(signum, frame):
157
logger.info("Received %s, shutting down...", signum)
158
app.kernel_dealer.stop()
159
app.ioloop.stop()
160
161
signal.signal(signal.SIGHUP, handler)
162
signal.signal(signal.SIGINT, handler)
163
signal.signal(signal.SIGTERM, handler)
164
165
app.listen(**listen)
166
app.ioloop.start()
167
pidlock.release()
168
logger.info('SageCell server stopped')
169
170