/**
 * File:
 *   evms_lib.ycp
 *
 * Module: 
 *    utility functions for evms config
 *
 * Summary:
 *
 * Authors:
 *   Thomas Fehr <fehr@suse.de>
 *
 *
 * $Id: evms_lib.ycp 22133 2005-03-02 10:16:36Z fehr $
 *
 */


{

textdomain "storage";

import "FileSystems";
import "Mode";

include "partitioning/partition_defines.ycp";

//////////////////////////////////////////////////////////////////////
// helptext for evms main dialog 
//////////////////////////////////////////////////////////////////////

define string getEvmsHelptext()
    ``{
    string help_text = "";

 // helptext EVMS partitioning. 
    help_text =  _("<p>The upper part of this dialog contains the container-related functionality. Here, create, edit, and modify EVMS containers. </p>");

    // helptext EVMS partitioning. 
    help_text = help_text + _("<p>
The lower part with the list contains all available EVMS devices.
If at least one EVMS container is available, you can create new 
devices that use the available space of the current container.</p>
");

    return( help_text );
    };

//////////////////////////////////////////////////////////////////////
// evms_pesize_check 
// in: <number>[kKmM][bB]
// 
// return 0 if input is invalid
// 
// pesize is valid   8K to 512M in power of 2
// 8 is 8k
// 16K == 16k == 16KB == 16kb
     
define integer evms_pesize_check( string input, string type )
    ``{
    integer num = kmgt_str_to_byte( input );
    if( findfirstnotof( input, "0123456789 " ) == nil )
	num = num*1024;

    integer ret = num;

    if( ret % 1024 != 0 )
	ret = 0;
    else
	{
	while( num>1 && ret>0 )
	    {
	    if( num%2 != 0 )
		ret = 0;
	    else
		num = num/2;
            }
        }
    y2milestone( "evms_pesize_check ret %1", ret );
    if( ret < 8*1024 || ret > 512*1024*1024 )
        {
        ret = 0;
        }
    y2milestone( "evms_pesize_check ret %1", ret );
    if( ret==0 )
	{
	// error popup text
	Popup::Error(_("The data entered is invalid.
Insert a physical extent size from
8KB to 512MB in powers of 2, for example, \"4M\" or \"512K\"
"));
	}
    return( ret );
    };

//////////////////////////////////////////////////////////////////////
// checks if name is a valid container name 
define boolean evms_check_name( string name )
    ``{
    if ( size( name ) == 0 ) return( false );
    return( ( findfirstof( name, " ;/`'´!,\"%#" ) == nil ) );
    }


define list<map> add_size_info( list<map> pvlist, integer cyl_size )
    ``{
    return( maplist( map p, pvlist,
	``{
	integer s = cyl_size * p["region",1]:0;
	p["size"] = s;
	p["size_str"] = ByteToHumanStringWithZero(s);
	return( p );
	})) ;
    }

define list<map> filter_deleted_parts( map<string,map> tg, list<map> plist )
    ``{
    list<map> ret = [];
    foreach( map p, plist, 
	``{
	y2milestone( "filter_deleted_parts p %1", p );
	if( find( p["device"]:"", "/dev/evms/" )==0 )
	    {
	    map disk = 
		Storage::GetDiskPartition( "/dev/" + 
					   substring( p["device"]:"", 10 ) );
	    if( size(filter( map pa, tg[disk["disk"]:"","partitions"]:[],
			     ``(sformat("%1",pa["nr"]:(any)"")==
				    sformat("%1",disk["nr"]:(any)0) &&
				pa["delete"]:false)))==0)
		{
		ret = add( ret, p );
		}
	    }
	else
	    {
	    ret = add( ret, p );
	    }
	});
    if( size(plist)!=size(ret) )
	{
	y2milestone( "filter_deleted_parts plist %1", plist );
	y2milestone( "filter_deleted_parts ret %1", ret );
	}
    return( ret );
    }

define list<map> get_pvlist( map<string,map> tg, string name )
    ``{
    list<map> pv = [];
    y2milestone( "get_pvlist name %1", name );
    foreach( string k, map e, tg,
	``{
	if( e["is_evms"]:false )
	    {
	    if( !e["is_evms_container"]:false )
		{
		list<map> tmp = filter( map p, e["partitions"]:[],
				        ``(p["evms_vol"]:""==name ||
					   (size(p["evms_vol"]:"")==0 &&
					    size(p["mount"]:"")==0)));
		tmp = filter_deleted_parts( tg, tmp );
		tmp = add_size_info( tmp, e["cyl_size"]:0 );
		y2milestone( "key %1 tmp %2", k, tmp );
		pv = (list<map>)merge( pv, tmp );
		}
	    }
	else
	    {
	    y2milestone( "get_pvlist key %1 disk %2", k, e );
	    list<map> tmp = filter( map p, e["partitions"]:[],
				    ``(size(name)>0 &&
				       p["evms_vol"]:""==name &&
			               !p["delete"]:false ));
	    tmp = (list<map>)merge( tmp,
				    filter( map p, e["partitions"]:[],
					    ``(p["create"]:false &&
					       p["type"]:`primary != `extended &&
					       !p["delete"]:false &&
					       size(p["mount"]:"")==0 &&
					       size(p["evms_vol"]:"")==0)));
	    y2milestone( "get_pvlist tmp %1", tmp );
	    tmp = add_size_info( tmp, e["cyl_size"]:0 );
	    y2milestone( "key %1 tmp %2", k, tmp );
	    pv = (list<map>)merge( pv, tmp );
	    }
	});
    pv = maplist( map p, pv, 
	``{
	string d = substring( p["device"]:"", 5 );
	if( find( d, "evms/" )==0 )
	    {
	    d = substring( d, 5 );
	    }
	p["device"] = d;
	return( p );
	});
    pv = sort( map a, map b, pv, ``(a["device"]:""<b["device"]:""));
    y2milestone( "pv %1", pv );
    string pdev = "";
    pv = filter( map p, pv, 
	``({
	boolean r = false;
	if( pdev!=p["device"]:"" )
	    {
	    pdev = p["device"]:"";
	    r = true;
	    }
	return( r );
	}));
    y2milestone( "unique %1", pv );
    return( pv );
    }

define list<term> get_table_entries( list<map> pvlist, string name )
    ``{
    y2milestone( "get_table_entries name %1", name );
    integer count=-1;
    list<term> ret = maplist( map p, pvlist,
	``{
	count = count+1;
	return( `item( `id(count), p["device"]:"", p["size_str"]:"",
	        size(p["evms_vol"]:"")>0?" "+name:"" ));
	});
    y2milestone( "get_table_entries ret %1", ret );
    return( ret );
    }

define integer evms_usable_size( integer siz, integer pesize )
    ``{
    integer r = siz - 128*1024;
    r = (r/pesize)*pesize;
    if( r < 0 ) r = 0;
    return( r );
    }

define integer get_container_size( map co, list pvlist )
    ``{
    integer pesize =  co["cyl_size"]:0;
    integer ret = co["cyl_count"]:0 * pesize;
    foreach( integer e, co["added"]:[],
	``{
	ret = ret + evms_usable_size( pvlist[e,"size"]:0, pesize );
	});
    foreach( integer e, co["removed"]:[],
	``{
	ret = ret - evms_usable_size( pvlist[e,"size"]:0, pesize );
	});
    y2milestone( "get_container_size ret %1", ret );
    return( ret );
    }

define void set_container_size( map co, integer new_size )
    ``{
    string s = ByteToHumanStringWithZero( new_size );
    UI::ChangeWidget( `id(`size), `Value, s );
    UI::RecalcLayout();
    }

define list get_index_list( list<string> l, list pvlist )
    ``{
    list nl = [];
    foreach( string e, l,
	``{
	integer i=0;
	while( i<size(pvlist) && pvlist[i,"device"]:""!=e )
	    {
	    i = i+1;
	    }
	if( i<size(pvlist) )
	    {
	    nl = add( nl, i );
	    }
	});
    y2milestone( "get_index_list l %1 ret %2", l, nl );
    return( nl );
    }

define void set_table_elements( list<term> entries, integer id )
    ``{
    if( id < 0 )
	{
	id = (integer) UI::QueryWidget( `id(`pv_table), `CurrentItem );
	}
    UI::ChangeWidget( `id(`pv_table), `Items, entries );
    UI::ChangeWidget( `id(`pv_table), `CurrentItem, id );
    }

define map dlg_modify_container( map<string,map> tg, map<string,any> co, 
                                 boolean create )
    ``{
    // popup header
    string head = _("Modify Existing EVMS Container");
    if( co["create"]:false )
	{
	if( create )
	    {
	    // popup header
	    head = _("Create EVMS Container");
	    }
	else
	    {
	    // popup header
	    head = _("Modify EVMS Container");
	    }
	}
    string txt =
	      // popup description
_("Change the settings for an EVMS container.
Container type, PE size, and container name can only be set when creating
the container.
");

    term dlg = 
	`HBox(
	    `HSpacing(2),
	    `VBox(
		  `Heading(head),
		  `Label( txt ),
		  // label text
		  `HBox(
		      `HSpacing(1),
		      `ComboBox( `id(`type), `opt(`notify), _("Container Type"),
						      // label text
			         [ `item( `id("lvm"), _("Linux LVM") ),
						      // label text
				   `item( `id("aix"), _("AIX LVM") )] ),
			  
		      `HSpacing(2),
		      // label text
		      `TextEntry( `id(`name), `opt(`notify), 
		                  _("Container &Name"), co["name"]:"" ),
		      `HSpacing(2),
		      // label text
		      `TextEntry(`id(`pesize), _("&Physical Extent Size"), ""),
		      `HSpacing(1)
		      ),
		  `Left(`HBox( 
		      `HSpacing(1),
		      `Label( _("Container Size:") ),
		      `Label( `id(`size), "" ),
		      `HSpacing(1)
		       )),
		  `VSpacing(1),
		  `HBox( 
		      `HSpacing(2),
		      `VBox(
			  `HBox( 
			      `Table(`id(`pv_table), `opt(`notify),
				     // header text
				     `header(_("Device"), `Right(_(" Size")), 
					     // header text
					     _(" Container")),
				     [] ),
			      `VSpacing(10)
			      ),
			  `VSpacing(1),
			  `HBox(
			      // button text
			      `PushButton(`id(`pv_add), _("&Add Volume")),
			      // button text
			      `PushButton(`id(`pv_remove), _("&Remove Volume"))
			      )
			  ),
		      `HSpacing(2)
		      ),
		  `VSpacing(1),
		  `HBox(
		      // Ok button
		      `PushButton(`id(`ok), `opt(`default), Label::OKButton() ),
		      // Cancel button
		      `PushButton(`id(`cancel), Label::CancelButton() )
		       ),
		  `VSpacing(0.5)
		 ),
	    `HSpacing(2)
	    );

    UI::OpenDialog( `opt(`decorated ), dlg );

    string name = co["name"]:"";
    string typename = "lvm";

    if( findlastof( name, "/" ) != nil )
	name = substring( name, findlastof( name, "/" )+1 );

    if( find( co["name"]:"", "aix/" )==0 )
	{
	typename = "aix";
	}

    co["name"] = typename + "/" + name;

    UI::ChangeWidget( `id(`name), `Value, name );
    if( create )
	{
	UI::ChangeWidget( `id(`name), `ValidChars, 
	                  FileSystems::nchars + "-._:" );
	}
    else
	{
	UI::ChangeWidget( `id(`name), `Enabled, false );
	UI::ChangeWidget( `id(`pesize), `Enabled, false );
	}
    UI::ChangeWidget( `id(`type), `Enabled, false );
    UI::ChangeWidget( `id(`pesize), `Value, 
                      ByteToHumanStringWithZero(co["cyl_size"]:0 ));
    UI::ChangeWidget( `id(`type), `Value, typename );

    list<map> pvlist = get_pvlist( tg, co["name"]:"" );
    y2milestone( "dlg_modify_container pvlist %1", pvlist );
    list nl = get_index_list( co["added"]:[], pvlist );
    if( size(nl)>0 )
	{
	co["added"] = nl;
	}
    nl = get_index_list( co["removed"]:[], pvlist );
    if( size(nl)>0 )
	{
	co["added"] = nl;
	}
    y2milestone( "dlg_modify_container co %1", co );
    set_table_elements( get_table_entries( pvlist, co["name"]:"" ), 0 );
    integer cur_size = co["cyl_size"]:0 * co["cyl_count"]:0;
    set_container_size( co, cur_size );
    symbol ret = nil;

    repeat
	{
	ret = (symbol)UI::UserInput();

	y2milestone( "dlg_modify_container ret %1", ret );

	if( ret == `type )
	    {
	    typename  = (string)UI::QueryWidget(`id(`type), `Value );
	    co["name"] = typename + "/" + name;
	    set_table_elements( get_table_entries( pvlist, co["name"]:"" ), 
	                        -1 );
	    }
	else if( ret == `name )
	    {
	    name = (string)UI::QueryWidget(`id(`name), `Value );
	    name = deletechars( name, " \t" );
	    co["name"] = typename + "/" + name;
	    if( size( filter( map p, pvlist, ``(size(p["evms_vol"]:"")>0)))>0)
		{
		set_table_elements( get_table_entries( pvlist, co["name"]:"" ), 
				    -1 );
		}
	    foreach( integer id, co["added"]:[],
		``{
		pvlist[id,"evms_vol"] = co["name"]:"";
		pvlist[id,"format"] = false;
		string tstr =  "/dev/evms/"+pvlist[id,"device"]:"";
		tg = Storage::SetPartitionData( tg, tstr, "evms_vol",
		                                co["name"]:"" );
		tg = Storage::SetPartitionData( tg, tstr, "format", false );
		tstr =  "/dev/"+pvlist[id,"device"]:"";
		tg = Storage::SetPartitionData( tg, tstr, "format", false );
		});

	    }
	else if( contains( [`pv_add, `pv_remove, `pv_table ], ret ) )
	    {
	    integer id = (integer)UI::QueryWidget(`id(`pv_table), `CurrentItem);
	    if( ret == `pv_table && id != nil )
		{
		ret = size(pvlist[id,"evms_vol"]:"")>0?`pv_remove:`pv_add;
		}
	    if( ret == `pv_add && id != nil && 
	        size(pvlist[id,"evms_vol"]:"")==0 )
		{
		if( contains( co["removed"]:[], id ) )
		    {
		    co["removed"] = filter( integer e, co["removed"]:[],
					    ``(e!=id) );
		    }
		else
		    {
		    co["added"] = add( co["added"]:[], id );
		    }
		pvlist[id,"evms_vol"] = co["name"]:"";
		string tstr =  "/dev/evms/"+pvlist[id,"device"]:"";
		tg = Storage::SetPartitionData( tg, tstr, "evms_vol",
		                                co["name"]:"" );
		tstr =  "/dev/"+pvlist[id,"device"]:"";
		tg = Storage::SetPartitionData( tg, tstr, "evms_vol", 
		                                co["name"]:"" );

		cur_size = cur_size + evms_usable_size( pvlist[id,"size"]:0, 
		                                        co["cyl_size"]:0 );
		set_container_size( co, cur_size );
		set_table_elements( get_table_entries( pvlist, co["name"]:"" ), 
				    id );
		}
	    else if( ret == `pv_remove && id != nil && 
	         size(pvlist[id,"evms_vol"]:"")>0 )
		{
		boolean changed = false;
		if( contains( co["added"]:[], id ))
		    {
		    co["added"] = filter( integer e, co["added"]:[],
					  ``(e!=id) );
		    changed = true;
		    }
		else if( co["cyl_free"]:0*co["cyl_size"]:0 >=
		         evms_usable_size( pvlist[id,"size"]:0, 
			                   co["cyl_size"]:0 ))
		    {
		    co["removed"] = add( co["removed"]:[], id );
		    changed = true;
		    }
		else
		    {
		    // message popup
		    Popup::Error(
_("If you remove the selected partition from the container,
there will not be enough space for all volumes in this container.
To delete the container completely, remove its volumes first."));
		    }
		if( changed )
		    {
		    pvlist[id,"evms_vol"] = "";
		    string tstr =  "/dev/evms/"+pvlist[id,"device"]:"";
		    tg = Storage::DelPartitionData( tg, tstr, "evms_vol" );
		    tstr =  "/dev/"+pvlist[id,"device"]:"";
		    tg = Storage::DelPartitionData( tg, tstr, "evms_vol" );

		    cur_size = cur_size - 
		               evms_usable_size( pvlist[id,"size"]:0, 
						 co["cyl_size"]:0 );
		    set_container_size( co, cur_size );
		    set_table_elements( get_table_entries( pvlist, 
		                                           co["name"]:"" ), 
					id );
		    }
		}
	    }

	else if( ret == `ok )
	    {
	    if( typename == "aix" )
		{
		co["manager"] = "AixRegMgr";
		}
	    else
		{
		co["manager"] = "LvmRegMgr";
		}

	    co["cyl_size"] = 
		evms_pesize_check( (string)UI::QueryWidget( `id(`pesize), 
		                                            `Value),
				   typename );

	    y2milestone( "dlg_modify_container co %1 type %2", co, typename );

	    if( co["cyl_size"]:0 == 0 )
	        {
		ret = `again;
		}

	    if( ret==`ok && !evms_check_name(name) )
		{
		// error popup text
		Popup::Error(_("The data entered is invalid.
Insert a container names like \"system\" or \"test\".
Do not use ;/`, in this name.
"));
		ret = `again;
		}
	  
	    if( ret==`ok && create &&
	        (haskey( tg, "/dev/evms/" + co["name"]:"" ) ||
	         haskey( tg, "/dev/" + name )) )
		{
		// error popup text
		Popup::Error(sformat(_("The container named \"%1\" already exists.
Choose a different name
or cancel this dialog.
"), name ));
		ret = `again;
		}

	    if( cur_size <= 0 )
		{
		// error popup text
		Popup::Error(_("The container contains no physical volume.
Every container must consist of at least one physical volume."));
		ret = `again;
		}
	    }
	} until( ret == `ok || ret == `cancel );

    UI::CloseDialog();

    map r = $["ok":false];
	     
    if (ret == `ok )
	{
	r["ok"] = true;
	r["name"] = co["name"]:"";

	co = filter( string k, any e, co, ``(k!="name") );
	y2milestone( "co %1", co );

	co["cyl_free"] = co["cyl_free"]:0 + cur_size/co["cyl_size"]:0 -
			 co["cyl_count"]:0;
	co["cyl_count"] =  cur_size/co["cyl_size"]:0;
	co["added"] = maplist( integer e, co["added"]:[], 
	                       ``(pvlist[e,"device"]:""));
	co["removed"] = maplist( integer e, co["removed"]:[], 
				 ``(pvlist[e,"device"]:""));

	tg[ "/dev/evms/"+r["name"]:"" ] = co;

	r["targets"] = tg;
	y2milestone( "dlg_modify_container ret co %1", co );
	}
    y2milestone( "dlg_modify_container ret ok %1", r["ok"]:false );
    return( r );
    };

define map add_evms_container( map<string,map> tg, boolean first )
    ``{
    map<string,any> co = 
	$[ "manager" : "LvmRegMgr",
	   "cyl_count" : 0,
	   "cyl_size" : 4 * 1024 * 1024,
	   "create" : true,
	   "is_evms" : true,
	   "is_evms_container" : true,
	   "name" : first?"lvm/system":"",
	   "partitions" : []
	];

    map ret = dlg_modify_container( tg, co, true );
    return( ret );
    };

define map edit_evms_container( map<string,map> tg, string key )
    ``{
    y2milestone( "edit_evms_container key %1", key );
    map<string,any> co = (map<string,any>)tg["/dev/evms/"+key]:$[];
    co["name"] = key;
    map ret = dlg_modify_container( tg, co, false );
    return( ret );
    };

define map remove_evms_container( map<string,map> tg, string key )
    ``{
    y2milestone( "remove_evms_container key %1", key );
    string mkey = "/dev/evms/"+key;
    map ret = $["ok":false];
    map co = tg[mkey]:$[];
    if( size(filter(map p, co["partitions"]:[], ``(!p["delete"]:false)))>0 )
	{
	// Message popup
	Popup::Message(sformat(
_("The container \"%1\" contains at least one volume.
It cannot be removed. Remove all volumes of this container
before removing the container"), key ));
	}
    else
	{
	// popup text
	if( Popup::ContinueCancel( sformat( _("Really remove container \"%1\"?"), key)))
	    {
	    tg[mkey,"delete"] = true;
	    foreach( string k, map e, tg,
		``{
		tg[k,"partitions"] = maplist( map p, e["partitions"]:[],
		    ``({
		    if( p["evms_vol"]:"" == key )
			{
			p["evms_vol"] = "";
			}
		    return( p );
		    }));
		});
	    if( tg[mkey,"create"]:false )
		{
		tg = remove( tg, mkey );
		}
	    else
		{
		tg[mkey,"added"] = [];
		tg[mkey,"removed"] = [];
		}
	    ret["ok"] = true;
	    }
	}
    if( ret["ok"]:false )
	{
	ret["targets"] = tg;
	}
    return( ret );
    }


//////////////////////////////////////////////////////////////////////
// show a new list of containers
//////////////////////////////////////////////////////////////////////

define void new_co_list( list co_list )
    ``{
    UI::ReplaceWidget(`id(`rcont), `ComboBox(`id(`cont), `opt(`notify),
		      // button text
		      _("EVMS &Container"), co_list ));
    };

define integer get_co_size( map co )
    ``{
    integer sum_byte = co["cyl_size"]:1 * co["cyl_count"]:0;
    return( sum_byte );
    }

define void new_co_size( map co )
    ``{
    integer total = get_co_size(co);
    integer free = co["cyl_size"]:1 * co["cyl_free"]:0;
    y2milestone( "total %1 free %2", total, free );
    string total_s = ByteToHumanStringWithZero( total );
    string free_s = ByteToHumanStringWithZero( free );
    UI::ChangeWidget( `id(`co_size), `Value, total_s );
    UI::ChangeWidget( `id(`co_free), `Value, free_s );
    if( UI::WidgetExists( `id(`co_graph)) )
	{
	UI::ChangeWidget( `id(`co_graph), `Labels,
	                  // e.g. %1 is replaced with a size e.g.. "used 5 GB"
			  [ sformat( _("used\n%1"), 
				     ByteToHumanStringWithZero(total-free)),
	                  // e.g. %1 is replaced with a size e.g.. "free 1 GB"
			    sformat( _("free\n%1"), free_s ) ] );
	UI::ChangeWidget( `id(`co_graph), `Values, [ (total-free)/1024/1024, 
	                                              free/1024/1024 ] );
	}
    UI::RecalcLayout();
    }

define term get_bargraph_if_possible()
    ``{
    term ret = `Empty();
    if( UI::HasSpecialWidget( `BarGraph ))
        {
        ret = `Left(`BarGraph( `id(`co_graph), `opt(`hstretch),
                               [ 0, 10 ], [ "", ""]) );
        }
    y2milestone( "get_bargraph_if_possible ret %1", ret );
    return( ret );
    };

define map get_co_map( string current, map tg )
    ``{
    return( tg["/dev/evms/"+current]:$[] );
    }

define list get_containers( map<string,map> tg, string none )
    ``{
    list ret = [];
    foreach( string k, map e, tg, 
	``{
	if( e["is_evms_container"]:false && !e["delete"]:false )
	    {
	    ret = add( ret, substring( k, 10 ));
	    }
	});
    if( size(ret)==0 )
	{
	ret = [ none ];
	}
    y2milestone( "get_containers ret %1", ret );
    return( ret );
    }

define list get_evms_volumes( map<string,map> tg )
    ``{
    list<string> tmp = [];
    foreach( string k, map e, tg,
	``{
	if( find( k, "/dev/evms" )==0 && !e["is_evms_container"]:false )
	    {
	    tmp = add( tmp, k );
	    }
	});
    foreach( string k, map e, tg,
	``{
	if( find( k, "/dev/evms" )!=0 && Storage::IsRealDisk( k, e ))
	    {
	    string ek = "/dev/evms" + substring( k, 4 );
	    if( find( string k, tmp, ``(k==ek) )==nil )
		{
		if( size( filter( map p, e["partitions"]:[],
		                  ``(p["create"]:false && !p["delete"]:false &&
			             p["type"]:`primary != `extended &&
				     size(p["mount"]:"")==0 && 
				     size(p["evms_vol"]:"")==0)))>0 )
		    {
		    y2milestone( "get_evms_volumes adding create %1", ek );
		    tmp = add( tmp, ek );
		    }
		}
	    }
	});
    list<string> keys = sort( tmp );
    tmp = [];
    foreach( string k, map e, tg,
	``{
	if( find( k, "/dev/evms" )==0 && e["is_evms_container"]:false )
	    {
	    tmp = add( tmp, k );
	    }
	});
    keys = (list<string>)merge( keys, sort(tmp) );
    list<map> parts = [];
    y2milestone( "get_evms_volumes keys %1", keys );
    foreach( string k, keys,
	``{
	list<map> pa = 
	    filter( map p, tg[k,"partitions"]:[], 
		    ``(!p["delete"]:false&&size(p["evms_vol"]:"")==0));
	pa = filter_deleted_parts( tg, pa );
	pa = maplist( map p, pa,
	    ``{
	    p["size"] = 
		ByteToHumanStringWithZero( p["region",1]:0*tg[k,"cyl_size"]:0 );
	    return( p );
	    });
	if( size(k)>10 )
	    {
	    string ndev = "/dev/" + substring( k, 10 );
	    y2milestone( "get_evms_volumes ndev %1", ndev );
	    if( Storage::IsRealDisk( ndev, tg[ndev]:$[] ))
		{
		list<map> pc = filter( map p, tg[ndev,"partitions"]:[],
				       ``(p["create"]:false && 
				          !p["delete"]:false &&
					  p["type"]:`primary != `extended &&
					  size(p["mount"]:"")==0 &&
					  size(p["evms_vol"]:"")==0));
		pc = maplist( map p, pc,
		    ``{
		    p["device"] = k + sformat( "%1", p["nr"]:0 );
		    p["size"] = 
			ByteToHumanStringWithZero( p["region",1]:0 *
			                           tg[ndev,"cyl_size"]:0 );
		    return( p );
		    });
		pa = (list<map>)merge( pa, pc );
		}
	    }
	parts = 
	    (list<map>)merge( parts, 
	                      sort( map x, map y, pa,
				    ``(x["nr"]:(any)""<y["nr"]:(any)"")));
	});
    y2milestone( "get_evms_volumes parts %1", parts );
    list ret = maplist( map p, parts, 
	``{
	string fo = "";
	if( p["loop_fs"]:false && p["crypt"]:"" != "none" )
	    {
	    fo = fo + "C";
	    }
	if( p["format"]:false )
	    {
	    fo = fo + "F";
	    }
	return( `item( `id(p["device"]:""), 
	               p["device"]:"", 
		       p["size"]:"", fo,
		       p["mount"]:"",
		       p["evms_native"]:false?"N":"C"
		       ));
	});
    y2milestone( "get_evms_volumes ret %1", ret );
    return( ret );
    }

define void volume_handle_max( integer stripe, string max_text )
    ``{
    y2milestone( "volume_handle_max stripe %1", stripe );
    UI::ChangeWidget( `id(`max_size), `Enabled, stripe==1 );
    UI::ChangeWidget( `id(`max_text), `Value, stripe==1?max_text:"" );
    }

define map<string,any> dlg_logical_volume( map<string,any> co, 
                                           map<string,any> part, 
					   boolean create, boolean is_cont )
    ``{
    y2milestone( "dlg_logical_volume create:%1 p=%2 container:%3", 
                 create, part, is_cont );
    y2milestone( "dlg_logical_volume co:%1", 
                 filter(string k, any e, co, ``(k!="partitions")) );

    integer new_size = part["region",1]:0 * co["cyl_size"]:0;
    integer old_size = part["ori_length"]:new_size;
    integer max_size = co["cyl_free"]:0 * co["cyl_size"]:0 - 1025;
    if( !create )
	{
	max_size = max_size + new_size;
	}
    string  curr_size = ByteToHumanStringWithZero(new_size);
    old_size = kmgt_str_to_byte(curr_size);
    string  vol_name  = part["nr"]:"";
    integer stripes   = part["stripes"]:1;
    integer stripesize = part["stripesize"]:64;
    integer pesize = co["cyl_size"]:1;
    string  mkey = "/dev/evms";
    if( is_cont )
	{
	mkey = mkey + "/" + co["name"]:"";
	}

    y2milestone( "dlg_logical_volume mkey %1 vol_name %2", mkey, vol_name );

    list stripes_poss = [
			  `item(`id(1), "1",  stripes == 1 ),
			  `item(`id(2), "2",  stripes == 2 ),
			  `item(`id(3), "3",  stripes == 3 ),
			  `item(`id(4), "4",  stripes == 4 ),
			  `item(`id(5), "5",  stripes == 5 ),
			  `item(`id(6), "6",  stripes == 6 ),
			  `item(`id(7), "7",  stripes == 7 ),
			  `item(`id(8), "8",  stripes == 8 )
			];

    list stripe_size = [
			  `item(`id(4),   "4",    stripesize == 4 ),
			  `item(`id(8),   "8",    stripesize == 8 ),
			  `item(`id(16),  "16",   stripesize == 16 ),
			  `item(`id(32),  "32",   stripesize == 32 ),
			  `item(`id(64),  "64",   stripesize == 64 ),
			  `item(`id(128), "128",  stripesize == 128 ),
			  `item(`id(256), "256",  stripesize == 256 ),
			  `item(`id(512), "512",  stripesize == 512 ),
                       ];
    
    string  heading = "";
    boolean is_create_mode = false;
    boolean is_resize_mode = false;
    boolean is_mount_mode = false;
    term    ui_vol_name = `Empty();
     
    // header for input field size of volume 
    string size_txt = sformat(_("&Size: (e.g., %1 %2)"), 
			      ByteToHumanStringWithZero(4196*1024*1024),
			      ByteToHumanStringWithZero(210*1024*1024));
    // header for input field maximum allowd size
    string max_text = sformat(_("max = %1 "), 
			      ByteToHumanStringWithZero(max_size));


    if( create && part["create"]:false )
        {
	// popup heading 
	heading = _("Create Logical Volume");
	is_create_mode = true; 
	}
    else if( !create && part["create"]:false )
        {
	// popup heading 
	heading = _("Edit Logical Volume");
	is_create_mode = false;
	}
    else 
	{
	// popup heading 
	if( is_cont )
	    {
	    heading = _("Resize Logical Volume");
	    }
	else
	    {
	    heading = _("Edit Logical Volume");
	    }
	is_create_mode = false;
	is_resize_mode = is_cont;
	is_mount_mode = !is_resize_mode;
	stripes_poss = [ `item(`id(1), "-", true) ];
	stripe_size = [ `item(`id(1), "-", true) ];
	}

    if( is_create_mode )
	{
	ui_vol_name = `VBox( `TextEntry(`id(`vol_name),
					// label text
					_("Volume &Name "), vol_name),
			     // label text
			     `Left(`Label(_("(e.g. var, opt)")))
			  );
	}
    else 
	{
	// display name of current volume group
	ui_vol_name = `VBox( `Label(""),
			     // label text
			     `HBox(`Label( _("Volume Name: ")),
				   `Heading(`opt(`outputField), vol_name ),
				   `HStretch()
				 ));
	}

    term evms_specials = `Epmty();

    if( !is_mount_mode )
	{
	evms_specials = `VBox(
	    `Top(`Left(ui_vol_name)),
	    `Top(`VBox( 
		`TextEntry(`id(`size), size_txt, curr_size),
		`HBox(
		    // set size to maxsize
		    `Left(`Label( `id(`max_text), max_text )),
		    // button text
		    `PushButton( `id(`max_size), _("ma&x")))
		)),
	    `VStretch(),
	    `Left(`ComboBox( `id(`stripes), `opt(`hstretch,`notify),
			     // button text
			     _("Stri&pes"), stripes_poss )),
	    `VStretch(),
	    `Left(`ComboBox( `id(`stripesize), `opt(`hstretch,`notify),
			     // button text
			     _("Stripe &Size"), stripe_size )),
	    `VStretch());
	}
    else
	{
			      // label text %1 is prelaced by e.g. 4 GB
	string txt = sformat( _("Size %1"), curr_size );
        evms_specials = `VBox( `Right( `Label( txt ) ) );
	}

    y2milestone( "is_create_mode %1 is_resize_mode %2 is_mount_mode %3", 
                 is_create_mode, is_resize_mode, is_mount_mode );

    map<symbol,map> fs = FileSystems::GetAllFileSystems(true, true);
 
    UI::OpenDialog(
	`opt(`decorated ),
	`HBox(`HWeight(30, 
	    `HBox(
		`HSpacing(1),
		`HStretch(),
		`VBox(
		    `Heading(heading),
		    `VStretch(),
		    `VSpacing(1),
		    `HBox(
			 `HWeight( 40, FormatDlg( part, fs )),
			 `HStretch(),
			 `HSpacing(0.5),
			 `HWeight( 40, `VBox(
			     `Top(evms_specials),
			     `VSpacing(1),
			     `ReplacePoint( `id(`mount_dlg_rp), 
					    MountDlg(part, []))
			     ))
			 ),
		    `VStretch(),  
		    `VSpacing(1),
		    `HBox(
			// Ok button
			`PushButton( `id(`ok), `opt(`default), 
			             Label::OKButton()  ),
			// Cancel button
			`PushButton( `id(`cancel), Label::CancelButton() )
			)
		    ),
		  `HStretch(),
		  `HSpacing(1)
		  )
         )));


      // configure main dialog for the first call
      if( is_create_mode )
	  {
	  UI::ChangeWidget( `id(`vol_name), `ValidChars, 
			    FileSystems::nchars + "-._:" );
	  }
      if( !is_mount_mode )
	  {
	  UI::ChangeWidget( `id(`stripes), `Enabled, !is_resize_mode );
	  UI::ChangeWidget( `id(`stripesize), `Enabled, 
			    !is_resize_mode && stripes>1 );
	  volume_handle_max( stripes, max_text );
	  }
  
      ////////////////////////////////////////////////////////////////////
      // User mainloop for dialog
      ////////////////////////////////////////////////////////////////////
      
      boolean input_is_ok = true;
      map<string,any> retval = part;

      retval = HandlePartWidgetChanges( true, `ok, fs, part, retval );

      any ret = `cancel;

      repeat
	  {
	  input_is_ok  = true;
	    
	  ret = UI::UserInput();
	  y2milestone( "dlg_logical_volume ret %1", ret );

	  /////////////////////////////////////////////////////////
	  if( ret == `stripes)
	      {
	      stripes = (integer)UI::QueryWidget( `id(`stripes), `Value);
	      UI::ChangeWidget( `id(`stripesize), `Enabled, 
				!is_resize_mode && stripes>1 );
	      volume_handle_max( stripes, max_text );
	      continue;
	      }

	  if( ret == `max_size)
	      {
	      UI::ChangeWidget( `id(`size), `Value, 
	                        ByteToHumanStringWithZero(max_size));
	      continue;
	      }

	  if( ret != `cancel )
	      {
	      retval = HandlePartWidgetChanges( false, ret, fs, part, retval );
	      }
	    
	  if( is_create_mode ) 
	      {
	      vol_name = (string)UI::QueryWidget( `id(`vol_name), `Value );
	      vol_name = deletechars( vol_name, " \t" );
	      }

	  if( UI::WidgetExists( `id(`stripes) ))
	      {
	      stripes = (integer)UI::QueryWidget(`id(`stripes), `Value);
	      stripesize = (integer)UI::QueryWidget(`id(`stripesize), `Value);
	      }

	  if( ret==`ok )
	      {
	      if( UI::WidgetExists( `id(`size) ))
		  {
		  new_size = 
		      kmgt_str_to_byte( (string)UI::QueryWidget(`id(`size),
				                                `Value));

		  if( !check_max_size(new_size, max_size) )
		      {
		      input_is_ok = false;
		      continue;
		      }
		  if( !check_ok_fssize(new_size, retval) )
		      {
		      input_is_ok = false;
		      continue;
		      }
		  y2milestone( "new_size %1 old_size %2", new_size, old_size );
		  }

	      if( is_resize_mode && !retval["format"]:false && 
		  new_size!=old_size )
		  {
		  if( !CheckResizePossible( true, true, new_size-old_size,
			      retval["used_fs"]:`unknown,
			      retval["mount"]:"" ))
		      {
		      UI::ChangeWidget(`id(`size), `Value, curr_size );
		      continue;
		      }
		  }

	      if( is_create_mode )
		  {
		  if( !evms_check_name(vol_name) )
		      {
		      input_is_ok = false;
		      // error popup text
		      // xgettext:no-c-format
		      Popup::Error(_("Check your volume name. 
Use names like \"opt\" or \"var\" ...
Do not use ;/\`',!\"%#"));
		      UI::SetFocus(`id(vol_name));
		      continue;
		      }
		  else 
		      {
		      list names = maplist( map p, co["partitions"]:[],
		                            ``(p["nr"]:""));
		      if( contains( names, vol_name ) )
			  {
			  input_is_ok = false;
			  // error popup text
			  Popup::Error(sformat(_("A volume named \"%1\" already exists
in container \"%2\". Choose another name
or cancel this dialog.
"), vol_name, co["name"]:"" ));
			  UI::SetFocus(`id(vol_name));
			  continue;
			  }
		      }   
		  }

	      map ret_mp = CheckOkMount( mkey+"/"+vol_name, part, retval );
	      retval = ret_mp["map"]:$[];
	      if( !ret_mp["ok"]:false )
		  {
		  if( ret_mp["field"]:`none != `none )
		      UI::SetFocus(`id( ret_mp["field"]:`none ));
		  input_is_ok = false;
		  continue;
		  }

	      y2milestone( "retval %1 Doit:%2", retval["mount"]:"", Storage::DoCheckEvmsNonEvms );

	      if( size(retval["mount"]:"")>0 && Storage::DoCheckEvmsNonEvms )
		  {
		  map<string,map> tg = Storage::GetTargetMap();
		  list<string> ud = Storage::GetUsedRealDisks( tg );
		  list<string> dl = Storage::GetEvmsRealDisk( tg, part );
		  list isc = filter( string d, dl, ``(contains( ud, d )));
		  if( size(isc)>0 )
		      {
		      if( !Popup::YesNo( evms_texts_evms_nonevms() ))
			  {
			  input_is_ok = false;
			  continue;
			  }
		      else
			  {
			  Storage::DoCheckEvmsNonEvms = false;
			  }
		      }
		  }

	      ret_mp = CheckCryptOk( retval );
	      if( !ret_mp["ok"]:false )
		  input_is_ok = false;
	      else
		  retval = ret_mp["map"]:$[];
	      }

	  /////////////////////////////////////////////////////////
	  retval["stripes"] = stripes;
	  if( stripes>1 )
	      {
	      retval["stripesize"] = stripesize;
	      }
	  if( is_resize_mode && new_size!=old_size && 
	      !haskey( retval, "ori_length"))
	      {
	      retval["ori_length"] = retval["region",1]:1 * pesize;
	      }
	  if( !is_mount_mode )
	      {
	      retval["nr"] = vol_name;
	      retval["device"] = Storage::GetDeviceName( mkey, vol_name );
	      if( new_size != old_size )
		  retval["region"] = [0, (new_size+pesize-1)/pesize ];
	      y2milestone( "region %1 new_size %2 old %3", retval["region"]:[],
	                   new_size, old_size );
	      }
	  } 
      until ( (ret==`ok&&input_is_ok) || ret == `cancel );

      UI::CloseDialog();
      
      if( ret == `cancel || !input_is_ok )
	  {
	  retval = $[];
	  }
      y2milestone( "retval=%1", retval );
      return( retval );
      };

define map add_evms_volume( string co_name, map<string,map> tg )
    ``{
    y2milestone( "add_evms_volume co_name %1", co_name );
    map ret = $[ "ok" : false ];
    map<string,any> part = 
	$[ "create" : true,
	   "used_fs" : Partitions::default_fs,
	   "stripes" : 1,
	   "format" : true,
	   "type" : `evms,
	   "fstype" : "EVMS",
	   "mount" : GetMountPointProposal( tg, [ Partitions::BootMount() ] )
	];
    map<string,any> co = (map<string,any>)tg["/dev/evms/"+co_name]:$[];
    if( co["cyl_free"]:0==0 )
	{
	// popup text %1 is replaced by a container name
	Popup::Message(sformat(_("There is no available space in the current container %1."), co_name ));
	}
    else
	{
	co["name"] = co_name;
	part["region"] = [ 0, co["cyl_free"]:0/4 ];
	part = dlg_logical_volume( co, part, true, true );
	if( size(part)>0 )
	    {
	    ret["ok"] = true;
	    co["cyl_free"] = co["cyl_free"]:0 - part["region",1]:0;
	    tg["/dev/evms/"+co_name] = co;
	    tg = Storage::SetPartition( tg, part );
	    ret["targets"] = tg;
	    }
	}
    return( ret );
    };

define map edit_evms_volume( string device, map<string,map> tg )
    ``{
    y2milestone( "edit_evms_volume device %1", device );
    map ret = $[ "ok" : false ];

    map d = Storage::GetDiskPartition( device );
    y2milestone( "edit_evms_volume d %1", d );

    string key = d["disk"]:"";
    if( !haskey( tg, key ) )
	{
	key = "/dev/evms";
	}
    y2milestone( "edit_evms_volume key %1", key );

    map<string,any> part = Storage::GetPartition( tg, device );
    y2milestone( "edit_evms_volume part %1", part );

    integer last_size = part["region",1]:0;
    part = dlg_logical_volume( (map<string,any>)tg[key]:$[], part, false, 
			       tg[key,"is_evms_container"]:false );
    if( size(part)>0 )
	{
	ret["ok"] = true;
	tg[key,"cyl_free"] = tg[key,"cyl_free"]:0 + last_size - 
			     part["region",1]:0;
	tg = Storage::SetPartition( tg, part );
	ret["targets"] = tg;
	}
    return( ret );
    };

define map delete_evms_volume( string device, map<string,map> tg )
    ``{
    y2milestone( "delete_evms_volume device %1", device );
    map ret = $[ "ok" : false ];

    map d = Storage::GetDiskPartition( device );

    string key = d["disk"]:"";
    if( !haskey( tg, key ) )
	{
	key = "/dev/evms";
	}

    map<string,any> part = Storage::GetPartition( tg, device );
    y2milestone( "delete_evms_volume part %1", part );

    if( !tg[key,"is_evms_container"]:false )
	{
	// popup text %1 is replaced by a container name
	Popup::Message(sformat(_("You can only delete logical volumes in a container.
%1 is not a logical volume produced by a container.
"), device ));
	}
    else if( check_partition_delete( tg[key]:$[], part, false, false,
                                     false, false, Mode::installation () ) == `delete )
	{
	string txt = sformat( _("Really delete %1?"), 
	                      part["device"]:"" );
	if( Popup::YesNo( txt ) )
	    {
	    ret["ok"] = true;
	    if( part["create"]:true )
		{
		tg[key,"partitions"] = filter( map p, tg[key,"partitions"]:[],
		                               ``(p["device"]:"" !=
					          part["device"]:""));
		}
	    else
		{
		part["delete"] = true;
		tg = Storage::SetPartition( tg, part );
		}
	    tg[key,"cyl_free"] = tg[key,"cyl_free"]:0 + part["region",1]:0;
	    ret["targets"] = tg;
	    }
	}
    return( ret );
    };

}
