]>
Commit | Line | Data |
---|---|---|
5470bdf1 MT |
1 | #!/usr/bin/python |
2 | ||
3 | import logging | |
4 | import os | |
5 | import tornado.httpserver | |
6 | import tornado.ioloop | |
7 | import tornado.locale | |
8 | import tornado.options | |
9 | import tornado.web | |
10 | ||
11 | from webapp import backend | |
12 | ||
13 | BASEDIR = os.path.dirname(__file__) | |
14 | ||
15 | # Enable logging | |
16 | tornado.options.enable_pretty_logging() | |
17 | tornado.options.parse_command_line() | |
18 | ||
a9bf3cbe | 19 | def word_wrap(s, width=45): |
7261a759 MT |
20 | paragraphs = s.split('\n') |
21 | lines = [] | |
22 | for paragraph in paragraphs: | |
23 | while len(paragraph) > width: | |
24 | pos = paragraph.rfind(' ', 0, width) | |
25 | if not pos: | |
26 | pos = width | |
27 | lines.append(paragraph[:pos]) | |
28 | paragraph = paragraph[pos:] | |
a9bf3cbe | 29 | lines.append(paragraph.lstrip()) |
7261a759 | 30 | return '\n'.join(lines) |
5470bdf1 MT |
31 | |
32 | class BaseHandler(tornado.web.RequestHandler): | |
33 | @property | |
34 | def netboot(self): | |
35 | return backend.NetBoot() | |
36 | ||
37 | ||
38 | class MenuGPXEHandler(BaseHandler): | |
39 | """ | |
40 | menu.gpxe | |
41 | """ | |
42 | def get(self): | |
43 | # XXX Check if version of the bootloader is allright | |
44 | ||
45 | # Devliver content | |
46 | self.set_header("Content-Type", "text/plain") | |
47 | self.write("#!gpxe\n") | |
48 | self.write("chain menu.c32 premenu.cfg\n") | |
49 | ||
50 | ||
51 | class MenuCfgHandler(BaseHandler): | |
52 | def _menu_string(self, menu, level=0): | |
53 | s = "" | |
54 | ||
55 | for entry in menu: | |
56 | s += self._menu_entry(entry, level=level) | |
57 | ||
58 | return s | |
59 | ||
60 | def _menu_entry(self, entry, level=0): | |
61 | lines = [] | |
62 | ||
63 | ident = "\t" * level | |
64 | ||
65 | if entry.type == "seperator": | |
2109db85 | 66 | lines.append(ident + "menu separator") |
5470bdf1 MT |
67 | |
68 | elif entry.type == "header": | |
69 | lines.append(ident + "menu begin %d" % entry.id) | |
70 | lines.append(ident + "\tmenu title %s" % entry.title) | |
71 | ||
72 | # Add "Back..." entry | |
73 | lines.append(ident + "\tlabel %d.back" % entry.id) | |
74 | lines.append(ident + "\t\tmenu label Back...") | |
75 | lines.append(ident + "\t\tmenu exit") | |
2109db85 | 76 | lines.append(ident + "\tmenu separator") |
5470bdf1 | 77 | |
2109db85 | 78 | lines.append("%s" % self._menu_string(entry.submenu, level=level+1)) |
5470bdf1 | 79 | lines.append(ident + "menu end") |
5470bdf1 MT |
80 | |
81 | elif entry.type == "config": | |
82 | lines.append(ident + "label %d" % entry.id) | |
83 | lines.append(ident + "\tmenu label %s" % entry.title) | |
84 | if entry.description: | |
85 | lines.append(ident + "\ttext help") | |
7261a759 | 86 | lines.append(word_wrap(entry.description)) |
5470bdf1 MT |
87 | lines.append(ident + "\tendtext") |
88 | lines.append(ident + "\tkernel /config/%s/boot.gpxe" % entry.item) | |
5470bdf1 | 89 | |
7261a759 | 90 | return "\n".join(lines + [""]) |
5470bdf1 MT |
91 | |
92 | def get(self): | |
93 | self.set_header("Content-Type", "text/plain") | |
94 | ||
95 | menu = self._menu_string(self.netboot.get_menu(1)) | |
96 | ||
97 | self.render("menu.cfg", menu=menu) | |
98 | ||
99 | ||
100 | class BootGPXEHandler(BaseHandler): | |
101 | def get(self, id): | |
102 | config = self.netboot.get_config(id) | |
103 | if not config: | |
104 | raise tornado.web.HTTPError(404, "Configuration with ID '%s' was not found" % id) | |
105 | ||
106 | lines = ["#!gpxe", "imgfree",] | |
107 | ||
108 | line = "kernel -n img %s" % config.image1 | |
109 | if line.endswith(".iso"): | |
110 | line += " iso" | |
111 | lines.append(line) | |
112 | ||
113 | if config.image2: | |
114 | lines.append("initrd -n img %s" % config.image2) | |
115 | ||
116 | lines.append("boot img") | |
117 | ||
118 | for line in lines: | |
119 | self.write("%s\n" % line) | |
120 | ||
121 | ||
122 | class Application(tornado.web.Application): | |
123 | def __init__(self): | |
124 | settings = dict( | |
125 | debug = True, | |
126 | gzip = True, | |
127 | static_path = os.path.join(BASEDIR, "static/netboot"), | |
128 | template_path = os.path.join(BASEDIR, "templates/netboot"), | |
129 | ) | |
130 | ||
131 | tornado.web.Application.__init__(self, **settings) | |
132 | ||
133 | self.add_handlers(r"boot.ipfire.org", [ | |
134 | # Configurations | |
5fb24f31 MT |
135 | (r"/menu.gpxe", MenuGPXEHandler), |
136 | (r"/menu.cfg", MenuCfgHandler), | |
5470bdf1 MT |
137 | (r"/config/([0-9]+)/boot.gpxe", BootGPXEHandler), |
138 | ||
139 | # Static files | |
5fb24f31 | 140 | (r"/(boot.png|custom.gpxe|premenu.cfg|vesamenu.c32|menu.c32)", |
5470bdf1 MT |
141 | tornado.web.StaticFileHandler, { "path" : self.settings["static_path"] }), |
142 | ]) | |
143 | ||
144 | @property | |
145 | def ioloop(self): | |
146 | return tornado.ioloop.IOLoop.instance() | |
147 | ||
148 | def shutdown(self, *args): | |
149 | logging.debug("Caught shutdown signal") | |
150 | self.ioloop.stop() | |
151 | ||
152 | def run(self, port=8002): | |
153 | logging.debug("Going to background") | |
154 | ||
155 | # All requests should be done after 30 seconds or they will be killed. | |
156 | self.ioloop.set_blocking_log_threshold(30) | |
157 | ||
158 | http_server = tornado.httpserver.HTTPServer(self, xheaders=True) | |
159 | ||
160 | # If we are not running in debug mode, we can actually run multiple | |
161 | # frontends to get best performance out of our service. | |
162 | if not self.settings["debug"]: | |
163 | http_server.bind(port) | |
164 | http_server.start(num_processes=4) | |
165 | else: | |
166 | http_server.listen(port) | |
167 | ||
168 | self.ioloop.start() | |
169 | ||
170 | if __name__ == "__main__": | |
171 | a = Application() | |
172 | ||
173 | a.run() | |
174 |