[PATCH 1/8] adppre.py

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



$Id: patch1.in /main/4 2009/07/10 00:35:49 alfords Exp $
Copyright 2009 Automatic Data Processing, Inc.

See the README file in the first patch of this series for
important details.

adppre.py is a Python script.  It queries the system through the anaconda
libraries, queries the user, and determines partition information, including
"mass storage" disks.  It outputs the partition information in a file.  That
file is later included by our kickstart scripts.  adppre.py lets ADP customize
disk partitioning based on what's on a particular system.  See adppre.py
itself for more details.

I'm not sure that adppre.py is useful to anyone but ADP.  It does illustrate
using the snack widgets.  It's LGPL because it was originally based, way back
when, on snack widget code which RedHat provided which was LGPL.

--Seth Alford
ADP Dealer Services
seth_alford@xxxxxxx

----adppre.py follows----
#!/usr/bin/python
# $Id: adppre.py /main/79 2009/07/01 21:51:47 alfords Exp $
#
# Copyright (c) 2001-2009 Automatic Data Processing Inc.  Since this code
# is based on code which we originally obtained from RedHat under the GNU
# library public license, this code may be freely redistributed under the
# terms of the GNU library public license.
#
# You should have received a copy of the GNU Library Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
# We are publishing this software as part of fulfilling our requirements
# under the GPL.  This software is offered with NO WARRANTY and NO SUPPORT
# of any kind.
#
# This script examines:
# 
#   * the hard drive dictionary defined in the isys module,
#   * the /var/log/dmesg file, 
#   * and /proc/scsi.  
# 
# Based on what it finds, and the flags with which it was invoked, the
# script may ask the user questions about:
# 
#   * how much additional swap space to reserve
#   * which disk(s) to reserve for mass storage
# 
# "Mass storage" is an ADP-specific term which describes a disk with a
# single partition that takes up the entire disk.  Having disk storage
# organized in this way is useful because, among other uses, it allows ADP
# or ADP clients a means to have some disks backed up on a different
# schedule, or have some disk storage be included in a SAN in a data
# center, etc.
# 
# Note that a mass storage "disk" can be a logical disk which is actually
# part of a hardware RAID array.
#
# There can be more than one mass storage disk.  Mass storage disks appear
# in the filesystem under /msN, where N is an integer.  The user must leave
# at least one disk as non-mass storage so that there will be a place to
# put /, /usr, /var, and /adp.  If there is only one disk on the system,
# or, if hardware RAID makes multiple disks appear as one disk, then the
# user cannot allocate any disks as mass storage.
# 
# One possible way to use mass storage disks is to save time during a
# system restore.  If the mass storage disks survived a system crash, then
# they do not need to be restored from tape.  To accommodate re-using mass
# storage or virtual server disks, the user can specify that the disk will
# not be "initialized" (ADP speak for having mke2fs run on it.)  However,
# this introduces the possibility for conflicting mass storage mount
# points, e.g., two /ms3's, if the user tries to initialize a new disk and
# then preserve an existing disk.  MSD attempts to detect and prevent these
# situations.
# 
# The script outputs a file for inclusion in the kickstart pre script.  The
# file contains kickstart directives which encapsulate the user's answers.
# 
# This script uses several standard python modules as well as some of the
# anaconda and the snack modules.  The comments in this file assume you
# know something about anaconda, snack and/or newt.
# 
# I could have patched this script into anaconda itself.  By making it a
# separate script, though, I can debug it on a system in multi-user mode.
# 
# Originally, this file was created with tabstops=4.  Later, I used
# "expand -t4" to replace tabs with spaces.
# 

# Stolen from the latest anaconda main script, March, 2009:
# Set up logging as early as possible.
import logging
from anaconda_log import logger, logLevelMap

log = logging.getLogger("anaconda")
stdoutLog = logging.getLogger("anaconda.stdout")

import optparse
import isys
from snack import *

# I am unsure if all these imports need to be present, --setha 3/4/2009
import re
import sys
import time
import parted
import partedUtils
import os
import copy
import string

# Prompts user for possible additional swap space.
#
# Parameters are the amount of memory detected on the system and
# the swap currently allocated.
#
def askAboutSwap(screen, curr_mem, curr_swap):
    swap_string = (
        "Detected memory is " +
        str(curr_mem) + " MB.  " +
        "Default swap allocation is " +
        str(curr_swap) + " MB.  ")
    ok_string = "Is this OK?"
    rc = ButtonChoiceWindow(screen, 'Swap Allocation',
        swap_string + ok_string,
        buttons = [ ("Yes"), ("No") ],
        width = 65)
    if( rc == "yes" ):
        return curr_swap

    swap_string += ('Please input the total swap space you want.  ' +
        'You might want more swap on a system with less memory.  ' +
        'You might want less swap on a system with more memory.  ' +
        '\n\nNote: in previous versions of this installer, ' +
        'you entered ADDITIONAL swap.  Now, please just enter ' +
        'the TOTAL swap that you want.')

    text = TextboxReflowed( 50, swap_string )

    total_swap = 0
    rc = ""

    entry = Entry(6, "")
    blankLabel = Label("          ")
    grid = GridForm( screen, "Swap Allocation", 1, 5)
    grid.add(text, 0, 0)
    grid.add(blankLabel, 0, 1)
    grid.add(entry, 0, 2)
    grid.add(blankLabel, 0, 3)
    grid.add(Button("Ok"), 0, 4)
    done = 0

    # Keep looping until they say "Ok" and they give us a reasonable
    # value.
    while( done == 0 ):
        done = 1
        entry.set("")
        grid.run()
        try:
            total_swap = int(entry.value())
        except:
            done = 0
            continue
        if( total_swap < 0 ):
            done = 0
            total_swap = 0
            continue

        rc = ButtonChoiceWindow( screen, 'Total Swap',
            'Total swap space is now ' + str(total_swap) + ' MB.',
            buttons = [ ("Ok"), ("Back")] )
        if( rc == "back" ):
            done = 0

    return total_swap

# Puts up a default "Please wait..." window in case all the other windows
# disappear.  Usually will hide under the other windows.  Caller initializes
# SnackScreen.
def pleaseWait(screen):
    l = Label("Please wait...")
    grid = GridForm(screen, "", 2,3)
    grid.add(l, 0, 1)
    grid.draw()

# 
# Fabricates a C number (which will be overwritten later on in the install)
#

def getCNumber(screen):
    try:
        hostname_file = open("/tmp/staging", "w")
    except:
        print "Internal error: could not open /tmp/staging for writing, giving up"
        sys.exit(-1)
    hostname_file.write("hostname=C000000\n")
    hostname_file.close()
    return

#
# getDSD creates the disk size dictionary.  This is a modified version
# of /usr/lib/anaconda/list-harddrives-stub

def getDSD(hdd):

    dsd={}

    for drive in hdd.keys():
        # Insert default value for dsd entry
        sizeGb_string="N/A"
        cyl = 0
        dsd[drive] =  ( sizeGb_string, cyl )
 
        if not isys.mediaPresent(drive):
            continue
     
        # try to open and get size
        skip = 0
        deviceFile =  "/tmp/%s" % (drive,)
        isys.makeDevInode(drive, deviceFile)
        try:
            dev = parted.PedDevice.get(deviceFile)
        except:
            skip = 1
        os.remove(deviceFile)
     
        if skip:
            pass    # dictionary already has default values
        else: 
            sizeGb = (float(dev.heads * dev.cylinders * dev.sectors) / (1024 * 1024 * 1024) * dev.sector_size)
            sizeGb_string=("%4.0f") % ( sizeGb )
            cyl = dev.cylinders
        dsd[drive] =  ( sizeGb_string, cyl )
    return dsd



class MSD:

    # The MSD class manages snack widgets.  The user interacts with the
    # widgets to determine which disks will become mass storage disks.
    # Formerly, MSD also allowed the user to choose a disk to allocate to a
    # virtual machine.  That feature was dropped since we no longer support
    # on-site VM's.  Hypothetical future on-site VM support will not be
    # tied to a separate disk.  I tried to delete references to virtual
    # server disks, VM disks, or virtual machines.
    #

    # makeLabelString is a helper function for init.  Here are the
    # parameters and what it does with them:
    #
    # cols          number of columns in the table which are displayed.
    #               Columns greater than cols are not displayed.
    # col_widths    list of column widths, should have cols entries
    # col_labels    list of labels for those columns, should have cols
    #               entries
    # col_pad       number of spaces to pad between columns.  This is just
    #               an integer, not an array.
    #
    # makeLabelString assembles the col_labels into a string with the
    # proper spacing, based on col_widths and col_pad.  Returns that
    # string.  The string is suitable for use as a header above the
    # CListBox widget.

    def makeLabelString(self, cols, col_widths, col_labels, col_pad):
        i = 0
        the_label=""
        while i < cols:
            this_width=col_widths[i] + col_pad
            this_string="%-*.*s" % (col_widths[i]+col_pad, col_widths[i], col_labels[i])
            the_label=the_label + this_string
            i += 1
        return the_label


    # MSD init asks the user whether to reserve any of the disks for mass
    # storage.  The caller is responsible for retrieving the isys.HardDrive
    # dictionary and passing it in the hdd parameter.  The caller is
    # responsible for calling getDSD, which creates the disk size
    # dictionary, which gets passed in as dsd.  getMSD assumes that the
    # caller has determined that there is more than one disk.
    # 
    # MSD init creates the self.msd dictionary.  The new dictionary uses
    # disk drives as keys.  The values of the corresponding dictionary
    # entries are lists.  The lists contain:
    #
    # Index:    Description:
    # 0         "[M]" if disk is allocated for mass storage
    #           "[ ]" if disk is not allocated for mass storage
    # 1         "[*]" disk should be initialized
    #           "[ ]" disk should not be initialized (but note that
    #           all non-mass storage disks will be initialized anyway.)
    # 2         Device name.  E.g., "sda".  Redundant with the key for the
    #           row.  Has to be duplicated so that the information can be
    #           displayed in CListbox. 
    # 3         Size, in Gbyte.
    # 4         abbreviated disk controller information
    # 5         abbreviated existing labels on the disk
    # 6         New mount point, only filled in for mass storage disks.
    # 7         Full controller information(a)
    # 8         the label dictionary for this disk.(a)
    #
    # (a) only displayed in the Info sub-screen.
    #
    # MSD init is probably far too large.
    #
    # Note also that MSD init, and class MSD in general, does not support
    # migrating disks from one system to another.  That is, disks can be
    # initialized, or continue to be used, on the same system.  But MSD
    # does not support randomly moving mass storage between one system and
    # another.  Doing so would potentially mean renaming mass storage
    # partitions to eliminate conflicting mount points.
    #

    def __init__(self, screen, hdd, dsd):
        self.screen=screen
        self.msd = {}
        rc = ButtonChoiceWindow(self.screen, 'Multiple Disks Detected',
                'Note: if you were not expecting to see ' +
                'multiple disks on this system, maybe someone ' +
                'left a thumb drive (also known as a flash drive, '+
                'memory stick, or USB drive) or other removable ' +
                'disk installed.  In that case, please remove ' +
                'the extra drive(s) and/or media, reset the ' +
                'system, and try again.' +
                '\n\nDo you want to reserve any disk(s) for Mass Storage?  ',
                buttons = [ ("Yes"), ("No") ],
                width = 65)

        # If they do not want any mass storage, then just return an empty
        # msd dictionary
        if ( rc == "no" ):
            return
            #NOTREACHED
    
        #ASSERT: rc=="yes"
    
        # (Re-)create a new grid
        try:
            del grid
        except:
            pass
    
        # grid is a snack GridFormHelp widget.  grid will hold other
        # widgets, including a CListbox, labels, buttons.  Most of the user
        # interaction with MSD will be through grid.
        grid = GridFormHelp(self.screen, "Choose Mass Storage Disks", "", 60, 40)
    
        # Set up various constants used in the display
        col_widths=[5,4,4,4,25,11,9]
        col_pad=1   # 2 originally
        width=75
        cols=7
        row_align=[LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT]
    
        # Set up a line of text which tells the user what to do
        t = TextboxReflowed(width-5, "Please choose which disks, if any, you want to use as mass storage (M) devices.  WARNING: ALL DISKS WHICH ARE NOT MASS STORAGE WILL BE INITIALIZED, INCLUDING THUMB DRIVES OR REMOVABLE MEDIA.")
        grid.add(t, 0, 0, (0,0,0,1))
    
        # Use makeLabelString multiple times to create a multi-line header
        # for the CListbox.
        the_label_string = self.makeLabelString(cols=cols,
            col_widths=col_widths, col_labels=[('MS'),
            ('Init'),
            ('Disk'),
            ('Size'),
            ('Description'),
            ('All'),
            ('New MS')],
            col_pad=col_pad)
        l=Label(the_label_string)
        grid.add(l, 0, 1, (0,0,0,0), anchorLeft=1)
    
        the_label_string = self.makeLabelString(cols=cols,
            col_widths=col_widths, col_labels=[('Dev?'),
            (' ?'),
            ('Dev'),
            (' Gb'),
            (''),
            ('existing'),
            ('labels') ],
            col_pad=col_pad)
        l=Label(the_label_string)
        grid.add(l, 0, 2, (0,0,0,0), anchorLeft=1)
    
        # Finally, set up the CListbox itself.  Use empty strings for
        # column labels that are only 2 rows long and which are already
        # displayed in the previous rows. 
        clb = CListbox(height=7,cols=7, 
            col_widths=col_widths,
            scroll=1, returnExit = 1,
            width=width,
            col_pad=col_pad,
            col_labels=[(''),
            (''),
            ('Name'),
            (''),
            (''),
            ('labels'),
            ('only')],
            col_label_align=row_align)
        
        # Retrieve the disk partition label information
    
        ds = partedUtils.DiskSet()
        ds.refreshDevices()
    
        # Set up the initial row information to display in the CListbox.
        # Copy it from the various dictionaries.  At the same time, also
        # initialize entries in the msd that will not be displayed in
        # CListbox.

        for i in hdd.keys():
            self.checkbox = r"[ ]"
            if dsd[i][0]=="N/A":
                self.checkbox = "N/A"

            full_all_part_labels=""
            label2node = {}
            if ds.disks.has_key(i):
                pedparts=partedUtils.get_all_partitions(ds.disks[i])
            else:
                pedparts=[]
            for part in pedparts:
                node=partedUtils.get_partition_name(part)
                this_part_label = isys.readExt2Label(node)
                if this_part_label:
                    label2node[this_part_label] = node

            full_all_part_labels = string.join(label2node.keys())
            if len(full_all_part_labels) > col_widths[5]:
                all_part_labels = "%.*s..." % (col_widths[5]-3, full_all_part_labels)
            else:
                all_part_labels = full_all_part_labels

            full_controller_info = hdd[i]
            if len(full_controller_info) > col_widths[4]:
                controller_info = "%.*s..." % (col_widths[4]-3, full_controller_info)
            else:
                controller_info = full_controller_info
    
    
            new_labels = ""
            new_row=[self.checkbox,
                self.checkbox,
                i,
                list(dsd[i])[0],
                controller_info,
                all_part_labels,
                new_labels,
                full_controller_info,
                label2node]

            # Add the new row to BOTH the self.msd AND to the CListbox
            # array.
            self.msd[i]=new_row
            clb.append(new_row, i, row_align)
            
    
        # Finally, add the CListbox to the grid
        grid.add(clb,0, 3, (0,0,0,0), anchorLeft=1)
    
        # Set up the button bar which will go along the button of the grid
        bb = ButtonBar(self.screen, (("Add", "add"),
            ("Remove", "remove"),
            ("Init", "initialize"),
            ("Info", "info"),
            ("Ok", "ok"), 
            ("Help", "help")))
        
        grid.add(bb, 0, 4, (0,1,0,0))
    
        # Now, run the grid.  The user will interact with the CListbox and
        # the ButtonBar.  The loop will only exit when they press "Ok", and
        # they have specified a valid disk configuration.

        # Start of the big while loop in msd.__init__
        while 1:
            res=grid.run()

            # Figure out which button they pressed.
            the_button=bb.buttonPressed(res)
            the_row = clb.current()
        
            # Now do something based on which button they pressed.
            if the_button == "ok" or the_button==None:
                if self.len() >= len(self.msd):
                    # They reserved all the disks for mass
                    # storage or virtual disk
                    rc = ButtonChoiceWindow(self.screen, 'Disk Selection Error',
                        'You cannot reserve all the disks for mass storage.  You must have at least 1 disk for regular system use.',
                        buttons = [ ("Ok") ] )
                    continue # the big while loop in msd.__init__
                    #NOTREACHED

                # ASSERT: they reserved some of the disks for mass storage
                # and left a few for other uses.  Have them verify their
                # choice.
                mass_reserved_disks = "(none.)"
                for d in self.msd:
                    if self.is_ms(d):
                        if mass_reserved_disks == "(none.)":
                            mass_reserved_disks = d + "."
                        else:
                            mass_reserved_disks = d + ", " + mass_reserved_disks
                
                rc = ButtonChoiceWindow(self.screen, 'Are you sure?', 
                        'You reserved the following disks for mass storage: ' +
                        mass_reserved_disks + 
                        '\n\nIs this correct?',
                        buttons = [ ("Ok"), ("Go back") ],
                        width=60 )
                if rc == "ok":
                    self.screen.popWindow()
                    return
                # end of the_button = "ok"
            elif the_button == "add":
                if self.msd[the_row][0] == "N/A":
                    rc = ButtonChoiceWindow(self.screen,
                        'Device Unavailable',
                        'This device is unavailable for use for mass storage.',
                        buttons = [ ("Ok") ],
                        width=65)
                    continue # the big while loop in msd.__init__
                    #NOTREACHED

                # Things to do now:
                # * See if the disk has a previous msN label.
                # * If it does, see if the disk could be mounted again
                #   using the same msN label.  Test if there is another msN
                #   already allocated.  If so, offer the user to init this
                #   disk, and use the next available msN.  Or, offer the user
                #   the option to remove the other disk.  If not, allocate
                #   msN to this disk, don't init.
                # * No previous msN label?  Then just allocate this disk
                #   with the next available msN.  and mark the disk for
                #   initialization.

                # Is there an msN label on this disk?
                if re.match("^/ms[1-9]\d*$", self.msd[the_row][5]):

                    # Is that label also in use elsewhere?
                    other_row = self.existMountByRow(the_row)

                    if other_row:
                        # There already is a disk which has this particular
                        # msN allocated.  Generate an error and suggested
                        # courses of action.
                        rc = ButtonChoiceWindow(self.screen,
                            "Conflicting Mass Storage Mount Points",
                            'This disk, ' +
                            the_row + ', was previously used for ' +
                            'mass storage, with label ' +
                            self.msd[the_row][5] + '.  ' +
                            'I want to re-use that label ' +
                            'on this disk.  I would do that, ' +
                            'except ' +
                            self.msd[the_row][5] + 
                            ' is already allocated to ' +
                            'another disk, ' +
                            other_row +
                            '.  ' +
                            'You can either initialize ' +
                            the_row +
                            ' and I will give ' + the_row +
                            ' a new label ' +
                            '(press Init, below.)  ' +
                            'If you initialize, though, you ' +
                            'will lose all ' +
                            'existing data on ' + the_row +
                            '.  Or, you can press ' +
                            'the Cancel button, then Remove ' +
                            other_row + 
                            ' as a mass storage disk, then ' +
                            'try again to add ' + the_row +
                            ' as a mass storage disk.  ' +
                            'Press Help for more information.',
                            buttons = [ ("Init"), ("Cancel"), ("Help") ],
                            width=65)

                        # Do something based on the button they pressed in
                        # this sub-screen.
                        if rc == "init":
                            self.msd[the_row][0] = "[M]"
                            self.msd[the_row][1] = "[*]"
                            self.msd[the_row][6] = self.nextMount(the_row, 'ms')
                            # elif rc == "cancel":
                            #   self.msd[the_row][0] = "[ ]"
                            #   self.msd[the_row][1] = "[ ]"

                        elif rc == "help":
                            rc=ButtonChoiceWindow(self.screen,
                                "Help for Conflicting Mass Storage Mount Points",
                                'If a disk ' +
                                'has only one pre-existing label of ' +
                                'the form msN (N ' +
                                'is a number greater ' +
                                'than 0,) and there is not ' +
                                'already another msN allocated, ' +
                                'you can skip initialization, ' +
                                'and re-use ' +
                                'that disk for mass storage.  ' +
                                'Skipping initialization means ' +
                                'you can skip a restore, and ' +
                                'maybe save some time.  But, if ' +
                                'there already is another msN ' +
                                'allocated, you cannot create ' +
                                'a second msN.\n\n' +
                                'Suggestion: first add the ms ' +
                                'disks which are NOT to be ' +
                                'initialized, then add ' +
                                'the ms disks ' +
                                'which are to be initialized.',
                                buttons = [ ("Ok") ],
                                width=65)
                    else:
                        # The previous msN on this disk has not
                        # been allocated elsewhere.
                        self.msd[the_row][6] = self.msd[the_row][5]
                        self.msd[the_row][0] = "[M]"

    
                else:
                    # No previous msN on this disk; so initialize this disk
                    # and give it the next available msN
                    self.msd[the_row][0] = "[M]"
                    self.msd[the_row][1] = "[*]"
                    self.msd[the_row][6] = self.nextMount(the_row, 'ms')
                # end of the button == add branch

            # If it is ever needed to support disk migration, the "rename"
            # branch should go here.  The help messages will also have to
            # be appropriately modified.

            elif the_button == "remove":
                self.msd[the_row][0] = "[ ]"
                self.msd[the_row][1] = "[ ]"
                self.msd[the_row][6] = ""
                # end of button = "remove" branch

            elif the_button == "initialize":
                # Check that the current disk is a mass storage  first.
                if self.is_ms(the_row):
                    # it is part of mass storage.  See if it is marked for init
                    if self.msd[the_row][1] == "[ ]":
                        self.msd[the_row][1] = "[*]"
                    else:
                        # They may be trying to illegally skip
                        # initialization.  Give an error message and
                        # suggest what to do next.
                        rc=ButtonChoiceWindow(self.screen,
                            'Not Initializing a MS Disk',
                            'This disk has been allocated as a mass storage disk, and is already marked for initialization.  If that is what you want, then press Ok, below.\n\nOn the other hand, are you trying to skip initialization for this disk?  In that case, was this disk used as a mass storage disk previously?\n\nYES:\n\nPress Remove.  Then press Add.  But skip pressing Init.\n\nNO:\n\nThe only way to skip initialization of a mass storage disk is if the disk was used as a mass storage disk previously.  If the disk was used as a mass storage disk previously, then there will be only one label of the form /msN, where N is greater than 0.  So, if there is more than one label, or a label not of the form /msN, or N is 0, then the installation software will insist on initializing the disk.',
                            buttons = [ ("Ok") ],
                            width=65)
                else: # if self.is.ms(the_row):
                    rc=ButtonChoiceWindow(self.screen,
                        'Disk not Allocated for Mass Storage or Virtual Server',
                        'First you have to include ' + the_row +
                        ' for use as a mass storage disk, then you can Init it.',
                        buttons = [ ("Ok") ] )
                # end of button = "initialize" branch

            elif the_button == "info":
                # Show the user some information about the disk.
                rc=ButtonChoiceWindow(self.screen,
                    'Information about %s' % (the_row),
                    'Allocated for use as a\n' +
                    'mass storage [M]?                     ' + self.msd[the_row][0] + '\n' +
                    'Will be initialized?                  ' + self.msd[the_row][1] + '\n' +
                    'Device file:                          /dev/' + the_row + '\n' + 
                    'Size:                                 ' + '%d' % string.atoi(self.msd[the_row][3]) + 'Gbyte\n\n' + 
                    'Disk controller information:          ' + self.msd[the_row][7] + '\n' +
                    'Existing labels (may be blank):       ' + string.join(self.msd[the_row][8]) + '\n' +
                    'New mount point (may be blank):       ' + self.msd[the_row][6],
                    buttons = [ ("Ok") ],
                    width=65)
                # end of button = "info" branch

            elif the_button == "help":
                # I split help up into 3 sub-screens.  Yes, it would be
                # simpler scripting to let them scroll one help screen.
                # But I was concerned that they might not know how, or be
                # able to scroll.  Remember, they are reading this help at
                # 2AM during a tape restore.
                show_help=1
                while show_help:
                    if show_help==1:
                        rc=ButtonChoiceWindow(self.screen,
                            'Allocating Mass Storage Disks Help, part 1',
                            'You can allocate zero or more mass ' +
                            'storage disks on the system.  Mass storage ' +
                            'disks each contain a single partition.  ' +
                            'Mass storage partitions get labels of ' +
                            'the form msN, where N is a number greater ' +
                            'than 0, such as ms1, ms2, ... ms7, etc.  ' +
                            'Each msN partition gets mounted under ' +
                            '/msN.  There can only be one particular ' +
                            'msN partition on the system.  That is, you ' +
                            'cannot have two ms3 partitions, for ' +
                            'example.  By default, the load software ' +
                            'does NOT allocate any mass storage ' +
                            'partitions.  You HAVE to specify which ' +
                            'partitions will be used for mass storage, ' +
                            'if any.',
                            buttons = [ ("More"), ("Cancel") ],
                            width=60)
                        if rc=="cancel":
                            show_help=0
                            continue
                            #NOTREACHED
                        show_help=2
                        continue
                        #NOTREACHED
                    elif show_help==2:
                        rc=ButtonChoiceWindow(self.screen,
                            'Allocating Mass Storage Disks Help, part 2',
                            'If you are restoring a system, and you are adding a new disk for use as a mass storage disk, you should choose to also initialize the disk.\n\nIf you are restoring a system, and you have a disk that was used for mass storage previously, and if you know FOR SURE that the disk still has valid data, you can choose to not initialize that disk, avoid reading another save tape, and save some time.  The load software will re-use the existing mass storage disk partition label on that disk.  So, if the disk previously held /ms1, for example, it will still hold /ms1.',
                            buttons = [ ("More"), ("Back"), ("Cancel") ],
                            width=60)
                        if rc=="cancel":
                            show_help=0
                            continue
                            #NOTREACHED
                        elif rc=="back":
                            show_help=1
                            continue
                            #NOTREACHED
                        show_help=3
                        continue
                        #NOTREACHED
                    elif show_help==3:
                        rc=ButtonChoiceWindow(self.screen,
                            'Allocating Mass Storage Disks Help, part 3',
                            'If you are restoring, and you are both keeping previously used mass storage disks, and adding new disks, first allocate the previously used disks.  Then allocate the new disks.  That way you will avoid a new disk trying to use the same msN as an old disk.\n\nNote: If there is a vertical bar on the right, it means that there is additional text in the message.  Use the arrow keys to scroll down so you can see that additional text.',
                            buttons = [ ("Done"), ("Back") ],
                            width=60)
                        if rc=="done":
                            show_help=0
                            continue
                            #NOTREACHED
                        show_help=2
                        continue
                        #NOTREACHED
                    else:
                        self.internal_error("Internal error: show_help has invalid value %d" % (show_help))
                        #NOTREACHED
                # end of the_button = "help" branch
            else:
                self.internal_error("Internal error: invalid value for the_button: %s, giving up" % (the_button))
                #NOTREACHED
        
            # Since they possibly modified the data in the current row in
            # self.msd, replace the corresponding row in the CListbox.
            clb.replace(self.msd[the_row], the_row, row_align)
            clb.setCurrent(the_row)
            grid.setCurrent(clb.listbox)
            grid.draw()


    #
    # msd2part_command outputs the part command from an entry from the MSD.
    # The output can be used in a kickstart file.  Parameter the_key is the
    # name of one of the disks.
    # 
    def msd2part_command(self, the_key):
        if self.msd=={}:
            # Nothing in the dictionary, nothing to do, return.
            return ""
        if not self.msd.has_key(the_key):
            self.internal_error("Internal error, msd2part_command given invalid key, giving up")
            #NOTREACHED
    
        # If they did not want mass storage on this disk, then there is
        # nothing to write to the kickstart file.  Return an empty string.
        if not self.is_ms(the_key):
            # The disk is not part of mass storage and not used by a virtual server
            return ""
            #NOTREACHED
    
        # ASSERT: this disk is part of mass storage

        # Specify ext2
        the_fstype = " --fstype ext2"

        # Do they want to initialize it?
        if self.msd[the_key][1] == "[ ]":
            # No, they do not want it initialized
            partname = self.msd[the_key][5]
            label2node = self.msd[the_key][8]
            mountpoint = label2node[partname]
            return "part " + partname + the_fstype + " --noformat --onpart=" + mountpoint + "\n"
            #NOTREACHED
    
        # ASSERT: disk is for use by mass storage They do want to
        # initialize it.

        # There is a tuple stored at dsd[i].  The tuple should have
        # 2 elements: size in gbyte and number of cylinders.
        # Previously, I used the number of cylinders in an --end
        # option of a part directive for a mass storage partition.
        # However, I have since changed the directive to use a 
        # --size=1 --grow, and to allocate all partitions to a
        # specific disk.  The end cylinder is still at dsd[i][1:] ,
        # in case I decide to go back to using --end, as in:
        # return "partition " + self.msd[the_key][6] + " --start=1 --end=%d " % dsd[i][1:] + "--ondisk=" + i + "\n"
        
        # Now I return the part command which uses --grow:
        return "part " + self.msd[the_key][6] + the_fstype + " --size=1 --grow --ondisk=" + the_key + "\n"
    #

    # msd2perm_command outputs the permissions command for the ks.perm
    # file.  This command means that the mass storage disk will get
    # allocated with DSDA's desired permissions.
    # 
    def msd2perm_command(self, the_key):
        if self.msd=={}:
            # Nothing in the dictionary, nothing to do, return.
            return ""
        if not self.msd.has_key(the_key):
            self.internal_error("Internal error, msd2perm_command given invalid key, giving up")
            #NOTREACHED
    
        # If they did not want to include this disk in mass storage, then
        # there is nothing to write to the ks.perm file.  Return an empty
        # string.
        if not self.is_ms(the_key):
            # The disk is not part of mass storage
            return ""
            #NOTREACHED
    
        # ASSERT: this disk is part of mass storage.
        partname = self.msd[the_key][6]

        return "chmod 775 %s; chown adp.clients %s; umount %s; chmod 700 %s; chown root.root %s; mount %s\n" % ( partname, partname, partname, partname, partname, partname) 

    #
    # getClearpart tells the caller whether the partition should be
    # cleared.  It returns 1 for yes, 0 for no.  The parameter is the
    # name of a disk.
    # 
    def getClearpart(self, the_key):
        if self.msd=={}:
            # Nothing in the dictionary, nothing to do, return.
            return 0
        if not self.msd.has_key(the_key):
            self.internal_error("Internal error, getClearpart given invalid key, giving up")
            # in case it gets past internal_error
            return 0
            #NOTREACHED
    
        # If they did not want mass storage to use this disk, then
        # they do want it clearparted.
        if not self.is_ms(the_key):
            return 1
            #NOTREACHED
    
        # ASSERT: mass storage use this disk.
        # Do they want to initialize it?
        if self.msd[the_key][1] == "[*]":
            # Yes, they want to initialize it.
            return 1
            #NOTREACHED
    
        # ASSERT: mass storage use this disk.
        # They do not want to initialize it.
        return 0
        #NOTREACHED

    #
    # Tell if a disk is reserved for mass storage.
    #
    def is_ms(self, key):
        return self.msd[key][0] == "[M]"

    # 
    # How many disks are reserved for mass storage?
    def len(self):
        i = 0
        for key in self.msd:
            if self.is_ms(key):
                i += 1
        return i

    #
    # Determine the next available mass storage mount.  Only looks at the
    # mount points that are actually allocated in the 6th column of
    # self.msd.  Does not worry about mount points that might want to be
    # allocated in the 5th column.  The design intends that the user will
    # allocate the disks that s/he wants to re-use first, then allocate the
    # new disks.  That way the new disks will just use the next available
    # msN, and old disks will be able to re-use a previous msN, for various
    # values of N.  I did try doing something fancier in which nextMount
    # tried to recognize the 5th column in msd.  But it got too confusing
    # and I gave up on it.
    # 
    # Before you implement a fancier automatic msN assignment system,
    # answer the question: what happens if they salvage 2 disks from
    # another system, and they both have ms2 labels?
    #

    def nextMount(self, the_row, label_letters):
        m = 1
        mount_point_prefix = "/" + label_letters
        while m < 1000:
            mount_point = mount_point_prefix + "%d" % (m)
            flag = 0
            for key in self.msd:
                if key == the_row:
                    continue
                    # NOTREACHED
                # If another disk already wants to use a particular msN,
                # then set the flag.  Figure out what to do next, shortly.
                if self.msd[key][6] == mount_point:
                    flag = 1
                    break   # out of the "for key in self.msd" loop

            if flag!=0:
                # Someone wants to use the msN in mount_point.  Try
                # the next one.
                m += 1
                continue    # continues the "while m < 1000" loop
                # NOTREACHED

            # If we get here, we found an unused msN.  Tell the caller.
            return mount_point
            # NOTREACHED

        # if we get here then things are seriously broken.  We tried 1000
        # mount points and could find one that worked.  Time to give up.
        self.internal_error("Could not allocate a mount point, tried up to %s, giving up." % (mount_point))
        #NOTREACHED

    # See if a proposed mount point is Ok.  
    # 
    # The caller passes the_row, a key of a row that is already in the msd
    # list.  existMountByRow assumes that column 5 of the_row has a
    # proposed mount point.  existMountByRow checks if that mount point has
    # been allocated elsewhere in the msd list.  "Allocated" means someone
    # has put that mount point in column 6 of another row.
    #
    # existMountByRow does not bother checking column 6 of the_row.  That
    # way the caller can provisionally allocate a mount point for a row,
    # and then ask if that mount point has been allocated elsewhere.  That
    # might seem silly, but it's useful for the case where the user adds
    # the same disk twice to mass storage.
    #
    # Returns the key of the other row which has the mount point already
    # allocated.  Or returns None if no such row exists.

    def existMountByRow(self, the_row):
        for key in self.msd:
            if key == the_row:
                # skip the current row
                continue
            if self.msd[key][6] == self.msd[the_row][5]:
                return key
        # if we get here then the mount point is not already allocated.
        return None

    # See if a proposed mount point is Ok.  
    # 
    # The caller passes the_name, a mount point the caller wants to use.
    # existMountByName checks if that mount point has been allocated
    # elsewhere in the msd list.  "Allocated" means someone has put that
    # mount point in column 6 in a row.
    #
    # Returns the key of the row which has the mount point already
    # allocated.  Or returns None if no such row exists.
    #
    # The caller must handle the situation of a user trying to re-reserve a
    # disk, two or more times.  In that case, existMountByName will return
    # a non-None value the second time it is called.  That non-None value
    # will be the row of the disk the user previously reserved.

    def existMountByName(self, the_name):
        for key in self.msd:
            if self.msd[key][6] == the_name:
                return key
        # if we get here then the mount point is not already allocated.
        return None


    # Yet another helper function.  Turns off the snack screen, outputs the
    # error message in text, and exits.  Used for handling internal
    # errrors.
    def internal_error(self, error_message):
        self.screen.finish()
        print error_message
        sys.exit(1)
        #NOTREACHED
        


#
# Main processing starts here
#

# Initialize a blank label variables
blankLabel=Label("     ")

# Process command line arguments:
p = optparse.OptionParser(description="Asks the user about swap space allocation and whether the system should reserve a disk for mass storage.",
    prog = "adppre.py",
    usage = "[ --swapmult N ] [ --adp restore|install] [ --output output_file ] [ --permfile permissions_file ] ",
    version = "$Revision: /main/79 $",
    )

p.add_option("--swapmult", default=2, type='int')
p.add_option("--swapmax", default=16384, type='int')
p.add_option("--adp", choices=["restore", "install"], default="install",
    type='choice')
p.add_option("--output", default="/tmp/ks.adpparts", type='string')
p.add_option("--permfile", default="/tmp/ks.perms", type='string')

(opt, extra) = p.parse_args()

swapmult = opt.swapmult
swapmax = opt.swapmax
adparg = opt.adp
outfile = opt.output
permfile = opt.permfile

# Open the kickstart output file, as specified in the outfile variable.
# Leave the file open for the rest of the script.  That way, adppre.py can
# write debugging information as comments to the output file as adppre.py
# runs.
#
try:
    ks_adpparts = open(outfile, "w")
except:
    print "Internal error: could not open " + outfile + " for writing, giving up"
    sys.exit(-1)

# Open the kickstart permissions file, as specified in the permfile
# variable.  Leave the file open for the rest of the script.  That way,
# adppre.py can write debugging information as comments to the output file
# as adppre.py runs.
#
try:
    ks_perms = open(permfile, "w")
except:
    print "Internal error: could not open " + permfile + " for writing, giving up"
    sys.exit(-1)

# Get e820 information, or if that does not work, look into iutil.
try:
    import e820
    meminfo = e820.e820Info('/tmp/syslog')
    memInstalled = meminfo.memtotal
    ks_adpparts.write ("# /proc/e820info memory size is %d\n" % memInstalled)
except:
    import iutil 
    memInstalled = iutil.memInstalled() / 1024
    ks_adpparts.write ("# iutil.memInstalled() reports %dM\n" % memInstalled)

screen = SnackScreen()
screen.pushHelpLine("<Tab> and arrows move around | <Space> de-/selects | <Enter> presses button")

# Put up the default message, which should hide under other windows.
pleaseWait(screen)

# Ask the user for the C number, if we are not doing a tape restore.
if (adparg == 'install'):
    getCNumber(screen)

# If there is more than 1 disk present, ask the user if s/he wants to
# reserve 1 or more disks for mass storage.
#
# But I don't want to do _anything_ with removable media today, so before I
# even check to see if there's more than one disk, weed out anything that
# isys says has no media present.  
# 
# FYI, what I _used_ to do here is ask isys if the disk was removable,
# regardless of whether it had a media inserted.  Unfortunately, I have not
# come up with a good way to handle thumb drives, aka memory sticks, aka
# flash drives.
#
hdd = isys.hardDriveDict()

for disk in hdd.keys():
    if not isys.mediaPresent(disk):
        ks_adpparts.write ("# %s has no media; don't use it\n" % disk)
        del hdd[disk]

num_disks = len(hdd)
ks_adpparts.write ("# isys.hardDriveDict number of logical disks is %d\n" % num_disks)

# getDSD creates the disk size dictionary.
dsd = getDSD(hdd)

# Initialize num_disks_for_swap to the total number of disks, at least by
# default.
num_disks_for_swap = len(hdd)
msd_len = 0

# ### Uncomment one of the following:
if(num_disks > 1):  # the correct version
# ### if(num_disks > 0):        # the development version
    
    # Initialize the mass storage dictionary, msd, using the MSD class.
    #
    msd=MSD(screen, hdd, dsd)

    # Reduce the number of disks for swap by the number of disks that
    # will be used for mass storage.  And update msd_len
    msd_len = msd.len()
    num_disks_for_swap -= msd_len

# Figure out the swap to allocate, and what spindles to put it on.
def_swap = memInstalled * swapmult
if( def_swap > swapmax ):
    def_swap = swapmax

final_swap = askAboutSwap( screen, memInstalled, def_swap )

# Spread out the final swap area size over the num_disks_for_swap
each_swap_size = final_swap/num_disks_for_swap
screen.finish()

# print "adppre.py: called screen.finish()"

ks_adpparts.write ("# each swap partition size is %d\n" % each_swap_size)

# For purposes of the volgroup directive, pesize is in kilobytes.
# That is despite vgcreate treating numbers with no letter as megabytes.
j = 0
vg="volgroup --pesize 32768 adp_vg "

# Create the clearpart_directive.  First, set up a default directive
# to clear all partitions.
if msd_len > 0:
    clearpart_directive = "clearpart --all --initlabel --drives="
else:
    clearpart_directive = "clearpart --all --initlabel,"   # comma gets stripped below

# Create the string to hold the partition commands
part_directives = ""

# adppre.py does not bother creating a string for writing information to
# ks.perm.  Rather, adppre.py just writes out the information that needs
# to be written to ks.perm.  That works because the ks.perm file is a shell
# script, and not a kickstart file.  If that changes, initialize that
# string here.
ks_perms.write("#!/bin/bash\n")

# Fill in details in clearpart_directive and part_directives.  Once
# the directives are complete, output them to the kickstart file.
#
for i in hdd:
    if msd_len > 0:
        if msd.getClearpart(i) == 1:
            clearpart_directive = clearpart_directive + i + ","
        this_part_directive = msd.msd2part_command(i)
        if this_part_directive != "":
            part_directives += this_part_directive
            this_part_perm = msd.msd2perm_command(i)
            ks_perms.write(this_part_perm + "\n")
            continue
            #NOTREACHED

    # disk i is not mass storage, or there are no mass storage drives
    part_directives += "part pv.%.2d --size=1 --grow --ondisk=%s\n" % (j, i)
    part_directives += "part swap --size=%d --ondisk=%s\n" % (each_swap_size, i)
    this_pv = "pv.%.2d " % j
    vg = vg + this_pv
    j = j + 1

# Now allocate partitions that do not use up an entire disk.  Modify this
# list if you want to change the sizes of these partitions

system_parts=[['/boot', '128'], ['/', '2000'], ['/var', '4000'], ['/usr', '4000']]

if msd_len == 0:
    # No mass storage disks, allocate the partitions the regular way.
    for sp in system_parts:
        # Yes, you could combine the following 2 lines into a single line and
        # eliminate the use of the this_part_directive variable.  But then you
        # would not have a place to set a breakpoint and a variable to examine.
        #
        this_part_directive = "part " + sp[0] + " --size=" + sp[1] + " --fstype ext2\n"
        part_directives += this_part_directive

else:
    # There are mass storage/virtual server disks.  Allocate the system
    # partitions over the non-mass storage disks.  This works because MSD
    # will not allocate all disks to mass storage.
    while system_parts != []:
        # iterate over all the hard disks on the system
        for i in hdd:
            # Skip this disk if it is in use by mass storage/virtual server
            if msd.is_ms(i):
                continue
                # NOTREACHED
            # ASSERT: this disk is not used for mass storage.  Try to put
            # the first of the remaining system_parts on it.
            this_part_directive = "part " + system_parts[0][0] + " --size=" + system_parts[0][1] + " --fstype ext2 --ondisk=" + i + "\n"
            part_directives += this_part_directive
            del system_parts[0]
            if system_parts == []:
                break

# Strip off the trailing comma from the clearpart directive
clearpart_directive = clearpart_directive[0:-1]

# First output the clearpart directive...
ks_adpparts.write("%s\n\n" % clearpart_directive)

# then output the partition directives
ks_adpparts.write("%s\n" % part_directives)

# and output the logical volume group directive which references the pv's
ks_adpparts.write("%s\nlogvol /adp --percent 100 --fstype ext2 --name=adp_lv --vgname=adp_vg\n" % vg)

ks_adpparts.close()
sys.exit(0)


This message and any attachments are intended only for the use of the addressee and may contain information that is privileged and confidential. If the reader of the message is not the intended recipient or an authorized representative of the intended recipient, you are hereby notified that any dissemination of this communication is strictly prohibited. If you have received this communication in error, please notify us immediately by e-mail and delete the message and any attachments from your system.

_______________________________________________
Anaconda-devel-list mailing list
Anaconda-devel-list@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/anaconda-devel-list

[Index of Archives]     [Kickstart]     [Fedora Users]     [Fedora Legacy List]     [Fedora Maintainers]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [Yosemite Photos]     [KDE Users]     [Fedora Tools]
  Powered by Linux