xref: /onnv-gate/usr/src/cmd/beadm/beadm.py (revision 13013:3c7681e3e323)
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