]>
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 | ||
8b9b631e | 11 | import backend |
5470bdf1 MT |
12 | |
13 | BASEDIR = os.path.dirname(__file__) | |
14 | ||
a9bf3cbe | 15 | def word_wrap(s, width=45): |
7261a759 MT |
16 | paragraphs = s.split('\n') |
17 | lines = [] | |
18 | for paragraph in paragraphs: | |
19 | while len(paragraph) > width: | |
20 | pos = paragraph.rfind(' ', 0, width) | |
21 | if not pos: | |
22 | pos = width | |
23 | lines.append(paragraph[:pos]) | |
24 | paragraph = paragraph[pos:] | |
a9bf3cbe | 25 | lines.append(paragraph.lstrip()) |
7261a759 | 26 | return '\n'.join(lines) |
5470bdf1 | 27 | |
8e2e1261 | 28 | class BootBaseHandler(tornado.web.RequestHandler): |
5470bdf1 MT |
29 | @property |
30 | def netboot(self): | |
31 | return backend.NetBoot() | |
32 | ||
33 | ||
8e2e1261 | 34 | class MenuGPXEHandler(BootBaseHandler): |
5470bdf1 MT |
35 | """ |
36 | menu.gpxe | |
37 | """ | |
38 | def get(self): | |
e847f85c MT |
39 | # Check if version of the bootloader is recent enough. |
40 | # Otherwise send the latest version of the PXE loader. | |
41 | user_agent = self.request.headers.get("User-Agent", None) | |
42 | if user_agent: | |
43 | try: | |
44 | client, version = user_agent.split("/") | |
45 | except: | |
46 | pass | |
47 | else: | |
48 | # We replaced gPXE by iPXE. | |
49 | if client == "gPXE": | |
50 | return self.serve_update() | |
51 | ||
52 | # Everything under version 1.0.0 should be | |
53 | # updated. | |
54 | if version < "1.0.0": | |
55 | return self.serve_update() | |
5470bdf1 MT |
56 | |
57 | # Devliver content | |
58 | self.set_header("Content-Type", "text/plain") | |
59 | self.write("#!gpxe\n") | |
e847f85c MT |
60 | |
61 | self.write("set 209:string premenu.cfg\n") | |
62 | self.write("set 210:string http://boot.ipfire.org/\n") | |
63 | self.write("chain pxelinux.0\n") | |
64 | ||
65 | def serve_update(self): | |
66 | self.set_header("Content-Type", "text/plain") | |
67 | self.write("#!gpxe\n") | |
68 | ||
69 | # Small warning | |
70 | self.write("echo\necho Your copy of gPXE/iPXE is too old. ") | |
71 | self.write("Upgrade to avoid seeing this every boot!\n") | |
72 | ||
73 | self.write("chain http://mirror0.ipfire.org/releases/ipfire-boot/latest/ipxe.kpxe\n") | |
5470bdf1 MT |
74 | |
75 | ||
8e2e1261 | 76 | class MenuCfgHandler(BootBaseHandler): |
5470bdf1 MT |
77 | def _menu_string(self, menu, level=0): |
78 | s = "" | |
79 | ||
80 | for entry in menu: | |
81 | s += self._menu_entry(entry, level=level) | |
82 | ||
83 | return s | |
84 | ||
85 | def _menu_entry(self, entry, level=0): | |
86 | lines = [] | |
87 | ||
88 | ident = "\t" * level | |
89 | ||
90 | if entry.type == "seperator": | |
2109db85 | 91 | lines.append(ident + "menu separator") |
5470bdf1 MT |
92 | |
93 | elif entry.type == "header": | |
94 | lines.append(ident + "menu begin %d" % entry.id) | |
95 | lines.append(ident + "\tmenu title %s" % entry.title) | |
96 | ||
97 | # Add "Back..." entry | |
98 | lines.append(ident + "\tlabel %d.back" % entry.id) | |
99 | lines.append(ident + "\t\tmenu label Back...") | |
100 | lines.append(ident + "\t\tmenu exit") | |
2109db85 | 101 | lines.append(ident + "\tmenu separator") |
5470bdf1 | 102 | |
2109db85 | 103 | lines.append("%s" % self._menu_string(entry.submenu, level=level+1)) |
5470bdf1 | 104 | lines.append(ident + "menu end") |
5470bdf1 MT |
105 | |
106 | elif entry.type == "config": | |
107 | lines.append(ident + "label %d" % entry.id) | |
108 | lines.append(ident + "\tmenu label %s" % entry.title) | |
109 | if entry.description: | |
110 | lines.append(ident + "\ttext help") | |
7261a759 | 111 | lines.append(word_wrap(entry.description)) |
5470bdf1 | 112 | lines.append(ident + "\tendtext") |
31c90b72 MT |
113 | |
114 | config = self.netboot.get_config(entry.item) | |
115 | if not config: return "" | |
116 | ||
117 | lines.append(ident + "\tkernel %s" % config.image1) | |
118 | if config.image2: | |
119 | lines.append(ident + "\tinitrd %s" % config.image2) | |
120 | if config.args: | |
121 | lines.append(ident + "\tappend %s" % config.args) | |
5470bdf1 | 122 | |
7261a759 | 123 | return "\n".join(lines + [""]) |
5470bdf1 MT |
124 | |
125 | def get(self): | |
126 | self.set_header("Content-Type", "text/plain") | |
127 | ||
128 | menu = self._menu_string(self.netboot.get_menu(1)) | |
129 | ||
085f94df | 130 | self.render("netboot/menu.cfg", menu=menu) |
5470bdf1 MT |
131 | |
132 | ||
8e2e1261 | 133 | class BootGPXEHandler(BootBaseHandler): |
5470bdf1 MT |
134 | def get(self, id): |
135 | config = self.netboot.get_config(id) | |
136 | if not config: | |
137 | raise tornado.web.HTTPError(404, "Configuration with ID '%s' was not found" % id) | |
138 | ||
139 | lines = ["#!gpxe", "imgfree",] | |
140 | ||
141 | line = "kernel -n img %s" % config.image1 | |
142 | if line.endswith(".iso"): | |
143 | line += " iso" | |
8b9b631e MT |
144 | if config.args: |
145 | line += " %s" % config.args | |
5470bdf1 MT |
146 | lines.append(line) |
147 | ||
148 | if config.image2: | |
149 | lines.append("initrd -n img %s" % config.image2) | |
150 | ||
151 | lines.append("boot img") | |
152 | ||
153 | for line in lines: | |
154 | self.write("%s\n" % line) |