home *** CD-ROM | disk | FTP | other *** search
/ Chip 2007 January, February, March & April / Chip-Cover-CD-2007-02.iso / boot / i386 / root / usr / share / YaST2 / modules / SpaceCalculation.ycp < prev    next >
Text File  |  2006-11-29  |  16KB  |  576 lines

  1. /**
  2.  * Module:        SpaceCalculation.ycp
  3.  *
  4.  * Authors:        Klaus Kaempf (kkaempf@suse.de)
  5.  *            Gabriele Strattner (gs@suse.de)
  6.  *            Stefan Schubert (schubi@suse.de)
  7.  *
  8.  * Purpose:        Package installation functions usable
  9.  *            when the installation media is available
  10.  *            on Installation::sourcedir
  11.  *
  12.  *
  13.  * $Id: SpaceCalculation.ycp 33383 2006-10-13 09:12:02Z lslezak $
  14.  */
  15.  
  16. {
  17.     module "SpaceCalculation";
  18.  
  19.     textdomain "packager";
  20.  
  21.     import "Installation";
  22.     import "Mode";
  23.     import "ProductFeatures";
  24.     import "Report";
  25.     import "String";
  26.     import "DirInstall";
  27.     import "Stage";
  28.  
  29.     boolean info_called = false;    // list partition_info already initialized?
  30.     list    partition_info = [];    // information about available partitions
  31.  
  32.  
  33.     /*
  34.      * Return partition info list
  35.      * @return list of available partitions
  36.      */
  37.     global define list GetPartitionList() ``{
  38.     return partition_info;
  39.     }
  40.  
  41.     /**
  42.      * Get mountpoint for a directory
  43.      * @param target directory
  44.      * @param partition partitions list
  45.      * @return mountpoint
  46.      */
  47.     string GetDirMountPoint(string target, list<map<string, string> > partition ) {
  48.  
  49.     list<string> d = splitstring(target, "/");
  50.     d=filter(string fd, d, ``(fd!=""));
  51.     string mountpoint = "";
  52.  
  53.     foreach( map part, partition,
  54.          ``{
  55.         //  dirinstall: /test/xen dir: /test
  56.         //  dirinstall:/var/tmp/xen dir: /
  57.  
  58.         string dir = part["name"]:"";
  59.         string tmpdir = "";
  60.         foreach(string dd ,d , ``{
  61.         tmpdir = sformat("%1/%2", tmpdir, dd );
  62.         y2debug("tmpdir: %1 dir: %2", tmpdir, dir );
  63.         if (dir == tmpdir )
  64.            mountpoint = dir;
  65.         });
  66.     });
  67.  
  68.     if (mountpoint=="")
  69.         mountpoint = "/";
  70.  
  71.     return mountpoint;
  72.     }
  73.  
  74.     /**
  75.      * Evaluate the free space on the file system. Runs the command "df" and creates a map
  76.      * containig information about used and free space on every partition.
  77.      * Free space is calculated respecting the spare_percentage given in second argument.
  78.      *
  79.      * @param  spare_percentage percentage of spare disk space, i.e. free space is increased
  80.      * @return list partition list, e.g.  [$["free":389318, "name":"/", "used":1487222],
  81.      *                     $["free":1974697, "name":"usr", "used":4227733]]
  82.      *
  83.      * @example EvaluateFreeSpace ( 5 );
  84.      *
  85.      * ***  This is needed during update !
  86.      */
  87.     global define list<map<string,any> > EvaluateFreeSpace (integer spare_percentage) ``{
  88.  
  89.     list<map<string, string> > partition = [];
  90.     // the sizes are in kB
  91.     integer min_spare = 1 * 1024;        // 1 MB
  92.     integer max_spare = 1024 * 1024;    // 1 GB
  93.  
  94.     string target = Installation::destdir;
  95.  
  96.     // get information about diskspace ( used/free space on every partition )
  97.     partition = (list<map<string, string> >)SCR::Read(.run.df);
  98.  
  99.     // filter out headline and other invalid entries
  100.     partition = filter( map<string, string> part, partition, ``( substring ( part["name"]:"", 0, 1 ) == "/" ) );
  101.  
  102.         if (DirInstall::installing_into_dir)
  103.         {
  104.             target = GetDirMountPoint(DirInstall::target, partition );
  105.         }
  106.  
  107.     // pkginfo expects names of partitions without "/" in front ( exception: "/" itself )
  108.     list<map<string,any> > part_input = [];
  109.  
  110.     foreach( map part, partition,
  111.          ``{
  112.         map<string,any> part_info = $[];
  113.         integer free_size = 0;
  114.         integer spare_size = 0;
  115.         string partName = "";
  116.         boolean add_part = true;
  117.  
  118.         string mountName = part["name"]:"";
  119.         string spec = part["spec"]:"";
  120.  
  121.         if ( target != "/" )
  122.         {
  123.         if ((size(mountName) >= size(target))
  124.             && (substring( mountName, 0, size(target)) == target) )
  125.         {
  126.             partName = substring( mountName, size(target) );
  127.             // nothing left, it was target root itself
  128.             if ( size ( partName ) == 0 )
  129.             {
  130.             part_info = add( part_info, "name", "/"  );
  131.             }
  132.             else
  133.             {
  134.             part_info = add( part_info, "name", substring( partName, 1 ) );  // remove "/" in front
  135.             }
  136.         }
  137.         else
  138.         {
  139.             add_part = false;
  140.         }
  141.         }
  142.         else // target is "/"
  143.         {
  144.         if ( mountName == "/" )
  145.         {
  146.             part_info = add ( part_info, "name", mountName );
  147.         }
  148.         // ignore some mount points
  149.         else if ( mountName != Installation::sourcedir
  150.               && mountName != "/cdrom"
  151.               && mountName != "/dev/shm"
  152.               && spec != "udev"
  153.               && !regexpmatch(mountName, "^/media/") )
  154.         {
  155.             part_info = add ( part_info, "name", substring( mountName, 1 ) );  // remove "/" in front
  156.         }
  157.         else
  158.         {
  159.             add_part = false;
  160.         }
  161.         }
  162.  
  163.         if ( add_part )
  164.         {
  165.         part_info = add ( part_info, "used", tointeger(part["used"]:"0") );
  166.  
  167.         free_size = tointeger(part["free"]:"0");
  168.         spare_size = (free_size*spare_percentage)/100;
  169.  
  170.         if ( spare_size < min_spare )
  171.             spare_size = min_spare;
  172.         else if ( spare_size > max_spare )
  173.             spare_size = max_spare;
  174.  
  175.         free_size = free_size - spare_size;
  176.         if ( free_size < 0 )
  177.             free_size = 0;    // don't add a negative size
  178.  
  179.         part_info = add ( part_info, "free",  free_size );
  180.  
  181.         part_input = add ( part_input, part_info );
  182.         }
  183.     });
  184.  
  185.     y2milestone( "UTILS *** EvaluateFreeSpace returns: %1", part_input );
  186.  
  187.     Pkg::TargetInitDU (part_input);
  188.  
  189.     return part_input;
  190.     };
  191.  
  192.  
  193.     /*
  194.      * Define a macro that transforms information about all partitions ( from
  195.      * Storage::GetTargetMap() ) into a list(map) with information about partitions
  196.      * which are available for installation, e.g.:
  197.      *
  198.      * [$["free":1625676, "name":"/boot", "used":0], $["free":2210406, "name":"/", "used":0]]
  199.      *
  200.      * Please note: there isn't any information about used space, so "used" at begin
  201.      *              of installation is initialized with zero;
  202.      *              size "free", "used" in KBytes
  203.      *
  204.      */
  205.  
  206.     define list get_partition_info () ``{
  207.     import "Storage";
  208.     // remove leading slash so it matches the packages.DU path
  209.     boolean remove_slash = true;
  210.  
  211.     if (!Stage::initial ())
  212.     {
  213.         // read /proc/mounts as a list of maps
  214.         // $["file":"/boot", "freq":0, "mntops":"rw", "passno":0, "spec":"/dev/sda1", "vfstype":"ext2"]
  215.         list<map<string, any> > mounts = (list<map<string, any> >) SCR::Read (.proc.mounts);
  216.         y2milestone ("mounts %1", mounts);
  217.  
  218.         list<map<string, any> > partitions = [];
  219.         foreach (map mpoint, mounts,
  220.              ``{
  221.         string name = mpoint["file"]:"";
  222.         if ((substring (name, 0, 1) == "/")
  223.             && (substring (name, 0, 5) != "/dev/")        // filter out /dev/pts etc.
  224.             && (mpoint["vfstype"]:"" != "rootfs"))        // filter out duplicate "/" entry
  225.         {
  226.             integer capacity = Pkg::TargetCapacity (name);
  227.             if (capacity != 0)        // dont look at pseudo-devices (proc, shmfs, ...)
  228.             {
  229.             integer used = Pkg::TargetUsed (name);
  230.             partitions = add (partitions, $["name" : name, "free" : capacity-used, "used" : used]);
  231.             }
  232.         }
  233.         });
  234.         Pkg::TargetInitDU (partitions);
  235.         y2milestone ("get_partition_info: %1", partitions);
  236.         return partitions;
  237.  
  238.     } // !Stage::initial ()
  239.  
  240.     map<string, map> targets = Storage::GetTargetMap();
  241.     if ( Mode::test () )
  242.     {
  243.         targets = (map<string, map>) SCR::Read(.target.yast2, "test_target_map.ycp");
  244.     }
  245.  
  246.     list<map> target_partitions = [];
  247.     integer min_spare    = 5 * 1024 * 1024; // minimum free space ( 5 MB )
  248.  
  249.     foreach( string disk, map diskinfo, targets,
  250.          ``{
  251.  
  252.         list<map> part_info = diskinfo["partitions"]:[];
  253.  
  254.         foreach( map part, part_info,
  255.              ``{
  256.         integer free_size = 0;
  257.  
  258.         if (part["mount"]:nil != nil
  259.             && substring( part["mount"]:"", 0, 1 ) == "/" )
  260.         {
  261.             if (part["create"]:nil == true
  262.             || part["delete"]:nil == false
  263.             || (part["create"]:nil == nil
  264.                 && part["delete"]:nil == nil ) )
  265.             {
  266.             y2debug( "get_partition_info: adding partition: %1", part );
  267.  
  268.             // get free_size on partition in kBytes
  269.             free_size = part["size_k"]:0 * 1024;
  270.             free_size = free_size - min_spare;
  271.  
  272.             integer used = 0;
  273.             if (! (part["create"]:false || part["format"]:false))
  274.             {
  275.                 string tmpdir = (string)SCR::Read (.target.tmpdir);
  276.                 tmpdir = tmpdir + "/diskspace_mount";
  277.                 SCR::Execute (.target.bash, sformat (
  278.                 "test -d %1 || mkdir -p %1", tmpdir));
  279.                 SCR::Execute (.target.bash, sformat (
  280.                 "/bin/mount -o ro %1 %2",
  281.                 part["device"]:"", tmpdir));
  282.                 list<map<string,string> > partition =
  283.                 (list<map<string, string> >)
  284.                 SCR::Read(.run.df);
  285.                 foreach (map p, partition, {
  286.                 if (p["name"]:"" == tmpdir)
  287.                 {
  288.                     y2internal ("P: %1", p);
  289.                     free_size = tointeger (p["free"]:"0")
  290.                     * 1024;
  291.                     used = tointeger (p["used"]:"0") * 1024;
  292.                 }
  293.                 });
  294.                 SCR::Execute (.target.bash, sformat (
  295.                 "/bin/umount %1", tmpdir));
  296.             }
  297.  
  298.             // convert into kB for TargetInitDU
  299.             free_size = free_size / 1024;
  300.             used = used / 1024;
  301.  
  302.             y2milestone ("available partition: mount: %1, free: %2 KB, used: %3 KB", part["mount"]:"", free_size, used);
  303.             if ( !remove_slash)
  304.             {
  305.                 target_partitions = add (target_partitions, $["name":part["mount"]:"", "used":used, "free":free_size]);
  306.             }
  307.             else
  308.             {
  309.                 string part_name = "";
  310.                 string mount_name = part["mount"]:"";
  311.  
  312.                 if ( mount_name != "/" )
  313.                 {
  314.                 part_name = substring( mount_name, 1, size(mount_name) );
  315.                 }
  316.                 else
  317.                 {
  318.                 part_name = mount_name;
  319.                 }
  320.  
  321.                 target_partitions = add ( target_partitions, $["name":part_name, "used":used, "free":free_size]);
  322.             }
  323.  
  324.             }
  325.         }
  326.         } ); // foreach (`part)
  327.     } ); // foreach (`disk)
  328.  
  329.     y2milestone( "get_partition_info: part %1", target_partitions );
  330.     Pkg::TargetInitDU (target_partitions);
  331.  
  332.     return ( target_partitions );
  333.     };
  334.  
  335.     /*
  336.      * Get information about available partitions either from "targetMap"
  337.      * in case of a new installation or from 'df' command (continue mode
  338.      * and installation on installed system).
  339.      * Returns a list containing available partitions and stores the list
  340.      * in "partition_info".
  341.      *
  342.      * @return list partition list, e.g.  [$["free":389318, "name":"/", "used":1487222],
  343.      *                     $["free":1974697, "name":"usr", "used":4227733]]
  344.      *
  345.      *
  346.      * @example GetPartitionInfo();
  347.      *
  348.      * Will be called from Packages when re-doing proposal !!
  349.      */
  350.     global define list GetPartitionInfo () ``{
  351.  
  352.     list partition = [];
  353.  
  354.     if ( Stage::cont () )
  355.     {
  356.         partition = EvaluateFreeSpace ( 0 ); // free spare already checked during first part of installation
  357.     }
  358.     else if ( Mode::update () )
  359.     {
  360.         partition = EvaluateFreeSpace ( 5 ); // 5% free spare for update/upgrade
  361.     }
  362.     else if ( Mode::normal () )
  363.     {
  364.         partition = EvaluateFreeSpace ( 5 ); // 5% free spare for post installation
  365.     }
  366.     else    // Stage::initial ()
  367.     {
  368.         partition = get_partition_info( );
  369.     }
  370.     y2milestone( "INIT done, SpaceCalculation - partitions: %1", partition );
  371.  
  372.     info_called = true;
  373.     partition_info = partition;    // store partition_info
  374.  
  375.     return partition;
  376.     }
  377.  
  378.  
  379.  
  380.     /*
  381.      * get current space data for partitions
  382.      * current_partitions = list of maps of
  383.      * $["format":bool, "free":integer, "name" : string, "used" :integer, "used_fs": symbol]
  384.      * from Storage module
  385.      * returns list of maps of
  386.      * $["name" : string, "free" : integer, "used" : integer ]
  387.      *
  388.      */
  389.     global define list CheckCurrentSpace (list<map> current_partitions) ``{
  390.  
  391.     list<map> output = [];
  392.  
  393.     foreach (map par, current_partitions, ``{
  394.         map outdata = $[];
  395.         outdata["name"] = par["name"]:"";
  396.         outdata["used"] = Pkg::TargetUsed (Installation::destdir + par["name"]:"");
  397.         outdata["free"] = Pkg::TargetCapacity (Installation::destdir + par["name"]:"") - outdata["used"]:0;
  398.         output = add (output, eval (outdata));
  399.     });
  400.     y2milestone ("CheckCurrentSpace(%1) = %2", current_partitions, output);
  401.  
  402.     return output;
  403.     }
  404.  
  405.     global list<string> GetPartitionWarning () {
  406.     if ( !info_called )
  407.     {
  408.         SpaceCalculation::GetPartitionInfo();
  409.     }
  410.     integer used = 0;
  411.     list<string> message = [];
  412.  
  413.     //$[ "dir" : [ total, usednow, usedfuture ], .... ]
  414.  
  415.     foreach( string dir, list sizelist, Pkg::TargetGetDU(), ``{
  416.         y2milestone ("dir %1, sizelist (total, current, future) %2", dir, sizelist);
  417.         integer needed = sizelist[2]:0 - sizelist[0]:0;    // usedfuture - total
  418.         if ( needed > 0 )
  419.         {
  420.         // Warning message, e.g.: Partition /usr needs 35 MB more disk space
  421.         message = add (message, sformat ( _("Partition \"%1\" needs %2 more disk space."),
  422.                   // needed is in kB
  423.                   dir, String::FormatSize(needed*1024)));
  424.         }
  425.         used = used + sizelist[2]:0;
  426.     } );
  427.  
  428.     y2debug( "Total used space (kB): %1", used );
  429.  
  430.     if ( size ( message ) > 0 )
  431.     {
  432.         // dont ask user to deselect packages for imap server, product
  433.         if ( ProductFeatures::GetFeature ("software", "selection_type") == `auto)
  434.         {
  435.         if (Mode::update ())
  436.             message = add (message, "\n" +
  437. // popup message
  438. _("Deselect some packages or delete some data
  439. or temporary files before updating the system."));
  440.  
  441.         else
  442.             message = add (message, "\n" +
  443. // popup message
  444. _("Please deselect some packages."));
  445.         }
  446.     }
  447.     return message;
  448.     }
  449.  
  450.     //
  451.     // Popup displays warning about exhausted disk space
  452.     //
  453.     global define boolean ShowPartitionWarning () ``{
  454.     list<string> message = GetPartitionWarning ();
  455.     if (size (message) > 0)
  456.     {
  457.         y2warning("Warning: %1", message );
  458.         Report::Message(mergestring (message, "\n"));
  459.         return true;
  460.     }
  461.     else
  462.     {
  463.         return false;
  464.     }
  465.     };
  466.  
  467.  
  468.     //
  469.     // Calculate required disk space
  470.     //
  471.     global define string GetRequSpace (boolean initialize) ``{
  472.  
  473.     if ( !info_called )
  474.     {
  475.         SpaceCalculation::GetPartitionInfo();
  476.     }
  477.  
  478.     // used space in kB
  479.     integer used = 0;
  480.  
  481.     //$[ "dir" : [ total, usednow, usedfuture ], .... ]
  482.     foreach( string dir, list sizelist, Pkg::TargetGetDU(),
  483.          ``{
  484.         used = used + sizelist[2]:0;
  485.     } );
  486.     y2milestone ("GetReqSpace Pkg::TargetGetDU() %1", Pkg::TargetGetDU());
  487.     // used is in kB
  488.     return ( String::FormatSize (used*1024) );
  489.     };
  490.  
  491.  
  492.     //
  493.     // Check, if the current selection fits on the disk
  494.     // return true or false
  495.     //
  496.     global define boolean CheckDiskSize () ``{
  497.  
  498.     boolean fit = true;
  499.  
  500.     if ( !info_called )
  501.     {
  502.         SpaceCalculation::GetPartitionInfo();
  503.     }
  504.  
  505.     integer used = 0;
  506.  
  507.     string message = "";
  508.     //$[ "dir" : [ total, usednow, usedfuture ], .... ]
  509.     foreach (string dir, list sizelist, Pkg::TargetGetDU(),
  510.          ``{
  511.         y2milestone ("%1: %2", dir, sizelist);
  512.         integer needed = sizelist[2]:0 - sizelist[0]:0;    // usedfuture - total
  513.         if ( needed > 0 )
  514.         {
  515.         y2warning("Partition \"%1\" needs %2 more disk space.",
  516.               // size is in kB
  517.               dir, String::FormatSize (needed*1024) );
  518.         fit = false;
  519.         }
  520.  
  521.         used = used + sizelist[2]:0;
  522.     });
  523.  
  524.     y2milestone ("Total used space (kB): %1, fits ?: %2", used, fit);
  525.  
  526.     return fit;
  527.     };
  528.  
  529.     /**
  530.     * Check, if there is enough free space after installing the current selection
  531.     * @param free_percent minimal free space after installation (in percent)
  532.     * @return list of partitions which have less than free_percent free size
  533.     */
  534.     global define list<map> CheckDiskFreeSpace(integer free_percent, integer max_unsufficient_free_size)
  535.     {
  536.     if ( !info_called )
  537.     {
  538.         SpaceCalculation::GetPartitionInfo();
  539.     }
  540.  
  541.     y2milestone("min. free space: %1%%, max. unsufficient free space: %2", free_percent, max_unsufficient_free_size);
  542.  
  543.     list<map> ret = [];
  544.  
  545.     if (free_percent > 0)
  546.     {
  547.         //$[ "dir" : [ total, usednow, usedfuture ], .... ]
  548.         foreach (string dir, list sizelist, Pkg::TargetGetDU(),
  549.         {
  550.             y2milestone ("%1: %2", dir, sizelist);
  551.  
  552.             integer total = sizelist[0]:0;
  553.             integer used_future = sizelist[2]:0;
  554.             integer current_free_size = (total - used_future);
  555.             integer current_free_percent = current_free_size*100/total;
  556.  
  557.             if (current_free_size > 0)
  558.             {
  559.             if (current_free_percent < free_percent && current_free_size < max_unsufficient_free_size )
  560.             {
  561.                 y2warning("Partition %1: less than %2%% free space (%3%%, %4)", dir, free_percent, current_free_percent, current_free_size);
  562.  
  563.                 ret = add(ret, $["dir":dir, "free_percent":current_free_percent, "free_size":current_free_size ]);
  564.             }
  565.             }
  566.         }
  567.         );
  568.     }
  569.  
  570.     y2milestone("Result: %1", ret);
  571.  
  572.     return ret;
  573.     };
  574. }
  575.  
  576.