def test_slice_on_part(self):
        self.disk.geometry.nheads = 24
        self.disk.geometry.nsectors = 848
        self.disk.geometry.blocksize = 512
        self.disk.geometry.ncyl = 14089

        part = Partition(2)
        part.action = "create"
        part.start_sector = 0
        part.size = Size("50" + Size.gb_units)
        part.part_type = "primary"
        part.bootid = Partition.ACTIVE

        self.disk.insert_children(part)

        slice1 = Slice(1)
        slice1.action = "create"
        slice1.size = Size(str(GBSECTOR) + Size.sector_units)
        slice1.start_sector = CYLSIZE
        slice1.tag = const.V_USR
        slice1.flag = 0

        part.insert_children(slice1)

        t = instantiation.TargetInstantiation("test_ti")
        try:
            t.execute(dry_run=True)
        except Exception as err:
            self.fail(str(err))
Example #2
0
    def create(self, dry_run):
        """ method to create a zvol.
        """
        if not self.exists:
            # check self.size.  If it's a Size() object, convert it to a string
            # ZFS expects
            if isinstance(self.size, Size):
                zvol_size = str(int(self.size.get(Size.mb_units))) + "M"
            elif self.size == "max":
                # Allocate 90 % of available space on pool to zvol.
                # Size of zvol is capped to 90 % to avoid full zpool
                # issues.
                if self.parent is not None:
                    fs = Filesystem(self.parent.name)
                    fs_size = Size(fs.get("available"))
                    zvol_size = str(int(fs_size.get(Size.mb_units) * 0.9)) + \
                                    "M"
                    self.size = Size(zvol_size)
            else:
                zvol_size = self.size

            cmd = [ZFS, "create", "-p", "-V", zvol_size]

            zfs_options = self.get_first_child(class_type=Options)
            if zfs_options is not None:
                cmd.extend(zfs_options.get_arg_list())

            cmd.append(self.full_name)

            if not dry_run:
                Popen.check_call(cmd, stdout=Popen.STORE, stderr=Popen.STORE,
                                 logger=ILN, env={"LC_ALL": "C"})

                # check the "use" attribute
                if self.use == "swap":
                    cmd = [SWAP, "-a",
                           os.path.join("/dev/zvol/dsk", self.full_name)]
                    Popen.check_call(cmd, stdout=Popen.STORE,
                                     stderr=Popen.STORE, logger=ILN,
                                     stderr_loglevel=logging.DEBUG)
                elif self.use == "dump":
                    cmd = [DUMPADM, "-d",
                           os.path.join("/dev/zvol/dsk", self.full_name)]
                    if self.create_failure_ok:
                        results = (0, 1)
                    else:
                        results = (0,)
                    p = Popen.check_call(cmd, stdout=Popen.STORE,
                                         stderr=Popen.STORE, logger=ILN,
                                         stderr_loglevel=logging.DEBUG,
                                         check_result=results)
                    if p.returncode == 1:
                        logger = logging.getLogger(ILN)
                        logger.warning("Unable to create dump Zvol "
                                       "with size %s." % zvol_size)
    def discover_slice(self, slc, blocksize):
        """ discover_slices - method to discover a physical disk's slice
        layout.

        slc - slice object as discovered by ldm
        blocksize - blocksize of the disk
        """
        # store the attributes locally so libdiskmgt doesn't have to keep
        # looking them up
        slc_attributes = slc.attributes

        new_slice = Slice(str(slc_attributes.index))
        new_slice.action = "preserve"
        new_slice.tag = slc_attributes.tag
        new_slice.flag = slc_attributes.flag
        new_slice.size = Size(str(slc_attributes.size) + Size.sector_units,
                              blocksize=blocksize)
        new_slice.start_sector = long(slc_attributes.start)
        new_slice.size_in_sectors = slc_attributes.size

        stats = slc.use_stats
        if "used_by" in stats:
            new_slice.in_use = stats

        return new_slice
Example #4
0
    def set_geometry(self, dma, new_disk):
        """ set_geometry() - method to set the geometry of the Disk DOC object
        from the libdiskmgt drive object.

        dma - the drive's media attributes as returned by libdiskmgt
        new_disk - Disk DOC object
        """
        new_geometry = None

        # If the disk has a GPT label (or no label), ncylinders will be
        # None
        if dma.ncylinders is None:
            # XXX - libdiskmgt calculation of DM_NACCESSIBLE for GPT disks
            # is broken, so we use DM_SIZE since we can access all blocks on
            # the disk anyway because we don't need to worry about VTOC
            # imposed cylinder alignment on disk size.
            # Bugster CR: 7099417
            new_disk.disk_prop.dev_size = Size(
                str(dma.size) + Size.sector_units, dma.blocksize)

            # set only the blocksize (not the cylinder size)
            new_geometry = DiskGeometry(dma.blocksize, None)

            # set the label of the disk, if possible
            if dma.efi:
                new_disk.label = "GPT"
                new_geometry.efi = True
        else:
            new_disk.label = "VTOC"

            # set the disk's volid
            new_disk.volid = dma.label

            ncyl = dma.ncylinders
            nhead = dma.nheads
            nsect = dma.nsectors
            new_disk.disk_prop.dev_size = Size(
                str(dma.naccessible) + Size.sector_units, dma.blocksize)
            new_geometry = DiskGeometry(dma.blocksize, nhead * nsect)
            new_geometry.ncyl = ncyl
            new_geometry.nheads = nhead
            new_geometry.nsectors = nsect

        new_disk.geometry = new_geometry
        return new_disk
Example #5
0
def decimal_valid(edit_field, disk_win=None):
    '''Check text to see if it is a decimal number of precision no
    greater than the tenths place.

    '''
    text = edit_field.get_text().lstrip()
    radixchar = locale.localeconv()['decimal_point']
    if text.endswith(" "):
        raise UIMessage(_('Only the digits 0-9 and %s are valid.') % radixchar)
    vals = text.split(radixchar)
    if len(vals) > 2:
        raise UIMessage(_('A number can only have one %s') % radixchar)
    try:
        if len(vals[0]) > 0:
            int(vals[0])
        if len(vals) > 1 and len(vals[1]) > 0:
            int(vals[1])
    except ValueError:
        raise UIMessage(_('Only the digits 0-9 and %s are valid.') % radixchar)
    if len(vals) > 1 and len(vals[1]) > 1:
        raise UIMessage(_("Size can be specified to only one decimal place."))
    if disk_win is not None:
        text = text.rstrip(radixchar)
        if not text:
            text = "0"

        # encode user input per locale for floating point conversion
        text = text.encode(get_encoding())
        new_size = Size(str(locale.atof(text)) + Size.gb_units)
        max_size = edit_field.data_obj.get_max_size()

        # When comparing sizes, check only to the first decimal place,
        # as that is all the user sees. (Rounding errors that could
        # cause the partition/slice layout to be invalid get cleaned up
        # prior to target instantiation)
        new_size_rounded = round(new_size.get(Size.gb_units), 1)
        max_size_rounded = round(max_size.get(Size.gb_units), 1)
        if new_size_rounded > max_size_rounded:
            locale_new_size = locale.format("%.1f", new_size_rounded)
            locale_max_size = locale.format("%.1f", max_size_rounded)
            msg = _("The new size ") + locale_new_size + \
                _(" is greater than the available space ") + locale_max_size
            raise UIMessage(msg)
    return True
    def test_update_vtoc(self):
        slice1 = Slice(1)
        slice1.action = "create"
        slice1.size = Size("4" + Size.sector_units)
        slice1.start_sector = 1
        slice1.tag = const.V_BOOT
        slice1.flag = const.V_RONLY

        slice2 = Slice(2)
        slice2.action = "create"
        slice2.size = Size("8" + Size.sector_units)
        slice2.start_sector = 10
        slice2.tag = const.V_BACKUP
        slice2.flag = const.V_RONLY

        slice3 = Slice(3)
        slice3.action = "preserve"
        slice3.size = Size("20" + Size.sector_units)
        slice3.start_sector = 20
        slice3.tag = const.V_USR
        slice3.flag = const.V_UNMNT

        slices_list = [slice1, slice2, slice3]
        extvtocp = C.pointer(cstruct.extvtoc())
        c = extvtocp.contents
        self.disk.geometry.ncly = 5
        self.disk._update_vtoc_struct(extvtocp, slices_list, 10)
        self.failIf(\
            c.v_part[int(slice1.name)].p_size != slice1.size.sectors or \
            c.v_part[int(slice1.name)].p_start != slice1.start_sector or \
            c.v_part[int(slice1.name)].p_tag != slice1.tag or \
            c.v_part[int(slice1.name)].p_flag != slice1.flag)

        self.failIf(\
            c.v_part[int(slice2.name)].p_size != slice2.size.sectors or \
            c.v_part[int(slice2.name)].p_start != slice2.start_sector or \
            c.v_part[int(slice2.name)].p_tag != slice2.tag or \
            c.v_part[int(slice2.name)].p_flag != slice2.flag)

        self.failIf(\
            c.v_part[int(slice3.name)].p_size != slice3.size.sectors or \
            c.v_part[int(slice3.name)].p_start != slice3.start_sector or \
            c.v_part[int(slice3.name)].p_tag != slice3.tag or \
            c.v_part[int(slice3.name)].p_flag != slice3.flag)
Example #7
0
    def set_geometry(self, dma, new_disk):
        """ set_geometry() - method to set the geometry of the Disk DOC object
        from the libdiskmgt drive object.

        dma - the drive's media attributes as returned by libdiskmgt
        new_disk - Disk DOC object
        """
        new_geometry = None

        # If the disk has a GPT label (or no label), ncylinders will be
        # None
        if dma.ncylinders is None:
            new_disk.disk_prop.dev_size = Size(str(dma.naccessible) +
                                               Size.sector_units,
                                               dma.blocksize)

            # set only the blocksize (not the cylinder size)
            new_geometry = DiskGeometry(dma.blocksize, None)

            # set the label of the disk, if possible
            if dma.efi:
                new_disk.label = "GPT"
                new_geometry.efi = True
        else:
            new_disk.label = "VTOC"

            # set the disk's volid
            new_disk.volid = dma.label

            ncyl = dma.ncylinders
            nhead = dma.nheads
            nsect = dma.nsectors

            new_disk.disk_prop.dev_size = Size(str(ncyl * nhead * nsect) +
                                               Size.sector_units,
                                               dma.blocksize)
            new_geometry = DiskGeometry(dma.blocksize, nhead * nsect)
            new_geometry.ncyl = ncyl
            new_geometry.nheads = nhead
            new_geometry.nsectors = nsect
            new_disk.geometry = new_geometry

        return new_disk
    def test_initial_disk_selected_by_size(self):
        '''Validate that initial disk matches size criteria'''
        tc = TargetController(self.doc)
        returned_disks = tc.initialize(image_size=Size("110GB"))

        self.assertEqual(returned_disks[0].ctd, "c2t2d0",
            "incorrect initial disk returned")
        desired_disks = self._get_desired_disks()
        self.assertEqual(desired_disks[0].ctd, "c2t2d0",
            "incorrect initial disk selected")
    def test_part_not_preserve(self):
        part = Partition(1)
        part.action = "create"
        part.part_type = "primary"
        part.bootid = Partition.ACTIVE
        part.size = Size("2gb")

        part2 = Partition(2)
        part2.action = "destroy"
        part2.part_type = "primary"
        part2.bootid = 0
        part2.size = Size("2gb")

        self.disk.insert_children([part, part2])

        t = instantiation.TargetInstantiation("test_ti")
        try:
            t.execute(dry_run=True)
        except Exception as err:
            self.fail(str(err))
Example #10
0
    def validate(self):
        '''Validate the size of the disk.'''

        warning_txt = list()

        disk = self.disk_detail.ui_obj.doc_obj
        disk_size_gb = disk.disk_prop.dev_size.get(Size.gb_units)
        max_vtoc_size_gb = Size(MAX_VTOC).get(Size.gb_units)
        # Disk size warning should only be displayed if we are restricted to
        # VTOC boot disks.
        if not can_use_gpt and disk_size_gb > max_vtoc_size_gb:
            warning_txt.append(self.disk_warning_too_big)
        warning_txt = " ".join(warning_txt)

        if warning_txt:
            # warn the user and give user a chance to change
            result = self.main_win.pop_up(DiskScreen.DISK_WARNING_HEADER,
                                          warning_txt,
                                          DiskScreen.CANCEL_BUTTON,
                                          DiskScreen.CONTINUE_BUTTON)

            if not result:
                raise UIMessage()  # let user select different disk
            # if user didn't quit it is always OK to ignore disk size,
            # that will be forced less than the maximum in partitioning.

        warning_txt = list()

        # We also need to warn the user if we need to relabel the disk from
        # GPT to SMI-VTOC
        if disk.label == "GPT" and not can_use_gpt and \
           disk.disk_prop.dev_type != "iSCSI":
            warning_txt.append(DiskScreen.DISK_WARNING_RELABEL)
        warning_txt = " ".join(warning_txt)

        if warning_txt:
            # warn the user and give user a chance to change
            result = self.main_win.pop_up(DiskScreen.DISK_WARNING_HEADER,
                                          warning_txt,
                                          DiskScreen.CANCEL_BUTTON,
                                          DiskScreen.CONTINUE_BUTTON)

            if not result:
                raise UIMessage()  # let user select different disk

            # if user didn't Cancel it is  OK to relabel the disk.
            # This is one of the lesser known (and potentially dangerous)
            # features of target controller: select_disk() with
            # use_whole_disk=True can force a relabeling of the disk from GPT
            # to VTOC is necessary for booting from the disk
            disk = self.tc.select_disk(disk, use_whole_disk=True)[0]

            # The DiskWindow object needs its disk reference updated too
            self.disk_detail.set_disk_info(disk_info=disk)
Example #11
0
    def discover_partition(self, partition, blocksize):
        """ discover_partition - method to discover a physical disk's
        partition layout

        partition - partition object as discovered by ldm
        blocksize - blocksize of the disk
        """
        # store the attributes locally so libdiskmgt doesn't have to keep
        # looking them up
        partition_attributes = partition.attributes

        # partition name is ctdp path.  Split the string on "p"
        root_path, _none, index = partition.name.partition("p")

        # create a DOC object for this partition.  Set validate_children to
        # False so the shadow code doesn't adjust the start sector or size for
        # any children discovered
        new_partition = Partition(index, validate_children=False)
        new_partition.action = "preserve"
        new_partition.part_type = partition_attributes.id

        # check the partition's ID to set the partition type correctly
        if partition_attributes.id == \
            Partition.name_to_num("Solaris/Linux swap"):
            try:
                # try to call prtvtoc on slice 2.  If it succeeds, this is a
                # solaris1 partition.
                slice2 = root_path + "s2"
                cmd = [PRTVTOC, slice2]
                run(cmd, stdout=Popen.DEVNULL)
            except CalledProcessError:
                # the call to prtvtoc failed which means this partition is
                # Linux swap. To be sure, prtvtoc failure might also mean an
                # unlabeled Solaris1 partition but we aren't going to worry
                # about that for now. Displaying an unlabeled (and therefore
                # unused) Solaris1 partition should not have any unforeseen
                # consequences in terms of data loss.
                new_partition.is_linux_swap = True

        new_partition.size = Size(str(partition_attributes.nsectors) + \
                             Size.sector_units, blocksize=blocksize)
        new_partition.start_sector = long(partition_attributes.relsect)
        new_partition.size_in_sectors = partition_attributes.nsectors

        # If the partition is an EFI system partition check to see if it
        # is PCFS formatted. We only do this on EFI system partitions to save
        # time since mounting can be slow.
        if new_partition.is_efi_system:
            ctdp = partition.name.rsplit('/', 1)[-1]
            new_partition._is_pcfs_formatted = self.is_pcfs_formatted(ctdp)

        return new_partition
    def test_slice_on_part_not_preserve(self):
        part = Partition(1)
        part.action = "preserve"
        part.part_type = "primary"
        part.bootid = Partition.ACTIVE
        part.size = Size("50" + Size.gb_units)
        part.start_sector = 0

        part2 = Partition(2)
        part2.action = "preserve"
        part2.part_type = "primary"
        part2.bootid = 0
        part2.size = Size("50" + Size.gb_units)
        part2.start_sector = 50 * GBSECTOR + 50

        self.disk.insert_children([part, part2])

        slice1 = Slice(1)
        slice1.action = "create"
        slice1.size = Size(str(GBSECTOR) + Size.sector_units)
        slice1.start_sector = CYLSIZE

        slice2 = Slice(2)
        slice2.action = "create"
        slice2.size = Size(str(GBSECTOR) + Size.sector_units)
        slice2.start_sector = CYLSIZE + GBSECTOR + 1

        slice3 = Slice(3)
        slice3.action = "destroy"
        slice3.size = Size(str(GBSECTOR) + Size.sector_units)
        slice3.start_sector = CYLSIZE + (2 * GBSECTOR + 2)

        part.insert_children([slice1, slice2, slice3])

        t = instantiation.TargetInstantiation("test_ti")
        try:
            t.execute(dry_run=True)
        except Exception as err:
            self.fail(str(err))
    def test_create_multiple_partitions(self):
        part = Partition(2)
        part.action = "create"
        part.start_sector = 0
        part.size = Size("20" + Size.gb_units)
        part.part_type = "primary"
        part.bootid = Partition.ACTIVE

        part2 = Partition(4)
        part2.action = "create"
        part2.start_sector = 50 * GBSECTOR + 50
        part2.size = Size("50" + Size.gb_units)
        part2.part_type = "extended"
        part2.bootid = 0

        self.disk.insert_children([part, part2])

        t = instantiation.TargetInstantiation("test_ti")
        try:
            t.execute(dry_run=True)
        except Exception as err:
            self.fail(str(err))
Example #14
0
    def __init__(self, main_win, target_controller):
        """ screen object containing the disk selection choice for the user
        """

        global LOGGER
        LOGGER = logging.getLogger(INSTALL_LOGGER_NAME)

        super(DiskScreen, self).__init__(main_win)
        if platform.processor() == "i386":
            self.found_text = DiskScreen.FOUND_x86
            self.proposed_text = DiskScreen.PROPOSED_x86
        else:
            self.found_text = DiskScreen.FOUND_SPARC
            self.proposed_text = DiskScreen.PROPOSED_SPARC

        self.gpt_found_text = DiskScreen.FOUND_GPT
        self.gpt_proposed_text = DiskScreen.PROPOSED_GPT

        disk_header_text = []
        for header in DiskScreen.DISK_HEADERS:
            header_str = fit_text_truncate(header[1],
                                           header[0] - 1,
                                           just="left")
            disk_header_text.append(header_str)

        self.disk_header_text = " ".join(disk_header_text)
        self.max_vtoc_disk_size = (Size(MAX_VTOC)).get(Size.tb_units)
        self.disk_warning_too_big = \
            DiskScreen.DISK_WARNING_TOOBIG % self.max_vtoc_disk_size

        self.disks = []
        self.existing_pools = []
        self.disk_win = None
        self.disk_detail = None
        self.num_targets = 0
        self.td_handle = None
        self._size_line = None
        self.selected_disk_index = 0
        self._minimum_size = None
        self._recommended_size = None

        self.engine = InstallEngine.get_instance()
        self.doc = self.engine.data_object_cache
        self.tc = target_controller
        self._target_discovery_completed = False
        self._target_discovery_status = InstallEngine.EXEC_SUCCESS
        self._image_size = None

        self.iscsi = None
        self._iscsi_target_discovery_completed = False
        self._iscsi_target_discovery_status = InstallEngine.EXEC_SUCCESS
Example #15
0
def on_exit_edit(edit_field, disk_win=None):
    '''On exit, if the user has left the field blank, set the size to 0'''

    text = edit_field.get_text()
    if not text.strip():
        text = "0"
        enctext = text.encode(get_encoding())
        # encode per locale for floating point conversion
        edit_field.set_text("%.1f" % locale.atof(enctext))

    part_order = disk_win.ui_obj.get_parts_in_use().index(edit_field.data_obj)
    LOGGER.debug("Part being resized is at index: %s", part_order)

    new_size_text = text.strip()

    LOGGER.debug("Resizing text=%s", new_size_text)
    # encode user input per locale for floating point conversion
    enctext = new_size_text.encode(get_encoding())
    new_size = Size(str(locale.atof(enctext)) + Size.gb_units)
    old_size = edit_field.data_obj.size

    new_size_byte = new_size.get(Size.byte_units)
    old_size_byte = old_size.get(Size.byte_units)

    precision = Size(UI_PRECISION).get(Size.byte_units)

    if abs(new_size_byte - old_size_byte) > precision:
        parent_doc_obj = edit_field.data_obj.doc_obj.parent
        if isinstance(parent_doc_obj, Disk):
            if isinstance(edit_field.data_obj.doc_obj, Partition):
                resized_obj = parent_doc_obj.resize_partition(
                    edit_field.data_obj.doc_obj, new_size.get(Size.gb_units),
                    size_units=Size.gb_units)
            else:
                resized_obj = parent_doc_obj.resize_slice(
                    edit_field.data_obj.doc_obj, new_size.get(Size.gb_units),
                    size_units=Size.gb_units)
        else:
            resized_obj = parent_doc_obj.resize_slice(
                edit_field.data_obj.doc_obj, new_size.get(Size.gb_units),
                size_units=Size.gb_units)

        if isinstance(resized_obj, Partition):
            resized_obj.in_zpool = ROOT_POOL
        else:
            if resized_obj.in_zpool == ROOT_POOL:
                resized_obj.tag = V_ROOT

        if disk_win is not None:
            disk_win.set_disk_info(ui_obj=disk_win.ui_obj)
            disk_win.activate_index(part_order)

    dump_doc("After resize")
    def test_slice_not_preserve(self):
        slice1 = Slice(1)
        slice1.action = "create"
        slice1.size = Size(str(GBSECTOR) + Size.sector_units)
        slice1.start_sector = CYLSIZE

        slice2 = Slice(2)
        slice2.action = "create"
        slice2.size = Size(str(GBSECTOR) + Size.sector_units)
        slice2.start_sector = CYLSIZE + GBSECTOR + 1

        slice3 = Slice(3)
        slice3.action = "destroy"
        slice3.size = Size(str(GBSECTOR) + Size.sector_units)
        slice3.start_sector = CYLSIZE + (2 * GBSECTOR + 2)

        self.disk.insert_children([slice1, slice2, slice3])

        t = instantiation.TargetInstantiation("test_ti")
        try:
            t.execute(dry_run=True)
        except Exception as err:
            self.fail(str(err))
    def test_create_single_partition(self):
        part = Partition(2)
        part.action = "create"
        part.start_sector = 0
        part.size = Size("50" + Size.gb_units)
        part.part_type = "primary"
        part.bootid = Partition.ACTIVE

        self.disk.insert_children(part)

        t = instantiation.TargetInstantiation("test_ti")
        try:
            t.execute(dry_run=True)
        except Exception as err:
            self.fail(str(err))
    def setUp(self):
        self.engine = engine_test_utils.get_new_engine_instance()
        self.doc = self.engine.data_object_cache.volatile

        # create some DOC objects
        self.target = Target(Target.DESIRED)
        self.doc.insert_children(self.target)

        self.disk = Disk("disk")
        self.disk.ctd = "c8t1d0"
        self.disk.disk_prop = DiskProp()
        self.disk.disk_prop.dev_size = Size(str(200) + Size.gb_units)
        self.disk.disk_prop.blocksize = BLOCKSIZE
        self.disk.disk_prop.cylsize = CYLSIZE
        self.disk.disk_geometry = DiskGeometry()
        self.target.insert_children(self.disk)
Example #19
0
    def discover_gptpartition(self, partition, blocksize, efipart):
        """ discover_gptpartition - method to discover a physical disk's GPT
        partition layout.

        # Note that libdiskmgmt terminology treats GPT partitions as slices
        # as does the Solaris cXtYdZsN notation
        # We shall refer to them as GPT partitions though, like the rest of
        # the world does.
        partition - object as discovered by ldm
        blocksize - blocksize of the disk
        efipart - DK_PART EFI cstruct
        """
        # store the attributes locally so libdiskmgt doesn't have to keep
        # looking them up
        gpart_attributes = partition.attributes

        new_gpart = GPTPartition(str(gpart_attributes.index))
        new_gpart.action = "preserve"
        new_gpart.size = Size(str(gpart_attributes.size) + Size.sector_units,
                              blocksize=blocksize)
        new_gpart.start_sector = long(gpart_attributes.start)
        new_gpart.size_in_sectors = gpart_attributes.size

        stats = partition.use_stats
        if "used_by" in stats:
            new_gpart.in_use = stats

        # EFI partition type GUID and p_flag not provided by libdiskmgt.  Fall
        # back on libefi provided DK_PART data.
        new_gpart.part_type = '{%s}' % str(efipart.p_guid)
        new_gpart.guid = copy.copy(efipart.p_guid)
        new_gpart.uguid = copy.copy(efipart.p_uguid)
        new_gpart.flag = efipart.p_flag

        # If the partition is an EFI system partition check to see if it
        # is PCFS formatted. We only do this on EFI system partition to save
        # time since mounting can be slow.
        if new_gpart.is_efi_system:
            ctds = partition.name.rsplit('/', 1)[-1]
            new_gpart._is_pcfs_formatted = self.is_pcfs_formatted(ctds)

        return new_gpart
Example #20
0
    def __init__(self, methodName="runTest"):
        unittest.TestCase.__init__(self, methodName)

        # extract drive informtion to construct a bare-bones DOC object
        dmd = diskmgt.descriptor_from_key(ALIAS, MASTER_CTD)
        alias = diskmgt.DMAlias(dmd.value)
        drive = alias.drive
        dma = drive.media.attributes

        if dma.ncylinders is None:
            raise RuntimeError("Unable to process disk label.  Please " +
                               "place a VTOC label on the disk.")

        # get the maximum size of the disk
        fd = os.open("/dev/rdsk/" + MASTER_CTD + "s2",
                     os.O_RDONLY | os.O_NDELAY)
        try:
            media_info = drive.DKMinfo()
            fcntl.ioctl(fd, diskmgt.DKIOCGMEDIAINFO, C.addressof(media_info))
        except IOError as error:
            print 'ioctl failed: ', str(error)
            raise
        finally:
            os.close(fd)

        # set the basic geometry
        self.disk = Disk(MASTER_CTD)
        self.disk.ctd = MASTER_CTD
        self.disk.disk_prop = DiskProp()
        self.disk.disk_prop.dev_size = Size(
            str(media_info.dki_capacity) + Size.sector_units)
        self.disk_size = self.disk.disk_prop.dev_size.sectors

        self.disk.geometry = DiskGeometry(dma.blocksize,
                                          dma.nheads * dma.nsectors)
        self.disk.geometry.ncyl = dma.ncylinders
        self.disk.geometry.nhead = dma.nheads
        self.disk.geometry.nsectors = dma.nsectors

        self.target = Target(Target.DESIRED)
        self.target.insert_children(self.disk)
    def validate(self):
        '''Validate the size of the disk.'''

        warning_txt = []

        disk = self.disk_detail.ui_obj.doc_obj
        disk_size_gb = disk.disk_prop.dev_size.get(Size.gb_units)
        max_size_gb = Size(MAX_VTOC).get(Size.gb_units)
        if disk_size_gb > max_size_gb:
            warning_txt.append(self.disk_warning_too_big)
        warning_txt = " ".join(warning_txt)

        if warning_txt:
            # warn the user and give user a chance to change
            result = self.main_win.pop_up(DiskScreen.DISK_WARNING_HEADER,
                                          warning_txt,
                                          DiskScreen.CANCEL_BUTTON,
                                          DiskScreen.CONTINUE_BUTTON)

            if not result:
                raise UIMessage()  # let user select different disk
Example #22
0
def get_image_size(logger):
    '''Total size of the software in the image is stored in the
       .image_info indicated by the keywoard IMAGE_SIZE.
       This function retrieves that value from the .image_file
       The size recorded in the .image_file is in KB, other functions
       in this file uses the value in MB, so, this function will
       return the size in MB

       Returns:
           size of retrieved from the .image_info file in MB

    '''

    # Depending on how we are booted, get the .image_info file accordingly.
    if is_net_booted(logger):
        image_info_file = os.path.join(NetPrepareMediaTransfer.MEDIA_SOURCE,
                                       IMAGE_INFO_FILENAME)
    else:
        image_info_file = os.path.join(PrepareMediaTransfer.MEDIA_SOURCE,
                                       IMAGE_INFO_FILENAME)

    img_size = 0
    with open(image_info_file, 'r') as ih:
        for line in ih:
            (opt, val) = line.split("=")
            if opt == IMAGE_SIZE_KEYWORD:
                # Remove the '\n' character read from
                # the file, and convert to integer
                img_size = int(val.rstrip('\n'))
                break

    if (img_size == 0):
        # We should have read in a size by now
        logger.error("Unable to read the image size from %s", image_info_file)
        raise RuntimeError

    logger.debug("Read from %s size of %s" % (image_info_file, img_size))
    return (Size(str(img_size) + Size.kb_units).get(Size.mb_units))
    def discover_zpools(self, search_name=""):
        """ discover_zpools - method to walk zpool list output to create Zpool
        objects.  Returns a logical DOC object with all zpools populated.
        """
        # create a logical element
        logical = Logical("logical")

        # set noswap and nodump to True until a zvol is found otherwise
        logical.noswap = True
        logical.nodump = True

        # retreive the list of zpools
        cmd = [ZPOOL, "list", "-H", "-o", "name"]
        p = run(cmd)

        # Get the list of zpools
        zpool_list = p.stdout.splitlines()

        # walk the list and populate the DOC
        for zpool_name in zpool_list:
            # if the user has specified a specific search name, only run
            # discovery on that particular pool name
            if search_name and zpool_name != search_name:
                continue

            self.logger.debug("Populating DOC for zpool:  %s", zpool_name)

            # create a new Zpool DOC object and insert it
            zpool = Zpool(zpool_name)
            zpool.action = "preserve"
            logical.insert_children(zpool)

            # check to see if the zpool is the boot pool
            cmd = [ZPOOL, "list", "-H", "-o", "bootfs", zpool_name]
            p = run(cmd)
            if p.stdout.rstrip() != "-":
                zpool.is_root = True

            # get the mountpoint of the zpool
            cmd = [ZFS, "get", "-H", "-o", "value", "mountpoint", zpool_name]
            p = run(cmd)
            zpool.mountpoint = p.stdout.strip()

            # set the vdev_mapping on each physical object in the DOC tree for
            # this zpool
            self.set_vdev_map(zpool)

            # for each zpool, get all of its datasets.  Switch to the C locale
            # so we don't have issues with LC_NUMERIC settings
            cmd = [
                ZFS, "list", "-r", "-H", "-o", "name,type,used,mountpoint",
                zpool_name
            ]
            p = run(cmd, env={"LC_ALL": "C"})

            # walk each dataset and create the appropriate DOC objects for
            # each.  Skip the first line of list output, as the top level
            # dataset (also the dataset with the same name as that of the
            # zpool) may have a different mountpoint than the zpool.
            for dataset in p.stdout.rstrip().split("\n")[1:]:
                try:
                    name, ds_type, ds_size, mountpoint = dataset.split(None, 3)
                except ValueError as err:
                    # trap on ValueError so any inconsistencies are captured
                    self.logger.debug("Unable to process dataset: %r" %
                                      dataset)
                    self.logger.debug(str(err))
                    continue

                # fix the name field to remove the name of the pool
                name = name.partition(zpool_name + "/")[2]

                if ds_type == "filesystem":
                    obj = Filesystem(name)
                    obj.mountpoint = mountpoint
                elif ds_type == "volume":
                    obj = Zvol(name)
                    obj.size = Size(ds_size)

                    # check for swap/dump.  If there's a match, set the zvol
                    # 'use' attribute and the noswap/nodump attribute of
                    # logical.  The zpool name needs to be re-attached to the
                    # zvol name to match what was already parsed
                    if os.path.join(zpool_name, name) in self.swap_list:
                        obj.use = "swap"
                        logical.noswap = False
                    if os.path.join(zpool_name, name) in self.dump_list:
                        obj.use = "dump"
                        logical.nodump = False

                obj.action = "preserve"
                zpool.insert_children(obj)

        return logical
Example #24
0
    def cylinder_boundary_adjustment(self, value):
        """ cylinder_boundary_adjustment() - method to adjust a Partition or
        Slice object's start_sector and size value to fall on cylinder
        boundaries

        value - DOC object to adjust
        """
        # only make adjustments when the action is 'create'
        if value.action != "create":
            return value

        # determine the cylsize based on the container object
        if hasattr(self.container, "geometry"):
            # container is a Disk object
            cyl_boundary = self.container.geometry.cylsize
            disk_size = self.container.disk_prop.dev_size.sectors
            arch = self.container.kernel_arch
        elif hasattr(self.container, "part_type"):
            # container is a Partition object
            cyl_boundary = self.container.parent.geometry.cylsize
            disk_size = self.container.parent.disk_prop.dev_size.sectors
            arch = self.container.parent.kernel_arch

        # adjust the start_sector up to the next cylinder boundary
        if value.start_sector % cyl_boundary != 0:
            new_start_sector = ((value.start_sector / cyl_boundary) * \
                               cyl_boundary) + cyl_boundary

            # adjust the size down by the same amount
            difference = new_start_sector - value.start_sector
            value.start_sector = new_start_sector
            value.size = Size(str(value.size.sectors - difference) + \
                              Size.sector_units)

        # check the start_sector of the object.  If it starts at zero, adjust
        # it to start at the first cylinder boundary instead so as not to
        # clobber the disk label
        if value.start_sector == 0:
            value.start_sector = cyl_boundary
            value.size = Size(str(value.size.sectors - cyl_boundary) + \
                              Size.sector_units)

        # adjust the size down to the nearest end cylinder
        if value.size.sectors % cyl_boundary != 0:
            new_size = (value.size.sectors / cyl_boundary) * cyl_boundary
            value.size = Size(str(new_size) + Size.sector_units)

        # x86 specific check for slices and partitions
        if arch == "x86":
            if hasattr(value, "force"):
                # The largest possible size for any slice (other than slice 2),
                # is disk size - 4 cylinders.  1 cylinder for the MBR, 2
                # cylinders for the VTOC label, and 1 cylinder for slice 8
                max_cyl = 4
            elif hasattr(value, "part_type"):
                # The largest possible size for any partition is disk_size - 1
                # cylinder (for the MBR)
                max_cyl = 1

            # adjust the value's size if it's larger than the cylinder maximum
            if (disk_size - value.size.sectors) / cyl_boundary < max_cyl:
                end_cylinder = ((disk_size / cyl_boundary) - max_cyl) * \
                               cyl_boundary
                value.size = Size(str(end_cylinder) + Size.sector_units)

        return value
Example #25
0
    def insert_partition(self, index, value):
        """ insert_partition() - override method for validation of
        Partition DOC objects.

        the following checks are done as part of validation:

        - Partition objects *must* have a part_type.

        - the parent Disk object does not have whole_disk attribute set

        - the start_sector of the slice is an int or long and is between 0 and
          the container's maximum size

        - if a partition is a logical partition, ensure there is an extended
          partition somewhere in indexes 1-4

        - no more than MAX_EXT_PARTS logical partitions

        - logical partitions fall within the boundaries of the extended
          partition

        - Only one active primary partition

        - primary partitions are not larger than the Disk

        - no overlapping boundaries of the partition with any other partitions
          already inserted

        - no duplicate indexes

        - the extended partition is at least 63 sectors in size and there is
          only one extended partition specified

        - none of the parent objects have an in_zpool or in_vdev attribute set
        """
        # verify part_type is not None
        if value.part_type is None:
            self.set_error(self.PartitionTypeMissingError())

        # verify the name of the partition is valid
        if not (1 <= int(value.name) <= (FD_NUMPART + MAX_EXT_PARTS)):
            self.set_error(
                self.InvalidPartitionNameError(str(value.name),
                                               self.container.ctd))

        # fix the start_sector and size to align to cylinder boundaries for
        # primary partitions
        if value.is_primary:
            value = self.cylinder_boundary_adjustment(value)

        # check the bounds of the partition to be added.
        if not (isinstance(value.start_sector, int) or \
           isinstance(value.start_sector, long)):
            self.set_error(self.InvalidPartitionStartSectorError())

        if hasattr(self.container.disk_prop, "dev_size") and \
           getattr(self.container.disk_prop, "dev_size") is not None:
            if not (0 <= value.start_sector <= \
               self.container.disk_prop.dev_size.sectors):
                self.set_error(self.InvalidPartitionStartSectorError())

        # verify that the Disk does not have 'whole_disk' set to 'true'
        if self.container.whole_disk:
            self.set_error(self.WholeDiskIsTrueError())

        # if the partition is a logical partition, ensure there is a partition
        # with part_type in Partition.EXTENDED_ID_LIST has already been
        # inserted.
        if value.is_logical:
            extended_part = None
            for partition in self._shadow:
                if partition.is_extended:
                    if partition.part_type in partition.EXTENDED_ID_LIST:
                        extended_part = partition

            if extended_part is None:
                self.set_error(self.NoExtPartitionsError())
            else:
                # verify there are not more than MAX_EXT_PARTS
                logical_list = [p for p in self._shadow if p.is_logical and \
                                p.action != "delete"]
                if len(logical_list) >= MAX_EXT_PARTS:
                    self.set_error(self.TooManyLogicalPartitionsError())

                # ensure this logical partition does not start too close to any
                # previously inserted logical partitions.

                # sort the logical list by start sector
                slist = sorted(
                    logical_list,
                    lambda x, y: cmp(x.start_sector, y.start_sector))

                # find the closest logical partition within the extended
                # partition
                closest_endpoint = 0
                for logical in slist:
                    end_point = logical.start_sector + logical.size.sectors
                    if end_point < value.start_sector and \
                       end_point > closest_endpoint:
                        closest_endpoint = end_point

                if closest_endpoint == 0:
                    # no logical partitions were found, so use the start of the
                    # extended partition, if the difference is smaller than the
                    # needed offset
                    diff = value.start_sector - extended_part.start_sector
                    if diff < LOGICAL_ADJUSTMENT and value.action == "create":
                        value.start_sector = extended_part.start_sector + \
                                             LOGICAL_ADJUSTMENT
                        new_size = value.size.sectors - LOGICAL_ADJUSTMENT
                        value.size = Size(str(new_size) + Size.sector_units)
                else:
                    diff = value.start_sector - closest_endpoint
                    # make sure there's at least 63 sectors between logical
                    # partitions
                    if diff < LOGICAL_ADJUSTMENT and value.action == "create":
                        value.start_sector += LOGICAL_ADJUSTMENT - diff

                        new_size = value.size.sectors - LOGICAL_ADJUSTMENT
                        value.size = Size(str(new_size) + Size.sector_units)

        # check the bootid attibute on primary partitions for multiple active
        # partitions
        if value.is_primary and value.bootid == value.ACTIVE:
            for partition in self._shadow:
                # skip logical partitions
                if partition.is_logical:
                    continue

                # check the bootid attribute
                if partition.bootid == value.ACTIVE:
                    self.set_error(
                        self.MultipleActivePartitionsError(partition.name))

        p_start = value.start_sector
        p_end = p_start + value.size.sectors - 1

        # walk each inserted partition already in the shadow list to ensure the
        # partition we're trying to insert doesn't cross boundaries.
        for partition in self._shadow:
            # start and end points of the partition to check
            if partition.is_primary:
                start = partition.start_sector
                end = start + partition.size.sectors - 1
            else:
                # for logical partitions, there needs to be a buffer of
                # LOGICAL_ADJUSTMENT on each end
                start = partition.start_sector - LOGICAL_ADJUSTMENT
                end = start + partition.size.sectors - 1 + LOGICAL_ADJUSTMENT

            if value.is_primary:
                # do not test logical partition boundaries for primary
                # partitions
                if partition.is_logical:
                    continue
            else:
                # verify the logical partition we're trying to insert fits
                # within the extended partition "parent"
                if partition.is_extended and partition.action != "delete":
                    if p_start < start or p_end > end:
                        self.set_error(self.LogicalPartitionOverlapError())

                # do not test primary partition boundaries for logical
                # partitions
                if partition.is_primary:
                    continue

            # check that the start sector of the partition we're trying to
            # insert is not within another partition and the existing partition
            # isn't inside the partition we're inserting.  Primary partitions
            # are only checked against other primary partitions and logical
            # partitions are only checked aginst other logical partitions.
            # Partitions marked for deletion should not be checked at all
            if value.action != "delete" and partition.action != "delete":
                if ((start <= p_start <= end) or (start <= p_end <= end)) or \
                   ((p_start <= start <= p_end) or (p_start <= end <= p_end)):
                    self.set_error(
                        self.OverlappingPartitionError(partition.name,
                                                       value.name))

        # check that a primary partition doesn't exceed the size of the Disk,
        # if the dev_size is specified
        if hasattr(self.container.disk_prop, "dev_size") and \
           self.container.disk_prop.dev_size is not None and \
           value.is_primary:
            disk_size = self.container.disk_prop.dev_size.sectors
            p_size = value.start_sector + value.size.sectors

        # check that the name of the partition is not already in the list
        if value.name in [p.name for p in self._shadow \
                          if p.action != "delete"]:
            self.set_error(self.DuplicatePartitionNameError(value.name))

        # if this is an extended partition, verify there are no other
        # partitions of the same type.  Also verify it's at least
        # LOGICAL_ADJUSTMENT sectors in size
        if value.is_extended:
            for partition in self._shadow:
                if partition.is_extended and partition.action != "delete":
                    self.set_error(self.TooManyExtPartitionsError())
            if value.size.sectors < LOGICAL_ADJUSTMENT:
                self.set_error(self.ExtPartitionTooSmallError())

        # if the partition type is FAT16, make sure it's not larger than 4GB
        fat16_list = [
            value.name_to_num("FAT16 (Upto 32M)"),
            value.name_to_num("FAT16 (>32M, HUGEDOS)"),
            value.name_to_num("WIN95 FAT16(LBA)")
        ]
        if value.part_type in fat16_list and value.action == "create":
            if value.size.byte_value > Size.units["gb"] * 4:
                self.set_error(self.FAT16PartitionTooLargeError())

        # check to see if the container object has the same in_zpool attribute
        if value.in_zpool is not None:
            if getattr(self.container, "in_zpool") == value.in_zpool:
                self.set_error(self.OverlappingPartitionZpoolError())

        # check to see if the container object has the same in_vdev attribute
        if value.in_vdev is not None:
            if getattr(self.container, "in_vdev") == value.in_vdev:
                self.set_error(self.OverlappingPartitionVdevError())

        # insert the partition
        ShadowList.insert(self, index, value)
 def test_error_if_no_suitable_disk(self):
     '''Validate error raised if "discovered targets" are too small'''
     tc = TargetController(self.doc)
     self.assertRaises(BadDiskError, tc.initialize,
         image_size=Size("1TB"))
Example #27
0
    def _show(self):
        '''Create a list of disks to choose from and create the window
        for displaying the partition/slice information from the selected
        disk
        '''

        self.wait_for_disks()
        self.wait_for_iscsi_disk()

        discovered_target = self.doc.persistent.get_first_child( \
            name=Target.DISCOVERED)

        LOGGER.debug(discovered_target)
        if discovered_target is None:
            self.center_win.add_paragraph(DiskScreen.NO_DISKS,
                                          1,
                                          1,
                                          max_x=(self.win_size_x - 1))
            return

        self.disks = discovered_target.get_children(class_type=Disk)
        if not self.disks:
            self.center_win.add_paragraph(DiskScreen.NO_TARGETS,
                                          1,
                                          1,
                                          max_x=(self.win_size_x - 1))
            return

        if self._image_size is None:
            try:
                self._image_size = Size(str(get_image_size(LOGGER)) + \
                    Size.mb_units)
                LOGGER.debug("Image_size: %s", self._image_size)
            except:
                # Unable to get the image size for some reason, allow
                # the target controller to use it's default size.
                LOGGER.debug("Unable to get image size")
                self._image_size = FALLBACK_IMAGE_SIZE

        # initialize the target controller so the min/max size for the
        # installation can be calculated.  Explicitly do not want to select an
        # initial disk at this time in case none of the disks discovered is
        # usable.  The target controller initialization needs to be done
        # everytime we show the disk selection screen so the desired target
        # node in the DOC can be re-populated with information from target
        # discovery.
        self.tc.initialize(image_size=self._image_size, no_initial_disk=True)

        # Go through all the disks found and find ones that have enough space
        # for installation.  At the same time, see if any existing disk is the
        # boot disk.  If a boot disk is found, move it to the front of the list
        num_usable_disks = 0
        boot_disk = None
        for disk in self.disks:
            LOGGER.debug("size: %s, min: %s" % \
                         (disk.disk_prop.dev_size, self.minimum_size))
            if disk.disk_prop.dev_size >= self.minimum_size:
                if disk.is_boot_disk():
                    boot_disk = disk
                num_usable_disks += 1

        if boot_disk is not None:
            self.disks.remove(boot_disk)
            self.disks.insert(0, boot_disk)

        if num_usable_disks == 0:
            self.center_win.add_paragraph(DiskScreen.NO_DISKS,
                                          1,
                                          1,
                                          max_x=(self.win_size_x - 1))
            return

        self.main_win.reset_actions()
        self.main_win.show_actions()

        y_loc = 1
        self.center_win.add_text(DiskScreen.PARAGRAPH, y_loc, 1)

        y_loc += 1
        self.center_win.add_text(self.size_line, y_loc, 1)

        y_loc += 2
        self.center_win.add_text(self.disk_header_text, y_loc, 1)

        y_loc += 1
        self.center_win.window.hline(y_loc, self.center_win.border_size[1] + 1,
                                     curses.ACS_HLINE,
                                     textwidth(self.disk_header_text))

        y_loc += 1
        disk_win_area = WindowArea(4,
                                   textwidth(self.disk_header_text) + 2, y_loc,
                                   0)
        disk_win_area.scrollable_lines = len(self.disks) + 1
        self.disk_win = ScrollWindow(disk_win_area, window=self.center_win)

        disk_item_area = WindowArea(1, disk_win_area.columns - 2, 0, 1)
        disk_index = 0
        len_type = DiskScreen.DISK_HEADERS[0][0] - 1
        len_size = DiskScreen.DISK_HEADERS[1][0] - 1
        len_boot = DiskScreen.DISK_HEADERS[2][0] - 1
        len_dev = DiskScreen.DISK_HEADERS[3][0] - 1
        len_notes = DiskScreen.DISK_HEADERS[4][0] - 1
        for disk in self.disks:
            disk_text_fields = []
            dev_type = disk.disk_prop.dev_type
            if dev_type is not None:
                type_field = dev_type[:len_type]
                type_field = ljust_columns(type_field, len_type)
            else:
                type_field = " " * len_type
            disk_text_fields.append(type_field)
            disk_size = disk.disk_prop.dev_size.get(Size.gb_units)
            size_field = locale.format("%*.1f", (len_size, disk_size))
            disk_text_fields.append(size_field)
            if disk.is_boot_disk():
                bootable_field = "+".center(len_boot)
            else:
                bootable_field = " " * (len_boot)
            disk_text_fields.append(bootable_field)

            #
            # Information will be displayed in the device column with
            # the following priority:
            #
            # First priority is to display receptacle information,
            # if available.  If receptacle information is displayed,
            # ctd name will not be displayed.
            #
            # If receptacle information is not available, the ctd name
            # will be displayed.
            #
            # Both items above can take as much as the 44 character wide
            # column as needed.
            #
            # If the receptacle/ctd name is less than 30 characters,
            # manufacturer information will be displayed in the left
            # over space.  There won't be a column heading for the
            # manufacturer information.
            #

            device = disk.receptacle or disk.ctd
            added_device_field = False
            # is there enough room to display the manufacturer?
            if (len_dev - len(device)) >= DiskScreen.VENDOR_LEN:
                vendor = disk.disk_prop.dev_vendor
                if vendor is not None:
                    dev_display_len = len_dev - DiskScreen.VENDOR_LEN
                    device_field = ljust_columns(device, dev_display_len)
                    disk_text_fields.append(device_field)
                    vendor_field = vendor[:DiskScreen.VENDOR_LEN - 1]
                    vendor_field = ljust_columns(vendor_field,
                                                 DiskScreen.VENDOR_LEN - 1)
                    disk_text_fields.append(vendor_field)
                    added_device_field = True

            if not added_device_field:
                device_field = device[:len_dev]
                device_field = ljust_columns(device_field, len_dev)
                disk_text_fields.append(device_field)

            # display "<" or ">" if the disk is too big or too small
            selectable = True
            if disk.disk_prop.dev_size < self.minimum_size:
                selectable = False
                notes_field = DiskScreen.TOO_SMALL.center(len_notes)
                disk_text_fields.append(notes_field)
            elif disk.disk_prop.dev_size > Size(MAX_VTOC):
                notes_field = DiskScreen.TOO_BIG.center(len_notes)
                disk_text_fields.append(notes_field)

            # check the blocksize of the disk.  If it's not 512 bytes and we
            # have an EFI firmware on x86, make the disk unselectable by the
            # user.  See PSARC 2008/769
            elif platform.processor() == "i386" and \
                 disk.geometry.blocksize != 512:
                firmware = SystemFirmware.get()
                if firmware.fw_name == "uefi64":
                    selectable = False
                    notes_field = DiskScreen.INVALID_DISK.center(len_notes)
                    disk_text_fields.append(notes_field)
                    LOGGER.debug(
                        "marking disk %s unselectable as its "
                        "blocksize is not 512 bytes on an UEFI "
                        "firmware x86 system.", disk.ctd)

            disk_text = " ".join(disk_text_fields)

            disk_item_area.y_loc = disk_index
            disk_list_item = ListItem(disk_item_area,
                                      window=self.disk_win,
                                      text=disk_text,
                                      add_obj=selectable)
            disk_list_item.on_make_active = on_activate
            disk_list_item.on_make_active_kwargs["disk"] = disk
            disk_list_item.on_make_active_kwargs["disk_select"] = self
            disk_index += 1

        self.disk_win.no_ut_refresh()

        y_loc += 7
        disk_detail_area = WindowArea(6, 70, y_loc, 1)

        self.disk_detail = DiskWindow(disk_detail_area,
                                      self.disks[0],
                                      target_controller=self.tc,
                                      window=self.center_win)

        self.main_win.do_update()
        self.center_win.activate_object(self.disk_win)
        self.disk_win.activate_object(self.selected_disk_index, jump=True)
class MockTC(object):
    ''' Mocks the target controller '''

    minimum_target_size = Size("3gb")
    recommended_target_size = Size("6gb")
Example #29
0
class DiskWindow(InnerWindow):
    '''Display and edit disk information, including partitions and slices'''

    STATIC_PARTITION_HEADERS = [(12, _("Primary"), _("Logical")),
                                (10, _(" Size(GB)"), _(" Size(GB)"))]

    EDIT_PARTITION_HEADERS = [(13, _("Primary"), _("Logical")),
                              (10, _(" Size(GB)"), _(" Size(GB)")),
                              (7, _(" Avail"), _(" Avail"))]

    STATIC_SLICE_HEADERS = [(13, _("Slice"), _("Slice")),
                            (2, "#", "#"),
                            (10, _(" Size(GB)"), _(" Size(GB)"))]

    EDIT_SLICE_HEADERS = [(13, _("Slice"), _("Slice")),
                          (2, "#", "#"),
                          (10, _(" Size(GB)"), _(" Size(GB)")),
                          (7, _(" Avail"), _(" Avail"))]

    ADD_KEYS = {curses.KEY_LEFT: no_action,
                curses.KEY_RIGHT: no_action}

    DEAD_ZONE = 3
    SCROLL_PAD = 2

    MIN_SIZE = None
    REC_SIZE = None

    SIZE_PRECISION = Size(UI_PRECISION).get(Size.gb_units)

    DESTROYED_MARK = EditField.ASTERISK_CHAR

    def __init__(self, area, disk_info, editable=False,
                 error_win=None, target_controller=None, **kwargs):
        '''See also InnerWindow.__init__

        disk_info (required) - Either a Disk or Partition object
        containing the data to be represented. If a Partition objects is
        provided, it will be used for displaying slice
        data within that partition. If Disk has partition(s), those are
        displayed. If not, but it has slices, then those are displayed. If
        neither partition data nor slice data are available, a ValueError is
        raised.

        headers (required) - List of tuples to populate the header of this
        window with. The first item in each tuple should be the width of the
        header, the second item should be the left side header.

        editable (optional) - If True, the window will be created such that
        data is editable.

        target_controller(optional) - Target controller

        '''

        global LOGGER
        LOGGER = logging.getLogger(INSTALL_LOGGER_NAME)

        self.headers = None
        self.orig_ext_part_field = None
        self.orig_logicals_active = False
        self.ext_part_field = None
        self.error_win = error_win
        self.editable = editable
        self.win_width = None
        self.left_win = None
        self.right_win = None
        self.list_area = None
        self.edit_area = None
        super(DiskWindow, self).__init__(area, add_obj=editable, **kwargs)
        self.left_header_string = None
        self.right_header_string = None
        self._orig_data = None
        self.disk_info = None
        self.has_partition_data = False
        self.key_dict[curses.KEY_LEFT] = self.on_arrow_key
        self.key_dict[curses.KEY_RIGHT] = self.on_arrow_key
        if self.editable:
            self.key_dict[curses.KEY_F5] = self.change_type

        self.tc = target_controller
        self._ui_obj = None
        self.ui_obj = disk_info

        self.set_disk_info(ui_obj=self.ui_obj)

        LOGGER.debug(self.ui_obj)

        if platform.processor() == "sparc":
            self.is_x86 = False
        else:
            self.is_x86 = True

    @property
    def ui_obj(self):
        return self._ui_obj

    @ui_obj.setter
    def ui_obj(self, part):
        ''' create and set the value for ui_obj depending on type '''
        if isinstance(part, Disk):
            self._ui_obj = UIDisk(self.tc, parent=None, doc_obj=part)
        elif isinstance(part, Partition):
            self._ui_obj = UIPartition(self.tc, parent=None, doc_obj=part)
        else:
            # Must be a either a Disk or Partition.  It's an error to be here
            raise RuntimeError("disk_info object is invalid")

    def _init_win(self, window):
        '''Require at least 70 columns and 6 lines to fit current needs for
        display of partitions and slices. Builds two inner ScrollWindows for
        displaying/editing the data.

        '''
        if self.area.columns < 70:
            raise ValueError("Insufficient space - area.columns < 70")
        if self.area.lines < 6:
            raise ValueError("Insufficient space - area.lines < 6")
        self.win_width = (self.area.columns - DiskWindow.DEAD_ZONE
                          + DiskWindow.SCROLL_PAD) / 2

        super(DiskWindow, self)._init_win(window)

        win_area = WindowArea(self.area.lines - 1, self.win_width, 2, 0)
        win_area.scrollable_lines = self.area.lines - 2
        self.left_win = ScrollWindow(win_area, window=self, add_obj=False)
        self.left_win.color = None
        self.left_win.highlight_color = None
        win_area.x_loc = self.win_width + DiskWindow.DEAD_ZONE
        win_area.scrollable_lines = 2 * MAX_EXT_PARTS
        self.right_win = ScrollWindow(win_area, window=self, add_obj=False)
        self.right_win.color = None
        self.right_win.highlight_color = None

    def set_disk_info(self, ui_obj=None, disk_info=None, no_part_ok=False):
        '''Set up this DiskWindow to represent disk_info'''

        if ui_obj is not None:
            disk_info = ui_obj.doc_obj
        elif disk_info is not None:
            self.ui_obj = disk_info
        else:
            # Should never be this case
            raise RuntimeError("Unable to find ui_obj or disk_info")

        part_list = disk_info.get_children(class_type=Partition)
        if part_list:
            self.has_partition_data = True
        else:
            slice_list = disk_info.get_children(class_type=Slice)
            if slice_list:
                self.has_partition_data = False
            else:
                # No partitions and no slices
                if no_part_ok:
                    if self.is_x86:
                        self.has_partition_data = True
                    else:
                        self.has_partition_data = False
                else:
                    return

        if self.has_partition_data:
            if self.editable:
                self.headers = DiskWindow.EDIT_PARTITION_HEADERS
                self.list_area = WindowArea(1, self.headers[0][0] +
                                            self.headers[1][0],
                                            0, DiskWindow.SCROLL_PAD)
                self.edit_area = WindowArea(1, self.headers[1][0], 0,
                                            self.headers[0][0])
            else:
                self.headers = DiskWindow.STATIC_PARTITION_HEADERS
        else:
            if self.editable:
                self.headers = DiskWindow.EDIT_SLICE_HEADERS
                self.list_area = WindowArea(1, self.headers[0][0] +
                                            self.headers[1][0] +
                                            self.headers[2][0],
                                            0, DiskWindow.SCROLL_PAD)
                self.edit_area = WindowArea(1, self.headers[2][0], 0,
                                            self.headers[0][0] +
                                            self.headers[1][0])
            else:
                self.headers = DiskWindow.STATIC_SLICE_HEADERS

        LOGGER.debug("have_partition: %s", self.has_partition_data)
        LOGGER.debug(self.ui_obj)

        self.ui_obj.add_unused_parts(no_part_ok=no_part_ok)

        self.left_win.clear()
        self.right_win.clear()
        self.window.erase()
        self.print_headers()

        if self.editable:
            self.active_object = None
            self.build_edit_fields()
            self.right_win.bottom = max(0, len(self.right_win.all_objects) -
                                        self.right_win.area.lines)
            if self.has_partition_data:
                self.orig_ext_part_field = None
                for obj in self.left_win.objects:
                    if (obj.data_obj.is_extended()):
                        self.orig_ext_part_field = obj
                        self.orig_logicals_active = True
                        break
        else:
            self.print_data()

    def print_headers(self):
        '''Print the headers for the displayed data.

        header[0] - The width of this column. header[1] and header[2] are
                    trimmed to this size
        header[1] - The internationalized text for the left window
        header[2] - The internationalized text for the right window

        '''
        self.left_header_string = []
        self.right_header_string = []
        for header in self.headers:
            left_header_str = header[1]
            right_header_str = header[2]
            # Trim the header to fit in the column width,
            # splitting columns with at least 1 space
            # Pad with extra space(s) to align the columns
            left_header_str = fit_text_truncate(left_header_str,
                                                header[0] - 1, just="left")
            self.left_header_string.append(left_header_str)
            right_header_str = fit_text_truncate(right_header_str,
                                                header[0] - 1, just="left")
            self.right_header_string.append(right_header_str)
        self.left_header_string = " ".join(self.left_header_string)
        self.right_header_string = " ".join(self.right_header_string)
        LOGGER.debug(self.left_header_string)
        self.add_text(self.left_header_string, 0, DiskWindow.SCROLL_PAD)
        right_win_offset = (self.win_width + DiskWindow.DEAD_ZONE +
                            DiskWindow.SCROLL_PAD)
        self.add_text(self.right_header_string, 0, right_win_offset)
        self.window.hline(1, DiskWindow.SCROLL_PAD, curses.ACS_HLINE,
                          textwidth(self.left_header_string))
        self.window.hline(1, right_win_offset, curses.ACS_HLINE,
                          textwidth(self.right_header_string))
        self.no_ut_refresh()

    def print_data(self):
        '''Print static (non-editable) data.

        Slices - fill the left side, then remaining slices on the right side.
        If for some reason not all slices fit, indicate how many more slices
        there area

        Partitions - Put standard partitions on the left, logical partitions
        on the right

        '''

        part_index = 0
        data = self.ui_obj.get_parts_in_use()

        if len(data) == 0:
            return   # should never be this case

        if self.has_partition_data:
            max_parts = MAX_PRIMARY_PARTS
        else:
            max_parts = min(len(data), self.left_win.area.lines)

        win = self.left_win
        y_loc = 0
        for next_data in data:
            LOGGER.debug("next_data: %s", next_data)
            if y_loc >= max_parts:
                if win is self.left_win:
                    win = self.right_win
                    y_loc = 0
                    max_parts = win.area.lines
                else:
                    num_extra = len(data) - part_index
                    if self.has_partition_data:
                        more_parts_txt = _("%d more partitions") % num_extra
                    else:
                        more_parts_txt = _("%d more slices") % num_extra
                    win.add_text(more_parts_txt, win.area.lines, 3)
                    break
            x_loc = DiskWindow.SCROLL_PAD
            field = 0
            win.add_text(next_data.get_description(), y_loc, x_loc,
                         self.headers[field][0] - 1)
            x_loc += self.headers[field][0]
            field += 1
            if not self.has_partition_data:
                win.add_text(str(next_data.name), y_loc, x_loc,
                             self.headers[field][0] - 1)
                x_loc += self.headers[field][0]
                field += 1
            win.add_text(locale.format("%*.1f", (self.headers[field][0] - 1,
                next_data.size.get(Size.gb_units))), y_loc, x_loc,
                self.headers[field][0] - 1)
            x_loc += self.headers[field][0]
            y_loc += 1
            field += 1
            part_index += 1
        self.right_win.use_vert_scroll_bar = False
        self.no_ut_refresh()

    def build_edit_fields(self):
        '''Build subwindows for editing partition sizes

        For slices, fill the left side, then the right (right side scrolling as
        needed, though this shouldn't happen unless the number of slices on
        disk exceeds 8 for some reason)

        For partitions, fill the left side up to MAX_PRIMARY_PARTS,
        and place all logical partitions on the right.

        '''

        data = self.ui_obj.get_parts_in_use()

        if self.has_partition_data:
            max_left_parts = MAX_PRIMARY_PARTS
        else:
            if len(data) == 0:
                return   # should never be this case
            max_left_parts = min(len(data), self.left_win.area.lines)

        part_iter = iter(data)
        try:
            next_part = part_iter.next()
            self.objects.append(self.left_win)
            for y_loc in range(max_left_parts):
                self.list_area.y_loc = y_loc
                self.create_list_item(next_part, self.left_win, self.list_area)
                next_part = part_iter.next()
            self.objects.append(self.right_win)
            for y_loc in range(self.right_win.area.scrollable_lines):
                self.list_area.y_loc = y_loc
                self.create_list_item(next_part, self.right_win,
                                      self.list_area)
                next_part = part_iter.next()
            if len(data) > max_left_parts:
                self.right_win.use_vert_scroll_bar = True
        except StopIteration:
            if len(self.right_win.all_objects) <= self.right_win.area.lines:
                self.right_win.use_vert_scroll_bar = False
            self.right_win.no_ut_refresh()
        else:
            raise ValueError("Could not fit all partitions in DiskWindow")
        self.no_ut_refresh()

    def create_list_item(self, next_part, win, list_area):
        '''Add an entry for next_part (a Partition or Slice) to
        the DiskWindow

        '''
        list_item = ListItem(list_area, window=win, data_obj=next_part)
        list_item.key_dict.update(DiskWindow.ADD_KEYS)
        edit_field = EditField(self.edit_area, window=list_item,
                               numeric_pad=" ",
                               validate=decimal_valid,
                               on_exit=on_exit_edit,
                               error_win=self.error_win,
                               add_obj=False,
                               data_obj=next_part)
        edit_field.right_justify = True
        edit_field.validate_kwargs["disk_win"] = self
        edit_field.on_exit_kwargs["disk_win"] = self
        edit_field.key_dict.update(DiskWindow.ADD_KEYS)
        self.update_part(part_field=list_item)
        return list_item

    def update_part(self, part_info=None, part_field=None):
        '''Sync changed partition data to the screen.'''
        if part_field is None:
            if part_info is None:
                raise ValueError("Must supply either part_info or part_field")
            part_field = self.find_part_field(part_info)[1]
        elif part_info is None:
            part_info = part_field.data_obj
        elif part_field.data_obj is not part_info:
            raise ValueError("part_field must be a ListItem associated with "
                             "part_info")
        if not isinstance(part_field, ListItem):
            raise TypeError("part_field must be a ListItem associated with "
                            "part_info")
        if self.has_partition_data:
            desc_text = part_info.get_description()
        else:
            desc_length = self.headers[0][0] - 1
            desc_text = "%-*.*s %s" % (desc_length, desc_length,
                                       part_info.get_description(),
                                       part_info.name)
        part_field.set_text(desc_text)
        edit_field = part_field.all_objects[0]
        edit_field.set_text(locale.format("%.1f",
                                          part_info.size.get(Size.gb_units)))
        self.mark_if_destroyed(part_field)
        self._update_edit_field(part_info, part_field, edit_field)

        self.update_avail_space(part_info=part_info)
        if self.has_partition_data:
            if part_info.is_extended():
                self.ext_part_field = part_field

    def _update_edit_field(self, part_info, part_field, edit_field):
        '''If the partition/slice is editable, add it to the .objects list.
        If it's also the part_field that's currently selected, then activate
        the edit field.

        '''
        if part_info.editable():
            part_field.objects = [edit_field]
            active_win = self.get_active_object()
            if active_win is not None:
                if active_win.get_active_object() is part_field:
                    part_field.activate_object(edit_field)
        else:
            edit_field.make_inactive()
            part_field.objects = []
            part_field.active_object = None

    def mark_if_destroyed(self, part_field):
        '''Determine if the partition/slice represented by part_field has
        changed such that its contents will be destroyed.

        '''
        part_info = part_field.data_obj
        destroyed = part_info.modified()
        self.mark_destroyed(part_field, destroyed)

    def mark_destroyed(self, part_field, destroyed):
        '''If destroyed is True, add an asterisk indicating that the
        partition or slice's content will be destroyed during installation.
        Otherwise, clear the asterisk

        '''
        y_loc = part_field.area.y_loc
        x_loc = part_field.area.x_loc - 1
        if part_field in self.right_win.objects:
            win = self.right_win
        else:
            win = self.left_win
        if destroyed:
            win.window.addch(y_loc, x_loc, DiskWindow.DESTROYED_MARK,
                             win.color_theme.inactive)
        else:
            win.window.addch(y_loc, x_loc, InnerWindow.BKGD_CHAR)

    def update_avail_space(self, part_number=None, part_info=None):
        '''Update the 'Avail' column for the specified slice or partition.
        If no number is given, all avail columns are updated

        '''
        if part_number is None and part_info is None:
            self._update_all_avail_space()
        else:
            self._update_avail_space(part_number, part_info)

    def _update_all_avail_space(self):
        '''Update the 'Avail' column for all slices or partitions.'''
        idx = 0
        for item in self.left_win.objects:
            self.update_avail_space(idx)
            idx += 1
        for item in self.right_win.objects:
            self.update_avail_space(idx)
            idx += 1
        y_loc = idx - len(self.left_win.objects)
        if self.has_partition_data:
            x_loc = self.headers[0][0] + self.headers[1][0] + 1
            field = 2
        else:
            x_loc = (self.headers[0][0] + self.headers[1][0] +
                     self.headers[2][0] + 1)
            field = 3
        if y_loc > 0:
            self.right_win.add_text(" " * self.headers[field][0],
                                    y_loc, x_loc)

    def _update_avail_space(self, part_number=None, part_info=None):
        '''Update the 'Avail' column for the specified slice or partition.'''
        if part_number is None:
            win, item = self.find_part_field(part_info)
        elif part_number < len(self.left_win.objects):
            win = self.left_win
            item = win.objects[part_number]
        else:
            win = self.right_win
            item = win.objects[part_number - len(self.left_win.objects)]
        if self.has_partition_data:
            x_loc = self.headers[0][0] + self.headers[1][0] + 1
            field = 2
        else:
            x_loc = (self.headers[0][0] + self.headers[1][0] +
                     self.headers[2][0] + 1)
            field = 3
        y_loc = item.area.y_loc
        part = item.data_obj
        max_space = part.get_max_size()
        max_space = locale.format("%*.1f", (self.headers[field][0],
                                             max_space.get(Size.gb_units)))
        win.add_text(max_space, y_loc, x_loc)

    def find_part_field(self, part_info):
        '''Given a PartitionInfo or SliceInfo object, find the associated
        ListItem. This search compares by reference, and will only succeed
        if you have a handle to the exact object referenced by the ListItem

        '''
        for win in [self.left_win, self.right_win]:
            for item in win.objects:
                if item.data_obj is part_info:
                    return win, item
        raise ValueError("Part field not found")

    def reset(self, dummy=None):
        '''Reset ui_obj to value found from Target Discovery.
        Meaningful only for editable DiskWindows

        '''
        if not self.editable:
            return
        doc = InstallEngine.get_instance().doc

        # "reset" the desired target
        reset_obj = None
        if isinstance(self.ui_obj, UIDisk):
            reset_obj = (self.tc.reset_layout(disk=self.ui_obj.doc_obj))[0]
        else:
            # reset the partition by removing the modified Partition, and
            # resetting it with the partition found during target discovery.

            discovered_obj = self.ui_obj.discovered_doc_obj

            desired_disk = get_desired_target_disk(doc)
            desired_part = get_solaris_partition(doc)

            desired_disk.delete_partition(desired_part)
            part_copy = deepcopy(discovered_obj)
            desired_disk.insert_children(part_copy)

            # get the updated reference
            reset_obj = get_solaris_partition(doc)

        dump_doc("After doing reset")

        self.set_disk_info(disk_info=reset_obj)
        self.activate_solaris_data()

    def activate_solaris_data(self):
        '''Find the Solaris Partition / ZFS Root Pool Slice and activate it.

        '''

        if self.editable:
            solaris_part = self.ui_obj.get_solaris_data()
            if solaris_part is None:
                LOGGER.debug("No Solaris data, activating default")
                self.activate_object()
                self.right_win.scroll(scroll_to_line=0)
                return
            disk_order = self.ui_obj.get_parts_in_use().index(solaris_part)
            LOGGER.debug("solaris disk at disk_order = %s", disk_order)
            self.activate_index(disk_order)

    def make_active(self):
        '''On activate, select the solaris partition or ZFS root pool,
        instead of defaulting to 0

        '''
        self.set_color(self.highlight_color)
        self.activate_solaris_data()

    def on_arrow_key(self, input_key):
        '''
        On curses.KEY_LEFT: Move from the right win to the left win
        On curses.KEY_RIGHT: Move from the left to the right

        '''
        if (input_key == curses.KEY_LEFT and
            self.get_active_object() is self.right_win and
            len(self.left_win.objects) > 0):

            active_object = self.right_win.get_active_object().area.y_loc
            if (active_object >= len(self.left_win.objects)):
                active_object = len(self.left_win.objects) - 1
            self.activate_object(self.left_win)
            self.left_win.activate_object(active_object)
            return None
        elif (input_key == curses.KEY_RIGHT and
              self.get_active_object() is self.left_win and
              len(self.right_win.objects) > 0):
            active_line = (self.left_win.active_object +
                             self.right_win.current_line[0])
            active_object = None
            force_to_top = False
            for obj in self.right_win.objects:
                if obj.area.y_loc >= active_line:
                    active_object = obj
                    off_screen = (self.right_win.current_line[0] +
                                  self.right_win.area.lines)
                    if active_object.area.y_loc > off_screen:
                        force_to_top = True
                    break
            if active_object is None:
                active_object = 0
            self.left_win.activate_object(-1, loop=True)
            self.activate_object(self.right_win)
            self.right_win.activate_object_force(active_object,
                                                 force_to_top=force_to_top)
            return None
        return input_key

    def no_ut_refresh(self, abs_y=None, abs_x=None):
        '''Refresh self, left win and right win explicitly'''
        super(DiskWindow, self).no_ut_refresh()
        self.left_win.no_ut_refresh(abs_y, abs_x)
        self.right_win.no_ut_refresh(abs_y, abs_x)

    def change_type(self, dummy):
        '''Cycle the type for the currently active object, and
        update its field

        '''
        LOGGER.debug("changing type")

        part_field = self.get_active_object().get_active_object()
        part_info = part_field.data_obj

        part_order = self.ui_obj.get_parts_in_use().index(part_info)

        old_obj = part_info.discovered_doc_obj
        old_type = list()
        if old_obj is not None:
            if self.has_partition_data:
                old_type.append(old_obj.part_type)
            else:
                if old_obj.in_zpool is not None:
                    old_type.append(old_obj.in_zpool)
                else:
                    in_use = part_info.doc_obj.in_use
                    if in_use is not None:
                        if in_use['used_name']:
                            old_type.append((in_use['used_name'])[0])

        LOGGER.debug("extra type to cycle: %s", old_type)
        part_info.cycle_type(extra_type=old_type)
        self.set_disk_info(ui_obj=self.ui_obj, no_part_ok=True)
        self.activate_index(part_order)

        return None

    def create_extended(self, ext_part_field):
        '''If this is the original extended partition, restore the original
        logical partitions. Otherwise, create a single unused logical
        partition.

        '''
        if not ext_part_field.data_obj.modified():
            self.right_win.clear()
            self.orig_logicals_active = True
            logicals = deepcopy(self._orig_data.get_logicals())
            self.disk_info.partitions.extend(logicals)
            for idx, logical in enumerate(logicals):
                self.list_area.y_loc = idx
                self.create_list_item(logical, self.right_win, self.list_area)
            if self.right_win not in self.objects:
                self.objects.append(self.right_win)
            self.right_win.activate_object_force(0, force_to_top=True)
            self.right_win.make_inactive()
            self.right_win.no_ut_refresh()
        else:
            # Leave old data be, create new Unused logical partition
            if self.right_win not in self.objects:
                self.objects.append(self.right_win)
            self.append_unused_logical()

    def activate_index(self, obj_index):
        '''Activate the object at the specified index '''

        if obj_index < len(self.left_win.objects):
            LOGGER.debug("activating in left_win")
            self.left_win.activate_object(obj_index)
            self.activate_object(self.left_win)
            self.right_win.scroll(scroll_to_line=0)
        else:
            activate = obj_index - len(self.left_win.objects)
            LOGGER.debug('activating in right win')
            self.right_win.activate_object_force(activate, force_to_top=True)
            self.activate_object(self.right_win)
            left_active = self.left_win.get_active_object()
            if left_active is not None:
                left_active.make_inactive()

    def append_unused_logical(self):
        '''Adds a single Unused logical partition to the right window'''
        new_part = self.disk_info.append_unused_logical()
        self.list_area.y_loc = len(self.right_win.all_objects)
        bottom = self.list_area.y_loc - self.right_win.area.lines + 1
        self.right_win.bottom = max(0, bottom)
        self.create_list_item(new_part, self.right_win, self.list_area)
        scroll = len(self.right_win.all_objects) > self.right_win.area.lines
        self.right_win.use_vert_scroll_bar = scroll
        self.right_win.no_ut_refresh()
    def _show(self):
        '''Create a list of disks to choose from and create the window
        for displaying the partition/slice information from the selected
        disk
        
        '''

        self.wait_for_disks()

        discovered_target = self.doc.persistent.get_first_child( \
            name=Target.DISCOVERED)

        LOGGER.debug(discovered_target)
        if discovered_target is None:
            self.center_win.add_paragraph(DiskScreen.NO_DISKS,
                                          1,
                                          1,
                                          max_x=(self.win_size_x - 1))
            return

        self.disks = discovered_target.get_children(class_type=Disk)
        if not self.disks:
            self.center_win.add_paragraph(DiskScreen.NO_TARGETS,
                                          1,
                                          1,
                                          max_x=(self.win_size_x - 1))
            return

        if self._image_size is None:
            try:
                self._image_size = Size(str(get_image_size(LOGGER)) + \
                    Size.mb_units)
                LOGGER.debug("Image_size: %s", self._image_size)
            except:
                # Unable to get the image size for some reason, allow
                # the target controller to use it's default size.
                LOGGER.debug("Unable to get image size")
                self._image_size = FALLBACK_IMAGE_SIZE

        # initialize the target controller so the min/max size for
        # the installation can be calculated.  Explicitly do not
        # want to select an initial disk at this time in case
        # none of the disks discovered is usable.  The target controller
        # initialization needs to be done everytime we show the disk selection
        # screen so the desired target node in the DOC can be re-populated
        # with information from target discovery.
        self.tc.initialize(image_size=self._image_size, no_initial_disk=True)

        # Go through all the disks found and find ones that have
        # enough space for installation.  At the same time, see if any
        # existing disk is the boot disk.  If a boot disk is found, move
        # it to the front of the list
        num_usable_disks = 0
        boot_disk = None
        for disk in self.disks:
            LOGGER.debug("size: %s, min: %s" % \
                         (disk.disk_prop.dev_size, self.minimum_size))
            if disk.disk_prop.dev_size >= self.minimum_size:
                if disk.is_boot_disk():
                    boot_disk = disk
                num_usable_disks += 1
        if boot_disk is not None:
            self.disks.remove(boot_disk)
            self.disks.insert(0, boot_disk)

        if num_usable_disks == 0:
            self.center_win.add_paragraph(DiskScreen.NO_DISKS,
                                          1,
                                          1,
                                          max_x=(self.win_size_x - 1))
            return

        self.main_win.reset_actions()
        self.main_win.show_actions()

        y_loc = 1
        self.center_win.add_text(DiskScreen.PARAGRAPH, y_loc, 1)

        y_loc += 1
        self.center_win.add_text(self.size_line, y_loc, 1)

        y_loc += 2
        self.center_win.add_text(self.disk_header_text, y_loc, 1)

        y_loc += 1
        self.center_win.window.hline(y_loc, self.center_win.border_size[1] + 1,
                                     curses.ACS_HLINE,
                                     textwidth(self.disk_header_text))

        y_loc += 1
        disk_win_area = WindowArea(4,
                                   textwidth(self.disk_header_text) + 2, y_loc,
                                   0)
        disk_win_area.scrollable_lines = len(self.disks) + 1
        self.disk_win = ScrollWindow(disk_win_area, window=self.center_win)

        disk_item_area = WindowArea(1, disk_win_area.columns - 2, 0, 1)
        disk_index = 0
        len_type = DiskScreen.DISK_HEADERS[0][0] - 1
        len_size = DiskScreen.DISK_HEADERS[1][0] - 1
        len_boot = DiskScreen.DISK_HEADERS[2][0] - 1
        len_dev = DiskScreen.DISK_HEADERS[3][0] - 1
        len_mftr = DiskScreen.DISK_HEADERS[4][0] - 1
        for disk in self.disks:
            disk_text_fields = []
            if disk.disk_prop is None or disk.disk_prop.dev_type is None:
                continue
            type_field = disk.disk_prop.dev_type[:len_type]
            type_field = ljust_columns(type_field, len_type)
            disk_text_fields.append(type_field)
            disk_size = disk.disk_prop.dev_size.get(Size.gb_units)
            size_field = locale.format("%*.1f", (len_size, disk_size))
            disk_text_fields.append(size_field)
            if disk.is_boot_disk():
                bootable_field = "+".center(len_boot)
            else:
                bootable_field = " " * (len_boot)
            disk_text_fields.append(bootable_field)
            device_field = disk.ctd[:len_dev]
            device_field = ljust_columns(device_field, len_dev)
            disk_text_fields.append(device_field)
            vendor = disk.disk_prop.dev_vendor
            if vendor is not None:
                mftr_field = vendor[:len_mftr]
                mftr_field = ljust_columns(mftr_field, len_mftr)
            else:
                mftr_field = " " * len_mftr
            disk_text_fields.append(mftr_field)
            selectable = True
            if disk.disk_prop.dev_size < self.minimum_size:
                note_field = self.too_small_text
                selectable = False
            elif disk_size > Size(MAX_VTOC).get(Size.gb_units):
                note_field = self.too_big_warn
            else:
                note_field = ""
            disk_text_fields.append(note_field)
            disk_text = " ".join(disk_text_fields)
            disk_item_area.y_loc = disk_index
            disk_list_item = ListItem(disk_item_area,
                                      window=self.disk_win,
                                      text=disk_text,
                                      add_obj=selectable)
            disk_list_item.on_make_active = on_activate
            disk_list_item.on_make_active_kwargs["disk"] = disk
            disk_list_item.on_make_active_kwargs["disk_select"] = self
            disk_index += 1

        self.disk_win.no_ut_refresh()

        y_loc += 7
        disk_detail_area = WindowArea(6, 70, y_loc, 1)

        self.disk_detail = DiskWindow(disk_detail_area,
                                      self.disks[0],
                                      target_controller=self.tc,
                                      window=self.center_win)

        self.main_win.do_update()
        self.center_win.activate_object(self.disk_win)
        self.disk_win.activate_object(self.selected_disk_index)