ACC SHELL

Path : /usr/share/YaST2/modules/
File Upload :
Current File : //usr/share/YaST2/modules/AutoinstPartPlan.ycp

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