ACC SHELL

Path : /usr/share/YaST2/include/partitioning/
File Upload :
Current File : //usr/share/YaST2/include/partitioning/ep-hd-lib.ycp

/**
 * File:	ep-hd-lib.ycp
 * Package:	yast2-storage
 * Summary:	Expert Partitioner
 * Authors:	Arvin Schnell <aschnell@suse.de>
 *
 * This file must only be included in other Expert Partitioner files ("ep-*.ycp").
 */
{
    textdomain "storage";


    void EpCreatePartitionTable(string disk_device)
    {
	if (disk_device == nil)
	{
	    // error popup
	    Popup::Error(_("No hard disk selected."));
	    return;
	}

	map<string, map> target_map = Storage::GetTargetMap();
	map disk = target_map[disk_device]:$[];

	if (!isempty(disk["used_by"]:[]))
	{
	    // error popup
	    Popup::Error(_("The disk is in use and cannot be modified."));
	    return;
	}

	string default_label = Storage::DefaultDiskLabel(disk_device);

	list<string> labels = [ default_label ];
	if (!contains(labels, "gpt"))
	    labels = add(labels, "gpt");

	string label = default_label;
	if (size(labels) > 1)
	{
	    term tmp = list::reduce(term t, string l, `VBox(), labels, {
		return add(t, `LeftRadioButton(`id(l), toupper(l), l == default_label));
	    });

	    UI::OpenDialog(`opt(`decorated),
			   Greasemonkey::Transform(
			       `VBox(
				   // dialog heading
				   `Label(sformat(_("Select new partition table type for %1."), disk_device)),
				   `MarginBox(2, 0.4, `RadioButtonGroup(`id(`labels), tmp)),
				   `ButtonBox(
				       `PushButton(`id(`ok), `opt(`default), Label::OKButton()),
				       `PushButton(`id(`cancel), Label::CancelButton())
				       )
				   )
			       )
		);

	    symbol widget = (symbol) UI::UserInput();

	    label = (string) UI::QueryWidget(`id(`labels), `Value);

	    UI::CloseDialog();

	    if (widget != `ok)
		return;
	}

	// popup text, %1 is be replaced by disk name e.g. /dev/sda
	if (Popup::YesNo(sformat(_("Really create new partition table on %1? This will delete all data
on %1 and all RAIDs and Volume Groups using partitions on %1."), disk_device)))
	{
	    Storage::CreatePartitionTable(disk_device, label);

	    UpdateMainStatus();
	    UpdateNavigationTree(nil);
	    TreePanel::Create();
	}
    }


    void EpDeleteDisk(string device)
    {
	if (device == nil)
	{
	    // error popup
	    Popup::Error(_("No disk selected."));
	    return;
	}

	map<string, map> target_map = Storage::GetTargetMap();
	map<string, any> disk = (map<string, any>) target_map[device]:$[];

	if (disk["type"]:`CT_UNKNOWN == `CT_DMRAID)
	{
	    // popup text
	    if (Popup::YesNo(sformat(_("Really delete BIOS RAID %1?"), device)))
	    {
		if (deleteAllDevPartitions(disk, Stage::initial()))
		    Storage::DeleteDmraid(device);

		UpdateMainStatus();
		UpdateNavigationTree(`hd);
		TreePanel::Create();
	    }
	}
	else if (disk["type"]:`CT_UNKNOWN == `CT_MDPART && disk["sb_ver"]:"" != "imsm")
	{
	    // popup text
	    if (Popup::YesNo(sformat(_("Really delete Partitioned RAID %1?"), device)))
	    {
		if (deleteAllDevPartitions(disk, Stage::initial()))
		    Storage::DeleteMdPartCo(device);

		UpdateMainStatus();
		UpdateNavigationTree(`hd);
		TreePanel::Create();
	    }
	}
	else
	{
	    //partition names
            list<string> pnames = Storage::GetAffectedDevices( device );
	    integer count = size(pnames);

	    if(count == 0)
	    {
	        // error ppup
	        Popup::Error(_("There are no partitions to delete on this disk."));
		return;
	    }

	    else
	    {
	        if (ConfirmPartitionsDelete(device, pnames) && deleteAllDevPartitions(disk, Stage::initial()))
	        {
	            UpdateMainStatus();
	            UpdateNavigationTree(`hd);
	            TreePanel::Create();
	        }
	    }
	}
    }


    map<symbol, list> GetPossibleSlots(map disk, string disk_device)
    {
	list<map> slots = [];
	Storage::GetUnusedPartitionSlots(disk_device, slots);
	y2milestone("slots %1", slots);

	// individual sort primary, extended and logical slots

	map<symbol, list> ret = $[];
	list<map> tmp = [];

	tmp = filter(map slot, slots, { return slot["primary_possible"]:false; });
	tmp = sort(map a, map b, tmp, { return a["region", 1]:0 > b["region", 1]:0; });
	if (size(tmp) > 0)
	    ret[`primary] = maplist(map slot, tmp, { return slot["region"]:[]; });

	tmp = filter(map slot, slots, { return slot["extended_possible"]:false; });
	tmp = sort(map a, map b, tmp, { return a["region", 1]:0 > b["region", 1]:0; });
	if (size(tmp) > 0)
	    ret[`extended] = maplist(map slot, tmp, { return slot["region"]:[]; });

	tmp = filter(map slot, slots, { return slot["logical_possible"]:false; });
	tmp = sort(map a, map b, tmp, { return a["region", 1]:0 > b["region", 1]:0; });
	if (size(tmp) > 0)
	    ret[`logical] = maplist(map slot, tmp, { return slot["region"]:[]; });

	if (size(ret) == 0)
	{
	    // error popup
	    Popup::Error(sformat(_("It is not possible to create a partition on %1."),
				 disk_device));
	}

	y2milestone("ret %1", ret);
	return ret;
    }


    /**
     * argument is device of hard disk
     */
    void EpCreatePartition(string disk_device)
    {
	if (disk_device == nil)
	{
	    // error popup
	    Popup::Error(_("No hard disk selected."));
	    return;
	}

	map<string, map> target_map = Storage::GetTargetMap();
	map disk = target_map[disk_device]:$[];

	if (!isempty(disk["used_by"]:[]))
	{
	    // error popup
	    Popup::Error(_("The disk is in use and cannot be modified."));
	    return;
	}

	if (disk["readonly"]:false)
	{
	    Popup::Error(Partitions::RdonlyText(disk["device"]:"", true));
	    return;
	}

	map slots = GetPossibleSlots(disk, disk_device);

	if (size(slots) > 0)
	{
	    map<string, any> data = $[ "new" : true,
				       "create" : true,
				       "disk_device" : disk_device,
				       "cyl_size" : disk["cyl_size"]:0,
				       "cyl_count" : disk["cyl_count"]:0,
				       "slots" : slots ];

	    if (DlgCreatePartition(data))
	    {
		string device = data["device"]:"error";

		symbol mby = data["mountby"]:Storage::GetMountBy(device);
		Storage::CreatePartition(data["disk_device"]:"", device,
					 data["type"]:`primary,
					 data["fsid"]:Partitions::fsid_native,
					 data["region",0]:0, data["region",1]:0, mby);
		Storage::ChangeVolumeProperties(data);

		UpdateMainStatus();
		UpdateNavigationTree(nil);
		TreePanel::Create();
		UpdateTableFocus(device);
	    }
	}
    }


    void EpEditPartition(string device)
    {
	if (device == nil)
	{
	    // error popup
	    Popup::Error(_("No partition selected."));
	    return;
	}

	map<string, map> target_map = Storage::GetTargetMap();
	map<string, any> part = Storage::GetPartition(target_map, device);

	if (!Storage::CanEdit(part, true))
	    return;

	if (!isempty(part["used_by"]:[]))
	{
	    // error popup, %1 is replace by partition device name e.g. /dev/sdb1
	    Popup::Error(sformat(_("The partition %1 is in use. It cannot be
edited. To edit %1, make sure it is not used."), device));
	    return;
	}

	if (part["type"]:`primary == `extended)
	{
	    // error popup text
	    Popup::Error(_("An extended partition cannot be edited."));
	    return;
	}

	if (DlgEditPartition(part))
	{
	    Storage::ChangeVolumeProperties(part);

	    UpdateMainStatus();
	    UpdateNavigationTree(nil);
	    TreePanel::Create();
	    UpdateTableFocus(device);
	}
    }


    void EpMovePartition(string device)
    {
	if (device == nil)
	{
	    // error popup
	    Popup::Error(_("No partition selected."));
	    return;
	}

	map<string, map> target_map = Storage::GetTargetMap();
	map<string, any> disk = Storage::GetDisk(target_map, device);
	map<string, any> part = Storage::GetPartition(target_map, device);

	if (disk["readonly"]:false)
	{
	    Popup::Error(Partitions::RdonlyText(disk["device"]:"", true));
	    return;
	}

	if (!part["create"]:false)
	{
	    // error popup, %1 is replace by partition device name, e.g. /dev/sdb1
	    Popup::Error(sformat(_("The partition %1 is already created on disk
and cannot be moved."), device));
	    return;
	}

	if (part["type"]:`primary == `extended)
	{
	    // error popup text
	    Popup::Error(_("An extended partition cannot be moved."));
	    return;
	}

	if (DlgMovePartition(part))
	{
	    if (Storage::UpdatePartition(device, part["region", 0]:0, part["region", 1]:0))
	    {
		UpdateMainStatus();
		TreePanel::Create();
		UpdateTableFocus(device);
	    }
	}
    }


    void EpResizePartition(string device)
    {
	if (device == nil)
	{
	    // error popup
	    Popup::Error(_("No partition selected."));
	    return;
	}

	map<string, map> target_map = Storage::GetTargetMap();
	map<string, any> disk = Storage::GetDisk(target_map, device);
	map<string, any> part = Storage::GetPartition(target_map, device);

	if (disk["readonly"]:false)
	{
	    Popup::Error(Partitions::RdonlyText(disk["device"]:"", true));
	    return;
	}

	if (!isempty(part["used_by"]:[]))
	{
	    // error popup, %1 is replace by partition device name, e.g. /dev/sdb1
	    Popup::Error(sformat(_("The partition %1 is in use. It cannot be
resized. To resize %1, make sure it is not used."), device));
	    return;
	}

	if (part["type"]:`primary == `extended)
	{
	    // error popup text
	    Popup::Error(_("An extended partition cannot be resized."));
	    return;
	}

	// Need to pass data on the whole disk, to determine free/available space
	if (DlgResizePartition(part, disk))
	{
	    Storage::ResizePartition(device, disk["device"]:"error", part["region", 1]:0);

	    UpdateMainStatus();
	    TreePanel::Create();
	    UpdateTableFocus(device);
	}
    }


    void EpDeletePartition(string device, symbol context)
    {
	if (device == nil)
	{
	    // error popup
	    Popup::Error(_("No partition selected."));
	    return;
	}

	map<string, map> target_map = Storage::GetTargetMap();
	map<string, any> disk = Storage::GetDisk(target_map, device);
	map<string, any> part = Storage::GetPartition(target_map, device);

	if (!Storage::CanDelete(part, disk, true))
	    return;

	string parent = ParentDevice(device);
	string next = NextDeviceAfterDelete(device);

	if (EpDeleteDevice(device))
	{
	    UpdateMainStatus();

	    switch (context)
	    {
		case `table:
		    UpdateNavigationTree(nil);
		    TreePanel::Create();
		    if (!isempty(next))
			UI::ChangeWidget(`id(`table), `CurrentItem, next);
		    break;

		case `overview:
		    UpdateNavigationTree(parent);
		    TreePanel::Create();
		    break;
	    }
	}
    }


    void EpCloneDisk(string device)
    {
	map<string, map> target_map = Storage::GetTargetMap();

	integer mysize = target_map[ device, "size_k"]:0;
	integer mycyl_size = target_map[ device, "cyl_size"]:0;
	list <map> myparts = target_map[ device, "partitions"]:[];
	string mypart_table_type = target_map[device, "label"]:Storage::DefaultDiskLabel(device);

	// helptext
	string helptext = _("<p>Select one or more (if available) hard disks
that will have the same partition layout as
this disk.</p>
<p>Disks marked with '*' sign contain one or
more partitions. After cloning, these
partitions will be deleted.</p>");

	list <string> AvailableTargetDisks()
	{
	    map <string, map> filtered_target_map =
		filter( string dev, map props, target_map, {
		    return dev != device &&
			Storage::IsDiskType( props["type"]:`CT_UNKNOWN ) &&
			isempty(props["used_by"]:[]) &&
			props["cyl_size"]:0 == mycyl_size;
		});

	    y2milestone("Available, suitable and unused disks (other than %1): %2", device, Map::Keys(filtered_target_map));

	    list <string> items = [];

	    foreach( string dev, map props, filtered_target_map,{
		if( props["size_k"]:0 >= mysize )
		    items = add(items, dev);
		else
		    y2milestone("%1 is smaller than needed, skipping it", device);
	    });

	    return items;
	}

	boolean ConfirmDeletePartitions( list <string> to_delete) {

	    UI::OpenDialog( `opt(`warncolor), `VBox(
		    `Left(`Label(_("The following partitions will be deleted\nand all data on them will be lost:"))),
		    `VSpacing(1),
		    `RichText(HTML::List( to_delete)),
		    `Left(`Label(_("Really delete these partitions?"))),
		    `VSpacing(1),
		    `ButtonBox(
			`PushButton(`id(`ok), `opt(`default), Label::DeleteButton()),
			`PushButton(`id(`cancel), Label::CancelButton())
		    )
		)
	    );
	    symbol ret = (symbol) UI::UserInput();
	    UI::CloseDialog();

	    return (ret == `ok);
	}

	if (isempty(myparts)) {
	    Popup::Error(_("There are no partitions on this disk (a clonable
disk must have at least one partition).
Create some partitions before cloning the disk."));
	    return;
	}

	list <string> mydisks = AvailableTargetDisks();

	if (isempty(mydisks)) {
	    Popup::Error(_("This disk cannot be cloned. There are no suitable
disks that could have the same partitioning layout."));
	   return;
	}

	list<term> ui_items = maplist( string one_disk, mydisks, {
	    boolean any_partitions = !isempty(target_map[ one_disk, "partitions" ]:[]);

	    return `item(`id(one_disk), sformat("%1%2 (%3)",one_disk,
			  any_partitions ? "*" : "",
			  Storage::KByteToHumanString( target_map[one_disk, "size_k"]:42)));
	});

	UI::OpenDialog (`MinSize( 60, 20, `VBox(
	    `Heading(sformat(_("Clone partition layout of %1"), device)),
	    `VSpacing(1),
	    `MultiSelectionBox(`id(`tdisk), _("Available target disks:"), ui_items ),
	    `VSpacing(1),
	    `ButtonBox(
		`PushButton(`id(`help), `opt(`helpButton), Label::HelpButton()),
	        `PushButton(`id(`ok), `opt(`default), Label::OKButton()),
	        `PushButton(`id(`cancel), Label::CancelButton())
	        )
	    )
	));

	UI::ChangeWidget(`help, `HelpText, helptext);

	any ret = nil;

	while(ret != `ok && ret != `cancel)
	{
	    ret = UI::UserInput();

	    if( ret == `ok) {
	        list <string> selected_disks = (list <string>) UI::QueryWidget(`id(`tdisk), `SelectedItems);

		if (isempty(selected_disks)) {
		    Popup::Error(_("Select some target disk for creating a clone"));
		    UI::SetFocus(`id(`tdisk));
		    ret = nil;
		    continue;
		}

		list <string> partitions_to_delete = [];

		//collect partitions to delete
		foreach (string this_disk, selected_disks, {
		    list <map> partitions = target_map[ this_disk, "partitions" ]:[];

		    if (!isempty(partitions)) {
			list <string> tmp = maplist( map one_part, partitions, {
			    return one_part["device"]:"";
			});
			partitions_to_delete = (list <string>) union( partitions_to_delete, tmp);
		    }
		});

		//if there is anything to delete, ask user if s/he really wants to delete
		if ( !isempty(partitions_to_delete) &&
		     !ConfirmDeletePartitions( partitions_to_delete )) {
		    ret = nil;
		    continue;
		}

		//We'll be deleting recursively, so that no longer valid
		//LVMs and RAIDs are not left behind
		boolean recursive = Storage::GetRecursiveRemoval();
		Storage::SetRecursiveRemoval(true);

		foreach (string this_disk, selected_disks, {
		    map <string,any> disk_info = Storage::GetDisk( target_map, this_disk );
		    list <map> partitions = target_map[ this_disk, "partitions" ]:[];
		    list<string> pnames = maplist(map part, partitions, { return part["device"]:""; });
		    string part_table_type = disk_info["label"]:Storage::DefaultDiskLabel(disk_info["device"]:"");

		    y2milestone("Deleting these partitions: %1", pnames);

		    if (!isempty(partitions)) {
			foreach( string one_partition, pnames,{
			    Storage::DeleteDevice(one_partition);
			});
		    }

		    if( mypart_table_type != part_table_type ) {
			y2milestone("%1 has different type of partition table: %2, will create a new one",
			    this_disk, part_table_type);
			Storage::CreatePartitionTable( disk_info["device"]:"", mypart_table_type );
		    }

		    foreach( map one_partition, myparts, {
		        map next = Storage::NextPartition( this_disk, one_partition["type"]:`none);
		        Storage::CreatePartition( this_disk, next["device"]:"error",
					      one_partition["type"]:`primary,
					      one_partition["fsid"]: Partitions::fsid_native,
					      one_partition["region",0]:0, one_partition["region",1]:0,
					      one_partition["mountby"]:Storage::GetMountBy(device) );
		        //FIXME: ChangeVolumeProperties too?
		    });
		});

		Storage::SetRecursiveRemoval( recursive );
	   }
	}

	UI::CloseDialog();

	if( ret == `ok ) {
	    UpdateMainStatus();
	    UpdateNavigationTree(nil);
	    TreePanel::Create();
	}
    }


    void EpDasdfmtDisk(string device)
    {
	map<string, map> target_map = Storage::GetTargetMap();

	map disk = target_map[device]:$[];

	if (!disk["dasdfmt"]:false)
	{
	    // popup text, %1 is replaced by a dasd name e.g. /dev/dasda
	    boolean doit = Popup::YesNo(sformat(_("Running dasdfmt deletes all data on the disk.
Really execute dasdfmt on disk %1?
"), device));

	    if (doit)
	    {
		Storage::InitializeDisk(device, true);
	    }
	}
	else
	{
	    // popup text
	    Popup::Message(_("The disk is no longer marked for dasdfmt.

Partitions currently present on this disk are again
displayed.
"));
	    Storage::InitializeDisk(device, false);
	}

	UpdateMainStatus();
	UpdateNavigationTree(nil);
	TreePanel::Create();
    }
}

ACC SHELL 2018