7405f58f3b6a2c3185f8661509a48f95da29d3f1
[automated-distro-installer] / pxe-server
1 #!/bin/bash
2 # Copyright (C) 2016 Ian Kelling
3
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
8
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
18
19 [[ $EUID == 0 ]] || exec sudo -E "${BASH_SOURCE[0]}" "$@"
20
21 x="$(readlink -f "$BASH_SOURCE")"; source "${x%/*}/bash-trace"
22
23
24 usage() {
25 cat <<EOF
26 Usage: ${0##*/} [OPTIONS] [HOST] [TYPE]
27 Configure dnsmasq boot options and fai-chboot if appropriate. This is
28 not general purpose, it has code specific to dhcp servers I run.
29
30 Without TYPE, disable server and fai server. In that case, HOST is only
31 needed for fsf office network.
32
33 HOST A hostname known to the dhcp server, or default for all.
34 TYPE One of arch, parabola, plain, fai.
35
36 -a Don't setup pxe, just Wait for 2 dhcp acks, then disable the pxe
37 server after a delay. First ack is for pxe boot, 2nd ack is
38 for os boot. Sometimes on debian, there is a 3rd one shortly
39 after the 2nd. I can't remember exactly why this caused a
40 problem, but I'm hoping the sleep will take care of it.
41 -d Don't alter dhcp config. Only make sense for fai type, and on network
42 other than home or fsf.
43 -k Pass -k to myfai-chboot.
44 -r Don't redeploy fai config. For example, if there is a different host
45 that is mid-install.
46
47 -S sets FAI_ACTION=sysinfo, see myfai-chboot for more info.
48 -w Setup pxe, then wait like -a.
49 -h|--help Print help and exit
50
51
52 Note, when switching between plain and arch or parabola, you will need to
53 do something like:
54 ssh wrt
55 cd /mnt/usb
56 rm tftpboot
57 ln -s <arch/parabola/debian iso dir> tftpboot
58
59
60 Note: Uses GNU getopt options parsing style
61 EOF
62 exit $1
63 }
64
65 pre="${0##*/}:"
66 m() { printf "$pre %s\n" "$*"; "$@"; }
67 e() { printf "$pre %s\n" "$*"; }
68 err() { echo "[$(date +'%Y-%m-%d %H:%M:%S%z')]: $pre: $*" >&2; }
69
70 PATH="/a/exe:$PATH"
71
72 ##### begin command line parsing ########
73
74 dhcp=true
75 redep=true
76 acks=2
77 wait=false
78 fsf=false
79
80 case $HOSTNAME in
81 x3|kw) fsf=true ;;
82 esac
83
84 chboot_args=()
85 temp=$(getopt -l help adkrSwh "$@") || usage 1
86 eval set -- "$temp"
87 while true; do
88 case $1 in
89 -a) wait=true; set=false; shift ;;
90 -d) dhcp=false; shift ;;
91 -k) chboot_args+=(-k); shift ;;
92 -r) redep=false; shift ;;
93 -S) chboot_args+=(-S); shift ;;
94 -w) wait=true; set=true; shift ;;
95 -h|--help) usage ;;
96 --) shift; break ;;
97 *) echo "$0: Internal error!" ; exit 1 ;;
98 esac
99 done
100
101 read -r host type <<<"$@"
102
103 case $# in
104 [01]);;
105 2)
106 case $type in
107 arch|parabola) cmd=archlike ;;
108 fai) cmd=fai ;;
109 *)
110 echo "$0: error expected type of arch|parabola|fai"
111 echo
112 usage 1
113 ;;
114 esac
115 ;;
116 *)
117 echo "$0: error: expected 0-2 arguments"
118 echo
119 usage 1
120 ;;
121 esac
122
123
124 if $wait && ! $dhcp; then
125 echo "$0: error -w conflicts with -d, choose one or other"
126 exit 1
127 fi
128
129
130 if [[ $host && $host != default ]]; then
131 host_tag="tag:$host,"
132 fi
133
134 ##### end command line parsing ########
135
136 archlike() {
137 cat <<EOF
138 ${host_tag}209,boot/syslinux/${type}iso.cfg
139 ${host_tag}210,/${type}/
140 ${host_tag}option:bootfile-name,/${type}/boot/syslinux/lpxelinux.0
141 EOF
142 }
143
144 plain() {
145 # if arch based was used before, this additionally needs
146 # the tftp link in /mnt/usb to be changed.
147 cat <<EOF
148 ${host_tag}option:bootfile-name,pxelinux.0
149 EOF
150 }
151
152 fai() {
153 cat <<EOF
154 ${host_tag}option:bootfile-name,pxelinux.0
155 ${host_tag}option:server-ip-address,$faiserverip
156 ${host_tag}option:tftp-server,$faiserverip
157 EOF
158 # Note, previously used normal dnsmasq option, but it requires dnsmasq
159 # restart, which causes momentary dns failures, which can bork an
160 # install.
161 #
162 # dhcp-boot=${host_tag}pxelinux.0,faiserver.b8.nz,faiserver.b8.nz
163 }
164
165 ack-wait() {
166 if $fsf; then
167 wait_cmd="ssh tarantula.office.fsf.org tail -n0 -f /var/log/syslog"
168 else
169 wait_cmd="ssh cmc logread -f"
170 fi
171 wait_count=$1
172 if [[ $host ]]; then
173 if $fsf; then
174 host_regex=" $(getent hosts kw | awk '{print $1}' | sed 's/\./\\./g')"
175 else
176 host_regex=" $host"
177 fi
178 fi
179 regex=".*DHCPACK.*$host_regex\b"
180 i=0
181 while (( i != wait_count )) && read -r line; do
182 if [[ $line =~ $regex ]]; then
183 i=$((i+1))
184 echo $line
185 fi
186 done < <($wait_cmd ||:) # tail returns 2 it seems
187 m sleep 20
188 }
189
190 set-pxe() {
191 $dhcp || return 0
192 if $fsf; then
193 if [[ ! $cmd ]]; then
194 e "removing pxe for $host on tarantula"
195 ssh tarantula.office.fsf.org bash -e <<EOF
196 sed -ri 's/^( *host +$host *\{).*/\1/' /etc/dhcp/dhcpd.conf
197 systemctl restart isc-dhcp-server
198 EOF
199 elif [[ $cmd == fai ]]; then
200 e "adding pxe for $host on tarantula"
201 ssh tarantula.office.fsf.org bash -e <<EOF
202 sed -ri 's/^( *host +$host *\{).*/\1 next-server faiserver.office.fsf.org; filename "pxelinux.0";/' /etc/dhcp/dhcpd.conf
203 systemctl restart isc-dhcp-server
204 EOF
205 fi
206 else
207 e "updating dnsmasq.conf:"
208 m $cmd
209 ${cmd:-:}|ssh cmc "dd of=/etc/dnsmasq-dhcpopts.conf; /etc/init.d/dnsmasq reload
210 $([[ $type == arch || $type == parabola ]] && echo archlike-pxe-mount)"
211 fi
212 }
213
214 type -t host &>/dev/null || sudo apt-get -y install dnsutils
215 faiserverip=$(host faiserver | sed -rn 's/^\S+ has address //p;T;q' ||:)
216 if [[ ! $faiserverip || $faiserverip =~ [[:space:]] ]]; then
217 echo "$0: error: failed to get \$faiserverip, got: $faiserverip"
218 exit 1
219 fi
220
221
222 if $set; then
223 set-pxe
224 if [[ $type == fai ]]; then
225 if $redep; then
226 m fai-redep
227 fi
228 m myfai-chboot ${chboot_args[@]} $host
229 else
230 # This will fail if faiserver is not setup, so ignore any
231 # failure and don't bother us about it.
232 myfai-chboot &>/dev/null ||:
233 fi
234 fi
235
236 if $wait; then
237 # fai's debian jessie 8.5ish does 2 dhcp requests when booting,
238 # roughly 4 seconds apart. Earlier
239 # versions did just 1. Now testing on a vm, it does 1.
240 # bleh.
241 echo "waiting for $acks dhcp acks then disabling pxe"
242 ack-wait $acks
243 type=
244 unset cmd
245 set-pxe
246
247 # previously tried waiting for one more ack then disabling faiserver,
248 # since it can contain sensitive info, so turn it off when not in use,
249 # but disabling that for now as it's inconvenient to clean this
250 # up and run it in the background etc.
251
252 # if [[ $type == fai ]]; then
253 # echo "waiting for 1 dhcp ack then disabling fai server"
254 # ack-wait 1
255 # faiserver-disable
256 # fi
257 fi