| tierno | f7aa8c4 | 2016-09-06 16:43:04 +0200 | [diff] [blame] | 1 | #!/bin/bash |
| 2 | |
| 3 | ## |
| 4 | # Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U. |
| tierno | 9a61c6b | 2016-09-08 10:57:02 +0200 | [diff] [blame] | 5 | # This file is part of openvim |
| tierno | f7aa8c4 | 2016-09-06 16:43:04 +0200 | [diff] [blame] | 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 | |
| 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 |