/**
 * File:
 *   lvm_pv_lib.ycp
 *
 * Module: 
 *    configuration of lvm: lib for handling of physical volumes
 *
 * Summary:
 *
 * Authors:
 *   mike <mike@suse.de>
 *
 *
 * $Id: lvm_pv_lib.ycp 20243 2004-11-08 17:03:10Z fehr $
 *
 */


{
     textdomain "storage";
     import "Storage";
     import "Partitions";

     include "partitioning/lvm_lib.ycp";
     include "partitioning/lvm_lv_lib.ycp";
     
    //////////////////////////////////////////////////////////////////////
    // get the current summ of physical volumes which belong to volume
    // group "current_vg" 
    /////////
    //
    // [  $["fsid":Partitions::fsid_lvm,
    //	  "fstype":"LVM",
    //	  "nr":"var",
    //	  "region":[255, 16],
    //	  "type":`primary],
    //	  $[
    //	   "fsid":131,
    //	   "fstype":"Linux native",
    //	   "nr":4, "region":[271, 844],
    //    ...
    //
    //  out: "10.6 GB"
     
define any get_vg_size( list<map> partition_list, string current_vg, symbol what )
    ``{
    integer sum_byte = vg_size( partition_list, current_vg );
    if( what == `string )
	return( ByteToHumanStringWithZero( sum_byte ));
    return sum_byte;
    };
		
	 

    //////////////////////////////////////////////////////////////////////
    // add a partition to the current volume group
    //
    // 	   $[ 1 : $[  "use_module" : "lvm_ll"
    // 		      "type"       : "create_pv",
    // 		      "vgname"     : "system"     -- optional, wenn da dann vgextend nach pvcreate
    // 		      "device"     : "/dev/sda1"
    //
    //   !!!!!!! changes targetMap by reference !!!!!!
    // 	
    //////////////////////////////////////////////////////////////////////

define map addPhysicalVolume( map<integer,map> modify_targets, map<string,map> targetMap, string id, 
                              string current_vg )
    ``{
    string maindev = Storage::GetMainDevKey(id);  // i.e. /dev/hda for /dev/hda1
    string subdev  = Storage::GetSubDevIndex( id);  // i.e. 1  for /dev/hda1

    map  disk       = targetMap[maindev]:$[];
    map partition   = 
	Storage::GetPartition( targetMap, 
	                       Storage::GetDeviceName(maindev, tointeger(subdev)) );
    y2milestone( "partition %1", partition );
    
    if( partition["fsid"]:0 != Partitions::fsid_lvm && 
        contains( [`primary, `logical], partition["type"]:`none ) )
	{
	if( !partition["change_fsid"]:false )
	    {
	    partition["change_fsid"] = true;
	    partition["ori_fsid"] = partition["fsid"]:0;
	    partition["ori_fstype"] = partition["fstype"]:"";
	    }
	partition["fsid"] = Partitions::fsid_lvm;
	partition["fstype"] = "Linux LVM";
	}

    // if we have /data? the automatic generated mountpoint: delete it  
    partition["mount"] = "";
    if( haskey( partition, "format" ))
	{
	partition["format"] = false;
	}
    
    /////////////////////////////////////////////////
    // new entry in execution stack "modify_targets"

    string vg_key = "/dev/"+current_vg;
    
    map new_create_pv = $[
			  "use_module" : "lvm_ll",
			  "type"       : "create_pv",
			  "vgname"     : current_vg,
			  "lvm2"       : targetMap[vg_key,"lvm2"]:false,
			  "device"     : partition["device"]:"" ];

    modify_targets = addTargetModification( modify_targets, new_create_pv );
    y2debug( "EEE s %1", modify_targets);

    partition["lvm_group"] = current_vg;

    targetMap = Storage::SetPartition( targetMap, partition );

    return( $[ "modify_targets":modify_targets, "targets":targetMap, "error":false ]);
};
	

    //////////////////////////////////////////////////////////////////////
    // removes a partition from its current volume group
    //
    // 	   $[ 1 : $[  "use_module" : "lvm_ll"
    // 		      "type"       : "remove_pv",
    // 		      "vgname"     : "system"
    // 		      "device"     : "/dev/sda1"
    //
    //////////////////////////////////////////////////////////////////////

define map removePhysicalVolume( map<integer,map> modify_targets, map<string,map> targetMap, string id,
                                 string current_vg )
    ``{
    string maindev = Storage::GetMainDevKey(id);  // i.e. /dev/hda for /dev/hda1
    string subdev  = Storage::GetSubDevIndex(id); // i.e. 1        for /dev/hda1

    map<string,any> partition  = (map<string,any>)Storage::GetPartition( targetMap, 
                                            Storage::GetDeviceName(maindev, tointeger(subdev)) );

    if( partition["change_fsid"]:false )
	{
	partition["fsid"] = partition["ori_fsid"]:0;
	partition["fstype"] = partition["ori_fstype"]:"";
	partition = filter( string key, any value, partition, 
			    ``( key != "ori_fsid" && 
				key != "change_fsid" && 
				key != "ori_fstype" ));
	}

    partition = filter( string key, any val, partition, ``( key != "lvm_group" ));

    targetMap = (map<string,map>)Storage::SetPartition( targetMap, partition );

    map new_remove_pv = $[
			  "use_module" : "lvm_ll",
			  "type"       : "remove_pv",
			  "vgname"     : current_vg,
			  "device"     : partition["device"]:"" ];
	
    modify_targets = addTargetModification( modify_targets, new_remove_pv );

    return( $[ "modify_targets":modify_targets, "targets":targetMap, 
               "error":false ] );
    };
	

//////////////////////////////////////////////////////////////////////
// belongs the the partition "id" already to a volume group
//////////////////////////////////////////////////////////////////////

define boolean isItemPv( map tg, string id )
    ``{
    map part = Storage::IdFromTarget( tg, (string)id );
    if ( part["lvm_group"]:"" == "" )
	{
	return( false );
	}
    else
	{
	return( true );
	}
    };


//////////////////////////////////////////////////////////////////////
// belongs the the partition "id" already to a volume group
// if YES: popup a proper Message and return(false)
//////////////////////////////////////////////////////////////////////

define boolean CheckItemIsNotPv( map tg, string id )
    ``{
    if( ! isItemPv(tg,id) )
	{
	//////////////
	return( true );
	//////////////
	}

    // error case:
    if( id != nil )
	{
	// message popup
	Popup::Message(sformat(_("The item %1%2 is already a physical volume.
To add it to a different volume group,
remove it from its current volume group
then add it to the new group.
"), Storage::GetMainDevKey(id), Storage::GetSubDevIndex(id)));
	}
    else
	{
	// message popup
	Popup::Message(_("You have to select one device in the table"));
	}
    return( false );
    };


    
    //////////////////////////////////////////////////////////////////////
    // belongs the the partition "id" already to a volume group?
    // if NO: popup a proper Message and return(false)
    //////////////////////////////////////////////////////////////////////

define boolean CheckItemIsPv( map tg, string id )
    ``{
	if ( isItemPv(tg,id) )
	{
	    //////////////
	    return( true );
	    //////////////
	}

	// error case: 
	if ( id != nil )
	{
	    // Message popup
	    Popup::Message(sformat(_("The item %1%2 cannot be removed.
It does not belong to a volume group.
"), Storage::GetMainDevKey(id), Storage::GetSubDevIndex(id)));
	}
	else
	{
	    //Message popup
	    Popup::Message(_("You have to select one device in the table"));
	}

	return( false );
    };
    
    

/////////////////////////////////////////////////////////////////
// Get all partitions, we can probably use as physical volumes
// Add needed information: devkey, is_lv, disksize 

define list<map> get_possible_pvs( map<string,map> targetMap )
    ``{
    list<map> ret = [];
	
    //////////////////////////////////////////////////////////////////////
    // add the devicename i.e /dev/hda1 or /dev/system/usr to partition list
    // and the device key  <subdevice>/<maindevice> i.e. 1//dev/hda
    
    targetMap = mapmap( string dev, map devmap, targetMap,
	``{
	boolean is_lvm_vg  = devmap["is_lvm_vg"]:false;
	integer cyl_size   = devmap["cyl_size"]:1000000;
					    
	list partitions = maplist( map part, devmap["partitions"]:[],
	    ``{
	    integer bytes_of_part = 0;
	    integer nb_cyl = part["region",1]:0;
    
	    bytes_of_part = nb_cyl * cyl_size;
	    part["size_byte"] = bytes_of_part;
	    part["size_str"] = ByteToHumanStringWithZero(bytes_of_part);
	    if( is_lvm_vg )
		{
		part["is_lv"] = true;
		}
	    part["maindev"] = dev;
	    part["devkey"] = sformat( "%2/%1", dev, part["nr"]:(any)"");
	    return( part );
	    });
		
	return( $[ dev: add(devmap, "partitions", partitions) ] );
	});

    ////////////////////////////////////////////////////////////
    // Look for all partitions:
    // not LVM ( here I mean /dev/<lvm_volumegroup>/<lv> entries!
    //           there are only the lv's in the targetMap under /dev/<lvm_volumegroup>/<lv> !)
    // no mountpoint
    // id 0x83 or 0x8e or 0xfe
    // RAID

    foreach( string dev, map devmap, targetMap,
	``{
	if( !devmap["is_evms"]:false )
	    {
	    ret = (list<map>)merge( ret, 
		       filter( map part, devmap["partitions"]:[],
			       ``(( part["mount"]:"" == "" ||
				    substring(part["mount"]:"", 0, 5) == "/data" )
				 &&
				 ( !part["is_lv"]:false && 
				   part["raid_name"]:"" == "" )
				 &&
				   !part["delete"]:false 
				 &&
				   ( part["fsid"]:0 == Partitions::fsid_lvm ||
				     part["fsid"]:0 == Partitions::fsid_raid ||
				     part["fsid"]:0 == Partitions::fsid_native )
		     )));
	    }
	});
    y2milestone( "get_possible_pvs ret %1", ret );
    return( ret );
    };


    
    //////////////////////////////////////////////////////////////////////
    // partition list to widget table
    // in:
    // [  $["fsid":Partitions::fsid_lvm,
    //	  "fstype":"LVM",
    //	  "nr":"var",
    //	  "region":[255, 16],
    //	  "type":`primary],
    //	  $[
    //	   "fsid":131,
    //	   "fstype":"Linux native",
    //	   "nr":4, "region":[271, 844],
    //    ...
    //
    // out:
    // [
    // 	`item(`id("/dev/hda1"), "/dev/hda1 ",   " 2G ",  " LVM ",   " system"),
    // 	`item(`id("/dev/hda2"), "/dev/hda2 ",   " 1G ",  " Linux ", " ")
    // ];

define list get_pv_widget_table( list<map> possPvList )
    ``{
    list    ret = [];

    possPvList = 
    sort( map x, map y, possPvList,
	  ``{
	  if( x["maindev"]:"" == y["maindev"]:"")
	      {
	      return( x["nr"]:(any)"" < y["nr"]:(any)"");
	      }
	  else
	      {
	      return( x["maindev"]:"" < y["maindev"]:"" );
	      }
	  });
    
    return( maplist( map partition, possPvList,
	 ``{
	     return(`item( `id(partition["devkey"]:"--"),
			   partition["device"]:"--",
			   partition["size_str"]:"--",
			   partition["fstype"]:"--",
			   partition["lvm_group"]:"--"
			   ));
	    }
	  ));
    };

    

    /**
     * Check if the user can delete the partition.
     * -> Existing LVM partition with volume group
     * -> size of volume group to small for all logical partition
     */
define symbol check_pv_delete( map<string,map> targetMap, string id, string current_vg ) 
    ``{
    string maindev = Storage::GetMainDevKey(id);  // i.e. /dev/hda for /dev/hda1
    string subdev  = Storage::GetSubDevIndex(id); // i.e. 1        for /dev/hda1
    map partition  = Storage::GetPartition( targetMap, 
                                            Storage::GetDeviceName(maindev, tointeger(subdev)) );

    y2milestone( "check_pv_delete part %1 current_vg: %2", partition, current_vg );

    //new partition should be deleted -> check size for volume group
    //total size of volume group  
    integer vg_siz = (integer)get_vg_size( get_possible_pvs(targetMap), 
                                           current_vg, `integer );
    y2milestone( "check_pv_delete vg_size : %1", vg_siz);

    //used size (sum logical partitions)
    list<map> parts = get_lvs_and_mounted_partitions( targetMap, false, current_vg );
    y2milestone( "check_pv_delete parts=%1 current_vg=%2", parts, current_vg );
	    
    integer used_size = get_lv_size_info( targetMap, parts, current_vg )[0]:0;

    integer num_pvs = size( filter( map p, get_possible_pvs(targetMap),
				    ``(p["lvm_group"]:""==current_vg)));
    y2milestone( "check_pv_delete num_pvs:%1", num_pvs );
    if( num_pvs<=1 )
	{
	// message popup
	Popup::Error(_("You cannot remove the last PV from a volume group.
Delete the volume group completely instead.
"));
	y2milestone( "check_pv_delete `nodelete" );
	return `nodelete;
	}
	
	
    // size of the seleted partition
    integer bytes_of_part = 0;
    integer nb_cyl = partition["region",1]:0;
	
    bytes_of_part = nb_cyl * targetMap[maindev,"cyl_size"]:1000000; 

    y2milestone( "check_pv_delete used_size %1 bytes_of_part %2", used_size, bytes_of_part );

    if( used_size>0 && vg_siz - bytes_of_part < used_size)
	{
	// message popup
	Popup::Error(_("If you remove the selected partition from the volume group,
there will not be enough space for all logical volumes in this group.
To delete the volume group completely, remove its
logical volumes first.
"));
	y2milestone( "check_pv_delete `nodelete" );
	return `nodelete;
	}
    y2milestone( "check_pv_delete `delete" );
    return `delete;
    }
    
}

