ACC SHELL
/**
* File: modules/AutoinstCommon.ycp
* Package: Auto-installation/Partition
* Summary: Module representing a partitioning plan
* Author: Sven Schober (sschober@suse.de)
*
* $Id: AutoinstPartPlan.ycp 2813 2008-06-12 13:52:30Z sschober $
*/
{
textdomain "autoinst";
module "AutoinstPartPlan";
include "autoinstall/types.ycp";
include "autoinstall/common.ycp";
include "autoinstall/tree.ycp";
import "AutoinstCommon";
import "AutoinstDrive";
import "AutoinstPartition";
import "Summary";
import "Popup";
import "Mode";
import "StorageDevices";
import "Storage";
import "Partitions";
/**
* The general idea with this moduls is that it manages a single
* partition plan (AutoPartPlan) and the user can work on that
* plan without having to sepcify that variable on each method
* call.
* This is on the one hand convenient for the user and on the
* other hand we have control over the plan.
*/
/**
* PRIVATE
*/
/* The single plan instance managed by this module.
*
* The partition plan is technically a list of DriveT'.
*/
define AutoinstPartPlanT AutoPartPlan = [];
/* default value of settings modified */
define boolean modified = false;
/**
* Function sets internal variable, which indicates, that any
* settings were modified, to "true"
*/
global define void SetModified ()
{
y2milestone("SetModified");
modified = true;
}
/**
* Functions which returns if the settings were modified
*
* @return boolean settings were modified
*/
global define boolean GetModified ()
{
return modified;
}
/**
* Select a drive by its ID, as in the tree item strings (e.g.
* "drive_1" -> ID = 1).
*
* @param plan The partition plan
* @param which ID of the drive to return
*
* @return The drive with the specified id, or the an empty map if
* no drive with that id exists.
*/
define DriveT
internalGetDrive( AutoinstPartPlanT plan, integer which ){
DriveT result = $[];
foreach( DriveT currentDrive, plan, {
if( which == currentDrive["_id"]:-1 ){
result = currentDrive;
break;
}
});
return result;
}
/**
* Select a drive by its position in the drive list (a.k.a
* partition plan).
*
* @param plan Partition plan.
* @param index Index of drive in the list
*
* @return The spcified drive, or an empty map if the index wasn't
* valid.
*/
define DriveT
internalGetDriveByListIndex( AutoinstPartPlanT plan, integer index ){
return plan[index]:$[];
}
/**
* Get a list index for a drive id.
*
* @param plan Partition plan.
* @param which the drive id.
*
* @return The list index or -1 if id wasn't found.
*/
define integer
internalGetListIndex( AutoinstPartPlanT plan, integer which){
integer index = 0;
boolean found = false;
foreach( DriveT currentDrive, plan, {
integer currentID = currentDrive["_id"]:-1;
if( which == currentID ){
found = true;
break;
}
index = index + 1;
});
if( found ){
return index;
}
return -1;
}
/**
* ==========
* Mutators
* ==========
*/
/**
* Add a drive to the partition plan.
*
* @param plan Partition plan.
* @param drive The drive to be added
*
* @return The partition plan containing the new drive.
*/
define AutoinstPartPlanT
internalAddDrive( AutoinstPartPlanT plan, DriveT drive ){
if( AutoinstDrive::isDrive( drive )){
/**
* TODO: implement insertion constraints
*/
return add(plan, drive);
}
else{
y2error( "No valid drive '%1'.", drive);
}
}
/**
* Remove a drive from the plan.
*
* @param plan Partition plan.
* @param which The id of the drive to remove.
*
* @return The partition plan lacking the drive that was removed if
* it was present.
*/
define AutoinstPartPlanT
internalRemoveDrive( AutoinstPartPlanT plan, integer which ){
AutoinstPartPlanT result = [];
DriveT drive = internalGetDrive( plan, which );
result = filter( DriveT curDrive, plan,{
return drive["_id"]:100 != curDrive["_id"]:111;
});
return result;
}
/**
* Update a drive in the partition plan.
*
* @param plan Partition plan.
* @param drive The drive to be updated.
*
* @return Partition plan containing the updated drive.
*/
define AutoinstPartPlanT
internalUpdateDrive( AutoinstPartPlanT plan, DriveT drive){
if( AutoinstDrive::isDrive( drive ) ){
plan = maplist(DriveT curDrive, AutoPartPlan, {
if( curDrive["_id"]:100 == drive["_id"]:111 ){
return drive;
}
else{
return curDrive;
}
});
}
else{
y2error( "No valid drive: '%1'", drive );
}
return plan;
}
/**
* Get a list of all drives, that are of a volgroup type.
*
* @param plan The partition plan.
*
* @return Partition plan containing only volgroups.
*/
define AutoinstPartPlanT
internalGetAvailableVolgroups( AutoinstPartPlanT plan ){
AutoinstPartPlanT result = [];
result = filter( DriveT curDrive, AutoPartPlan, {
return curDrive["type"]:`CT_DISK != `CT_DISK;
});
return result;
}
/**
* Get a list of all physical (not volgroup) drives.
*
* @param plan The partition plan.
*
* @return Partition plan containing only physical drives.
*/
define AutoinstPartPlanT
internalGetAvailablePhysicalDrives( AutoinstPartPlanT plan ){
AutoinstPartPlanT result = [];
result = filter( DriveT curDrive, AutoPartPlan, {
return curDrive["type"]:`CT_LVM == `CT_DISK;
});
return result;
}
/**
* Get a list of used mountpoint strings.
*
* @param plan The partition plan.
*
* @return A list of mountpoint strings in use by any partition.
*/
define list<string>
internalGetUsedMountPoints( AutoinstPartPlanT plan ){
list<string> result = [];
foreach( DriveT drive, plan, {
foreach( PartitionT part, drive["partitions"]:[], {
string mountPoint = part["mount"]:"";
if( "" != mountPoint ){
result = add( result, mountPoint );
}
});
});
y2milestone( "Used mount points: '%1'", result );
return result;
}
/**
* Volume group checks:
* - check that each VG has at least one PV
* - <others to be implemented>
*
* @param plan The partition plan
*
* @return true if each volume group has a supplying physical
* volume.
*/
define boolean
internalCheckVolgroups( AutoinstPartPlanT plan ){
boolean sane = true;
/* Check that each volume group has at least
* one physical volume
*/
AutoinstPartPlanT volGroups = internalGetAvailableVolgroups( plan );
AutoinstPartPlanT physDrives = internalGetAvailablePhysicalDrives( plan );
foreach( DriveT volGroup, volGroups, {
/* check all physical drives for physical volumes
* "feeding" current volume group
*/
boolean found = false;
string volGroupName = removePrefix( volGroup["device"]:"xyz" , "/dev/" );
foreach( DriveT physDrive, physDrives, {
foreach( PartitionT part, physDrive["partitions"]:[], {
if( volGroupName == part["lvm_group"]:"zxy" ){
found = true;
y2milestone( "Found 'feeding' partition for volume group '%1'", volGroupName );
}
});
});
/* if no feeder (PV) was found for current volume group
* the next instructions taints result
*/
if( ! found ){
Popup::Error(sformat( _("Volume group '%1' must have at least one physical volume. Please provide one."), volGroupName) );
}
sane = found;
});
return sane;
}
/**
* Check the sanity of the partition plan.
*
* @param plan The partition plan
*
* @return true if plan is sane, false otherwise.
*/
define boolean
internalCheckSanity( AutoinstPartPlanT plan ){
boolean sane = true;
sane = internalCheckVolgroups( plan );
/* ... */
return sane;
}
/**
* Create tree structure from AutoPartPlan
*/
global define void updateTree()
{
y2milestone("entering updateTree");
/* remember selected tree item */
string item = currentTreeItem();
list<term> tree = [];
/* let tree widget reflect AutoPartPlan */
foreach(DriveT drive, AutoPartPlan,{
tree = add( tree, AutoinstDrive::createTree( drive ) );
});
y2milestone( "Setting tree: '%1'", tree );
if( size( AutoPartPlan ) > 0 ){
setTree( tree );
/* restore former selection */
if( nil != item && "" != item ){
y2milestone("reselecting item '%1' after tree update.", item);
selectTreeItem(item);
}
/* there was no former selection, so select first drive */
else{
DriveT firstDrive = internalGetDriveByListIndex( AutoPartPlan, 0 );
selectTreeItem( AutoinstDrive::getNodeReference( firstDrive ));
}
}
else{
/* Tree is empty now; this is handled in
* StorageDialog::StorageEventHandler() */
}
}
/**
* Create a partition plan for the calling client
* @return list partition plan
*/
define list ReadHelper()
{
Mode::SetMode("normal");
StorageDevices::InitDone();
map<string, map> StorageMap = eval(Storage::GetTargetMap());
StorageMap=filter(string d, map p, StorageMap, ``( d != "/dev/evms" && d != "/dev/nfs" && size(p["partitions"]:[]) > 0));
y2milestone("Storagemap %1", StorageMap);
// list evms_vgs = [];
list drives = maplist(string k, map v, StorageMap, ``{
list partitions = [];
list winp = [];
list no_format_list = [ 65, 6, 222 ];
list no_create_list = [ 222 ];
list<integer> usepartitions = [];
integer cyl_size = v["cyl_size"]:0;
boolean no_create = false;
foreach(map pe, v["partitions"]:[], ``{
if( pe["type"]:`x == `extended )
continue;
map new_pe = $[];
new_pe["create"] = true;
boolean skipwin = false;
if (haskey(pe,"enc_type")) {
new_pe["enc_type"] = pe["enc_type"]:`twofish;
new_pe["crypt_key"] = "ENTER KEY HERE";
new_pe["loop_fs"] = true;
new_pe["crypt_fs"] = true;
}
if (haskey(pe,"fsid"))
{
integer fsid = pe["fsid"]:131;
list wintypes = union(Partitions::fsid_wintypes, Partitions::fsid_dostypes);
list allwin = union(wintypes, Partitions::fsid_ntfstypes);
if (contains(allwin, fsid) && (!issubstring(pe["mount"]:"", "/boot") && !pe["boot"]:false) )
// if (contains(allwin, fsid) && ! issubstring(pe["mount"]:"", "/boot") )
{
y2debug("Windows partitions found: %1", fsid );
winp = add(winp, pe["nr"]:0);
skipwin = true;
if( size(partitions) > 0 ) {
no_create = true;
}
}
if( contains(allwin, fsid) && issubstring(pe["mount"]:"", "/boot") ) {
new_pe["partition_id"] = 259;
} else {
new_pe["partition_id"] = pe["fsid"]:131;
}
if( contains( no_format_list, pe["fsid"]:0 ) )
new_pe["format"] = false;
if( contains( no_create_list, pe["fsid"]:0 ) )
new_pe["create"] = false;
}
if( haskey(pe,"type") && pe["type"]:`x == `primary ) {
new_pe["partition_type"] = "primary"; // can we always copy that element?
}
if (haskey(pe,"region") && new_pe["create"]:true == true )
{
// don't clone the exact region.
// I don't see any benefit in cloning that strict.
//new_pe["region"] = pe["region"]:[];
// new_pe["size"] = sformat("%1", pe["size_k"]:0*1024);
if( (pe["size_k"]:0*1024-cyl_size) < cyl_size ) // bnc#415005
new_pe["size"] = sformat("%1",cyl_size);
else
new_pe["size"] = sformat("%1", pe["size_k"]:0*1024-cyl_size); // one cylinder buffer for #262535
}
if (haskey(pe,"label")) {
new_pe["label"] = pe["label"]:"";
}
if (haskey(pe,"mountby")) {
new_pe["mountby"] = pe["mountby"]:`nomb;
}
if ( haskey(pe, "fstopt") ) {
new_pe["fstopt"] = pe["fstopt"]:"defaults";
}
// LVM Group
if (haskey(pe,"used_by_type") && pe["used_by_type"]:`nothing == `UB_LVM)
{
new_pe["lvm_group"] = substring(pe["used_by_device"]:"", 5);
}
// LV
if (pe["type"]:`unknown == `lvm )
{
new_pe["lv_name"] = pe["name"]:"";
new_pe["size"] = sformat("%1", pe["size_k"]:0*1024);
if( haskey( pe, "stripes" ) ) {
new_pe["stripes"] = pe["stripes"]:0;
new_pe["stripesize"] = pe["stripesize"]:4;
}
}
if (haskey(pe,"used_by_type") && pe["used_by_type"]:`nothing == `UB_MD)
{
new_pe["raid_name"] = pe["used_by_device"]:"";
}
// Used Filesystem
// Raid devices get the filesystem lying on them as
// detected_fs!
if (haskey(pe,"used_fs") && pe["fsid"]:0 != 253)
{
new_pe["filesystem"] = pe["used_fs"]:Partitions::DefaultFs();
new_pe["format"] = new_pe["format"]:pe["format"]:true;
}
// if the filesystem is unknown, we have detected_fs and no longer used_fs
// don't know why yast2-storage is having two keys for that.
// maybe it would be even okay to look only for "detected_fs" to set format to false
// bnc#542331 (L3: AutoYaST clone module fails to set format option for non-formatted logical volumes)
if( pe["detected_fs"]:`known == `unknown ) {
new_pe["format"] = false;
}
if (haskey(pe,"nr") && pe["type"]:`unknown != `lvm)
{
if (!skipwin)
{
y2debug("Adding partition to be used: %1", pe["nr"]:0);
usepartitions = add(usepartitions, pe["nr"]:0);
}
new_pe["partition_nr"] = pe["nr"]:0;
}
if (pe["mount"]:"" != "")
{
new_pe["mount"] = pe["mount"]:"";
}
if (k == "/dev/md")
{
map raid_options = $[];
raid_options["persistent_superblock"] =
pe["persistent_superblock"]:false;
raid_options["raid_type"] = pe["raid_type"]:"raid0";
new_pe["raid_options"] = raid_options;
}
if (!skipwin && new_pe["partition_id"]:0 != 15 ) {
partitions=add(partitions,new_pe);
}
});
// don't create partitions that are between windows partitions
// they must exist
map drive = $[];
drive["type"] = v["type"]:`CT_DISK;
if( no_create ) {
partitions = maplist( map m, (list<map>)partitions, {
m["create"] = false;
return m;
});
}
drive["partitions"] = partitions;
drive["device"] = k;
if( v["type"]:`CT_UNKNOWN==`CT_LVM)
{
drive["pesize"] = sformat("%1M", v["pesize"]:1 / (1024*1024) );
drive["type"] = `CT_LVM;
}
if (haskey(v,"lvm2") && v["lvm2"]:false)
{
drive["lvm2"] = true;
}
if (size(partitions) > 0 )
{
if (size(winp) == 0 )
{
drive["use"] = "all";
}
else
{
list<string> up = [];
foreach(integer i, usepartitions, ``{
up = add (up, sformat("%1", i));
});
drive["use"] = mergestring(up, ",");
}
}
return drive;
});
// drives = filter( map v, (list<map>)drives, ``{
// if( ! (contains( evms_vgs, v["device"]:"") && v["type"]:`x == `CT_LVM ) )
// return true;
// y2milestone("kicking LVM %1 out of the profile because an EVMS with that name exists",v);
// return false;
// });
/* remove drives with no mountpoint */
drives = filter( map v, (list<map>)drives, ``{
boolean keep = false;
foreach( map p, (list<map>)v["partitions"]:[], ``{
if( p["mount"]:"" != "" || haskey(p,"lvm_group") || haskey(p,"raid_name") ) {
keep = true;
break;
}
});
return keep;
});
Mode::SetMode("autoinst_config");
return drives;
}
/**
* PUBLIC INTERFACE
*/
/**
* INTER FACE TO CONF TREE
*/
/**
* Return summary of configuration
* @return string configuration summary dialog
*/
global define string Summary()
{
string summary = "";
summary = Summary::AddHeader(summary, _("Drives"));
integer num = size(AutoPartPlan);
summary = Summary::AddLine( summary, sformat(_("Total of %1 drive", "Total of %1 drives", num), num));
summary = Summary::OpenList(summary);
foreach(DriveT drive, AutoPartPlan, ``{
string driveDesc = AutoinstDrive::getNodeName( drive, true );
summary = Summary::AddListItem( summary, driveDesc );
summary = Summary::OpenList(summary);
foreach(PartitionT part, drive["partitions"]:[], {
summary =
Summary::AddListItem(summary, AutoinstPartition::getPartitionDescription( part, true ));
});
summary = Summary::CloseList( summary );
summary = Summary::AddNewLine( summary );
});
summary = Summary::CloseList(summary);
return summary;
}
/**
* Get all the configuration from a map.
* When called by inst_auto<module name> (preparing autoinstallation data)
* the list may be empty.
* @param settings a list [...]
* @return boolean success
*/
global define boolean Import(list<map> settings)
{
y2milestone("entering Import with %1", settings);
AutoPartPlan = [];
foreach( map drive, settings, {
DriveT newDrive = AutoinstDrive::parseDrive( drive );
if( AutoinstDrive::isDrive( newDrive ) ){
AutoPartPlan = internalAddDrive( AutoPartPlan, newDrive );
}
else{
y2error( "Couldn't construct DriveT from '%1'", drive );
}
});
return true;
}
global define boolean Read(){
return Import( (list<map>) ReadHelper());
}
/**
* Dump the settings to a map, for autoinstallation use.
* @return list
*/
global define list<map> Export()
{
y2milestone("entering Export");
list<map> drives = maplist( DriveT drive, AutoPartPlan, {
return AutoinstDrive::Export( drive );
});
list<map> clean_drives = maplist(map d, drives, ``{
list p = maplist(map part, d["partitions"]:[] , ``{
if (haskey(part, "fsid"))
{
part = remove(part, "fsid");
}
if (haskey(part, "used_fs")) {
part = remove(part, "used_fs");
}
return part;
});
d["partitions"] = p;
// this is to delete the dummy "auto" filled in by UI
if( haskey(d,"device") && d["device"]:"" == "auto" ) {
d = remove(d, "device");
y2milestone("device 'auto' dropped");
}
return d;
});
return clean_drives;
}
global define void Reset(){
AutoPartPlan = [];
}
/**
* =============================
* General info about the plan
* =============================
*/
/**
* Get a list of mount point strings that are currently not in use
*
* @return List of currently unused mount points
*/
global define list<string>
getAvailableMountPoints(){
list<string> usedMountPoints = internalGetUsedMountPoints( AutoPartPlan );
list<string> availableMountPoints = [];
availableMountPoints = filter( string mp, AutoinstPartition::getDefaultMountPoints(),{
return ( ! contains( usedMountPoints, mp ) );
});
y2milestone( "Available mount points: '%1'", availableMountPoints );
return availableMountPoints;
}
/**
* Get the next free/unused mount point string.
*
* @return Next free mount point string.
*/
global define string
getNextAvailableMountPoint(){
list<string> availableMountPoints = getAvailableMountPoints();
return availableMountPoints[0]:"";
}
/**
* Get list of (device) names of volume groups present in the
* plan.
*
* @return List of currently present volume group device names.
*/
global define list<string>
getAvailableVolgroups(){
return maplist( DriveT vg, internalGetAvailableVolgroups( AutoPartPlan ), {
return vg["device"]:"not-set";
});
}
/**
* Triggers a sanity check over the current state of the plan.
*
* @return true if plan is sane, false otherwise.
*/
global boolean
checkSanity(){
return internalCheckSanity( AutoPartPlan );
}
/**
* ==================
* DRIVE OPERATIONS
* ==================
*/
/**
* Get drive identified by id (as in the tree node strings
* ["device_1"]). Caution: this is not the same as the index (see
* getDriveByListIndex()), as the id never changes, independent of
* the position of this drive in the list.
*
* @param which ID of drive to acquire.
*
* @return The drive identified by ID if a drive with that ID
* exists; and empty list otherwise.
*/
global define DriveT
getDrive( integer which ){
return internalGetDrive( AutoPartPlan, which );
}
/**
* Get drive identified by its position in the partition plan.
*
* @param index The list index identifying the drive.
*
* @return The drive identifyied by index, the empty list
* otherwise.
*/
global define DriveT
getDriveByListIndex( integer index ){
return internalGetDriveByListIndex( AutoPartPlan, index );
}
/**
* Returns the number of drives present in plan.
*
* @return Number of drives present in plan.
*/
global define integer
getDriveCount(){
return size( AutoPartPlan );
}
/**
* Remove drive identified by drive ID from the partition plan.
* Selects preceeding drive, if deleted drive was last in list.
* Otherwise the successor is selected.
*
* @param which ID of drive to delete.
*/
global define void
removeDrive( integer which ){
/* most of the complexity here is due to correct
* reselection behaviour after a drive has been deleted
*/
integer removalDriveIdx = internalGetListIndex( AutoPartPlan, which );
integer oldDriveCount = getDriveCount();
AutoPartPlan = internalRemoveDrive( AutoPartPlan, which );
DriveT drive = $[];
if( oldDriveCount > 1 && removalDriveIdx == oldDriveCount - 1 ){
/* lowest drive in tree was deleted, select predecessor */
drive = getDriveByListIndex( removalDriveIdx - 1 );
}
else{
/* a top or middle one was deleted, select successor */
drive = getDriveByListIndex( removalDriveIdx );
}
selectTreeItem( AutoinstDrive::getNodeReference( drive ));
updateTree();
}
/**
* Add a new drive to the plan.
*
* @param drive The new drive to add.
*/
global define DriveT
addDrive( DriveT drive ){
AutoPartPlan = internalAddDrive( AutoPartPlan, drive );
updateTree();
return drive;
}
/**
* Update a drive in the plan. If the drive didn't exist in the
* first place nothing happens (use add in that case).
*
* @param The drive to update.
*/
global define void
updateDrive( DriveT drive ){
AutoPartPlan = internalUpdateDrive( AutoPartPlan, drive );
updateTree();
}
/**
* ======================
* PARTITION OPERATIONS
* ======================
*/
/**
* Get partition identified by partitionIdx on drive with
* specified id.
*
* Note: Partition index refers to the position of the partition in
* the list on the drive and thus is subject to invalidation on any
* modifications of that list.
*
* @param driveId The integer id of the drive containing the
* partition.
* @param partitionIdx Index of partition to get.
*
* @return The partition if driveId and partitionIdx were valid,
* an empty map otherwise.
*/
global define PartitionT
getPartition( integer driveId, integer partitionIdx ){
DriveT currentDrive = getDrive(driveId);
y2milestone("Loaded drive '%1'", currentDrive );
return AutoinstDrive::getPartition( currentDrive, partitionIdx );
}
/**
* Update a partition on a drive.
*
* Note: Partition index refers to the position of the partition in
* the list on the drive and thus is subject to invalidation on any
* modifications of that list.
*
* @param driveId The integer id of the drive containing the
* partition.
* @param partitionIdx Index of the partition to update.
* @param partition The updated/new partition.
*
* @return true if update was successfull, false otherwise.
*/
global define boolean
updatePartition( integer driveId, integer partitionIdx, PartitionT partition ){
DriveT drive = AutoinstPartPlan::getDrive( driveId );
if( AutoinstDrive::isDrive( drive ) ){
drive = AutoinstDrive::updatePartition( drive, partitionIdx, partition );
updateDrive( drive );
return true;
}
else{
y2milestone( "Could not update partition. Invalid driveId: '%1'", driveId );
return false;
}
}
/**
* Create a new partition on a drive.
*
* The new partition is assinged a default mountpoint and a default
* parition number, or, in case its parent drive is a LVM, a volume
* name.
*
* @param driveId The drive to create the new partition on.
*
* @return The index of the newly created partition.
*/
global define integer
newPartition( integer driveId ){
integer newPartitionIndex = -1;
DriveT parentDrive = AutoinstPartPlan::getDrive( driveId );
if( AutoinstDrive::isDrive( parentDrive ) ){
string mountPoint = getNextAvailableMountPoint();
integer newPartitionNumber = AutoinstDrive::getNextAvailablePartitionNumber( parentDrive );
PartitionT newPart = AutoinstPartition::new( mountPoint );
newPart = AutoinstPartition::set( newPart, "partition_nr", newPartitionNumber );
if( `CT_DISK != parentDrive["type"]:`Empty ){
newPart = AutoinstPartition::set( newPart, "lv_name",
AutoinstPartition::getLVNameFor( mountPoint ));
}
parentDrive = AutoinstDrive::addPartition(parentDrive, newPart );
newPartitionIndex = AutoinstDrive::getPartitionCount( parentDrive ) - 1;
updateDrive( parentDrive );
}
else{
y2error( "Cannot create new partition on invalid drive with id '%1'.", driveId );
}
return newPartitionIndex;
}
/**
* Delete a partition on a drive.
*
* @param driveId Drive containing the partition to be deleted.
* @param partitionIdx The partition index identifying the parition
* to be deleted.
*
* @return true if removal was successfull, false otherwise.
*/
global define boolean
deletePartition( integer driveId, integer partitionIdx ){
DriveT drive = AutoinstPartPlan::getDrive( driveId );
if( AutoinstDrive::isDrive( drive ) ){
integer oldPartitionCount = AutoinstDrive::getPartitionCount( drive );
drive = AutoinstDrive::removePartition( drive, partitionIdx );
updateDrive( drive );
if( AutoinstDrive::getPartitionCount( drive ) == 0 ){
/* if no partitions are left select parent drive */
selectTreeItem( AutoinstDrive::getNodeReference( drive ) );
}
else if( partitionIdx == oldPartitionCount - 1 ){
/* the removed partition was the last one */
selectTreeItem( "part_" + tostring(driveId) + "_" + tostring(partitionIdx - 1) );
}
return true;
}
else{
y2error("Cannot delete partition on invalid drive with index '%1'.", driveId );
return false;
}
}
}
ACC SHELL 2018