Fixed some typos
[osm/openvim.git] / scripts / host-add.sh
1 #!/bin/bash
2
3 ##
4 # Copyright 2015 Telef�nica Investigaci�n y Desarrollo, S.A.U.
5 # This file is part of openvim
6 # All Rights Reserved.
7 #
8 # Licensed under the Apache License, Version 2.0 (the "License"); you may
9 # not use this file except in compliance with the License. You may obtain
10 # a copy of the License at
11 #
12 # http://www.apache.org/licenses/LICENSE-2.0
13 #
14 # Unless required by applicable law or agreed to in writing, software
15 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17 # License for the specific language governing permissions and limitations
18 # under the License.
19 #
20 # For those usages not covered by the Apache License, Version 2.0 please
21 # contact with: nfvlabs@tid.es
22 ##
23
24 #Get configuration of a host for using it as a compute node
25
26 function usage(){
27 echo -e "usage: $0 user ip_name [>> host.yaml]\n Get host parameters and generated a yaml file to be used for openvim host-add"
28 exit 1
29 }
30
31 function load_vf_driver(){
32 local pf_driver=$1
33 if [[ `lsmod | cut -d" " -f1 | grep $pf_driver | grep -v vf` ]] && [[ ! `lsmod | cut -d" " -f1 | grep ${pf_driver}vf` ]]
34 then
35 >&2 echo "$pf_driver is loaded but not ${pf_driver}vf. This is required in order to properly add SR-IOV."
36 read -p "Do you want to load ${pf_driver}vf [Y/n] " load_driver
37 case $load_driver in
38 [nN]* ) exit 1;;
39 * ) >&2 echo "Loading ${pf_driver}vf..."
40 modprobe ${pf_driver}vf;
41 >&2 echo "Reloading ${pf_driver}..."
42 modprobe -r $pf_driver;
43 modprobe $pf_driver;;
44 esac
45 fi
46 }
47
48 function remove_vf_driver(){
49 local pf_driver=$1
50 if [[ `lsmod | cut -d" " -f1 | grep $pf_driver | grep -v vf` ]] && [[ `lsmod | cut -d" " -f1 | grep ${pf_driver}vf` ]]
51 then
52 >&2 echo "${pf_driver}vf is loaded. In order to ensure proper SR-IOV behavior the driver must be removed."
53 read -p "Do you want to remove ${pf_driver}vf now? [Y/n] " remove_driver
54 case $remove_driver in
55 [nN]* ) >&2 echo "OK. Remember to remove the driver prior start using the compute node executing:";
56 >&2 echo "modprobe -r ${pf_driver}vf";
57 >&2 echo "modprobe -r ${pf_driver}";
58 >&2 echo "modprobe ${pf_driver}";;
59 * ) >&2 echo "Removing ${pf_driver}vf..."
60 modprobe -r ${pf_driver}vf;
61 >&2 echo "Reloading ${pf_driver}..."
62 modprobe -r $pf_driver;
63 modprobe $pf_driver;;
64 esac
65 fi
66 }
67
68 function get_hash_value() { echo `eval echo $\{\`echo $1[$2]\`\}`; }
69
70 function xmlpath_args()
71 {
72 local expr="${1//\// }"
73 local path=()
74 local chunk tag data
75 local exit_code=1
76 local print_line=0
77 local closing_tag=0
78
79 while IFS='' read -r -d '<' chunk; do
80 data=arguments=""
81 IFS='>' read -r tag_arg data <<< "$chunk"
82 IFS=' ' read -r tag arguments <<< "$tag_arg"
83 #If last tag was single level remove it from path
84 if [[ $closing_tag -eq 1 ]]
85 then
86 unset path[${#path[@]}-1]
87 closing_tag=0
88 fi
89 #In case the tag is closed in the same line mark it
90 [[ $arguments = ?*'/' ]] && closing_tag=1
91 arguments="${arguments//\//}"
92 case "$tag" in
93 '?'*) ;;
94 '!--'*) ;;
95 ?*'/') ;;
96 '/'?*) unset path[${#path[@]}-1] ;;
97 ?*) path+=("$tag");;
98 esac
99
100 #echo "\"${path[@]}\" \"$expr\" \"$data\" \"$arguments\" $exit_code $print_line"
101
102 if [[ "${path[@]}" == "$expr" ]]
103 then
104 #If there is data print it and append arguments if any
105 if [ "$data" != "" ]
106 then
107 echo "$data $arguments"
108 #return code 0 means data was found
109 exit_code=0
110 continue
111 #if there is no data but there are arguments print arguments
112 elif [ "$arguments" != "" ]
113 then
114 echo "$arguments"
115 #return code 2 means no data but arguments were found
116 exit_code=2
117 continue
118 #otherwise switch flag to start/stop echoing each line until the tag is closed
119 elif [[ $exit_code -eq 1 ]]
120 then
121 print_line=$(((print_line+1)%2))
122 #return code 3 means that the whole xml segment is returned
123 exit_code=3
124 fi
125 fi
126 [[ $print_line == "1" ]] && echo "<"$chunk
127 done
128 return $exit_code
129 }
130
131
132 #check root privileges and non a root user behind
133
134 [[ "$#" -lt "2" ]] && echo "Missing parameters" && usage
135 load_vf_driver ixgbe
136 load_vf_driver i40e
137 /etc/rc.local
138 HOST_NAME=`cat /etc/hostname`
139 FEATURES=`grep "^flags" /proc/cpuinfo`
140 FEATURES_LIST=""
141 if echo $FEATURES | grep -q pdpe1gb ; then FEATURES_LIST="${FEATURES_LIST},lps"; fi
142 if echo $FEATURES | grep -q dca ; then FEATURES_LIST="${FEATURES_LIST},dioc"; fi
143 if echo $FEATURES | egrep -q "(vmx|svm)" ; then FEATURES_LIST="${FEATURES_LIST},hwsv"; fi
144 if echo $FEATURES | egrep -q "(ept|npt)" ; then FEATURES_LIST="${FEATURES_LIST},tlbps"; fi
145 if echo $FEATURES | grep -q ht ; then FEATURES_LIST="${FEATURES_LIST},ht"; fi
146 if uname -m | grep -q x86_64 ; then FEATURES_LIST="${FEATURES_LIST},64b"; fi
147 if cat /var/log/dmesg | grep -q -e Intel-IOMMU ; then FEATURES_LIST="${FEATURES_LIST},iommu"; fi
148 FEATURES_LIST=${FEATURES_LIST#,}
149
150 NUMAS=`gawk 'BEGIN{numas=0;}
151 ($1=="physical" && $2=="id" ){ if ($4+1>numas){numas=$4+1} };
152 END{printf("%d",numas);}' /proc/cpuinfo`
153
154 CPUS=`gawk '($1=="processor"){pro=$3;}
155 ($1=="physical" && $2=="id"){ phy=$4;}
156 ($1=="core" && $2=="id"){printf " %d-%d-%d", phy,$4,pro;}' /proc/cpuinfo`
157
158 if grep -q isolcpus /proc/cmdline
159 then
160 isolcpus=`cat /proc/cmdline`
161 isolcpus=${isolcpus##*isolcpus=}
162 isolcpus=${isolcpus%% *}
163 isolcpus=${isolcpus//,/ }
164 else
165 isolcpus=""
166 fi
167
168
169 #obtain interfaces information
170 unset dpid
171 read -p "Do you want to provide the interfaces connectivity information (datapathid/dpid of the switch and switch port id)? [Y/n] " conn_info
172 case $conn_info in
173 [Nn]* ) prov_conn=false;;
174 * ) prov_conn=true;
175 read -p "What is the switch dapapathid/dpdi? (01:02:03:04:05:06:07:08) " dpid;
176 [[ -z $dpid ]] && dpid="01:02:03:04:05:06:07:08";
177 PORT_RANDOM=$RANDOM
178 iface_counter=0;;
179 esac
180 OLDIFS=$IFS
181 IFS=$'\n'
182 unset PF_list
183 unset VF_list
184 for device in `virsh nodedev-list --cap net | grep -v net_lo_00_00_00_00_00_00`
185 do
186 virsh nodedev-dumpxml $device > device_xml
187 name=`xmlpath_args "device/capability/interface" < device_xml`
188 name="${name// /}"
189 address=`xmlpath_args "device/capability/address" < device_xml`
190 address="${address// /}"
191 parent=`xmlpath_args "device/parent" < device_xml`
192 parent="${parent// /}"
193 #the following line created variables 'speed' and 'state'
194 eval `xmlpath_args "device/capability/link" < device_xml`
195 virsh nodedev-dumpxml $parent > parent_xml
196 driver=`xmlpath_args "device/driver/name" < parent_xml`
197 [ $? -eq 1 ] && driver="N/A"
198 driver="${driver// /}"
199
200 #If the device is not up try to bring it up and reload state
201 if [[ $state == 'down' ]] && ( [[ $driver == "ixgbe" ]] || [[ $driver == "i40e" ]] )
202 then
203 >&2 echo "$name is down. Trying to bring it up"
204 ifconfig $name up
205 sleep 2
206 virsh nodedev-dumpxml $device > device_xml
207 eval `xmlpath_args "device/capability/link" < device_xml`
208 fi
209
210 if [[ $state == 'down' ]] && ( [[ $driver == "ixgbe" ]] || [[ $driver == "i40e" ]] )
211 then
212 >&2 echo "Interfaces must be connected and up in order to properly detect the speed. You can provide this information manually or skip the interface"
213 keep_asking=true
214 skip_interface=true
215 unset speed
216 while $keep_asking; do
217 read -p "Do you want to skip interface $name ($address) [y/N] " -i "n" skip
218 case $skip in
219 [Yy]* ) keep_asking=false;;
220 * ) skip_interface=false;
221 default_speed="10000"
222 while $keep_asking; do
223 read -p "What is the speed of the interface expressed in Mbps? ($default_speed) " speed;
224 [[ -z $speed ]] && speed=$default_speed
225 [[ $speed =~ ''|*[!0-9] ]] && echo "The input must be an integer" && continue;
226 keep_asking=false ;
227 done;;
228 esac
229 done
230
231 $skip_interface && continue
232 fi
233 #the following line creates a 'node' variable
234 eval `xmlpath_args "device/capability/numa" < parent_xml`
235 #the following line creates the variable 'type'
236 #in case the interface is a PF the value is 'virt_functions'
237 #in case the interface is a VF the value is 'phys_function'
238 type="N/A"
239 eval `xmlpath_args "device/capability/capability" < parent_xml`
240 #obtain pci
241 #the following line creates the variables 'domain' 'bus' 'slot' and 'function'
242 eval `xmlpath_args "device/capability/iommuGroup/address" < parent_xml`
243 pci="${domain#*x}:${bus#*x}:${slot#*x}.${function#*x}"
244 underscored_pci="${pci//\:/_}"
245 underscored_pci="pci_${underscored_pci//\./_}"
246
247 if ( [[ $driver == "ixgbe" ]] || [[ $driver == "i40e" ]] )
248 then
249 underscored_pci="pf"$underscored_pci
250 PF_list[${#PF_list[@]}]=$underscored_pci
251 eval declare -A $underscored_pci
252 eval $underscored_pci["name"]=$name
253 eval $underscored_pci["numa"]=$node
254 eval $underscored_pci["mac"]=$address
255 eval $underscored_pci["speed"]=$speed
256 eval $underscored_pci["pci"]=$pci
257 #request switch port to the user if this information is being provided and include it
258 if $prov_conn
259 then
260 unset switch_port
261 read -p "What is the port name in the switch $dpid where port $name ($pci) is connected? (${name}-${PORT_RANDOM}/$iface_counter) " switch_port
262 [[ -z $switch_port ]] && switch_port="${name}-${PORT_RANDOM}/$iface_counter"
263 iface_counter=$((iface_counter+1))
264 eval $underscored_pci["dpid"]=$dpid
265 eval $underscored_pci["switch_port"]=$switch_port
266 fi
267
268 #A�ado el pci de cada uno de los hijos
269 SRIOV_counter=0
270 for child in `xmlpath_args "device/capability/capability/address" < parent_xml`
271 do
272 SRIOV_counter=$((SRIOV_counter+1))
273 #the following line creates the variables 'domain' 'bus' 'slot' and 'function'
274 eval $child
275 eval $underscored_pci["SRIOV"$SRIOV_counter]="${domain#*x}_${bus#*x}_${slot#*x}_${function#*x}"
276 done
277 eval $underscored_pci["SRIOV"]=$SRIOV_counter
278
279 #Si se trata de un SRIOV (tiene una capability con type 'phys_function')
280 elif [[ $type == 'phys_function' ]]
281 then
282 underscored_pci="vf"$underscored_pci
283 VF_list[${#VF_list[@]}]=$underscored_pci
284 eval declare -A $underscored_pci
285 eval $underscored_pci["source_name"]=$name
286 eval $underscored_pci["mac"]=$address
287 eval $underscored_pci["pci"]=$pci
288 fi
289 rm -f device_xml parent_xml
290 done
291 IFS=$OLDIFS
292
293 echo "#This file was created by $0"
294 echo "#for adding this compute node to openvim"
295 echo "#copy this file to openvim controller and run"
296 echo "#openvim host-add <this>"
297 echo
298 echo "host:"
299 echo " name: $HOST_NAME"
300 echo " user: $1"
301 echo " ip_name: $2"
302 echo "host-data:"
303 echo " name: $HOST_NAME"
304 echo " user: $1"
305 echo " ip_name: $2"
306 echo " ranking: 100"
307 echo " description: $HOST_NAME"
308 echo " features: $FEATURES_LIST"
309 echo " numas:"
310
311 numa=0
312 while [[ $numa -lt $NUMAS ]]
313 do
314 echo " - numa_socket: $numa"
315 #MEMORY
316 if [ -f /sys/devices/system/node/node${numa}/hugepages/hugepages-1048576kB/nr_hugepages ]
317 then
318 echo " hugepages: " `cat /sys/devices/system/node/node${numa}/hugepages/hugepages-1048576kB/nr_hugepages`
319 else
320 #TODO hugepages of 2048kB size
321 echo " hugepages: 0"
322 fi
323 memory=`head -n1 /sys/devices/system/node/node${numa}/meminfo | gawk '($5=="kB"){print $4}'`
324 memory=$((memory+1048576-1)) #memory must be ceiled
325 memory=$((memory/1048576)) #from `kB to GB
326 echo " memory: $memory"
327
328 #CORES
329 echo " cores:"
330 FIRST="-" #first item in a list start with "-" in yaml files, then it will set to " "
331 for cpu in $CPUS
332 do
333 PHYSICAL=`echo $cpu | cut -f 1 -d"-"`
334 CORE=`echo $cpu | cut -f 2 -d"-"`
335 THREAD=`echo $cpu | cut -f 3 -d"-"`
336 [[ $PHYSICAL != $numa ]] && continue #skip non physical
337 echo " - core_id: $CORE"
338 echo " thread_id: $THREAD"
339 #check if eligible
340 cpu_isolated="no"
341 for isolcpu in $isolcpus
342 do
343 isolcpu_start=`echo $isolcpu | cut -f 1 -d"-"`
344 isolcpu_end=`echo $isolcpu | cut -f 2 -d"-"`
345 if [ "$THREAD" -ge "$isolcpu_start" -a "$THREAD" -le "$isolcpu_end" ]
346 then
347 cpu_isolated="yes"
348 break
349 fi
350 done
351 [[ $cpu_isolated == "no" ]] && echo " status: noteligible"
352 FIRST=" "
353 done
354
355 #NIC INTERFACES
356 interfaces_nb=0
357 for ((i=0; i<${#PF_list[@]};i++))
358 do
359 underscored_pci=${PF_list[$i]}
360 pname=$(get_hash_value $underscored_pci "name")
361 pnuma=$(get_hash_value $underscored_pci "numa")
362 [[ $pnuma != $numa ]] && continue
363 pmac=$(get_hash_value $underscored_pci "mac")
364 ppci=$(get_hash_value $underscored_pci "pci")
365 pspeed=$(get_hash_value $underscored_pci "speed")
366 pSRIOV=$(get_hash_value $underscored_pci "SRIOV")
367 [[ $interfaces_nb -eq 0 ]] && echo " interfaces:"
368 interfaces_nb=$((interfaces_nb+1))
369 sriov_nb=0
370 echo " - source_name: $pname"
371 echo " Mbps: $pspeed"
372 echo " pci: \"$ppci\""
373 echo " mac: \"$pmac\""
374 if $prov_conn
375 then
376 pdpid=$(get_hash_value $underscored_pci "dpid")
377 pswitch_port=$(get_hash_value $underscored_pci "switch_port")
378 echo " switch_dpid: $pdpid"
379 echo " switch_port: $pswitch_port"
380 fi
381 for ((j=1;j<=$pSRIOV;j++))
382 do
383 childSRIOV="vfpci_"$(get_hash_value $underscored_pci "SRIOV"$j)
384 pname=$(get_hash_value $childSRIOV "source_name")
385 index=${pname##*_}
386 pmac=$(get_hash_value $childSRIOV "mac")
387 ppci=$(get_hash_value $childSRIOV "pci")
388 [[ $sriov_nb -eq 0 ]] && echo " sriovs:"
389 sriov_nb=$((sriov_nb+1))
390 echo " - mac: \"$pmac\""
391 echo " pci: \"$ppci\""
392 echo " source_name: $index"
393 done
394 done
395
396 numa=$((numa+1))
397 done
398 remove_vf_driver ixgbe
399 remove_vf_driver i40e
400 #Bring up all interfaces
401 for ((i=0; i<${#PF_list[@]};i++))
402 do
403 underscored_pci=${PF_list[$i]}
404 pname=$(get_hash_value $underscored_pci "name")
405 ifconfig $pname up
406 done