Merge "Corrected directory name"
[genesis.git] / fuel / prototypes / auto-deploy / deploy / dha-adapters / ipmi.sh
1 #!/bin/bash
2 ##############################################################################
3 # Copyright (c) 2015 Ericsson AB and others.
4 # stefan.k.berg@ericsson.com
5 # jonas.bjurel@ericsson.com
6 # All rights reserved. This program and the accompanying materials
7 # are made available under the terms of the Apache License, Version 2.0
8 # which accompanies this distribution, and is available at
9 # http://www.apache.org/licenses/LICENSE-2.0
10 ##############################################################################
11
12
13
14 ########################################################################
15 # Internal functions BEGIN
16
17
18 dha_f_err()
19 {
20     local rc
21     local cmd
22
23     rc=$1
24     shift
25
26     echo "$@" >&2
27     echo "Exit with code $rc" >&2
28
29     exit $rc
30 }
31
32 dha_f_run()
33 {
34   $@
35   rc=$?
36   if [ $rc -ne 0 ]; then
37      dha_f_err $rc "running $@" >&2
38      exit $rc
39   fi
40 }
41
42
43 dha_f_ipmi()
44 {
45
46     local nodeId
47     local ipmiIp
48     local ipmiUser
49     local ipmiPass
50
51     nodeId=$1
52     shift
53
54     ipmiIp=$($DHAPARSE $DHAFILE getNodeProperty $nodeId ipmiIp)
55     ipmiUser=$($DHAPARSE $DHAFILE getNodeProperty $nodeId ipmiUser)
56     ipmiPass=$($DHAPARSE $DHAFILE getNodeProperty $nodeId ipmiPass)
57
58     test -n "$ipmiIp" || error_exit "Could not get IPMI IP"
59     test -n "$ipmiUser" || error_exit "Could not get IPMI username"
60     test -n "$ipmiPass" || error_exit "Could not get IPMI password"
61
62     ipmitool -I lanplus -A password -H $ipmiIp -U $ipmiUser -P $ipmiPass \
63         $@
64 }
65
66 # Internal functions END
67 ########################################################################
68
69
70 true=0
71 false=1
72
73 # API: Get the DHA API version supported by this adapter
74 dha_getApiVersion ()
75 {
76     echo "1.0"
77 }
78
79 # API: Get the name of this adapter
80 dha_getAdapterName ()
81 {
82     echo "ipmi"
83 }
84
85 # API: ### Node identity functions ###
86 # API: Node numbering is sequential.
87
88 # API: Get a list of all defined node ids, sorted in ascending order
89 dha_getAllNodeIds()
90 {
91     dha_f_run $DHAPARSE $DHAFILE getNodes | sort -n
92 }
93
94
95 # API: Get ID for Fuel node ID
96 dha_getFuelNodeId()
97 {
98     for node in `dha_getAllNodeIds`
99     do
100         if [ -n "`dha_f_run $DHAPARSE $DHAFILE getNodeProperty $node isFuel`" ]
101         then
102             echo $node
103         fi
104     done
105 }
106
107 # API: Get node property
108 # API: Argument 1: node id
109 # API: Argument 2: Property
110 dha_getNodeProperty()
111 {
112     dha_f_run $DHAPARSE $DHAFILE getNodeProperty $1 $2
113 }
114
115
116 # API: Get MAC address for the PXE interface of this node. If not
117 # API: defined, an empty string will be returned.
118 # API: Argument 1: Node id
119 dha_getNodePxeMac()
120 {
121     dha_getNodeProperty $1 pxeMac
122 }
123
124
125 ### Node operation functions ###
126
127 # API: Use custom installation method for Fuel master?
128 # API: Returns 0 if true, 1 if false
129 dha_useFuelCustomInstall()
130 {
131     $DHAPARSE $DHAFILE get fuelCustomInstall | grep -qi true
132     rc=$?
133     return $rc
134 }
135
136 # API: Fuel custom installation method
137 # API: Leaving the Fuel master powered on and booting from ISO at exit
138 # API: Argument 1: Full path to ISO file to install
139 dha_fuelCustomInstall()
140 {
141     if [ ! -e $1 ]; then
142         error_exit "Could not access ISO file $1"
143     fi
144
145     dha_useFuelCustomInstall || dha_f_err 1 "dha_fuelCustomInstall not supported"
146
147     fuelIp=`dea getFuelIp` || error_exit "Could not get fuel IP"
148     fuelNodeId=`dha getFuelNodeId` || error_exit "Could not get fuel node id"
149     virtName=`$DHAPARSE $DHAFILE getNodeProperty $fuelNodeId libvirtName`
150
151     # Power off the node
152     virsh destroy $virtName
153     sleep 5
154
155     # Zero the MBR
156     fueldisk=`virsh dumpxml $virtName | \
157      grep "<source file" | grep raw | sed "s/.*'\(.*\)'.*/\1/"`
158     disksize=`ls -l $fueldisk | awk '{ print $5 }'`
159     rm -f $fueldisk
160     fallocate -l $disksize $fueldisk
161
162     # Set the boot order
163     for order in disk iso
164     do
165         if [ "$order" == "pxe" ]; then
166             bootline+="<boot dev='network'\/>\n"
167         elif [ "$order" == "disk" ]; then
168             bootline+="<boot dev='hd'/\>\n"
169         elif [ "$order" == "iso" ]; then
170             bootline+="<boot dev='cdrom'/\>\n"
171         else
172             error_exit "Unknown boot type: $order"
173         fi
174     done
175
176     virsh dumpxml $virtName | grep -v "<boot dev.*>" | \
177         sed "/<\/os>/i\
178     ${bootline}" > $tmpdir/vm.xml || error_exit "Could not set bootorder"
179     virsh define $tmpdir/vm.xml || error_exit "Could not set bootorder"
180
181
182     # Get name of CD device
183     cdDev=`virsh domblklist $virtName | tail -n +3 | awk '{ print $1 }' | grep ^hd`
184
185     # Eject and insert ISO
186     virsh change-media $virtName --config --eject $cdDev
187     sleep 5
188     virsh change-media $virtName --config --insert $cdDev $1 || error_exit "Could not insert CD $1"
189     sleep 5
190
191     virsh start $virtName || error_exit "Could not start $virtName"
192     sleep 5
193
194     # wait for node up
195     echo "Waiting for Fuel master to accept SSH"
196     while true
197     do
198         ssh root@${fuelIp} date 2>/dev/null
199         if [ $? -eq 0 ]; then
200             break
201         fi
202         sleep 10
203     done
204
205     # Wait until fuelmenu is up
206     echo "Waiting for fuelmenu to come up"
207     menuPid=""
208     while [ -z "$menuPid" ]
209     do
210         menuPid=`ssh root@${fuelIp} "ps -ef" 2>&1 | grep fuelmenu | grep -v grep | awk '{ print $2 }'`
211         sleep 10
212     done
213
214     # This is where we inject our own astute.yaml settings
215     scp -q $deafile root@${fuelIp}:. || error_exit "Could not copy DEA file to Fuel"
216     echo "Uploading build tools to Fuel server"
217     ssh root@${fuelIp} rm -rf tools || error_exit "Error cleaning old tools structure"
218     scp -qrp $topdir/tools root@${fuelIp}:. || error_exit "Error copying tools"
219     echo "Running transplant #0"
220     ssh root@${fuelIp} "cd tools; ./transplant0.sh ../`basename $deafile`" \
221         || error_exit "Error running transplant sequence #0"
222
223
224
225     # Let the Fuel deployment continue
226     echo "Found menu as PID $menuPid, now killing it"
227     ssh root@${fuelIp} "kill $menuPid" 2>/dev/null
228
229     # Wait until installation complete
230     echo "Waiting for bootstrap of Fuel node to complete"
231     while true
232     do
233         ssh root@${fuelIp} "ps -ef" 2>/dev/null \
234             | grep -q /usr/local/sbin/bootstrap_admin_node
235         if [ $? -ne 0 ]; then
236             break
237         fi
238         sleep 10
239     done
240
241     echo "Waiting for one minute for Fuel to stabilize"
242     sleep 1m
243
244 }
245
246 # API: Get power on strategy from DHA
247 # API: Returns one of two values:
248 # API:   all:        Power on all nodes simultaneously
249 # API:   sequence:   Power on node by node, wait for Fuel detection
250 dha_getPowerOnStrategy()
251 {
252     local strategy
253
254     strategy=`$DHAPARSE $DHAFILE get powerOnStrategy`
255
256     if [ "$strategy" == "all" ]; then
257         echo $strategy
258     elif
259         [ "$strategy" == "sequence" ]; then
260         echo $strategy
261     else
262         dha_f_err 1 "Could not parse strategy from DHA, got $strategy"
263     fi
264 }
265
266 # API: Power on node
267 # API: Argument 1: node id
268 dha_nodePowerOn()
269 {
270     local nodeId
271
272     nodeId=$1
273     state=$(dha_f_ipmi $1 chassis power status) || error_exit "Could not get IPMI power status"
274     echo "state $state"
275
276
277     if [ "$(echo $state | sed 's/.* //')" == "off" ]; then
278         dha_f_ipmi $1 chassis power on
279     fi
280 }
281
282 # API: Power off node
283 # API: Argument 1: node id
284 dha_nodePowerOff()
285 {
286     local nodeId
287
288     nodeId=$1
289     state=$(dha_f_ipmi $1 chassis power status) || error_exit "Could not get IPMI power status"
290     echo "state $state"
291
292
293     if [ "$(echo $state | sed 's/.* //')" != "off" ]; then
294         dha_f_ipmi $1 chassis power off
295     fi
296 }
297
298 # API: Reset node
299 # API: Argument 1: node id
300 dha_nodeReset()
301 {
302     local nodeId
303
304     nodeId=$1
305     state=$(dha_f_ipmi $1 chassis power reset) || error_exit "Could not get IPMI power status"
306     echo "state $state"
307
308
309     if [ "$(echo $state | sed 's/.* //')" != "off" ]; then
310         dha_f_ipmi $1 chassis power reset
311     fi
312 }
313
314 # Boot order and ISO boot file
315
316 # API: Is the node able to commit boot order without power toggle?
317 # API: Argument 1: node id
318 # API: Returns 0 if true, 1 if false
319 dha_nodeCanSetBootOrderLive()
320 {
321   return $true
322 }
323
324 # API: Set node boot order
325 # API: Argument 1: node id
326 # API: Argument 2: Space separated line of boot order - boot ids are "pxe", "disk" and "iso"
327 # Strategy for IPMI: Always set boot order to persistent except in the case of CDROM.
328 dha_nodeSetBootOrder()
329 {
330     local id
331     local order
332
333     id=$1
334     shift
335     order=$1
336
337     if [ "$order" == "pxe" ]; then
338         dha_f_ipmi $id chassis bootdev pxe options=persistent || error_exit "Could not get IPMI power status"
339     elif [ "$order" == "iso" ]; then
340         dha_f_ipmi $id chassis bootdev cdrom || error_exit "Could not get IPMI power status"
341     elif [ "$order" == "disk" ]; then
342         dha_f_ipmi $id chassis bootdev disk options=persistent  || error_exit "Could not get IPMI power status"
343     else
344         error_exit "Unknown boot type: $order"
345     fi
346 }
347
348 # API: Is the node able to operate on ISO media?
349 # API: Argument 1: node id
350 # API: Returns 0 if true, 1 if false
351 dha_nodeCanSetIso()
352 {
353   return $false
354 }
355
356 # API: Is the node able to insert add eject ISO files without power toggle?
357 # API: Argument 1: node id
358 # API: Returns 0 if true, 1 if false
359 dha_nodeCanHandeIsoLive()
360 {
361   return $false
362 }
363
364 # API: Insert ISO into virtualDVD
365 # API: Argument 1: node id
366 # API: Argument 2: iso file
367 dha_nodeInsertIso()
368 {
369     error_exit "Node can not handle InsertIso"
370 }
371
372 # API: Eject ISO from virtual DVD
373 # API: Argument 1: node id
374 dha_nodeEjectIso()
375 {
376     error_exit "Node can not handle InsertIso"
377 }
378
379 # API: Wait until a suitable time to change the boot order to
380 # API: "disk iso" when ISO has been booted. Can't be too long, nor
381 # API: too short...
382 # API: We should make a smart trigger for this somehow...
383 dha_waitForIsoBoot()
384 {
385     echo "waitForIsoBoot: Not used by ipmi"
386 }
387
388 # API: Is the node able to reset its MBR?
389 # API: Returns 0 if true, 1 if false
390 dha_nodeCanZeroMBR()
391 {
392     return $false
393 }
394
395 # API: Reset the node's MBR
396 dha_nodeZeroMBR()
397 {
398     error_exit "Node $1 does not support ZeroMBR"
399 }
400
401
402 # API: Entry point for dha functions
403 # API: Typically do not call "dha_node_zeroMBR" but "dha node_ZeroMBR"
404 # API:
405 # API: Before calling dha, the adapter file must gave been sourced with
406 # API: the DHA file name as argument
407 dha()
408 {
409     if [ -z "$DHAFILE" ]; then
410         error_exit "dha_setup has not been run"
411     fi
412
413
414     if type dha_$1 &>/dev/null; then
415         cmd=$1
416         shift
417         dha_$cmd $@
418         return $?
419     else
420         error_exit "No such function dha_$1 defined"
421     fi
422 }
423
424 if [ "$1" == "api" ]; then
425   egrep "^# API: |dha.*\(\)" $0 | sed 's/^# API: /# /' | grep -v dha_f_ | sed 's/)$/)\n/'
426 else
427     dhatopdir=$(dirname $(readlink -f $BASH_SOURCE))
428     DHAPARSE="$dhatopdir/dhaParse.py"
429     DHAFILE=$1
430
431     if [ ! -f $DHAFILE ]; then
432         error_exit "No such DHA file: $DHAFILE"
433     else
434         echo "Adapter init"
435         echo "$@"
436         echo "DHAPARSE: $DHAPARSE"
437         echo "DHAFILE: $DHAFILE"
438     fi
439
440 fi