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