diff usr/src/cmd/beadm/BootEnvironment.py @ 13025:3c7681e3e323

PSARC 2010/059 SNAP BE Management 6964804 SNAP BE management into ON 6971379 libbe should capture and give useful error when installgrub or ict.py fails. 6971390 beadm does not support labeled brand zones 6971394 BEADM_ERR_BE_DOES_NOT_EXIST has an extra space 6971397 libbe error messages need internationalization 6971402 Remove be_get_last_zone_be_callback 6971409 be_create_menu returns errors from both be_errno_t and errno sets
author Glenn Lagasse <glenn.lagasse@oracle.com>
date Wed, 04 Aug 2010 12:28:19 -0700
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/beadm/BootEnvironment.py	Wed Aug 04 12:28:19 2010 -0700
@@ -0,0 +1,444 @@
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+"""Boot Environment classes used by beadm."""
+
+import datetime
+
+class BootEnvironment:
+    """Boot Environment object that is used by beadm to manage command line
+    options, arguments and the log."""
+
+    def __init__(self):
+        self.trgt_rpool = None
+        self.trgt_be_name_or_snapshot = None
+        self.src_be_name_or_snapshot = None
+        self.properties = {}
+        self.log_id = None
+        self.log = None
+        self.msg_buf = {}
+        self.description = None
+
+class listBootEnvironment:
+    """Base class for beadm list
+    Determine the BE's to display. Prints command output according to option:
+    -d - dataset
+    -s - snapshot
+    -a - all (both dataset and snapshot)
+    <none> - only BE information
+    The -H option produces condensed, parseable output
+        The ';' delimits each field in the output.  BEs with multiple
+        datasets will have multiple lines in the output.
+    """
+
+    def list(self, be_list, ddh, be_name):
+        """ print all output for beadm list command
+        be_list - list of all BEs
+        ddh - if True, Do not Display Headers - just parseable data
+        be_name - user-specified BE, if any
+
+        returns 0 for success
+        side effect: beadm list output printed to stdout
+        """
+
+        #If we're listing Headers, initialize the array holding the
+        #column widths with the header widths themselves.  Later on,
+        #the data in this array will get adjusted as we process actual
+        #row data and find that a piece of data is wider than its
+        #column header.
+        bemaxout = [0 for i in range(len(self.hdr[0]))]
+        if not ddh:
+            #iterate all header rows since their fields may not
+            #be of equal length.
+            for header in self.hdr:
+                icol = 0
+                for hc in header:
+                    if len(hc) + 1 > bemaxout[icol]:
+                        bemaxout[icol] = len(hc) + 1
+                    icol += 1
+
+        #collect BEs
+        beout = {}     #matrix of output text [row][attribute]
+        beoutname = {} #list of BE names [row]
+        be_space = {}  #space used totals for BE [BE name]['space_used','ibei']
+        ibe = 0        #BE index
+        spacecol = -1  #to contain column where space used is displayed
+        for be in be_list:
+            if 'orig_be_name' in be:
+                cur_be = be['orig_be_name']
+                cur_be_obj = be
+
+            #if BE name specified, collect matching BEs
+            if be_name is not None and not self.beMatch(be, be_name): 
+                continue
+            attrs = ()
+            #identify BE|dataset|snapshot attributes
+            att = ''
+            for att in ('orig_be_name', 'dataset', 'snap_name'):
+                if att in be and att in self.lattrs:
+                    attrs = self.lattrs[att]
+                    if att == 'orig_be_name':
+                        be_space[cur_be] = {}
+                        be_space[cur_be]['space_used'] = 0
+                        be_space[cur_be]['ibe'] = ibe
+                        if not ddh and len(cur_be) + 1 > bemaxout[0]:
+                            bemaxout[0] = len(cur_be) + 1
+                    break
+            beout[ibe] = {}
+            beoutname[ibe] = cur_be
+
+            icol = 0 #first column
+            for at in attrs:
+                #for option -s, withhold subordinate datasets
+                if self.__class__.__name__ == 'SnapshotList' and \
+                    att == 'snap_name' and  'snap_name' in be and \
+                    '/' in be[att]:
+                    break
+                #convert output to readable format and save
+                save = self.getAttr(at, be, ddh, cur_be_obj)
+                beout[ibe][at] = save
+                #maintain maximum column widths
+                if not ddh and len(save) + 1 > bemaxout[icol]:
+                    bemaxout[icol] = len(save) + 1
+                #sum all snapshots for BE
+                if at == 'space_used' and 'space_used' in be:
+                    spacecol = icol
+                icol += 1 #next column
+            ibe += 1
+            if 'space_used' in be:
+                #sum all snapshots and datasets for BE in 'beadm list'
+                if isinstance(self, BEList):
+                    be_space[cur_be]['space_used'] += be.get('space_used')
+                elif cur_be in be_space and \
+                    ('space_used' not in be_space[cur_be] or 
+                        be_space[cur_be]['space_used'] == 0):
+                    #list space used separately for other options
+                    be_space[cur_be]['space_used'] = be.get('space_used')
+
+        #output format total lengths for each BE with any snapshots
+        for cur_be in be_space:
+            save = self.getSpaceValue(be_space[cur_be]['space_used'], ddh)
+            ibe = be_space[cur_be]['ibe']
+            beout[ibe]['space_used'] = save
+            #expand column if widest column entry
+            if (spacecol != -1) and \
+               (not ddh and len(save) + 1 > bemaxout[spacecol]):
+                bemaxout[spacecol] = len(save) + 1
+
+        #print headers in columns
+        if not ddh:
+            for header in self.hdr:
+                outstr = ''
+                for icol in range(len(header)):
+                    outstr += header[icol].ljust(bemaxout[icol])
+                if outstr != '': 
+                    print outstr
+
+        #print collected output in columns
+        outstr = ''
+        prev_be = None
+        cur_be = None
+        for ibe in beout: #index output matrix
+            if beoutname[ibe] != None: 
+                cur_be = beoutname[ibe]
+            #find attributes for BE type
+            curtype = None
+            for att in ('orig_be_name', 'dataset', 'snap_name'):
+                if att in beout[ibe]:
+                    attrs = self.lattrs[att]
+                    curtype = att
+                    break
+
+            if curtype == None: #default to BE
+                curtype = 'orig_be_name'
+                if 'orig_be_name' in self.lattrs:
+                    attrs = self.lattrs['orig_be_name']
+                else: attrs = ()
+
+            if not ddh:
+                if prev_be != cur_be and cur_be != None:
+                    #for -d,-s,-a, print BE alone on line
+                    if self.__class__.__name__ != 'BEList':
+                        print cur_be
+                    prev_be = cur_be
+
+            #print for one BE/snapshot/dataset
+            icol = 0 #first column
+
+            #if this is a 'dataset' or 'snap_name', start line with BE 
+            #name token
+            if ddh and curtype != 'orig_be_name':
+                outstr = cur_be
+
+            for at in attrs: #for each attribute specified in table
+                if ddh: #add separators for parsing
+                    if outstr != '': 
+                        outstr += ';' #attribute separator
+                    if at in beout[ibe] and beout[ibe][at] != '-' and \
+                        beout[ibe][at] != '':
+                        outstr += beout[ibe][at]
+                else: #append text justified in column
+                    if at in beout[ibe]:
+                        outstr += beout[ibe][at].ljust(bemaxout[icol])
+                icol += 1 #next column
+
+            if outstr != '': 
+                print outstr
+            outstr = ''
+
+        return 0
+
+    def beMatch(self, be, be_name):
+        """find match on user-specified BE."""
+
+        if 'orig_be_name' in be:
+            return be.get('orig_be_name') == be_name
+        if 'dataset' in be:
+            if be.get('dataset') == be_name: 
+                return True
+            out = be.get('dataset').split("/")
+            return out[0] == be_name
+        if 'snap_name' in be:
+            if be.get('snap_name') == be_name: 
+                return True
+            out = be.get('snap_name').split('@')
+            if out[0] == be_name: 
+                return True
+            out = be.get('snap_name').split('/')
+            return out[0] == be_name
+        return False
+
+    def getAttr(self, at, be, ddh, beobj):
+        """
+        Extract information by attribute and format for printing
+        returns '?' if normally present attribute not found - error.
+        """
+        if at == 'blank': 
+            return ' '
+        if at == 'dash': 
+            return '-'
+        if at == 'orig_be_name':
+            if at not in be: 
+                return '-'
+            ret = be[at]
+            if ddh or self.__class__.__name__ == 'BEList':
+                return ret
+            return '   ' + ret #indent
+        if at == 'snap_name':
+            if at not in be: 
+                return '-'
+            if self.__class__.__name__ == 'CompleteList':
+                ret = self.prependRootDS(be[at], beobj)
+            else: 
+                ret = be[at]
+            if ddh: 
+                return ret
+            return '   ' + ret #indent
+        if at == 'dataset':
+            if at not in be: 
+                return '-'
+            if self.__class__.__name__ == 'DatasetList' or \
+               self.__class__.__name__ == 'CompleteList':
+                ret = self.prependRootDS(be[at], beobj)
+            else: 
+                ret = be[at]
+            if ddh: 
+                return ret
+            return '   ' + ret #indent
+        if at == 'active':
+            if at not in be: 
+                return '-'
+            ret = ''
+            if 'active' in be and be['active']: 
+                ret += 'N'
+            if 'active_boot' in be and be['active_boot']: 
+                ret += 'R'
+            if ret == '': 
+                return '-'
+            return ret
+        if at == 'mountpoint':
+            if at not in be: 
+                return '-'
+            if 'mounted' not in be or not be['mounted']: 
+                return '-'
+            return be[at]
+        if at == 'space_used':
+            if at not in be: 
+                return '0'
+            return self.getSpaceValue(be[at], ddh)
+        if at == 'mounted':
+            if at not in be: 
+                return '-'
+            return be[at]
+        if at == 'date':
+            if at not in be: 
+                return '?'
+            if ddh: 
+                return str(be[at]) #timestamp in seconds
+            sec = str(datetime.datetime.fromtimestamp(be[at]))
+            return sec[0:len(sec)-3] #trim seconds
+        if at == 'policy':
+            if at not in be: 
+                return '?'
+            return be[at]
+        if at == 'root_ds':
+            if at not in be: 
+                return '?'
+            if ddh or self.__class__.__name__ == 'BEList':
+                return be[at]
+            return '   ' + be[at]
+        if at == 'uuid_str':
+            if at not in be: 
+                return '-'
+            return be[at]
+        #default case - no match on attribute
+        return be[at]
+
+    def getSpaceValue(self, num, ddh):
+        """Readable formatting for disk space size."""
+
+        if ddh: 
+            return str(num) #return size in bytes as string
+
+        kilo = 1024.0
+        mega = 1048576.0
+        giga = 1073741824.0
+        tera = 1099511627776.0
+
+        if num == None: 
+            return '0'
+        if num < kilo: 
+            return str(num) + 'B'
+        if num < mega: 
+            return str('%.1f' % (num / kilo)) + 'K'
+        if num < giga: 
+            return str('%.2f' % (num / mega)) + 'M'
+        if num < tera: 
+            return str('%.2f' % (num / giga)) + 'G'
+        return str('%.2f' % (num / tera)) + 'T'
+
+    def prependRootDS(self, val, beobj):
+        """Prepend root dataset name with BE name stripped."""
+
+        root_ds = beobj.get('root_ds')
+        return root_ds[0:root_ds.rfind('/')+1] + val
+
+
+"""Top level "beadm list" derived classes defined here.
+        Only table definition is done here - all methods are in the base class.
+        Tables driving list:
+                hdr - list of text to output for each column
+                lattrs - dictionary of attributes
+                        Each entry specifies either BE, dataset, snapshot with
+                        an attribute key:
+                                orig_be_name - for BEs
+                                dataset - for datasets
+                                snap_name - for snapshots
+                        Each list item in entry indicates specific datum for 
+                        column
+                Number of hdr columns must equal number of lattrs entries
+                        unless ddh (dontDisplayHeaders) is true.
+"""
+class BEList(listBootEnvironment):
+    """specify header and attribute information for BE-only output"""
+
+    def __init__(self, ddh):
+        """Init function for the class."""
+        self.hdr = \
+            ('BE','Active','Mountpoint','Space','Policy','Created'), \
+            ('--','------','----------','-----','------','-------')
+        if ddh:
+            self.lattrs = {'orig_be_name':('orig_be_name', 'uuid_str', 
+                           'active', 'mountpoint', 'space_used', 'policy', 
+                           'date')}
+        else:
+            self.lattrs = {'orig_be_name':('orig_be_name', 'active', 
+                           'mountpoint', 'space_used', 'policy', 'date')}
+
+class DatasetList(listBootEnvironment):
+    """
+    specify header and attribute information for dataset output,
+    -d option
+    """
+    def __init__(self, ddh):
+        """Init function for the class."""
+
+        self.hdr = \
+            ('BE/Dataset','Active','Mountpoint','Space','Policy','Created'), \
+            ('----------','------','----------','-----','------','-------')
+        if ddh:
+            self.lattrs = { \
+                'orig_be_name':('orig_be_name', 'root_ds', 'active', 
+                'mountpoint', 'space_used', 'policy', 'date'), \
+                'dataset':('dataset', 'dash', 'mountpoint', 'space_used', 
+                'policy', 'date')}
+        else:
+            self.lattrs = { \
+                'orig_be_name':('root_ds', 'active', 'mountpoint', 
+                'space_used', 'policy', 'date'), \
+                'dataset':('dataset', 'dash', 'mountpoint', 'space_used', 
+                'policy', 'date')}
+
+class SnapshotList(listBootEnvironment):
+    """
+    specify header and attribute information for snapshot output,
+    -s option
+    """
+    def __init__(self, ddh):
+        """Init function for the class."""
+
+        self.hdr = \
+            ('BE/Snapshot','Space','Policy','Created'), \
+            ('-----------','-----','------','-------')
+        self.lattrs = {'snap_name':('snap_name', 'space_used', 'policy',
+                                    'date')}
+
+class CompleteList(listBootEnvironment):
+    """
+    specify header and attribute information for BE and/or dataset and/or
+    snapshot output,
+    -a or -ds options 
+    """
+    def __init__(self, ddh):
+        """Init function for the class."""
+
+        self.hdr = \
+    ('BE/Dataset/Snapshot','Active','Mountpoint','Space','Policy','Created'), \
+    ('-------------------','------','----------','-----','------','-------')
+        if ddh:
+            self.lattrs = { \
+                'orig_be_name':('orig_be_name', 'root_ds', 'active',
+                'mountpoint', 'space_used', 'policy', 'date'),
+                'dataset':('dataset', 'dash', 'mountpoint', 'space_used',
+                'policy', 'date'),
+                'snap_name':('snap_name', 'dash', 'dash', 'space_used',
+                'policy', 'date')}
+        else:
+            self.lattrs = { \
+                'orig_be_name':('root_ds', 'active', 'mountpoint',
+                                'space_used', 'policy', 'date'), \
+                'dataset':('dataset', 'dash', 'mountpoint', 'space_used',
+                'policy', 'date'),
+                'snap_name':('snap_name', 'dash', 'dash', 'space_used',
+                'policy', 'date')}