netboot: Allow booting multiple architectures
[people/shoehn/ipfire.org.git] / webapp / handlers_boot.py
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 import backend
12
13 from handlers_base import BaseHandler
14
15 BASEDIR = os.path.dirname(__file__)
16
17 def word_wrap(s, width=45):
18         paragraphs = s.split('\n')
19         lines = []
20         for paragraph in paragraphs:
21                 while len(paragraph) > width:
22                         pos = paragraph.rfind(' ', 0, width)
23                         if not pos:
24                                 pos = width
25                         lines.append(paragraph[:pos])
26                         paragraph = paragraph[pos:]
27                 lines.append(paragraph.lstrip())
28         return '\n'.join(lines)
29
30 class BootBaseHandler(BaseHandler):
31         @property
32         def arch(self):
33                 arch = self.get_argument("arch", "i386")
34                 if not arch in ("x86_64", "i586", "i386"):
35                         raise tornado.web.HTTPError(400, "Invalid architecture")
36
37                 if arch == "i386":
38                         arch = "i586"
39
40                 return arch
41
42         @property
43         def platform(self):
44                 platform = self.get_argument("platform", "pcbios")
45                 if not platform in ("pcbios", "efi"):
46                         raise tornado.web.HTTPError(400, "Invalid platform")
47
48                 return platform
49
50
51 class MenuGPXEHandler(BootBaseHandler):
52         """
53                 menu.gpxe
54         """
55         def get(self):
56                 # Check if version of the bootloader is recent enough.
57                 # Otherwise send the latest version of the PXE loader.
58                 user_agent = self.request.headers.get("User-Agent", None)
59                 if user_agent:
60                         try:
61                                 client, version = user_agent.split("/")
62                         except:
63                                 pass
64                         else:
65                                 # We replaced gPXE by iPXE.
66                                 if client == "gPXE":
67                                         return self.serve_update()
68
69                                 # Everything under version 1.0.0 should be
70                                 # updated.
71                                 if version < "1.0.0":
72                                         return self.serve_update()
73
74                 # Devliver content
75                 self.set_header("Content-Type", "text/plain")
76                 self.write("#!gpxe\n")
77
78                 self.write("set 209:string premenu.cfg?arch=%s&platform=%s\n" \
79                         % (self.arch, self.platform))
80                 self.write("set 210:string http://boot.dev.ipfire.org/\n")
81                 self.write("chain pxelinux.0\n")
82
83         def serve_update(self):
84                 self.set_header("Content-Type", "text/plain")
85                 self.write("#!gpxe\n")
86
87                 # Small warning
88                 self.write("echo\necho Your copy of gPXE/iPXE is too old. ")
89                 self.write("Upgrade to avoid seeing this every boot!\n")
90
91                 self.write("chain http://mirror0.ipfire.org/releases/ipfire-boot/latest/ipxe.kpxe\n")
92
93 class PremenuCfgHandler(BootBaseHandler):
94         def get(self):
95                 self.set_header("Content-Type", "text/plain")
96
97                 self.render("netboot/premenu.cfg", arch=self.arch, platform=self.platform)
98
99
100 class MenuCfgHandler(BootBaseHandler):
101         class NightlyBuildReleaseDummy(object):
102                 id = 100
103                 name = "Nightly Build"
104                 sname = "nightly-build"
105
106                 def supports_arch(self, arch):
107                         return arch in ("i586", "x86_64")
108
109                 def supports_platform(self, platform):
110                         return platform == "pcbios"
111
112                 def netboot_kernel_url(self, arch, platform):
113                         return "http://nightly.ipfire.org/next/latest/%s/images/vmlinuz" % arch
114
115                 def netboot_initrd_url(self, arch, platform):
116                         return "http://nightly.ipfire.org/next/latest/%s/images/instroot" % arch
117
118                 def netboot_args(self, arch, platform):
119                         return "installer.download-url=http://nightly.ipfire.org/next/latest/%s/images/installer.iso" % arch
120
121                 def is_netboot_capable(self):
122                         return True
123
124         nightly_build = NightlyBuildReleaseDummy()
125
126         def get(self):
127                 self.set_header("Content-Type", "text/plain")
128
129                 latest_release = self.releases.get_latest()
130                 if latest_release and not latest_release.supports_arch(self.arch):
131                         latest_release = None
132
133                 stable_releases = [r for r in self.releases.get_stable()
134                         if r.supports_arch(self.arch) and r.supports_platform(self.platform)]
135                 try:
136                         stable_releases.remove(latest_release)
137                 except ValueError:
138                         pass
139
140                 development_releases = [r for r in self.releases.get_unstable()
141                         if r.supports_arch(self.arch) and r.supports_platform(self.platform)]
142                 development_releases.insert(0, self.nightly_build)
143
144                 self.render("netboot/menu.cfg", latest_release=latest_release,
145                         stable_releases=stable_releases, development_releases=development_releases,
146                         arch=self.arch, platform=self.platform)