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