1#!/bin/bash 2#\ 3exec expect "$0" -- ${1+"$@"} 4 5# This file contains utilities for working with Serial over Lan (SOL). 6 7# Example use case: 8# sol_utils.tcl --os_host=ip --os_password=password --os_username=username 9# --openbmc_host=ip --openbmc_password=password --openbmc_username=username 10# --proc_name=boot_to_petitboot --host_sol_port=port 11 12source [exec bash -c "which source.tcl"] 13my_source \ 14[list print.tcl opt.tcl valid.tcl call_stack.tcl tools.exp cmd.tcl host.tcl] 15 16longoptions openbmc_host: openbmc_username:=root openbmc_password: host_sol_port:=2200\ 17 os_host: os_username:=root os_password: proc_name: ftp_username: \ 18 ftp_password: os_repo_url: autoboot_setting: test_mode:=0 quiet:=0 debug:=0 19pos_parms 20 21set valid_proc_name [list os_login boot_to_petitboot go_to_petitboot_shell \ 22 install_os time_settings software_selection root_password set_autoboot] 23 24# Create help dictionary for call to gen_print_help. 25set help_dict [dict create\ 26 ${program_name} [list "${program_name} is an SOL utilities program that\ 27 will run the user's choice of utilities. See the \"proc_name\" parm below\ 28 for details."]\ 29 openbmc_host [list "The OpenBMC host name or IP address." "host"]\ 30 openbmc_username [list "The OpenBMC username." "username"]\ 31 openbmc_password [list "The OpenBMC password." "password"]\ 32 os_host [list "The OS host name or IP address." "host"]\ 33 os_username [list "The OS username." "username"]\ 34 os_password [list "The OS password." "password"]\ 35 host_sol_port [list "The HOST SOL port number." "port"]\ 36 proc_name [list "The proc_name you'd like to run. Valid values are as\ 37 follows: [regsub -all {\s+} $valid_proc_name {, }]."]\ 38 autoboot_setting [list "The desired state of autoboot." "true/flase"]\ 39] 40 41 42# Setup state dictionary. 43set state [dict create\ 44 ssh_logged_in 0\ 45 os_login_prompt 0\ 46 os_logged_in 0\ 47 petitboot_screen 0\ 48 petitboot_shell_prompt 0\ 49] 50 51 52proc help {} { 53 54 gen_print_help 55 56} 57 58 59proc exit_proc { {ret_code 0} } { 60 61 # Execute whenever the program ends normally or with the signals that we 62 # catch (i.e. TERM, INT). 63 64 dprintn ; dprint_executing 65 dprint_var ret_code 66 67 set cmd_buf os_logoff 68 qprintn ; qprint_issuing 69 eval ${cmd_buf} 70 71 set cmd_buf sol_logoff 72 qprintn ; qprint_issuing 73 eval ${cmd_buf} 74 75 qprint_pgm_footer 76 77 exit $ret_code 78 79} 80 81 82proc validate_parms {} { 83 84 trap { exit_proc } [list SIGTERM SIGINT] 85 86 valid_value openbmc_host 87 valid_value openbmc_username 88 valid_value openbmc_password 89 valid_value os_host 90 valid_value os_username 91 valid_password os_password 92 valid_value host_sol_port 93 global valid_proc_name 94 global proc_name proc_names 95 set proc_names [split $proc_name " "] 96 if { [lsearch -exact $proc_names "install_os"] != -1 } { 97 valid_value ftp_username 98 valid_password ftp_password 99 valid_value os_repo_url 100 } 101 if { [lsearch -exact $proc_names "set_autoboot"] != -1 } { 102 valid_value autoboot_setting {} [list "true" "false"] 103 } 104 105} 106 107 108proc sol_login {} { 109 110 # Login to the SOL console. 111 112 dprintn ; dprint_executing 113 114 global spawn_id 115 global expect_out 116 global state 117 global openbmc_host openbmc_username openbmc_password host_sol_port 118 global cr_lf_regex 119 global ssh_password_prompt 120 121 set cmd_buf "spawn -nottycopy ssh -p $host_sol_port $openbmc_username@$openbmc_host" 122 qprint_issuing 123 eval $cmd_buf 124 125 append bad_host_regex "ssh: Could not resolve hostname ${openbmc_host}:" 126 append bad_host_regex " Name or service not known" 127 set expect_result [expect_wrap\ 128 [list $bad_host_regex $ssh_password_prompt]\ 129 "an SOL password prompt" 5] 130 131 if { $expect_result == 0 } { 132 puts stderr "" 133 print_error "Invalid openbmc_host value.\n" 134 exit_proc 1 135 } 136 137 send_wrap "${openbmc_password}" 138 139 append bad_user_pw_regex "Permission denied, please try again\." 140 append bad_user_pw_regex "${cr_lf_regex}${ssh_password_prompt}" 141 set expect_result [expect_wrap\ 142 [list $bad_user_pw_regex "sh: xauth: command not found"]\ 143 "an SOL prompt" 10] 144 145 switch $expect_result { 146 0 { 147 puts stderr "" ; print_error "Invalid OpenBmc username or password.\n" 148 exit_proc 1 149 } 150 1 { 151 # Currently, this string always appears but that is not necessarily 152 # guaranteed. 153 dict set state ssh_logged_in 1 154 } 155 } 156 157 if { [dict get $state ssh_logged_in] } { 158 qprintn ; qprint_timen "Logged into SOL." 159 dprintn ; dprint_dict state 160 return 161 } 162 163 # If we didn't get a hit on the "sh: xauth: command not found", then we just 164 # need to see a linefeed. 165 set expect_result [expect_wrap [list ${cr_lf_regex}] "an SOL prompt" 5] 166 167 dict set state ssh_logged_in 1 168 qprintn ; qprint_timen "Logged into SOL." 169 dprintn ; dprint_dict state 170 171} 172 173 174proc sol_logoff {} { 175 176 # Logoff from the SOL console. 177 178 dprintn ; dprint_executing 179 180 global spawn_id 181 global expect_out 182 global state 183 global openbmc_host 184 185 if { ! [dict get $state ssh_logged_in] } { 186 qprintn ; qprint_timen "No SOL logoff required." 187 return 188 } 189 190 send_wrap "~." 191 192 set expect_result [expect_wrap\ 193 [list "Connection to $openbmc_host closed"]\ 194 "a connection closed message" 5] 195 196 dict set state ssh_logged_in 0 197 qprintn ; qprint_timen "Logged off SOL." 198 dprintn ; dprint_dict state 199 200} 201 202 203proc get_post_ssh_login_state {} { 204 205 # Get the initial state following sol_login. 206 207 # The following state global dictionary variable is set by this procedure. 208 209 dprintn ; dprint_executing 210 211 global spawn_id 212 global expect_out 213 global state 214 global os_login_prompt_regex 215 global os_prompt_regex 216 global petitboot_screen_regex 217 global petitboot_shell_prompt_regex 218 global installer_screen_regex 219 220 if { ! [dict get $state ssh_logged_in] } { 221 puts stderr "" 222 append message "Programmer error - [get_stack_proc_name] must only be" 223 append message " called after sol_login has been called." 224 print_error_report $message 225 exit_proc 1 226 } 227 228 # The first thing one must do after signing into ssh -p 2200 is hit enter to 229 # see where things stand. 230 send_wrap "" 231 set expect_result [expect_wrap\ 232 [list $os_login_prompt_regex $os_prompt_regex $petitboot_screen_regex \ 233 $petitboot_shell_prompt_regex $installer_screen_regex] \ 234 "any indication of status" 5 0] 235 236 switch $expect_result { 237 0 { 238 dict set state os_login_prompt 1 239 } 240 1 { 241 dict set state os_logged_in 1 242 } 243 2 { 244 dict set state petitboot_screen 1 245 } 246 3 { 247 dict set state petitboot_shell_prompt 1 248 } 249 } 250 251 dprintn ; dprint_dict state 252 253} 254 255 256proc os_login {} { 257 258 # Login to the OS. 259 260 dprintn ; dprint_executing 261 262 global spawn_id 263 global expect_out 264 global state 265 global openbmc_host os_username os_password 266 global os_password_prompt 267 global os_prompt_regex 268 269 if { [dict get $state os_logged_in] } { 270 printn ; print_timen "We are already logged in to the OS." 271 return 272 } 273 274 send_wrap "${os_username}" 275 276 append bad_host_regex "ssh: Could not resolve hostname ${openbmc_host}:" 277 append bad_host_regex " Name or service not known" 278 set expect_result [expect_wrap\ 279 [list $os_password_prompt]\ 280 "an OS password prompt" 5] 281 282 send_wrap "${os_password}" 283 set expect_result [expect_wrap\ 284 [list "Login incorrect" "$os_prompt_regex"]\ 285 "an OS prompt" 10] 286 switch $expect_result { 287 0 { 288 puts stderr "" ; print_error "Invalid OS username or password.\n" 289 exit_proc 1 290 } 291 } 292 293 dict set state os_logged_in 1 294 dict set state os_login_prompt 0 295 qprintn ; qprint_timen "Logged into OS." 296 dprintn ; dprint_dict state 297 298} 299 300 301proc os_logoff {} { 302 303 # Logoff from the SOL console. 304 305 dprintn ; dprint_executing 306 307 global spawn_id 308 global expect_out 309 global state 310 global os_login_prompt_regex 311 312 if { ! [dict get $state os_logged_in] } { 313 qprintn ; qprint_timen "No OS logoff required." 314 return 315 } 316 317 send_wrap "exit" 318 set expect_result [expect_wrap\ 319 [list $os_login_prompt_regex]\ 320 "an OS prompt" 5] 321 322 dict set state os_logged_in 0 323 qprintn ; qprint_timen "Logged off OS." 324 dprintn ; dprint_dict state 325 326} 327 328 329proc boot_to_petitboot {} { 330 331 # Boot the machine until the petitboot screen is reached. 332 333 dprintn ; dprint_executing 334 335 global spawn_id 336 global expect_out 337 global state 338 global os_prompt_regex 339 global petitboot_screen_regex 340 global autoboot_setting 341 342 if { [dict get $state petitboot_screen] } { 343 qprintn ; qprint_timen "Already at petiboot." 344 return 345 } 346 347 if { [dict get $state petitboot_shell_prompt] } { 348 qprintn ; qprint_timen "Now at the shell prompt. Going to petitboot." 349 send_wrap "exit" 350 set expect_result [expect_wrap [list $petitboot_screen_regex]\ 351 "the petitboot screen" 900] 352 dict set state petitboot_shell_prompt 0 353 dict set state petitboot_screen 1 354 return 355 } 356 357 if { [dict get $state os_login_prompt] } { 358 set cmd_buf os_login 359 qprintn ; qprint_issuing 360 eval ${cmd_buf} 361 } 362 363 # Turn off autoboot. 364 set_autoboot "false" 365 366 # Reboot and wait for petitboot. 367 send_wrap "reboot" 368 369 # Once we've started a reboot, we are no longer logged into OS. 370 dict set state os_logged_in 0 371 dict set state os_login_prompt 0 372 373 set expect_result [expect_wrap\ 374 [list $petitboot_screen_regex]\ 375 "the petitboot screen" 900] 376 set expect_result [expect_wrap\ 377 [list "Exit to shell"]\ 378 "the 'Exit to shell' screen" 10] 379 dict set state petitboot_screen 1 380 381 qprintn ; qprint_timen "Arrived at petitboot screen." 382 dprintn ; dprint_dict state 383 384} 385 386 387proc go_to_petitboot_shell {} { 388 389 # Go to petitboot shell. 390 global spawn_id 391 global state 392 global expect_out 393 global petitboot_shell_prompt_regex 394 395 if { [dict get $state petitboot_shell_prompt] } { 396 qprintn ; qprint_timen "Already at the shell prompt." 397 return 398 } 399 400 eval boot_to_petitboot 401 send_wrap "x" 402 set expect_result [expect_wrap [list $petitboot_shell_prompt_regex]\ 403 "the shell prompt" 10] 404 dict set state petitboot_screen 0 405 dict set state petitboot_shell_prompt 1 406 qprintn ; qprint_timen "Arrived at the shell prompt." 407 qprintn ; qprint_timen state 408 409} 410 411 412proc set_autoboot { { autoboot_setting {}} } { 413 414 # Set the state of autoboot. 415 # Defaults to the value of the global autoboot_setting. 416 417 # This will work regardless of whether the OS is logged in or at petitboot. 418 419 # Description of argument(s): 420 # autoboot_setting The desired state of autoboot (true/flase). 421 422 dprintn ; dprint_executing 423 global spawn_id 424 global expect_out 425 global state 426 global os_prompt_regex 427 global petitboot_shell_prompt_regex 428 429 set_var_default autoboot_setting [get_stack_var autoboot_setting 0 2] 430 set prompt_regex $petitboot_shell_prompt_regex 431 432 if { [dict get $state os_login_prompt] } { 433 set cmd_buf os_login 434 qprintn ; qprint_issuing 435 eval ${cmd_buf} 436 set prompt_regex $os_prompt_regex 437 } 438 439 if { [dict get $state petitboot_screen ] } { 440 set cmd_buf go_to_petitboot_shell 441 qprintn ; qprint_issuing 442 eval ${cmd_buf} 443 } 444 445 if { [dict get $state os_logged_in ] } { 446 set prompt_regex $os_prompt_regex 447 } 448 449 450 set cmd_result [shell_command\ 451 "nvram --update-config auto-boot?=$autoboot_setting" $prompt_regex] 452 set cmd_result [shell_command "nvram --print-config" $prompt_regex] 453 454} 455 456 457proc installation_destination {} { 458 459 # Set the software installation destination. 460 461 # Expectation is that we are starting at the "Installation" options screen. 462 463 dprintn ; dprint_executing 464 465 global spawn_id 466 global expect_out 467 global state 468 global installer_screen_regex 469 470 qprintn ; qprint_timen "Presumed to be at \"Installation\" screen." 471 472 qprintn ; qprint_timen "Setting Installation Destination." 473 # Option 5). Installation Destination 474 qprintn ; qprint_timen "Selecting \"Installation Destination\" option." 475 send_wrap "5" 476 expect_wrap [list "Installation Destination"] "installation destination\ 477 menu" 30 478 479 qprintn ; qprint_timen "Selecting \"Select all\" option." 480 send_wrap "3" 481 expect_wrap [list "Select all"] "selected all disks" 10 482 483 qprintn ; qprint_timen "Selecting \"continue\" option." 484 send_wrap "c" 485 expect_wrap [list "Autopartitioning"] "autopartitioning options" 10 486 487 qprintn ; qprint_timen "\ 488 Selecting \"Replace Existing Linux system(s)\" option." 489 send_wrap "1" 490 expect_wrap [list "Replace"] "selected stanard partition" 10 491 492 qprintn ; qprint_timen "Selecting \"continue\" option." 493 send_wrap "c" 494 expect_wrap [list "Partition Scheme"] "partition scheme options" 10 495 496 qprintn ; qprint_timen "Selecting \"LVM\" option." 497 send_wrap "3" 498 expect_wrap [list "LVM"] "lvm option" 10 499 500 qprintn ; qprint_timen "Selecting \"continue\" option." 501 send_wrap "c" 502 expect_wrap [list $installer_screen_regex] "installation options screen" 10 503 504} 505 506 507proc time_settings {} { 508 509 # Set the time/zone via the petitboot shell prompt "Time settings" menu. 510 511 # Expectation is that we are starting at the "Installation" options screen. 512 513 dprintn ; dprint_executing 514 515 global spawn_id 516 global expect_out 517 global state 518 global installer_screen_regex 519 520 # Option 2). Timezone. 521 522 qprintn ; qprint_timen "Presumed to be at \"Installation\" screen." 523 qprintn ; qprint_timen "Setting time." 524 525 qprintn ; qprint_timen "Selecting \"Time settings\"." 526 send_wrap "2" 527 expect_wrap [list "Set timezone" "Time settings"] "Time settings menu" 30 528 529 qprintn ; qprint_timen "Selecting \"Change timezone\"." 530 send_wrap "1" 531 expect_wrap [list "Available regions"] "available regions menu" 10 532 533 qprintn ; qprint_timen "Selecting \"US\"." 534 send_wrap "11" 535 expect_wrap [list "region US"] "select region in US menu" 10 536 537 qprintn ; qprint_timen "Selecting \"Central\"." 538 send_wrap "3" 539 expect_wrap [list $installer_screen_regex] "installation options screen" 10 540 541} 542 543 544proc software_selection {} { 545 546 # Set the base environment via the petitboot shell prompt 547 # "Software Selection" menu. 548 549 # Expectation is that we are starting at the "Installation" options 550 # screen. 551 552 dprintn ; dprint_executing 553 554 global spawn_id 555 global expect_out 556 global state 557 global installer_screen_regex 558 559 qprintn ; qprint_timen "Presumed to be at \"Installation\" screen." 560 qprintn ; qprint_timen "Software selection." 561 # Option 4). Software selection. 562 set expect_result 0 563 while { $expect_result != 1 } { 564 qprintn ; qprint_timen "Selecting \"Software selection\"." 565 send_wrap "4" 566 set expect_result [expect_wrap\ 567 [list "Installation source needs to be set up first." \ 568 "Base environment"] "base environment menu" 10 0] 569 570 switch $expect_result { 571 0 { 572 qprintn ; qprint_timen "Selecting \"continue\"." 573 send_wrap "c" 574 expect_wrap [list $installer_screen_regex] \ 575 "installation options screen" 15 576 } 577 1 { 578 break 579 } 580 } 581 } 582 583 qprintn ; qprint_timen "Selecting \"Infrastructure Server\"." 584 send_wrap "2" 585 expect_wrap [list "Infrastructure"] "selected infrastructure" 15 586 587 qprintn ; qprint_timen "Selecting \"continue\"." 588 send_wrap "c" 589 expect_wrap [list $installer_screen_regex] "installation options screen" 15 590 591} 592 593 594proc root_password {} { 595 596 # Set the os root password via the petitboot shell prompt "Root password" 597 # option. 598 599 # Expectation is that we are starting at the "Installation" options screen. 600 601 dprintn ; dprint_executing 602 603 global spawn_id 604 global expect_out 605 global state 606 global os_password 607 global installer_screen_regex 608 609 qprintn ; qprint_timen "Presumed to be at \"Installation\" screen." 610 qprintn ; qprint_timen "Setting root password." 611 612 # Option 8). Root password. 613 qprintn ; qprint_timen "Selecting \"Root password\"." 614 send_wrap "8" 615 expect_wrap [list "Password:"] "root password prompt" 30 616 617 qprintn ; qprint_timen "Entering root password." 618 send_wrap "$os_password" 619 expect_wrap [list "confirm"] "confirm root password prompt" 15 620 621 qprintn ; qprint_timen "Re-entering root password." 622 send_wrap "$os_password" 623 set expect_result [expect_wrap\ 624 [list $installer_screen_regex "The password you have provided is weak"] \ 625 "root password accepted" 10 0] 626 switch $expect_result { 627 0 { 628 break 629 } 630 1 { 631 qprintn ; qprint_timen "Confirming weak password." 632 send_wrap "yes" 633 } 634 } 635 expect_wrap [list $installer_screen_regex] "installation options screen" 10 636 637} 638 639 640proc install_os {} { 641 642 # Install an os on the machine. 643 global spawn_id 644 global expect_out 645 global petitboot_shell_prompt_regex 646 global installer_screen_regex 647 global ftp_username ftp_password os_repo_url 648 global os_host os_username os_password 649 650 lassign [get_host_name_ip $os_host 0] os_hostname short_host_name ip_address 651 set netmask [get_host_netmask $os_host $os_username $os_password {} 0] 652 set gateway [get_host_gateway $os_host $os_username $os_password 0] 653 set mac_address \ 654 [get_host_mac_address $os_host $os_username $os_password {} 0] 655 set name_servers [get_host_name_servers $os_host $os_username $os_password 0] 656 set dns [lindex $name_servers 0] 657 set domain [get_host_domain $os_host $os_username $os_password 0] 658 659 # Go to shell and download files for installation 660 eval go_to_petitboot_shell 661 after 10000 662 set vmlinuz_url \ 663 "ftp://$ftp_username:$ftp_password@$os_repo_url/ppc/ppc64/vmlinuz" 664 set initrd_url \ 665 "ftp://$ftp_username:$ftp_password@$os_repo_url/ppc/ppc64/initrd.img" 666 send_wrap "wget -c $vmlinuz_url" 667 expect_wrap [list "vmlinuz *100%"] "wget vmlinuz file success" 30 668 send_wrap "wget -c $initrd_url" 669 expect_wrap [list "initrd.img *100%"] "wget initrd file success" 30 670 671 # Setup parms and run kexec. 672 set colon "::" 673 set squashfs_url \ 674 "ftp://$ftp_username:$ftp_password@$os_repo_url/LiveOS/squashfs.img" 675 set kexec_args "kexec -l vmlinuz --initrd initrd.img\ 676 --append='root=live:$squashfs_url \ 677 repo=ftp://$ftp_username:$ftp_password@$os_repo_url rd.dm=0 rd.md=0\ 678 nodmraid console=hvc0 ifname=net0:$mac_address\ 679 ip=$os_host$colon$gateway:$netmask:$os_hostname:net0:none nameserver=$dns\ 680 inst.text'" 681 send_wrap "$kexec_args" 682 dprintn ; dprint_vars expect_out 683 set expect_result [expect_wrap [list $petitboot_shell_prompt_regex]\ 684 "the shell prompt" 10] 685 686 # Turn on autoboot. 687 set_autoboot "true" 688 689 send_wrap "kexec -e" 690 691 # Begin installation process, go to settings screen. 692 set expect_result [expect_wrap [list "Starting installer"]\ 693 "starting installer log" 900] 694 set expect_result [expect_wrap [list "Use text mode"]\ 695 "install mode selection prompt" 120] 696 send_wrap "2" 697 expect_wrap [list $installer_screen_regex] "installation options screen" 15 698 699 installation_destination 700 time_settings 701 software_selection 702 root_password 703 704 # Now begin installation process. 705 set expect_result [expect_wrap\ 706 [list $os_repo_url "Processing..."] \ 707 "installation source processing" 10 0] 708 709 switch $expect_result { 710 0 { 711 break 712 } 713 1 { 714 expect_wrap [list $os_repo_url] "source processing complete" 240 715 } 716 } 717 send_wrap "b" 718 set expect_result [expect_wrap \ 719 [list "Installation complete* Press return to quit"] \ 720 "os installation complete message" 2000] 721 send_wrap ""; # Reboots to petitboot. 722 return 723 724} 725 726 727# Main 728 729 gen_get_options $argv 730 731 validate_parms 732 733 qprint_pgm_header 734 735 # Global variables for current prompts of the SOL console. 736 set ssh_password_prompt ".* password: " 737 set os_login_prompt_regex "login: " 738 set os_password_prompt "Password: " 739 set petitboot_screen_regex "Petitboot" 740 set cr_lf_regex "\[\n\r\]" 741 set os_prompt_regex "(\\\[${os_username}@\[^ \]+ ~\\\]# )" 742 set petitboot_shell_prompt_regex "/ #" 743 set installer_screen_regex \ 744 {Installation[\r\n].*Please make your choice from above.* to refresh\]: } 745 746 dprintn ; dprint_dict state 747 748 set cmd_buf sol_login 749 qprint_issuing 750 eval ${cmd_buf} 751 752 set cmd_buf get_post_ssh_login_state 753 qprintn ; qprint_issuing 754 eval ${cmd_buf} 755 756 foreach proc_name $proc_names { 757 set cmd_buf ${proc_name} 758 qprintn ; qprint_issuing 759 eval ${cmd_buf} 760 } 761 762 exit_proc 763