ACC SHELL

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

/**
 * Module:		SpaceCalculation.ycp
 *
 * Authors:		Klaus Kaempf (kkaempf@suse.de)
 *			Gabriele Strattner (gs@suse.de)
 *			Stefan Schubert (schubi@suse.de)
 *
 * Purpose:		Package installation functions usable
 *			when the installation media is available
 *			on Installation::sourcedir
 *
 *
 * $Id: SpaceCalculation.ycp 54664 2009-01-15 16:11:19Z lslezak $
 */

{
    module "SpaceCalculation";

    textdomain "packager";

    import "Installation";
    import "Mode";
    import "ProductFeatures";
    import "Report";
    import "String";
    import "Stage";

    boolean info_called = false;	// list partition_info already initialized?
    list    partition_info = [];	// information about available partitions


    /*
     * Return partition info list
     * @return list of available partitions
     */
    global define list GetPartitionList() ``{
	return partition_info;
    }

    /**
     * Get mountpoint for a directory
     * @param target directory
     * @param partition partitions list
     * @return mountpoint
     */
    string GetDirMountPoint(string target, list<map<string, string> > partition ) {
 
	list<string> d = splitstring(target, "/");
	d=filter(string fd, d, ``(fd!=""));
	string mountpoint = "";

	foreach( map part, partition,
		 ``{
	    //  dirinstall: /test/xen dir: /test
	    //  dirinstall:/var/tmp/xen dir: /

	    string dir = part["name"]:"";
	    string tmpdir = "";
	    foreach(string dd ,d , ``{
		tmpdir = sformat("%1/%2", tmpdir, dd );
		y2debug("tmpdir: %1 dir: %2", tmpdir, dir );
		if (dir == tmpdir )
		   mountpoint = dir;
	    });
	});

	if (mountpoint=="")
		mountpoint = "/";

	return mountpoint;
    }

    /**
     * Evaluate the free space on the file system. Runs the command "df" and creates a map
     * containig information about used and free space on every partition.
     * Free space is calculated respecting the spare_percentage given in second argument.
     *
     * @param  spare_percentage percentage of spare disk space, i.e. free space is increased
     * @return list partition list, e.g.  [$["free":389318, "name":"/", "used":1487222],
     *				     $["free":1974697, "name":"/usr", "used":4227733]]
     *
     * @example EvaluateFreeSpace ( 5 );
     *
     * ***  This is needed during update !
     */
    global define list<map<string,any> > EvaluateFreeSpace (integer spare_percentage) ``{

	list<map<string, string> > partition = [];
	// the sizes are in kB
	integer min_spare = 1 * 1024;		// 1 MB
	integer max_spare = 1024 * 1024;	// 1 GB

	string target = Installation::destdir;

	// get information about diskspace ( used/free space on every partition )
	partition = (list<map<string, string> >)SCR::Read(.run.df);

	// filter out headline and other invalid entries
	partition = filter( map<string, string> part, partition, ``( substring ( part["name"]:"", 0, 1 ) == "/" ) );

        if (Installation::dirinstall_installing_into_dir)
        {
            target = GetDirMountPoint(Installation::dirinstall_target, partition );
	    y2milestone("Installing into a directory, target directory: %1, target mount point: %2", Installation::dirinstall_target, target);
        }

	list<map<string,any> > part_input = [];

	foreach( map part, partition,
		 ``{
	    map<string,any> part_info = $[];
	    integer free_size = 0;
	    integer spare_size = 0;
	    string partName = "";
	    boolean add_part = true;

	    string mountName = part["name"]:"";
	    string spec = part["spec"]:"";

	    if (Installation::dirinstall_installing_into_dir)
	    {
		if (substring(mountName, 0, 1) != "/")
		{
		    mountName = "/" + mountName;
		}

		string dir_target = Installation::dirinstall_target;

		y2debug("mountName: %1, dir_target: %2", mountName, dir_target);

		if (size(mountName) > size(dir_target)
		    && (substring( mountName, 0, size(dir_target)) == dir_target) )
		{
		    part_info = add(part_info, "name", mountName);
		}
		else if (mountName == target)
		{
		    part_info = add ( part_info, "name", "/");
		}
		else
		{
		    add_part = false;
		}
	    }
	    else if ( target != "/")
	    {
		if ((size(mountName) >= size(target))
		    && (substring( mountName, 0, size(target)) == target) )
		{
		    partName = substring( mountName, size(target) );
		    // nothing left, it was target root itself
		    if ( size ( partName ) == 0 )
		    {
			part_info = add( part_info, "name", "/" );
		    }
		    else
		    {
			part_info = add( part_info, "name", partName );
		    }
		}
		else
		{
		    add_part = false;
		}
	    }
	    else // target is "/"
	    {
		if ( mountName == "/" )
		{
		    part_info = add ( part_info, "name", mountName );
		}
		// ignore some mount points
		else if ( mountName != Installation::sourcedir
			  && mountName != "/cdrom"
			  && mountName != "/dev/shm"
			  && spec != "udev"
			  && !regexpmatch(mountName, "^/media/")
			  && !regexpmatch(mountName, "^var/adm/mount/" )
		    )
		{
		    part_info = add ( part_info, "name", mountName );
		}
		else
		{
		    add_part = false;
		}
	    }

	    if ( add_part )
	    {
		part_info = add ( part_info, "used", tointeger(part["used"]:"0") );

		free_size = tointeger(part["free"]:"0");
		spare_size = (free_size*spare_percentage)/100;

		if ( spare_size < min_spare )
		    spare_size = min_spare;
		else if ( spare_size > max_spare )
		    spare_size = max_spare;

		free_size = free_size - spare_size;
		if ( free_size < 0 )
		    free_size = 0;	// don't add a negative size

		part_info = add ( part_info, "free",  free_size );

		part_input = add ( part_input, part_info );
	    }
	});

	y2milestone( "UTILS *** EvaluateFreeSpace returns: %1", part_input );

	Pkg::TargetInitDU (part_input);

	return part_input;
    };


    /*
     * Define a macro that transforms information about all partitions ( from
     * Storage::GetTargetMap() ) into a list(map) with information about partitions
     * which are available for installation, e.g.:
     *
     * [$["free":1625676, "name":"/boot", "used":0], $["free":2210406, "name":"/", "used":0]]
     *
     * Please note: there isn't any information about used space, so "used" at begin
     *              of installation is initialized with zero;
     *              size "free", "used" in KBytes
     *
     */

    define list get_partition_info () ``{
	// remove leading slash so it matches the packages.DU path
	boolean remove_slash = true;

	if (!Stage::initial ())
	{
	    // read /proc/mounts as a list of maps
	    // $["file":"/boot", "freq":0, "mntops":"rw", "passno":0, "spec":"/dev/sda1", "vfstype":"ext2"]
	    list<map<string, any> > mounts = (list<map<string, any> >) SCR::Read (.proc.mounts);
	    y2milestone ("mounts %1", mounts);

	    list<map<string, any> > partitions = [];
	    foreach (map mpoint, mounts,
		     ``{
		string name = mpoint["file"]:"";
		if ((substring (name, 0, 1) == "/")
		    && (substring (name, 0, 5) != "/dev/")		// filter out /dev/pts etc.
		    && (mpoint["vfstype"]:"" != "rootfs"))		// filter out duplicate "/" entry
		{
		    integer capacity = Pkg::TargetCapacity (name);
		    if (capacity != 0)		// dont look at pseudo-devices (proc, shmfs, ...)
		    {
			integer used = Pkg::TargetUsed (name);
			partitions = add (partitions, $["name" : name, "free" : capacity-used, "used" : used]);
		    }
		}
	    });
	    Pkg::TargetInitDU (partitions);
	    y2milestone ("get_partition_info: %1", partitions);
	    return partitions;

	} // !Stage::initial ()

	// installation stage - Storage:: is definitely present
	// call Storage::GetTargetMap()
	map<string, map> targets = (map<string, map>)WFM::call("wrapper_storage", ["GetTargetMap"]);

	if (targets == nil)
	{
	    y2error("Target map is nil, Storage:: is probably missing");
	}

	if ( Mode::test () )
	{
	    targets = (map<string, map>) SCR::Read(.target.yast2, "test_target_map.ycp");
	}

	list<map> target_partitions = [];
	integer min_spare	= 5 * 1024 * 1024; // minimum free space ( 5 MB )

	foreach( string disk, map diskinfo, targets,
		 ``{

	    list<map> part_info = diskinfo["partitions"]:[];

	    foreach( map part, part_info,
		     ``{
		symbol used_fs = part["used_fs"]:`unknown;

		// ignore VFAT and NTFS partitions (bnc#)
		if (used_fs == `vfat || used_fs == `ntfs)
		{
		    y2warning("Ignoring partition %1 with %2 filesystem", part["device"]:"", used_fs);
		}
		else
		{
		    integer free_size = 0;

		    if (part["mount"]:nil != nil
			&& substring( part["mount"]:"", 0, 1 ) == "/" )
		    {
			if (part["create"]:nil == true
			    || part["delete"]:nil == false
			    || (part["create"]:nil == nil
				&& part["delete"]:nil == nil ) )
			{
			    y2debug( "get_partition_info: adding partition: %1", part );

			    // get free_size on partition in kBytes
			    free_size = part["size_k"]:0 * 1024;
			    free_size = free_size - min_spare;

			    integer used = 0;
			    if (! (part["create"]:false || part["format"]:false))
			    {
				string tmpdir = (string)SCR::Read (.target.tmpdir);
				tmpdir = tmpdir + "/diskspace_mount";
				SCR::Execute (.target.bash, sformat (
				    "test -d %1 || mkdir -p %1", tmpdir));

				// mount in read-only mode (safer)
				list<string> mount_options = ["ro"];

				// add "nolock" if it's a NFS share (bnc#433893)
				if (used_fs == `nfs)
				{
				    y2milestone("Mounting NFS with 'nolock' option");
				    mount_options = add(mount_options, "nolock");
				}

				// join the options
				string mount_options_str = mergestring(mount_options, ",");

				string mount_command = sformat("/bin/mount -o %1 %2 %3",
				    mount_options_str, part["device"]:"", tmpdir);

				y2milestone("Executing mount command: %1", mount_command);

				SCR::Execute(.target.bash, mount_command);

				list<map<string,string> > partition =
				    (list<map<string, string> >)
				    SCR::Read(.run.df);
				foreach (map p, partition, {
				    if (p["name"]:"" == tmpdir)
				    {
					y2internal ("P: %1", p);
					free_size = tointeger (p["free"]:"0")
					    * 1024;
					used = tointeger (p["used"]:"0") * 1024;
				    }
				});
				SCR::Execute (.target.bash, sformat (
				    "/bin/umount %1", tmpdir));
			    }

			    // convert into kB for TargetInitDU
			    free_size = free_size / 1024;
			    used = used / 1024;

			    y2milestone ("available partition: mount: %1, free: %2 KB, used: %3 KB", part["mount"]:"", free_size, used);
			    if ( !remove_slash)
			    {
				target_partitions = add (target_partitions, $["name":part["mount"]:"", "used":used, "free":free_size]);
			    }
			    else
			    {
				string part_name = "";
				string mount_name = part["mount"]:"";

				if ( mount_name != "/" )
				{
				    part_name = substring( mount_name, 1, size(mount_name) );
				}
				else
				{
				    part_name = mount_name;
				}

				target_partitions = add ( target_partitions, $["name":part_name, "used":used, "free":free_size]);
			    }

			}
		    }
		}
	    } ); // foreach (`part)
	} ); // foreach (`disk)

	y2milestone( "get_partition_info: part %1", target_partitions );
	Pkg::TargetInitDU (target_partitions);

	return ( target_partitions );
    };

    /*
     * Get information about available partitions either from "targetMap"
     * in case of a new installation or from 'df' command (continue mode
     * and installation on installed system).
     * Returns a list containing available partitions and stores the list
     * in "partition_info".
     *
     * @return list partition list, e.g.  [$["free":389318, "name":"/", "used":1487222],
     *				     $["free":1974697, "name":"usr", "used":4227733]]
     *
     *
     * @example GetPartitionInfo();
     *
     * Will be called from Packages when re-doing proposal !!
     */
    global define list GetPartitionInfo () ``{

	list partition = [];

	if ( Stage::cont () )
	{
	    partition = EvaluateFreeSpace ( 0 ); // free spare already checked during first part of installation
	}
	else if ( Mode::update () )
	{
	    partition = EvaluateFreeSpace ( 5 ); // 5% free spare for update/upgrade
	}
	else if ( Mode::normal () )
	{
	    partition = EvaluateFreeSpace ( 5 ); // 5% free spare for post installation
	}
	else	// Stage::initial ()
	{
	    partition = get_partition_info( );
	}
	y2milestone( "INIT done, SpaceCalculation - partitions: %1", partition );

	info_called = true;
	partition_info = partition;	// store partition_info

	return partition;
    }



    /*
     * get current space data for partitions
     * current_partitions = list of maps of
     * $["format":bool, "free":integer, "name" : string, "used" :integer, "used_fs": symbol]
     * from Storage module
     * returns list of maps of
     * $["name" : string, "free" : integer, "used" : integer ]
     *
     */
    global define list CheckCurrentSpace (list<map> current_partitions) ``{

	list<map> output = [];

	foreach (map par, current_partitions, ``{
	    map outdata = $[];
	    outdata["name"] = par["name"]:"";
	    outdata["used"] = Pkg::TargetUsed (Installation::destdir + par["name"]:"");
	    outdata["free"] = Pkg::TargetCapacity (Installation::destdir + par["name"]:"") - outdata["used"]:0;
	    output = add (output, eval (outdata));
	});
	y2milestone ("CheckCurrentSpace(%1) = %2", current_partitions, output);

	return output;
    }

    global list<string> GetPartitionWarning () {
	if ( !info_called )
	{
	    SpaceCalculation::GetPartitionInfo();
	}
	integer used = 0;
	list<string> message = [];

	//$[ "dir" : [ total, usednow, usedfuture ], .... ]

	foreach( string dir, list sizelist, Pkg::TargetGetDU(), ``{
	    y2milestone ("dir %1, sizelist (total, current, future) %2", dir, sizelist);
	    integer needed = sizelist[2]:0 - sizelist[0]:0;	// usedfuture - total
	    if ( needed > 0 )
	    {
		// Warning message, e.g.: Partition /usr needs 35 MB more disk space
		message = add (message, sformat ( _("Partition \"%1\" needs %2 more disk space."),
			      // needed is in kB
			      dir, String::FormatSize(needed*1024)));
	    }
	    used = used + sizelist[2]:0;
	} );

	y2debug( "Total used space (kB): %1", used );

	if ( size ( message ) > 0 )
	{
	    // dont ask user to deselect packages for imap server, product
	    if ( ProductFeatures::GetFeature ("software", "selection_type") == `auto)
	    {
		if (Mode::update ())
		    message = add (message, "\n" +
// popup message
_("Deselect some packages or delete some data
or temporary files before updating the system."));

		else
		    message = add (message, "\n" +
// popup message
_("Please deselect some packages."));
	    }
	}
	return message;
    }

    //
    // Popup displays warning about exhausted disk space
    //
    global define boolean ShowPartitionWarning () ``{
	list<string> message = GetPartitionWarning ();
	if (size (message) > 0)
	{
	    y2warning("Warning: %1", message );
	    Report::Message(mergestring (message, "\n"));
	    return true;
	}
	else
	{
	    return false;
	}
    };


    //
    // Calculate required disk space
    //
    global define string GetRequSpace (boolean initialize) ``{

	if ( !info_called )
	{
	    SpaceCalculation::GetPartitionInfo();
	}

	// used space in kB
	integer used = 0;

	//$[ "dir" : [ total, usednow, usedfuture ], .... ]
	foreach( string dir, list sizelist, Pkg::TargetGetDU(),
		 ``{
	    used = used + sizelist[2]:0;
	} );
	y2milestone ("GetReqSpace Pkg::TargetGetDU() %1", Pkg::TargetGetDU());
	// used is in kB
	return ( String::FormatSize (used*1024) );
    };


    //
    // Check, if the current selection fits on the disk
    // return true or false
    //
    global define boolean CheckDiskSize () ``{

	boolean fit = true;

	if ( !info_called )
	{
	    SpaceCalculation::GetPartitionInfo();
	}

	integer used = 0;

	string message = "";
	//$[ "dir" : [ total, usednow, usedfuture ], .... ]
	foreach (string dir, list sizelist, Pkg::TargetGetDU(),
		 ``{
	    y2milestone ("%1: %2", dir, sizelist);
	    integer needed = sizelist[2]:0 - sizelist[0]:0;	// usedfuture - total
	    if ( needed > 0 )
	    {
		y2warning("Partition \"%1\" needs %2 more disk space.",
			  // size is in kB
			  dir, String::FormatSize (needed*1024) );
		fit = false;
	    }

	    used = used + sizelist[2]:0;
	});

	y2milestone ("Total used space (kB): %1, fits ?: %2", used, fit);

	return fit;
    };

    /**
    * Check, if there is enough free space after installing the current selection
    * @param free_percent minimal free space after installation (in percent)
    * @return list of partitions which have less than free_percent free size
    */
    global define list<map> CheckDiskFreeSpace(integer free_percent, integer max_unsufficient_free_size)
    {
	if ( !info_called )
	{
	    SpaceCalculation::GetPartitionInfo();
	}

	y2milestone("min. free space: %1%%, max. unsufficient free space: %2", free_percent, max_unsufficient_free_size);

	list<map> ret = [];

	if (free_percent > 0)
	{
	    //$[ "dir" : [ total, usednow, usedfuture ], .... ]
	    foreach (string dir, list sizelist, Pkg::TargetGetDU(),
		{
		    y2milestone ("Disk usage of directory %1: %2", dir, sizelist);

		    integer total = sizelist[0]:0;
		    integer used_future = sizelist[2]:0;
		    integer used_now = sizelist[1]:0;
		    integer current_free_size = (total - used_future);
		    integer current_free_percent = current_free_size*100/total;

		    // ignore the partitions which were already full and no files will be installed there (bnc#259493)
		    if (used_future > used_now && current_free_size > 0)
		    {
			if (current_free_percent < free_percent && current_free_size < max_unsufficient_free_size )
			{
			    y2warning("Partition %1: less than %2%% free space (%3%%, %4)", dir, free_percent, current_free_percent, current_free_size);

			    ret = add(ret, $["dir":dir, "free_percent":current_free_percent, "free_size":current_free_size ]);
			}
		    }
		}
	    );
	}

	y2milestone("Result: %1", ret);

	return ret;
    };
}


ACC SHELL 2018