1#!/usr/bin/python2.6 2# 3# CDDL HEADER START 4# 5# The contents of this file are subject to the terms of the 6# Common Development and Distribution License (the "License"). 7# You may not use this file except in compliance with the License. 8# 9# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10# or http://www.opensolaris.org/os/licensing. 11# See the License for the specific language governing permissions 12# and limitations under the License. 13# 14# When distributing Covered Code, include this CDDL HEADER in each 15# file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16# If applicable, add the following below this CDDL HEADER, with the 17# fields enclosed by brackets "[]" replaced with your own identifying 18# information: Portions Copyright [yyyy] [name of copyright owner] 19# 20# CDDL HEADER END 21# 22 23# 24# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. 25# 26 27""" 28beadm - The Boot Environment Administration tool. Use this CLI to 29manage boot environments. 30""" 31 32import getopt 33import gettext 34import os 35import sys 36import shutil 37import traceback 38import time 39import subprocess 40 41from beadm import _ 42from beadm.BootEnvironment import * 43import beadm.messages as msg 44import libbe_py as lb 45 46#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 47def usage(): 48 '''Defines parameters and options of the command beadm.''' 49 print >> sys.stderr, _(""" 50Usage: 51 beadm subcommand cmd_options 52 53 subcommands: 54 55 beadm activate beName 56 beadm create [-a] [-d description] 57 [-e non-activeBeName | beName@snapshot] 58 [-o property=value] ... [-p zpool] beName 59 beadm create beName@snapshot 60 beadm destroy [-fF] beName | beName@snapshot 61 beadm list [[-a] | [-d] [-s]] [-H] [beName] 62 beadm mount beName mountpoint 63 beadm rename beName newBeName 64 beadm unmount [-f] beName""") 65 sys.exit(1) 66 67 68#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 69# Public Command Line functions described in beadm(1) 70#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 71 72#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 73def activate(opts): 74 """ 75 Function: activate 76 77 Description: Activate a Boot Environment.The following is the 78 subcommand, options and args that make up the 79 opts object passed in: 80 81 Parameters: 82 opts - A string containing the active subcommand 83 84 Returns: 85 0 - Success 86 1 - Failure 87 """ 88 89 if len(opts) != 1: 90 msg.printMsg(msg.Msgs.BEADM_ERR_OPT_ARGS, None, -1) 91 usage() 92 93 be = BootEnvironment() 94 95 if lb.beVerifyBEName(opts[0]) != 0: 96 msg.printMsg(msg.Msgs.BEADM_ERR_BENAME, None, -1) 97 return 1 98 99 rc = lb.beActivate(opts[0]) 100 if rc == 0: 101 return 0 102 103 be.msg_buf["0"] = opts[0] 104 if rc == msg.Msgs.BE_ERR_BE_NOENT: 105 be.msg_buf["1"] = \ 106 msg.getMsg(msg.Msgs.BEADM_ERR_BE_DOES_NOT_EXIST, opts[0]) 107 elif rc == msg.Msgs.BE_ERR_PERM or rc == msg.Msgs.BE_ERR_ACCESS: 108 be.msg_buf["1"] = msg.getMsg(msg.Msgs.BEADM_ERR_PERMISSIONS, rc) 109 msg.printMsg(msg.Msgs.BEADM_ERR_ACTIVATE, be.msg_buf, -1) 110 return 1 111 else: 112 be.msg_buf["1"] = lb.beGetErrDesc(rc) 113 if be.msg_buf["1"] == None: 114 be.msg_buf["1"] = \ 115 msg.getMsg(msg.Msgs.BEADM_ERR_NO_MSG, rc) 116 117 msg.printMsg(msg.Msgs.BEADM_ERR_ACTIVATE, be.msg_buf, -1) 118 return 1 119 120 121#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 122def create(opts): 123 """ 124 Function: create 125 126 Description: Create a Boot Environment. The following is the 127 subcommand, options and args that make up the 128 opts object passed in: 129 130 create [-a] [-d description] 131 [-e non-activeBeName | beName@Snapshot] 132 [-o property=value] ... [-p zpool] beName 133 134 create beName@Snapshot 135 136 Parameters: 137 opts - A object containing the create subcommand 138 and all the options and arguments passed in 139 on the command line mentioned above. 140 141 Returns: 142 0 - Success 143 1 - Failure 144 """ 145 146 be = BootEnvironment() 147 148 activate = False 149 150 try: 151 opts_args, be.trgt_be_name_or_snapshot = getopt.getopt(opts, 152 "ad:e:o:p:") 153 except getopt.GetoptError: 154 msg.printMsg(msg.Msgs.BEADM_ERR_OPT_ARGS, None, -1) 155 usage() 156 157 # Counters for detecting multiple options. 158 # e.g. beadm create -p rpool -p rpool2 newbe 159 num_a_opts = 0 160 num_e_opts = 0 161 num_p_opts = 0 162 num_d_opts = 0 163 164 for opt, arg in opts_args: 165 if opt == "-a": 166 activate = True 167 num_a_opts += 1 168 elif opt == "-e": 169 be.src_be_name_or_snapshot = arg 170 num_e_opts += 1 171 elif opt == "-o": 172 key, value = arg.split("=") 173 be.properties[key] = value 174 elif opt == "-p": 175 be.trgt_rpool = arg 176 num_p_opts += 1 177 elif opt == "-d": 178 be.description = arg 179 num_d_opts += 1 180 181 if num_a_opts > 1 or num_e_opts > 1 or num_p_opts > 1 or num_d_opts > 1: 182 msg.printMsg(msg.Msgs.BEADM_ERR_OPT_ARGS, None, -1) 183 usage() 184 185 # Check that all info provided from the user is legitimate. 186 if (verifyCreateOptionsArgs(be) != 0): 187 usage() 188 189 if initBELog("create", be) != 0: 190 return 1 191 192 msg.printMsg(msg.Msgs.BEADM_MSG_BE_CREATE_START, 193 be.trgt_be_name_or_snapshot[0], be.log_id) 194 195 if '@' in be.trgt_be_name_or_snapshot[0]: 196 # Create a snapshot 197 rc = createSnapshot(be) 198 else: 199 if lb.beVerifyBEName(be.trgt_be_name_or_snapshot[0]) != 0: 200 msg.printMsg(msg.Msgs.BEADM_ERR_BENAME, None, -1) 201 return 1 202 203 # Create a BE based on a snapshot 204 if be.src_be_name_or_snapshot is not None and \ 205 '@' in be.src_be_name_or_snapshot: 206 # Create a BE from a snapshot 207 rc = createBEFromSnapshot(be) 208 else: 209 rc = createBE(be) 210 211 # Activate the BE if the user chose to. 212 if activate and rc == 0: 213 rc = activateBE(be) 214 cleanupBELog(be) 215 216 return rc 217 218#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 219def destroy(opts): 220 """ 221 Function: destroy 222 223 Description: Destroy a Boot Environment. The following is the 224 subcommand, options and args that make up the 225 opts object passed in: 226 227 destroy [-fF] beName | beName@snapshot 228 229 Parameters: 230 opts - A object containing the destroy subcommand 231 and all the options and arguments passed in 232 on the command line mentioned above. 233 234 Returns: 235 0 - Success 236 1 - Failure 237 """ 238 239 force_unmount = 0 240 suppress_prompt = False 241 be_active_on_boot = None 242 be = BootEnvironment() 243 244 try: 245 opts_args, be.trgt_be_name_or_snapshot = getopt.getopt(opts, "fF") 246 except getopt.GetoptError: 247 msg.printMsg(msg.Msgs.BEADM_ERR_OPT_ARGS, None, -1) 248 usage() 249 250 # Counters for detecting multiple options. 251 # e.g. beadm destroy -f -f newbe 252 num_f_opts = 0 253 num_sf_opts = 0 254 255 for opt, arg in opts_args: 256 if opt == "-f": 257 force_unmount = 1 258 num_sf_opts += 1 259 elif opt == "-F": 260 suppress_prompt = True 261 num_f_opts += 1 262 263 if num_sf_opts > 1 or num_f_opts > 1: 264 msg.printMsg(msg.Msgs.BEADM_ERR_OPT_ARGS, None, -1) 265 usage() 266 267 if len(be.trgt_be_name_or_snapshot) != 1: 268 msg.printMsg(msg.Msgs.BEADM_ERR_OPT_ARGS, None, -1) 269 usage() 270 271 is_snapshot = False 272 273 if "@" in be.trgt_be_name_or_snapshot[0]: 274 is_snapshot = True 275 be_name, snap_name = be.trgt_be_name_or_snapshot[0].split("@") 276 if lb.beVerifyBEName(be_name) != 0: 277 msg.printMsg(msg.Msgs.BEADM_ERR_BENAME, None, -1) 278 return 1 279 else: 280 if lb.beVerifyBEName(be.trgt_be_name_or_snapshot[0]) != 0: 281 msg.printMsg(msg.Msgs.BEADM_ERR_BENAME, None, -1) 282 return 1 283 284 # Get the 'active' BE and the 'active on boot' BE. 285 be_active, be_active_on_boot = getActiveBEAndActiveOnBootBE() 286 287 # If the user is trying to destroy the 'active' BE then quit now. 288 if not is_snapshot and be_active == be.trgt_be_name_or_snapshot[0]: 289 be.msg_buf["0"] = be.msg_buf["1"] = be_active 290 msg.printMsg(msg.Msgs.BEADM_ERR_DESTROY_ACTIVE, be.msg_buf, -1) 291 return 1 292 293 if not suppress_prompt: 294 295 # Display a destruction question and wait for user response. 296 # Quit if negative user response. 297 298 if not displayDestructionQuestion(be): 299 return 0 300 301 if is_snapshot: 302 303 # Destroy a snapshot. 304 rc = lb.beDestroySnapshot(be_name, snap_name) 305 else: 306 307 # Destroy a BE. Passing in 1 for the second arg destroys 308 # any snapshots the BE may have as well. 309 310 rc = lb.beDestroy(be.trgt_be_name_or_snapshot[0], 1, force_unmount) 311 312 # Check if the BE that was just destroyed was the 313 # 'active on boot' BE. If it was, display a message letting 314 # the user know that the 'active' BE is now also the 315 # 'active on boot' BE. 316 if be_active_on_boot == be.trgt_be_name_or_snapshot[0] and rc == 0: 317 msg.printMsg(msg.Msgs.BEADM_MSG_ACTIVE_ON_BOOT, 318 be_active, -1) 319 320 if rc == 0: 321 try: 322 shutil.rmtree("/var/log/beadm/" + \ 323 be.trgt_be_name_or_snapshot[0], True) 324 except: 325 msg.printMsg(msg.Msgs.BEADM_ERR_LOG_RM, 326 "/var/log/beadm/" + be.trgt_be_name_or_snapshot[0], -1) 327 328 return 0 329 330 be.msg_buf["0"] = be.trgt_be_name_or_snapshot[0] 331 if rc == msg.Msgs.BE_ERR_MOUNTED: 332 be.msg_buf["1"] = be.msg_buf["2"] = be.trgt_be_name_or_snapshot[0] 333 msg.printMsg(msg.Msgs.BEADM_ERR_MOUNTED, be.msg_buf, -1) 334 return 1 335 elif rc == msg.Msgs.BE_ERR_DESTROY_CURR_BE: 336 msg.printMsg(msg.Msgs.BEADM_ERR_DESTROY_ACTIVE, \ 337 be.msg_buf["0"], -1) 338 return 1 339 elif rc == msg.Msgs.BE_ERR_ZONES_UNMOUNT: 340 be.msg_buf["1"] = be.trgt_be_name_or_snapshot[0] 341 msg.printMsg(msg.Msgs.BE_ERR_ZONES_UNMOUNT, be.msg_buf, -1) 342 return 1 343 elif rc == msg.Msgs.BE_ERR_PERM or rc == msg.Msgs.BE_ERR_ACCESS: 344 be.msg_buf["1"] = msg.getMsg(msg.Msgs.BEADM_ERR_PERMISSIONS, rc) 345 msg.printMsg(msg.Msgs.BEADM_ERR_DESTROY, be.msg_buf, -1) 346 return 1 347 else: 348 be.msg_buf["1"] = lb.beGetErrDesc(rc) 349 if be.msg_buf["1"] == None: 350 be.msg_buf["1"] = \ 351 msg.getMsg(msg.Msgs.BEADM_ERR_NO_MSG, rc) 352 353 msg.printMsg(msg.Msgs.BEADM_ERR_DESTROY, be.msg_buf, -1) 354 return 1 355 356#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 357def list(opts): 358 """ 359 Description: List the attributes of a Boot Environment. 360 The following is the subcommand, options 361 and args that make up the opts object 362 passed in: 363 364 list [[-a] | [-d] [-s]] [-H] [beName] 365 366 -a displays all info 367 -d displays BE info plus dataset info 368 -s displays BE info plus snapshot info 369 -H displays info parsable by a computer 370 371 Parameters: 372 opts - A object containing the list subcommand 373 and all the options and arguments passed in 374 on the command line mentioned above. 375 376 Returns: 377 0 - Success 378 1 - Failure 379 """ 380 381 be = BootEnvironment() 382 383 list_all_attrs = "" 384 list_datasets = "" 385 list_snapshots = "" 386 dont_display_headers = False 387 be_name = None 388 be_list = None 389 390 # Counters for detecting multiple options. 391 # e.g. beadm list -a -a newbe 392 num_a_opts = 0 393 num_d_opts = 0 394 num_s_opts = 0 395 num_h_opts = 0 396 397 try: 398 opts_args, be.trgt_be_name_or_snapshot = getopt.getopt(opts, "adHs") 399 except getopt.GetoptError: 400 msg.printMsg(msg.Msgs.BEADM_ERR_OPT_ARGS, None, -1) 401 usage() 402 403 for opt, arg in opts_args: 404 if opt == "-a": 405 list_all_attrs = opt 406 num_a_opts += 1 407 elif opt == "-d": 408 list_datasets = opt 409 num_d_opts += 1 410 elif opt == "-s": 411 list_snapshots = opt 412 num_s_opts += 1 413 elif opt == "-H": 414 dont_display_headers = True 415 num_h_opts += 1 416 417 if num_a_opts > 1 or num_d_opts > 1 or num_s_opts > 1 or num_h_opts > 1: 418 msg.printMsg(msg.Msgs.BEADM_ERR_OPT_ARGS, None, -1) 419 usage() 420 421 if len(be.trgt_be_name_or_snapshot) > 1: 422 msg.printMsg(msg.Msgs.BEADM_ERR_OPT_ARGS, None, -1) 423 usage() 424 425 if len(be.trgt_be_name_or_snapshot) == 1: 426 be_name = be.trgt_be_name_or_snapshot[0] 427 if lb.beVerifyBEName(be_name) != 0: 428 msg.printMsg(msg.Msgs.BEADM_ERR_BENAME, None, -1) 429 return 1 430 431 if (list_all_attrs == "-a" and (list_datasets == "-d" \ 432 or list_snapshots == "-s")): 433 msg.printMsg(msg.Msgs.BEADM_ERR_MUTUALLY_EXCL, 434 list_all_attrs + " " + list_datasets + " " + 435 list_snapshots, -1) 436 usage() 437 438 list_options = "" 439 440 # When zones are implemented add "listZones == "-z" below 441 442 # Coelesce options to pass to displayBEs 443 444 if (list_datasets == "-d" and list_snapshots == "-s" or \ 445 list_all_attrs == "-a"): 446 list_options = "-a" 447 elif list_datasets != "" or list_snapshots != "" or list_all_attrs != "": 448 list_options = list_datasets + " " + list_snapshots 449 450 rc, be_list = lb.beList() 451 if rc != 0: 452 if rc == msg.Msgs.BE_ERR_BE_NOENT: 453 if be_name == None: 454 msg.printMsg(msg.Msgs.BEADM_ERR_NO_BES_EXIST, 455 None, -1) 456 return 1 457 458 string = \ 459 msg.getMsg(msg.Msgs.BEADM_ERR_BE_DOES_NOT_EXIST, 460 be_name) 461 else: 462 string = lb.beGetErrDesc(rc) 463 if string == None: 464 string = \ 465 msg.getMsg(msg.Msgs.BEADM_ERR_NO_MSG, rc) 466 467 msg.printMsg(msg.Msgs.BEADM_ERR_LIST, string, -1) 468 return 1 469 470 # classify according to command line options 471 if list_options.find("-a") != -1 or \ 472 (list_options.find("-d") != -1 and list_options.find("-s") != -1): 473 list_object = CompleteList(dont_display_headers) #all 474 elif list_options.find("-d") != -1: 475 list_object = DatasetList(dont_display_headers) #dataset 476 elif list_options.find("-s") != -1: 477 list_object = SnapshotList(dont_display_headers) #snapshot 478 else: list_object = BEList(dont_display_headers) #only BE 479 480 # use list method for object 481 if list_object.list(be_list, dont_display_headers, be_name) != 0: 482 msg.printMsg(msg.Msgs.BEADM_ERR_LIST_DATA, None, -1) 483 return 1 484 485 return 0 486 487#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 488def mount(opts): 489 """ 490 Description: Mount a Boot Environment on a directory. 491 The following is the subcommand, options 492 and args that make up the opts object 493 passed in: 494 495 mount beName [mountpoint] 496 497 Parameters: 498 opts - A object containing the mount subcommand 499 and all the options and arguments passed in 500 on the command line mentioned above. 501 502 Returns: 503 0 - Success 504 1 - Failure 505 """ 506 507 be = BootEnvironment() 508 509 mountpoint = None 510 511 try: 512 be_name_mnt_point = getopt.getopt(opts, "")[1] 513 except getopt.GetoptError: 514 msg.printMsg(msg.Msgs.BEADM_ERR_OPT_ARGS, None, -1) 515 usage() 516 517 mnt_point_len = len(be_name_mnt_point) 518 519 if mnt_point_len != 2: 520 msg.printMsg(msg.Msgs.BEADM_ERR_OPT_ARGS, None, -1) 521 usage() 522 else: 523 # Check for leading / in mount point 524 mountpoint = be_name_mnt_point[1] 525 if not mountpoint.startswith('/'): 526 msg.printMsg(msg.Msgs.BEADM_ERR_MOUNTPOINT, 527 mountpoint, -1) 528 return 1 529 530 if lb.beVerifyBEName(be_name_mnt_point[0]) != 0: 531 msg.printMsg(msg.Msgs.BEADM_ERR_BENAME, None, -1) 532 return 1 533 534 rc = lb.beMount(be_name_mnt_point[0], mountpoint) 535 if rc == 0: 536 return 0 537 538 be.msg_buf["0"] = be_name_mnt_point[0] 539 if rc == msg.Msgs.BE_ERR_MOUNTED: 540 be.msg_buf["1"] = \ 541 msg.getMsg(msg.Msgs.BEADM_ERR_MOUNT_EXISTS, 542 be_name_mnt_point[0]) 543 elif rc == msg.Msgs.BE_ERR_BE_NOENT: 544 be.msg_buf["1"] = \ 545 msg.getMsg(msg.Msgs.BEADM_ERR_BE_DOES_NOT_EXIST, 546 be_name_mnt_point[0]) 547 elif rc == msg.Msgs.BE_ERR_PERM or rc == msg.Msgs.BE_ERR_ACCESS: 548 be.msg_buf["1"] = msg.getMsg(msg.Msgs.BEADM_ERR_PERMISSIONS, rc) 549 msg.printMsg(msg.Msgs.BEADM_ERR_MOUNT, be.msg_buf, -1) 550 return 1 551 else: 552 be.msg_buf["1"] = lb.beGetErrDesc(rc) 553 if be.msg_buf["1"] == None: 554 be.msg_buf["1"] = \ 555 msg.getMsg(msg.Msgs.BEADM_ERR_NO_MSG, rc) 556 557 msg.printMsg(msg.Msgs.BEADM_ERR_MOUNT, be.msg_buf, -1) 558 return 1 559 560#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 561def unmount(opts): 562 """ 563 Description: Unmount a Boot Environment. 564 The following is the subcommand, options 565 and args that make up the opts object 566 passed in: 567 568 unmount [-f] beName 569 570 Parameters: 571 opts - A object containing the unmount subcommand 572 and all the options and arguments passed in 573 on the command line mentioned above. 574 575 Returns: 576 0 - Success 577 1 - Failure 578 """ 579 580 be = BootEnvironment() 581 582 force_unmount = 0 583 584 # Counter for detecting multiple options. 585 # e.g. beadm unmount -f -f newbe 586 num_f_opts = 0 587 588 try: 589 optlist, args = getopt.getopt(opts, "f") 590 except getopt.GetoptError: 591 msg.printMsg(msg.Msgs.BEADM_ERR_OPT_ARGS, None, -1) 592 usage() 593 594 for opt, arg in optlist: 595 if opt == "-f": 596 force_unmount = 1 597 num_f_opts += 1 598 599 if num_f_opts > 1: 600 msg.printMsg(msg.Msgs.BEADM_ERR_OPT_ARGS, None, -1) 601 usage() 602 603 if len(args) != 1: 604 msg.printMsg(msg.Msgs.BEADM_ERR_OPT_ARGS, None, -1) 605 usage() 606 607 if lb.beVerifyBEName(args[0]) != 0: 608 msg.printMsg(msg.Msgs.BEADM_ERR_BENAME, None, -1) 609 return 1 610 611 rc = lb.beUnmount(args[0], force_unmount) 612 if rc == 0: 613 return 0 614 615 be.msg_buf["0"] = args[0] 616 if rc == msg.Msgs.BE_ERR_UMOUNT_CURR_BE: 617 be.msg_buf["1"] = \ 618 msg.getMsg(msg.Msgs.BEADM_ERR_UNMOUNT_ACTIVE, 619 args[0]) 620 elif rc == msg.Msgs.BE_ERR_UMOUNT_SHARED: 621 be.msg_buf["1"] = \ 622 msg.getMsg(msg.Msgs.BEADM_ERR_SHARED_FS, args[0]) 623 elif rc == msg.Msgs.BE_ERR_PERM or rc == msg.Msgs.BE_ERR_ACCESS: 624 be.msg_buf["1"] = msg.getMsg(msg.Msgs.BEADM_ERR_PERMISSIONS, rc) 625 msg.printMsg(msg.Msgs.BEADM_ERR_UNMOUNT, be.msg_buf, -1) 626 return 1 627 else: 628 be.msg_buf["1"] = lb.beGetErrDesc(rc) 629 if be.msg_buf["1"] == None: 630 be.msg_buf["1"] = \ 631 msg.getMsg(msg.Msgs.BEADM_ERR_NO_MSG, rc) 632 633 msg.printMsg(msg.Msgs.BEADM_ERR_UNMOUNT, be.msg_buf, -1) 634 return 1 635 636#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 637def rename(opts): 638 """ 639 Description: Rename the name of a Boot Environment. 640 The following is the subcommand, options 641 and args that make up the opts object 642 passed in: 643 644 rename beName newBeName 645 646 Parameters: 647 opts - A object containing the mount subcommand 648 and all the options and arguments passed in 649 on the command line mentioned above. 650 651 Returns: 652 0 - Success 653 1 - Failure 654 """ 655 656 be = BootEnvironment() 657 658 try: 659 be_names = getopt.getopt(opts, "")[1] 660 except getopt.GetoptError: 661 msg.printMsg(msg.Msgs.BEADM_ERR_OPT_ARGS, None, -1) 662 usage() 663 664 if len(be_names) != 2: 665 msg.printMsg(msg.Msgs.BEADM_ERR_OPT_ARGS, None, -1) 666 usage() 667 668 if lb.beVerifyBEName(be_names[0]) != 0: 669 msg.printMsg(msg.Msgs.BEADM_ERR_BENAME, None, -1) 670 return 1 671 672 if lb.beVerifyBEName(be_names[1]) != 0: 673 msg.printMsg(msg.Msgs.BEADM_ERR_BENAME, None, -1) 674 return 1 675 676 rc = lb.beRename(be_names[0], be_names[1]) 677 678 if rc == 0: 679 return 0 680 681 be.msg_buf["0"] = be_names[0] 682 if rc == msg.Msgs.BE_ERR_BE_NOENT: 683 be.msg_buf["1"] = \ 684 msg.getMsg(msg.Msgs.BEADM_ERR_BE_DOES_NOT_EXIST, 685 be_names[0]) 686 elif rc == msg.Msgs.BE_ERR_PERM or rc == msg.Msgs.BE_ERR_ACCESS: 687 be.msg_buf["1"] = msg.getMsg(msg.Msgs.BEADM_ERR_PERMISSIONS, rc) 688 msg.printMsg(msg.Msgs.BEADM_ERR_RENAME, be.msg_buf, -1) 689 return 1 690 else: 691 be.msg_buf["1"] = lb.beGetErrDesc(rc) 692 if be.msg_buf["1"] == None: 693 be.msg_buf["1"] = \ 694 msg.getMsg(msg.Msgs.BEADM_ERR_NO_MSG, rc) 695 696 msg.printMsg(msg.Msgs.BEADM_ERR_RENAME, be.msg_buf, -1) 697 return 1 698 699#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 700# End of CLI public functions 701#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 702 703#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 704# Verify the options and arguments for the beadm create subcommand 705 706def verifyCreateOptionsArgs(be): 707 """Check valid BE names.""" 708 709 len_be_args = len(be.trgt_be_name_or_snapshot) 710 if len_be_args < 1: 711 msg.printMsg(msg.Msgs.BEADM_ERR_OPT_ARGS, None, -1) 712 return 1 713 if len_be_args > 1: 714 msg.printMsg(msg.Msgs.BEADM_ERR_OPT_ARGS, None, -1) 715 idx = 0 716 while len_be_args > idx: 717 msg.printMsg(msg.Msgs.BEADM_MSG_FREE_FORMAT, 718 be.trgt_be_name_or_snapshot[idx], -1) 719 idx += 1 720 return 1 721 722 return 0 723 724#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 725def parseCLI(cli_opts_args): 726 """Parse command line interface arguments.""" 727 728 gettext.install("beadm", "/usr/lib/locale") 729 730 if len(cli_opts_args) == 0: 731 usage() 732 733 subcommand = cli_opts_args[0] 734 opts_args = cli_opts_args[1:] 735 736 if subcommand == "activate": 737 rc = activate(opts_args) 738 elif subcommand == "create": 739 rc = create(opts_args) 740 elif subcommand == "destroy": 741 rc = destroy(opts_args) 742 elif subcommand == "list": 743 rc = list(opts_args) 744 elif subcommand == "mount": 745 rc = mount(opts_args) 746 elif subcommand == "rename": 747 rc = rename(opts_args) 748 elif subcommand == "upgrade": 749 rc = upgrade(opts_args) 750 elif subcommand == "unmount" or \ 751 subcommand == "umount": #aliased for convenience 752 rc = unmount(opts_args) 753 elif subcommand == "verify": 754 rc = verify() 755 else: 756 msg.printMsg(msg.Msgs.BEADM_ERR_ILL_SUBCOMMAND, 757 subcommand, -1) 758 usage() 759 760 return(rc) 761 762#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 763def main(): 764 """main function.""" 765 766 gettext.install("beadm", "/usr/lib/locale") 767 768 if not isBeadmSupported(): 769 return(1) 770 771 return(parseCLI(sys.argv[1:])) 772 773#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 774def initBELog(log_id, be): 775 """ 776 Initiate the BE log 777 778 Format of the log 779 yyyymmdd_hhmmss - 20071130_140558 780 yy - year; 2007 781 mm - month; 11 782 dd - day; 30 783 hh - hour; 14 784 mm - minute; 05 785 ss - second; 58 786 """ 787 788 # /var/log/beadm/<beName>/<logId>.log.<yyyymmdd_hhmmss> 789 790 date = time.strftime("%Y%m%d_%H%M%S", time.localtime()) 791 792 be.log = "/var/log/beadm/" + be.trgt_be_name_or_snapshot[0] + \ 793 "/" + log_id + ".log" + "." + date 794 795 if not os.path.isfile(be.log) and not os.path.islink(be.log): 796 if not os.path.isdir(os.path.dirname(be.log)): 797 try: 798 os.makedirs(os.path.dirname(be.log), 0644) 799 except OSError: 800 be.msg_buf["0"] = be.trgt_be_name_or_snapshot[0] 801 be.msg_buf["1"] = \ 802 msg.getMsg(msg.Msgs.BEADM_ERR_PERMISSIONS, 803 0) 804 msg.printMsg(msg.Msgs.BEADM_ERR_CREATE, 805 be.msg_buf, -1) 806 return 1 807 try: 808 be.log_id = open(be.log, "a") 809 except IOError: 810 msg.printMsg(msg.Msgs.BEADM_ERR_LOG_CREATE, 811 None, -1) 812 return 1 813 else: 814 # Should never happen due to new time stamp each call 815 msg.printMsg(msg.Msgs.BEADM_ERR_LOG_CREATE, None, -1) 816 return 1 817 818 return 0 819 820#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 821def cleanupBELog(be): 822 """Clean up BE log.""" 823 824 be.log_id.close() 825 826#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 827def displayDestructionQuestion(be): 828 """Display a destruction question and wait for user response.""" 829 830 msg.printMsg(msg.Msgs.BEADM_MSG_DESTROY, be.trgt_be_name_or_snapshot[0], -1) 831 while True: 832 try: 833 value = raw_input().strip().upper() 834 except KeyboardInterrupt: 835 return False 836 if (value == 'Y' or value == 'YES'): 837 return True 838 elif len(value) == 0 or value == 'N' or value == 'NO': 839 msg.printMsg(msg.Msgs.BEADM_MSG_DESTROY_NO, 840 be.trgt_be_name_or_snapshot[0], -1) 841 return False 842 else: 843 msg.printMsg(msg.Msgs.BEADM_ERR_INVALID_RESPONSE, 844 -1) 845 846#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 847def setMaxColumnWidths(be_max_w, ds_max_w, ss_max_w, be_list): 848 """Figure out max column widths for BE's, Datasets and Snapshots.""" 849 850 for be_item in be_list: 851 if be_item.get("orig_be_name") is not None: 852 determineMaxBEColWidth(be_item, be_max_w) 853 if be_item.get("dataset") is not None: 854 determineMaxDSColWidth(be_item, ds_max_w) 855 if be_item.get("snap_name") is not None: 856 determineMaxSSColWidth(be_item, ss_max_w) 857 858#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 859def getActiveBEAndActiveOnBootBE(): 860 """Return the 'active on boot' BE, the 'active' BE or None.""" 861 862 active_be = None 863 active_be_on_boot = None 864 865 rc, be_list = lb.beList() 866 867 if rc != 0: 868 if rc == msg.Msgs.BE_ERR_BE_NOENT: 869 string = \ 870 msg.getMsg(msg.Msgs.BEADM_ERR_NO_BES_EXIST) 871 else: 872 string = lb.beGetErrDesc(rc) 873 if string == None: 874 string = \ 875 msg.getMsg(msg.Msgs.BEADM_ERR_NO_MSG, rc) 876 877 msg.printMsg(msg.Msgs.BEADM_ERR_LIST, string, -1) 878 return None 879 880 for be_vals in be_list: 881 srcBeName = be_vals.get("orig_be_name") 882 if be_vals.get("active"): 883 active_be = srcBeName 884 if be_vals.get("active_boot"): 885 active_be_on_boot = srcBeName 886 if active_be is not None and active_be_on_boot is not None: 887 break 888 889 return active_be, active_be_on_boot 890 891#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 892def createSnapshot(be): 893 """Create a snapshot.""" 894 895 be_name, snap_name = be.trgt_be_name_or_snapshot[0].split("@") 896 897 rc = lb.beCreateSnapshot(be_name, snap_name)[0] 898 899 if rc == 0: 900 return 0 901 902 be.msg_buf["0"] = be.trgt_be_name_or_snapshot[0] 903 if rc == msg.Msgs.BE_ERR_BE_NOENT: 904 be.msg_buf["1"] = \ 905 msg.getMsg(msg.Msgs.BEADM_ERR_BE_DOES_NOT_EXIST, 906 be_name) 907 elif rc == msg.Msgs.BE_ERR_SS_EXISTS: 908 be.msg_buf["1"] = msg.getMsg(msg.Msgs.BEADM_ERR_SNAP_EXISTS, 909 be.trgt_be_name_or_snapshot[0]) 910 elif rc == msg.Msgs.BE_ERR_PERM or rc == msg.Msgs.BE_ERR_ACCESS: 911 be.msg_buf["1"] = msg.getMsg(msg.Msgs.BEADM_ERR_PERMISSIONS, rc) 912 msg.printMsg(msg.Msgs.BEADM_ERR_CREATE, be.msg_buf, -1) 913 return 1 914 else: 915 be.msg_buf["1"] = lb.beGetErrDesc(rc) 916 if be.msg_buf["1"] == None: 917 be.msg_buf["1"] = \ 918 msg.getMsg(msg.Msgs.BEADM_ERR_NO_MSG, rc) 919 920 msg.printMsg(msg.Msgs.BEADM_ERR_CREATE, be.msg_buf, -1) 921 922 return 1 923 924#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 925def createBE(be): 926 """Create a Boot Environment.""" 927 928 rc = lb.beCopy(be.trgt_be_name_or_snapshot[0], be.src_be_name_or_snapshot, 929 None, be.trgt_rpool, be.properties, be.description)[0] 930 931 if rc == 0: 932 msg.printMsg(msg.Msgs.BEADM_MSG_BE_CREATE_SUCCESS, 933 be.trgt_be_name_or_snapshot[0], be.log_id) 934 return 0 935 936 be.msg_buf["0"] = be.trgt_be_name_or_snapshot[0] 937 if rc == msg.Msgs.BE_ERR_BE_NOENT: 938 be.msg_buf["1"] = \ 939 msg.getMsg(msg.Msgs.BEADM_ERR_BE_DOES_NOT_EXIST, 940 be.src_be_name_or_snapshot) 941 elif rc == msg.Msgs.BE_ERR_BE_EXISTS: 942 be.msg_buf["1"] = msg.getMsg(msg.Msgs.BEADM_ERR_BE_EXISTS, 943 be.trgt_be_name_or_snapshot[0]) 944 elif rc == msg.Msgs.BE_ERR_PERM or rc == msg.Msgs.BE_ERR_ACCESS: 945 be.msg_buf["1"] = msg.getMsg(msg.Msgs.BEADM_ERR_PERMISSIONS, rc) 946 msg.printMsg(msg.Msgs.BEADM_ERR_CREATE, be.msg_buf, -1) 947 return 1 948 else: 949 be.msg_buf["1"] = lb.beGetErrDesc(rc) 950 if be.msg_buf["1"] == None: 951 be.msg_buf["1"] = \ 952 msg.getMsg(msg.Msgs.BEADM_ERR_NO_MSG, rc) 953 954 msg.printMsg(msg.Msgs.BEADM_ERR_CREATE, be.msg_buf, be.log_id) 955 956 return 1 957 958#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 959def createBEFromSnapshot(be): 960 """Create a BE based off a snapshot.""" 961 962 be_name, snap_name = be.src_be_name_or_snapshot.split("@") 963 964 rc = lb.beCopy(be.trgt_be_name_or_snapshot[0], be_name, snap_name, 965 be.trgt_rpool, be.properties, be.description)[0] 966 967 if rc == 0: 968 msg.printMsg(msg.Msgs.BEADM_MSG_BE_CREATE_SUCCESS, 969 be.trgt_be_name_or_snapshot[0], be.log_id) 970 return 0 971 972 be.msg_buf["0"] = be.trgt_be_name_or_snapshot[0] 973 if rc == msg.Msgs.BE_ERR_SS_NOENT: 974 be.msg_buf["1"] = \ 975 msg.getMsg(msg.Msgs.BEADM_ERR_SNAP_DOES_NOT_EXISTS, 976 be.src_be_name_or_snapshot) 977 elif rc == msg.Msgs.BE_ERR_BE_EXISTS: 978 be.msg_buf["1"] = msg.getMsg(msg.Msgs.BEADM_ERR_BE_EXISTS, \ 979 be.trgt_be_name_or_snapshot[0]) 980 elif rc == msg.Msgs.BE_ERR_BE_NOENT: 981 be.msg_buf["1"] = \ 982 msg.getMsg(msg.Msgs.BEADM_ERR_BE_DOES_NOT_EXIST, \ 983 be_name) 984 elif rc == msg.Msgs.BE_ERR_PERM or rc == msg.Msgs.BE_ERR_ACCESS: 985 be.msg_buf["1"] = msg.getMsg(msg.Msgs.BEADM_ERR_PERMISSIONS, rc) 986 msg.printMsg(msg.Msgs.BEADM_ERR_CREATE, be.msg_buf, -1) 987 return 1 988 else: 989 be.msg_buf["1"] = lb.beGetErrDesc(rc) 990 if be.msg_buf["1"] == None: 991 be.msg_buf["1"] = \ 992 msg.getMsg(msg.Msgs.BEADM_ERR_NO_MSG, rc) 993 994 msg.printMsg(msg.Msgs.BEADM_ERR_CREATE, be.msg_buf, be.log_id) 995 996 return 1 997 998#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 999def activateBE(be): 1000 """ 1001 Activate a BE. Called from create() when -a is provided as CLI 1002 Option. 1003 """ 1004 1005 rc = lb.beActivate(be.trgt_be_name_or_snapshot[0]) 1006 if rc == 0: 1007 return 0 1008 1009 be.msg_buf["0"] = be.trgt_be_name_or_snapshot[0] 1010 if rc == msg.Msgs.BE_ERR_BE_NOENT: 1011 be.msg_buf["1"] = \ 1012 msg.getMsg(msg.Msgs.BEADM_ERR_BE_DOES_NOT_EXIST, opts[0]) 1013 else: 1014 be.msg_buf["1"] = lb.beGetErrDesc(rc) 1015 if be.msg_buf["1"] == None: 1016 be.msg_buf["1"] = \ 1017 msg.getMsg(msg.Msgs.BEADM_ERR_NO_MSG, rc) 1018 1019 msg.printMsg(msg.Msgs.BEADM_ERR_ACTIVATE, be.msg_buf, -1) 1020 1021 return 1 1022 1023#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1024def isBeadmSupported(): 1025 """ 1026 Currently the only environment that beadm is supported in is 1027 a global zone. Check that beadm is executing in a 1028 global zone and not in a non-global zone. 1029 """ 1030 1031 try: 1032 proc = subprocess.Popen("/sbin/zonename", 1033 stdout = subprocess.PIPE, 1034 stderr = subprocess.STDOUT) 1035 # Grab stdout. 1036 zonename = proc.communicate()[0].rstrip('\n') 1037 except OSError, (errno, strerror): 1038 msg.printMsg(msg.Msgs.BEADM_ERR_OS, strerror, -1) 1039 # Ignore a failed attempt to retreive the zonename. 1040 return True 1041 1042 if zonename != "global": 1043 msg.printMsg(msg.Msgs.BEADM_ERR_NOT_SUPPORTED_NGZ, None, -1) 1044 return False 1045 1046 return True 1047 1048 1049#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1050if __name__ == "__main__": 1051 try: 1052 RC = main() 1053 except SystemExit, e: 1054 raise e 1055 except: 1056 traceback.print_exc() 1057 sys.exit(99) 1058 sys.exit(RC) 1059