change partitioning to use lvm, refactor for fsf server
[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 readonly this_file="$(readlink -f -- "${BASH_SOURCE[0]}")"
22 script_dir="${this_file%/*}"
23 # shellcheck source=./bash-trace
24 source "${script_dir}/bash-trace"
25 cd $script_dir
26 PATH="$PATH:$PWD"
27
28
29 usage() {
30 cat <<EOF
31 Usage: ${0##*/} [OPTIONS] [HOST] [TYPE]
32 Configure dnsmasq boot options and fai-chboot if appropriate. This is
33 not general purpose, it has code specific to dhcp servers I run.
34
35 Without TYPE, disable server and fai server. In that case, HOST is only
36 needed for fsf office network.
37
38 HOST Only do dhcp pxe for HOST. The hostname must be known to the dhcp
39 server to target its mac. Use "default" for all hosts.
40 Required in fsf office environment.
41
42 TYPE One of arch, parabola, plain, fai.
43
44 -a Don't setup pxe, just Wait for 2 dhcp acks, then disable the pxe
45 server after a delay. First ack is for pxe boot, 2nd ack is
46 for os boot. Sometimes on debian, there is a 3rd one shortly
47 after the 2nd. I can't remember exactly why this caused a
48 problem, but I'm hoping the sleep will take care of it.
49 -d Don't alter dhcp config. Only make sense for fai type, and on network
50 other than home or fsf, or when using fai-cd.
51 -k Pass -k to myfai-chboot.
52 -r Don't redeploy fai config. For example, if there is a different host
53 that is mid-install.
54
55 -S sets FAI_ACTION=sysinfo, see myfai-chboot for more info.
56 -w Setup pxe, then wait like -a.
57 -h|--help Print help and exit
58
59
60 Note, when switching between plain and arch or parabola, you will need to
61 do something like:
62 ssh wrt
63 cd /mnt/usb
64 rm tftpboot
65 ln -s <arch/parabola/debian iso dir> tftpboot
66
67
68 Notes on debugging pxe dhcp tftp:
69
70 For debugging dhcp, add to /etc/dnsmasq.conf: log-dhcp
71
72 Newer openwrt runs dnsmasq with a whitelist of readable files and dirs:
73
74 ps ww :
75 /sbin/ujail -t 5 -n dnsmasq -u -l -r /bin/ubus -r /etc/TZ -r /etc/dnsmasq.conf -r /etc/ethers -r /etc/group -r /etc/hosts -r /etc/passwd -w /tmp/dhcp.leases -r /tmp/dnsmasq.d -r /tmp/hosts -r /tmp/resolv.conf.d -r /usr/bin/jshn -r /usr/lib/dnsmasq/dhcp-script.sh -r /usr/share/dnsmasq/dhcpbogushostname.conf -r /usr/share/dnsmasq/rfc6761.conf -r /usr/share/dnsmasq/trust-anchors.conf -r /usr/share/libubox/jshn.sh -r /var/etc/dnsmasq.conf.cfg01411c -w /var/run/dnsmasq/ -- /usr/sbin/dnsmasq -C /var/etc/dnsmasq.conf.cfg01411c -k -x /var/run/dnsmasq/dnsmasq.cfg01411c.pid
76
77 logging tftp requests:
78 /etc/default/tftpd-hpa:
79 add -vv:
80 TFTP_OPTIONS="--secure -vv"
81 jr -u tftpd-hpa -f
82
83 Note: Uses GNU getopt options parsing style
84 EOF
85 exit $1
86 }
87
88 pre="${0##*/}:"
89 m() { printf "$pre %s\n" "$*"; "$@"; }
90 e() { printf "$pre %s\n" "$*"; }
91 err() { echo "[$(date +'%Y-%m-%d %H:%M:%S%z')]: $pre: $*" >&2; }
92
93 PATH="/a/exe:$PATH"
94
95 ##### begin command line parsing ########
96
97 dhcp=true
98 redep=true
99 acks=2
100 wait=false
101 fsf_office=false
102
103 case $HOSTNAME in
104 x3|kw) fsf_office=true ;;
105 esac
106
107 chboot_args=()
108 temp=$(getopt -l help adkrSwh "$@") || usage 1
109 eval set -- "$temp"
110 while true; do
111 case $1 in
112 -a) wait=true; set=false; shift ;;
113 -d) dhcp=false; shift ;;
114 -k) chboot_args+=(-k); shift ;;
115 -r) redep=false; shift ;;
116 -S) chboot_args+=(-S); shift ;;
117 -w) wait=true; set=true; shift ;;
118 -h|--help) usage ;;
119 --) shift; break ;;
120 *) echo "$0: Internal error!" ; exit 1 ;;
121 esac
122 done
123
124 read -r host type <<<"$@"
125
126 case $# in
127 [01]);;
128 2)
129 case $type in
130 arch|parabola) cmd=archlike ;;
131 fai) cmd=fai ;;
132 *)
133 echo "$0: error expected type of arch|parabola|fai"
134 echo
135 usage 1
136 ;;
137 esac
138 ;;
139 *)
140 echo "$0: error: expected 0-2 arguments"
141 echo
142 usage 1
143 ;;
144 esac
145
146
147 if $wait && ! $dhcp; then
148 echo "$0: error -w conflicts with -d, choose one or other" >&2
149 exit 1
150 fi
151
152 if $fsf_office && [[ ! $host ]]; then
153 echo "$0: at fsf_office, provide HOST arg" >&2
154 exit 1
155 fi
156
157 if [[ $host && $host != default ]]; then
158 host_tag="tag:$host,"
159 fi
160
161 ##### end command line parsing ########
162
163 archlike() {
164 cat <<EOF
165 ${host_tag}209,boot/syslinux/${type}iso.cfg
166 ${host_tag}210,/${type}/
167 ${host_tag}option:bootfile-name,/${type}/boot/syslinux/lpxelinux.0
168 EOF
169 }
170
171 plain() {
172 # if arch based was used before, this additionally needs
173 # the tftp link in /mnt/usb to be changed.
174 cat <<EOF
175 ${host_tag}option:bootfile-name,pxelinux.0
176 EOF
177 }
178
179 fai() {
180 cat <<EOF
181 ${host_tag}option:bootfile-name,pxelinux.0
182 ${host_tag}option:server-ip-address,$faiserverip
183 ${host_tag}option:tftp-server,$faiserverip
184 EOF
185 # Note, previously used normal dnsmasq option, but it requires dnsmasq
186 # restart, which causes momentary dns failures, which can bork an
187 # install.
188 #
189 # dhcp-boot=${host_tag}pxelinux.0,faiserver.b8.nz,faiserver.b8.nz
190 }
191
192 ack-wait() {
193 if $fsf_office; then
194 wait_cmd="ssh tarantula.office.fsf.org tail -n0 -f /var/log/syslog"
195 else
196 wait_cmd="ssh cmc logread -f"
197 fi
198 wait_count=$1
199 if [[ $host ]]; then
200 if $fsf_office; then
201 host_regex=" $(getent hosts kw | awk '{print $1}' | sed 's/\./\\./g')"
202 else
203 host_regex=" $host"
204 fi
205 fi
206 regex=".*DHCPACK.*$host_regex\b"
207 i=0
208 while (( i != wait_count )) && read -r line; do
209 if [[ $line =~ $regex ]]; then
210 i=$((i+1))
211 echo $line
212 fi
213 done < <($wait_cmd ||:) # tail returns 2 it seems
214 m sleep 20
215 }
216
217 set-pxe() {
218 $dhcp || return 0
219 if $fsf_office; then
220 if [[ ! $cmd ]]; then
221 e "removing pxe for $host on tarantula"
222 ssh tarantula.office.fsf.org bash -e <<EOF
223 sed -ri 's/^( *host +$host *\{).*/\1/' /etc/dhcp/dhcpd.conf
224 systemctl restart isc-dhcp-server
225 EOF
226 elif [[ $cmd == fai ]]; then
227 e "adding pxe for $host on tarantula"
228 ssh tarantula.office.fsf.org bash -e <<EOF
229 sed -ri 's/^( *host +$host *\{).*/\1 next-server faiserver.office.fsf.org; filename "pxelinux.0";/' /etc/dhcp/dhcpd.conf
230 systemctl restart isc-dhcp-server
231 EOF
232 fi
233 else
234 e "updating dnsmasq.conf:"
235 m $cmd
236 ${cmd:-:}|ssh cmc "dd of=/var/run/dnsmasq/dhcpopts.conf; /etc/init.d/dnsmasq reload
237 $([[ $type == arch || $type == parabola ]] && echo archlike-pxe-mount)"
238 fi
239 }
240
241 type -t host &>/dev/null || sudo apt-get -y install dnsutils
242 faiserverip=$(host faiserver | sed -rn 's/^\S+ has address //p;T;q' ||:)
243 if [[ ! $faiserverip || $faiserverip =~ [[:space:]] ]]; then
244 echo "$0: error: failed to get \$faiserverip, got: $faiserverip"
245 exit 1
246 fi
247
248
249 if $set; then
250 set-pxe
251 if [[ $type == fai ]]; then
252 if $redep; then
253 m fai-redep
254 fi
255 m myfai-chboot ${chboot_args[@]} $host
256 else
257 # This will fail if faiserver is not setup, so ignore any
258 # failure and don't bother us about it.
259 m myfai-chboot &>/dev/null ||:
260 fi
261 fi
262
263 if $wait; then
264 # fai's debian jessie 8.5ish does 2 dhcp requests when booting,
265 # roughly 4 seconds apart. Earlier
266 # versions did just 1. Now testing on a vm, it does 1.
267 # bleh.
268 echo "waiting for $acks dhcp acks then disabling pxe"
269 ack-wait $acks
270 type=
271 unset cmd
272 set-pxe
273
274 # previously tried waiting for one more ack then disabling faiserver,
275 # since it can contain sensitive info, so turn it off when not in use,
276 # but disabling that for now as it's inconvenient to clean this
277 # up and run it in the background etc.
278
279 # if [[ $type == fai ]]; then
280 # echo "waiting for 1 dhcp ack then disabling fai server"
281 # ack-wait 1
282 # faiserver-disable
283 # fi
284 fi