ACC SHELL
/**
* 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