/**
 * File:
 *   raid_ui.ycp
 *
 * Module:
 *   Configuration of raid
 *
 * Summary:
 *   User interface functions.
 *
 * Authors:
 *   Michael Hager <mike@suse.de>
 *
 * $Id: raid_ui.ycp 19964 2004-10-26 15:37:35Z fehr $
 *
 * All user interface functions for RAID.
 *
 */

{

  textdomain "storage";

 
  import "Wizard";
  import "Storage";
  import "FileSystems";
  import "Partitions";
  import "Label";
  import "Popup";
  
  include "partitioning/raid_lib.ycp";
  include "partitioning/partition_defines.ycp";
  include "partitioning/custom_part_lib.ycp";

/**
 *  AbortDialog
 **/
define symbol AbortDialog()
    ``{
    if( Popup::ReallyAbort(true))
	return `yes;
    else 
	return `back;
    }


define boolean CheckRaidNumbers( string raidnr )
    ``{
    integer max_raid = 27;
    list<map> parts = get_possible_rds( Storage::GetTargetMap() );
    string dev = sformat( "/dev/md%1", raidnr );
    parts = filter( map e, parts, ``(e["raid_name"]:""==dev));
    y2milestone( "size %1 parts %2", size(parts), parts );
    if( size(parts) >= max_raid )
	{
	// popup text
	string text = sformat(_("
The maximum number of partitions in a software raid is %1.
You want to add more partitions to the raid than can be
handled by the current version of software raid.
"), max_raid);
	Popup::Error(text);
	}
    return( size(parts) < max_raid );
    }


/**
 * Initialize RAID creation.
 * @return boolean
 */
define void InitMainDevRaid()
    ``{
    if( !Storage::ExistsMainDevKeyEntry( "/dev/md" ))
	{
	// We have to create an "md" entry
	map<string,any> maindev   = $[ "name"       : Partitions::raid_name,
			   "cyl_count" : 1,          // not used
			   "cyl_size"  : 1024*1024,  // size in MB
			   "partitions": [ ]
			 ];

	Storage::AddMainDev("/dev/md", maindev );
	//Storage::DebugStorage();
	}
    };


/**
 * Help for RaidTypeDialog
 * @return string
 */
define string ChooseRaidTypeHelp()
    ``{
    // help text
     string help    = _("<p><b>RAID 0:</b> This level increases your disk performace.
There is <b>NO</b> redundancy in this mode. If one of the drives crashes, data recovery will not be possible.</p>
");

    // help text
     help = help +  _("<p><b>RAID 1:</b> <br>This mode has the best redundancy. It can be
used with two or more disks. This mode maintains an exact copy of all data on all
disks. As long as at least one disk is still working, no data is lost. The partitions
used for this type of RAID should have approximately the same size.</p>
");

    // help text
     help = help +  _("<p><b>RAID 5:</b> <br>This mode combines management of a larger number
of disks and still maintains some redundancy. This mode can be used on three disks or more.
If one disk fails, all data is still intact. If two disks fail simultaneously, all data is lost</p>
");

    // help text
     help = help +  _("<p><b>Multipath:</b> <br>This mode allow access to the same physical device
over multiple controllers for redundancy against a fault in a controller card. This mode can be used with at least two devices<p>
");

     return help;
    };


/**
 * Contents for dialog ChooseRaidType
 * @return term
 **/
define term get_choose_raid_type_contents( string raid, symbol what )
    ``{
    integer space = 0;
    term MpDetect = `Empty();

    if( what == `settings )
	{
	space = 5;
	}
    else
	{
	MpDetect = `Left( `HBox( `HSpacing(7), 
				 `PushButton(`id(`mp_auto), 
				             // button text
					      _("Auto&detect Multipath"))));
	}

    // Translators, 'Striping' is a technical term here.  Translate only if 
    // you are sure!! If in doubt, leave it in English.
    string str_text = _("RAID &0  (Striping)");
    // Translators, 'Mirroring' is a technical term here. Translate only if 
    // you are sure!! If in doubt, leave it in English.
    string mir_text = _("RAID &1  (Mirroring)");
    // Translators, 'Redundant Striping' is a technical term here. Translate 
    // only if you are sure!! If in doubt, leave it in English.
    string r5_text = _("RAID &5  (Redundant Striping)");
    // label text
    string mp_text = _("&Multipath (Redundant access over two controllers)");




    return
	 `VBox(
		`VSpacing(space),
		// popup text
		`Heading (_("What type of RAID would you like to create?")),
		`VSpacing(1),
		`RadioButtonGroup(
		    `id("raid_type"),
		    `HSquash(
			`VBox(
			    `Left(`RadioButton(`id("raid0"), str_text,
					       raid=="raid0"||raid=="linear")),
			    `VSpacing (1),
			    `Left(`RadioButton(`id("raid1"), mir_text,
					       raid=="raid1")),
			    `VSpacing (1),
			    `Left(`RadioButton(`id("raid5"), r5_text,
					       raid=="raid5")),
			    `VSpacing (1),
			    `Left(`RadioButton(`id("multipath"), mp_text,
			                       raid=="multipath")),
			    MpDetect
			     )
			 )
		    ),
		`VSpacing(space)
	      );


};

define string RaidMultipathAutoHelp()
    ``{
    // help text
    string help = _("<p>The list contains the devices that could be automatically
detected for multipath setup. Disable the devices not to 
have activated by double-clicking the table line and continue when 
finished. If you go back, none of the autodetected multipath raid devices
are created.</p>
<p>If you deselect lines, the names of the raid devices 
after the deselected lines will be changed.</p>
");
    return( help );
    };


/**
 * RaidMultipathAuto Dialog
 * @return symbol
 */
define symbol RaidMultipathAuto()
    ``{
    // Choose Raid Type ...
    /////////////////////////////////////////////////////////////////
    y2milestone( "enter" );

    // heading text
    string caption = _("RAID Wizard: Multipath Autodetection");
    map<string,map> tg = Storage::GetTargetMap();
    list<map> raids = tg["/dev/md","partitions"]:[];
    list<map> mp_raids = filter( map p, raids,
                            ``(!p["delete"]:false && p["create"]:false && 
			       p["raid_type"]:""=="multipath"));
    foreach( map p, mp_raids,
	``{
	foreach( string dev, map disk, tg,
	    ``{
	    list new_part = [];
	    foreach( map pt, disk["partitions"]:[],
		``{
		if( pt["raid_name"]:""==p["device"]:"" )
		    {
		    pt = filter( string key, any value, (map<string,any>)pt, ``( key != "raid_name" ));
		    if( pt["change_fsid"]:false )
			{
			pt["fsid"] = pt["ori_fsid"]:0;
			pt["fstype"] = pt["ori_fstype"]:"";
			pt = filter( string key, any value, (map<string,any>)pt,  
				      ``( key != "ori_fsid" && 
				          key != "change_fsid" &&
					  key != "ori_fstype" ));
			}
		    }
		new_part = add( new_part, pt );
		});
	    tg[dev,"partitions"] = new_part;
	    });
	raids = filter( map part, raids, ``(part["nr"]:0 != p["nr"]:0));
	});
    if( haskey( tg, "/dev/md" ))
	{
	tg["/dev/md","partitions"] = raids;
	}
    map<integer,list<string> > at = $[];
    list raid_list = [];
    list raid_enab = [];
    integer raid_num = -1;
    integer i=0;
    foreach(list<string> entry, Storage::AutodetectMultipathRaid( tg ),
	``{
	raid_num = tointeger(get_free_raid_nr( tg, raid_num+1 ));
	raid_list = add( raid_list, raid_num );
	raid_enab = add( raid_enab, true );
	string dev = Storage::GetDeviceName( "/dev/md", raid_num );
	at[i] = entry;
	i = i+1;
	});
    y2milestone( "at=%1", at );
    y2milestone( "rl=%1", raid_list );
    term contents = 
	// popup text
	`Label(_("No automatically configurable\nmultipath devices detected"));
    if( size(at)>0 )
	{
	integer i = 0;
	list ct = maplist( integer key, list content, at,
			   ``{
			   term a = `item( `id(i) );
			   // label text
			   a = add( a, _("Yes") );
			   a = add( a, Storage::GetDeviceName( "/dev/md",
			                                       raid_list[key]:255 ));
			   a = add( a, content[0]:"" );
			   a = add( a, content[1]:"" );
			   string rest = "";
			   if( size(content)>2 )
			       {
			       rest = mergestring( (list<string>)remove(remove(content,0),0),
			                           " ");
			       }
			   a = add( a, rest );
			   i = i + 1;
			   return( a );
			   });
	term header = `header();
        // heading text
	header = add( header, _("Create") );
        // heading text
	header = add( header, _("Raid Device") );
        // heading text
	header = add( header, _("Partition 1") );
        // heading text
	header = add( header, _("Partition 2") );
        // heading text
	header = add( header, _("Other...") );
	contents = `HBox(`HSpacing(8),
	                 `VBox(`VSpacing(2), 
			       `Table( `id(`mp_table), `opt(`notify), 
			                header, ct ),
			       `VSpacing(2) ),
			 `HSpacing(8));
	}

    Wizard::SetContentsButtons(caption, contents, RaidMultipathAutoHelp(),
			       Label::BackButton(), Label::NextButton());

    symbol ret = `next;

    do
	{
	ret = (symbol)UI::UserInput();
	y2milestone( "ret %1", ret );

	if( ret == `mp_table )
	    {
	    integer cur = (integer) UI::QueryWidget( `id(`mp_table), 
	                                             `CurrentItem );
	    integer sel_num = 0;
	    integer i = 0;
	    while( i<cur )
		{
		if( raid_enab[i]:false )
		    {
		    sel_num = sel_num + 1;
		    }
		i=i+1;
		}
	    string dev = "";
	    if( !raid_enab[i]:false )
		{
		dev = Storage::GetDeviceName( "/dev/md", 
		                              raid_list[sel_num]:255 );
		sel_num = sel_num + 1;
		}
	    raid_enab[i] = !raid_enab[i]:false;
	    UI::ChangeWidget(`id(`mp_table), `Item(cur, 1), dev );
	    UI::ChangeWidget(`id(`mp_table), `Item(cur, 0), 
			     // label text
			     size(dev)==0?_("No"):_("Yes") );
	    i = cur+1;
	    while( i<size(raid_enab) )
		{
		if( raid_enab[i]:false )
		    {
		    dev = Storage::GetDeviceName( "/dev/md", 
						  raid_list[sel_num]:255 );
		    sel_num = sel_num + 1;
		    UI::ChangeWidget(`id(`mp_table), `Item(i, 1), dev );
		    }
		i = i+1;
		}
	    }

	if( ret == `next )
	    {
	    integer i = 0;
	    list md_part = [];
	    while( i<size(raid_enab) )
		{
		if( raid_enab[i]:false )
		    {
		    string dev = Storage::GetDeviceName( "/dev/md", 
		                                         raid_list[i]:255 );
		    map rp = $[ "nr"         : raid_list[i]:255,
			        "device"     : dev,
			        "raid_type"  : "multipath",
			        "type"       : `sw_raid,
			        "fsid"       : Partitions::fsid_native,
			        "region"     : [0, 1], // dummy
			        "fstype"     : Partitions::raid_name,
			        "persistent_superblock"  : true,
			        "create"	    : true,
			        "used_fs"    : Partitions::default_fs	];
		    map p = $[];
		    foreach( string device, (list<string>)at[i]:[],
			``{
			p = Storage::GetPartition( tg, device );
			p["raid_name"] = dev;
			if( p["fsid"]:0 != Partitions::fsid_raid )
			    {
			    if( !p["change_fsid"]:false )
				{
				p["change_fsid"] = true;
				p["ori_fsid"] = p["fsid"]:0;
				p["ori_fstype"] = p["fstype"]:"";
				}
			    p["fsid"] = Partitions::fsid_raid;
			    p["fstype"] = 
				Partitions::FsIdToString(Partitions::fsid_raid);
			    }
			y2debug( "p1 %1", p );
			tg = (map<string,map>)Storage::SetPartition( tg, p );
			});

		    integer mdsize = raid_size_byte(tg,rp);
		    y2milestone( "mdsize=%1", mdsize );
		    rp["region",1] = mdsize / (1024*1024);
		    md_part = add( md_part, rp );
		    y2milestone( "enabled %1 num:%2 entry:%3", i, 
		                 raid_list[i]:255, at[i]:[] );
		    y2milestone( "rp %1", rp );
		    }
		i = i+1;
		}
	    if( size(md_part)>0 )
		{
		InitMainDevRaid();
		tg = Storage::GetTargetMap();
		tg["/dev/md","partitions"] = 
		    merge( tg["/dev/md","partitions"]:[], md_part );
		Storage::SetTargetMap( tg );
		}
	    }
	}
    while( ret != `back && ret !=`next && ret !=`abort );
    y2milestone( "ret %1", ret );
    return ret;
    }

/**
 * ChooseRaidType Dialog
 * @parm what -> `settings or `wizard
 * @return symbol
 */
define symbol ChooseRaidType(symbol  what)
    ``{
    string  subdevindex   = "0";

    Storage::CreateTargetBackup("raid");
   
    y2milestone( "what:%1", what );
    if( Storage::ExistsMainDevKeyEntry("/dev/md") )
	{
	subdevindex = get_free_raid_nr( Storage::GetTargetMap(), 0 );
	if( what == `settings )
	    Storage::SetWizardKey(Storage::GetSubDevKey("/dev/md", 
	                                                subdevindex) );
	}
    else
	{
	InitMainDevRaid();
	}
    
    Storage::SetWizardKey(Storage::GetSubDevKey("/dev/md", subdevindex) );
  
    map subdevraid = Storage::GetSubDev( Storage::GetWizardKey() );

    y2milestone( "Choose the raid type; subdevindex=%1 ", subdevindex );

    
    /////////////////////////////////////////////////////////////////
    // Choose Raid Type ...
    /////////////////////////////////////////////////////////////////

    // heading text
    string caption = _("RAID Wizard: Step 1.");
    term contents = 
	get_choose_raid_type_contents(subdevraid["raid_type"]:"raid1", what );

    if( what == `wizard )
	{
	Wizard::SetContentsButtons(caption,
				   contents,
				   ChooseRaidTypeHelp(),
				   Label::BackButton(),
				   Label::NextButton());
	}
    else if ( what == `settings )
	{
	contents = add( contents,
			`HBox(
			       // popup create partition:
			       `PushButton(`id(`ok), `opt(`default), 
					   Label::OKButton() ),
			       // popup create partition:
			       `PushButton(`id(`cancel), Label::CancelButton() )
			       ));

	UI::OpenDialog( `opt(`decorated ),
			`HBox(
			       `HWeight(50, `RichText( ChooseRaidTypeHelp() )),
			       `HStretch(),
			       `HSpacing(1),
			       `HWeight(70, contents ),
			       `HSpacing(1),
			       `HStretch()
			       ));
        }
    else
	{
	y2error( " Error what in ChooseRaidType must be symbol `wizard or `settings");
	}

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

    if( contains( [ `next, `ok, `accept ], ret ))
	{
	////////////////////////////////////////////////////////////
	//   Create initial raid datastructure, which comes to 
	//   /dev/md[partitions]

	string subdevtype = (string) UI::QueryWidget( `id("raid_type"), 
	                                              `CurrentButton );

	string chunk_size = "4";

	if ( subdevtype == "raid5" ) chunk_size = "128";
	if ( subdevtype == "raid0" ) chunk_size = "32";

	if( subdevraid == $[] || what == `settings )
	    {
	    string mprop = GetMountPointProposal( Storage::GetTargetMap(), [] );
	    symbol used_fs = Partitions::default_fs;
	    if( mprop == Partitions::BootMount() )
		{
		used_fs = Partitions::DefaultBootFs();
		}
	    subdevraid = $[ "nr" 	 : tointeger(subdevindex),
			    "device"     : Storage::GetDeviceName( "/dev/md", tointeger(subdevindex) ),
			    "raid_type"  : subdevtype,
			    "type"       : `sw_raid,
			    "fsid"       : Partitions::fsid_native,
			    "region"	 : [0, 1], // dummy
			    "fstype"     : Partitions::raid_name,
			    "chunk_size" : chunk_size,
			    "persistent_superblock" : true,
			    "create"	 : true,
			    "format"	 : true,
			    "mount"      : mprop,
			    "used_fs"    : used_fs
			  ];
	    }
	else
	    {
	    subdevraid = add( subdevraid, "raid_type", subdevtype );
	    subdevraid = add( subdevraid, "chunk_size", chunk_size );
	    }

        if( subdevtype == "raid5" ) 
	    subdevraid["parity_algorithm"] = "left-symmetric";

        Storage::SetSubDev( Storage::GetWizardKey() , subdevraid );
        //Storage::DebugStorage();
	ret = `next;
        }

    if( what == `settings)
	{
	UI::CloseDialog();
	}

    if( ret == `back && what==`wizard )
	{
	// Go back to last save settings in Storage  
	Storage::RestoreTargetBackup("raid");
	}
    y2milestone( "ret %1", ret );
    return ret;
    }

/**
 * Help for RaidDevices Dialog
 * @return string
 */
define string RaidDevicesHelp()
    ``{
    // help text, richtext format
    string helptext           = _("<p><b>Add partitions to your RAID.</b> According to
the RAID type, the usable disk size is the sum of these partitions (RAID0), the size
of the smallest partition (RAID 1), or (N-1)*smallest partition (RAID 5).</p>
");

    // help text, richtext format
    helptext = helptext + _("<p>Generally, the partitions should be on different drives,
to get the redundancy and performance you want.</p>
");

    // help text, richtext format
    helptext = helptext + _("<p><b>Expert options:</b><br>Here, set
things like chunk size to get the best performance
out of your system. These settings are used for all partitions of this RAID.</p>
");

    return helptext;
    }


/**
 * Popup to display an error when changing an existing raid.
 *
 * in    dev: device name of the raid device
 */
define void CannotEditExistingRaid( string dev )
    ``{
    // Error popup text
    Popup::Error(sformat( _("The RAID to change (%1) is already created on disk.
It can no longer be changed. To change %1, 
remove it and create it again.
"),dev));
    };


/**
 * partition add partition widget table.
 * @return term
 */
define term get_raid_devices_content( symbol what, 
                                             string current_raid_text, 
					     list all_raids, string size_md )
    ``{
    term top = `HBox(
		      `Left (`Label (current_raid_text)),
		      // Label in raid -wf
		      `Label (_("Size:")),
		      `Label (`id(`raid_size), `opt( `outputField), "---------")
		    );

    if( what == `settings )
	{
	top = `HBox(
		    `Left(`VBox(
				`ReplacePoint(`id(`raids_rp), 
				              `ComboBox(`id(`raids), 
					                `opt(`notify), 
							// label text
							_("RA&ID:"), all_raids)),
				`PushButton( `id(`md_raid_edit), 
					     // label text
				             `opt(`hstretch), _("O&ptions"))
				)),

		    `HSpacing(0.5),
		    // label text
		    `Left(`Label(_("Size:"))),
		    `Left(`Label( `id(`raid_size),
				  `opt( `hstretch, `outputField), "-------" )),
		    `HWeight(1,`HStretch()),
		    `Right(`VBox(
				 `PushButton( `id(`md_raid_remove), 
			 	              `opt(`hstretch), 
					      // button text
					      _("Remo&ve RAID")),
				 `PushButton( `id(`md_raid_add), 
				              `opt(`hstretch), 
					      // button text
					      _("Add RAI&D"))))
		    );
	}

    return( `HBox(
		  `HSpacing(1),
		  `VBox(
		  `VSpacing (0.5),
		  top,
		  `VSpacing (0.2),
		  `Table(`id(`raid_table), `opt(`notify),
			 // table header 
			 `header(_("    Device    "),
			 // table header
				 `Right(_("    Size    ")),
			 // table header
				 `Center(_("        Type        ")),
			 // table header
				 `Center(_("      RAID      "))),
			 []
			 ),
		  `HBox (
			 // Button in RAID-WF 
			 `PushButton (`id (`raid_add), _("A&dd")),
			 // Button in RAID-WF 
			 `PushButton (`id (`raid_remove), _("&Remove"))
			 ),
		  `VSpacing (0.5)
		  ),
		  `HSpacing(1)

	    )
	    );
    };


/**
 * RaidExpertDlgHelp
 * @return string
 */
define string RaidExpertDlgHelp()
    ``{
    // help text
    string helptext_edit_raid = _("<p><b>chunk size:</b><br>It is the smallest \"atomic\" mass
of data that can be written to the devices. A reasonable chunk size for RAID 5 is 128KB. For RAID 0,
32 KB is a good starting point. For RAID 1, the chunk size does not affect the array very much.</p>
");

    // help text
    helptext_edit_raid = helptext_edit_raid + _("<p><b>parity algorithm:</b><br>The parity algorithm to use with RAID5.
Left-symmetric is the one that offers maximum performance on typical disks with rotating platters.</p>
");

    // help text
    helptext_edit_raid = helptext_edit_raid + _("<p><b>Persistent superblock:</b><br>
The persistent superblock is mandatory if you want autodetection of your
RAID devices at system boot. It also adds rescue options. 
The only known reason to disable it is for
compatibility with older RAID software</p>
");

    return helptext_edit_raid;
    };

/**
 *  raidExpertDlg dialog
 *  edit expert options
 */
define map<string,any> RaidExpertDlg( symbol what, string device, map<string,any> cur_raid,
			  boolean installation, map<symbol,map> file_systems,
			  boolean edit_raid_type, map<string,map> targetMap )
    ``{
    string raid_type = cur_raid["raid_type"]:"";
    // heading text
    string caption   = sformat(_("Raid settings %1"), device );
    boolean created  = IsRaidCreated( device, what );
    
    term contents = `HBox(
			   FormatDlg( cur_raid, file_systems ),
			   `HSpacing(2),
			   `VBox(
				 `Top(    RaidOptionsDlg( cur_raid, true )),
				 `Bottom( 
				     `ReplacePoint( `id(`mount_dlg_rp), 
						    MountDlg( cur_raid, [] )))
				 )
			   );


    if( what == `settings )
	{
	contents = `VBox(
			 `Heading(caption),
			 `VSpacing(1),
			 `VStretch(),
			 contents,
			 `VSpacing(1),
			 `VStretch(),
			 `HBox(
			       // popup create partition:
			       `PushButton(`id(`ok), `opt(`default), 
					   Label::OKButton()  ),
			       // popup create partition:
			       `PushButton(`id(`cancel), Label::CancelButton() )
			       ));



	/////////////////////////////////////////////////////////////
	// Open main dialog for Raid
	/////////////////////////////////////////////////////////////
	if( ! UI::OpenDialog( `opt(`decorated ),
			      `HBox(
				    `HWeight(30,`RichText(RaidExpertDlgHelp())),
				    `HStretch(),
				    `HSpacing(1),
				    `HWeight(70, contents ),
				    `HSpacing(1),
				    `HStretch()
				    )))
	    return cur_raid;
	}
    else
	{
	// heading text
	Wizard::SetContentsButtons(_("RAID Wizard Step 3:"),
				     `HBox(
					   `HSpacing(),
					   `VBox(
						 `VSpacing(),
						 `HVCenter(`HVSquash(contents)),
						 `VSpacing(),
						 `VSpacing(1)
						 ),
					   `HSpacing()
					   ),
			      RaidExpertDlgHelp(),
			      Label::BackButton(), Label::FinishButton());

	}


     ////////////////////////////////////////////////////////////////
     // configure main dialog for the first call

     ChangeExistingSymbolsState( [`raid_combo], false);

     UI::ChangeWidget( `id(`raid_combo), `Value, raid_type );
     UI::ChangeWidget( `id(`chunk_size), `Value, cur_raid["chunk_size"]:"4" );
     UI::ChangeWidget( `id(`parity), `Enabled, created && raid_type=="raid5" );
     UI::ChangeWidget( `id(`chunk_size), `Enabled, 
                       created && raid_type!="multipath" );
     UI::ChangeWidget( `id(`persist), `Enabled, created );

     map<string,any> retval = eval(cur_raid);
     symbol ret = `ok;

     retval = HandlePartWidgetChanges( true, ret, file_systems, cur_raid, retval );

     repeat
	 {
	 ret = (symbol)UI::UserInput();
	 y2debug( "DlgRaid %1", ret);

	 ////////////////////////////////////////////////////////////
	 if( ret != `cancel )
	     {
	     retval = HandlePartWidgetChanges( false, ret, file_systems, 
	                                       cur_raid, retval );

	     if( edit_raid_type )
		 retval["raid_type"] = 
		     UI::QueryWidget(`id(`raid_combo), `Value);

	     retval["chunk_size"] = UI::QueryWidget(`id(`chunk_size), `Value);
	     retval["persistent_superblock"] = 
		 UI::QueryWidget(`id(`persist), `Value);
	     retval["parity_algorithm"] = 
		 UI::QueryWidget(`id(`parity), `Value);
	     }

	 if( ret == `raid_combo )
	     {
	     UI::ChangeWidget( `id(`parity), `Enabled, 
	                       created && raid_type=="raid5" );
	     UI::ChangeWidget( `id(`chunk_size), `Enabled, 
	                       created && raid_type!="multipath" );
	     }

	 if( ret == `ok || ret == `next )
	     {
	     string dev = Storage::GetDeviceName( "/dev/md", retval["nr"]:0 );
	     map ret_mp = CheckOkMount( dev, cur_raid, retval );
	     retval = ret_mp["map"]:$[];
	     if( !ret_mp["ok"]:false )
		{
		if( ret_mp["field"]:`none != `none )
		    UI::SetFocus(`id( ret_mp["field"]:`none ));
		ret = `again;
		continue;
		}

	     ret_mp = CheckCryptOk( retval );
	     if( !ret_mp["ok"]:false )
		{
		ret = `again;
		}
	     else
		{
		retval = ret_mp["map"]:$[];
		}
	     }

	 } until( ret == `ok || ret == `cancel || ret == `next ||
		  ret == `back || ret == `abort );

     y2milestone( "ret:%1 retval=%2", ret, retval );
     
     if( ret == `back || ret == `next )
	 {
	 retval["symbol"] = ret;
	 }
     else
	 {
	 UI::CloseDialog();
	 }

     if ( ret == `ok  || ret == `next  || ret == `back )
	 {
	 return retval;
	 }

    return cur_raid;
    }

/**
 * AddRaidDevices dialog
 *
 * Choose free partitions and add them to the raid.
 * additionally you can edit the raid options like chunk size
 * in -
 *  out:
 */
define symbol RaidDevices( symbol what )
    ``{
    y2milestone( "what %1", what );
    /* get the current raid e.g.: 1//dev/md */

    Storage::CreateTargetBackup("raid");
   
    string subdevraidkey = Storage::GetWizardKey();
    map<string,any> subdevraid = Storage::GetSubDev( subdevraidkey );
    boolean changed = false;

    /* e.g.: subdevraidindex = 1; */
    string subdevraidindex = sformat( "%1", subdevraid["nr"]:255 );
    // heading text
    string caption = _("RAID Wizard Step 2:");

    /* key for subdev that can assigned to a raid */
    string subdevpartkey = "";

    if( what == `settings )
	{
	// heading text
	caption   = _("RAID settings");
	}

    list<map> parts 	   =  []; 
    list    table 	   =  [];

    Wizard::SetContentsButtons( caption,
				get_raid_devices_content(what,
							 // label text
							 sformat(_("Current RAID: /dev/md%1"), subdevraidindex),
							 get_raid_devices(subdevraid["nr"]:0),
							 ""
							 ),
				RaidDevicesHelp(),
				Label::BackButton(), Label::NextButton() );

    UI::SetFocus( `id(`raid_table));

    //////////////////////////////////////////////////////////////////////
    // mainloop
    //////////////////////////////////////////////////////////////////////
  
    symbol ret = `start;

    repeat
	{
	//////////////////////////////////////////////////////////
	// Raid ComboBox 
	if( what == `settings )
	    {
	    subdevraidkey = (string)UI::QueryWidget(`id(`raids), `Value );
	    y2milestone( "Selected Raid in ComboBox; subdevraidkey=%1 ", 
	                 subdevraidkey );

	    if( Storage::IsKeyKey( subdevraidkey))
		{
		subdevraidindex	= Storage::GetSubDevIndex( subdevraidkey );
		subdevraid = Storage::GetSubDev( subdevraidkey );
		}
	    }
	y2milestone( "Current raid; subdevraidkey=%1", subdevraidkey );
	y2milestone( "ret %1", ret );

	///////////////////////////////////////////////////////////
	// Update raid widget table
	if( ret != `raids || ret == `start )
	    {
	    parts  = get_possible_rds( Storage::GetTargetMap() );
	    y2milestone( " parts %1", parts );
	    
	    parts = filter( map part, parts, ``( size(part["lvm_group"]:"")==0 ));

	    if( what == `wizard )
		parts = filter( map part, parts, 
		                ``( part["raid_name"]:"" == 
				        "/dev/md"+subdevraidindex ||
				    part["raid_name"]:"" == ""  ));


	    /////////////////////////////////////////////////////////////////
	    // Show the current state:
	    table = get_raid_widget_table( parts );

	    UI::ChangeWidget( `id(`raid_table), `Items, table);
	    }

	/////////////////////////////////////////////////////////////////
	// update size 
	string type = subdevraid["raid_type"]:"";
	if( what == `settings )
	    {
	    type = subdevraid["raid_type"]:"";
	    }
	
	UI::ChangeWidget( `id(`raid_size), `Value, 
	                  ByteToHumanStringWithZero( get_raid_size_byte( parts, subdevraidindex, type )));

	if( subdevpartkey != "" && (ret == `raid_add || ret == `raid_remove) )
	    {
	    UI::ChangeWidget( `id(`raid_table), `CurrentItem, subdevpartkey);
	    }


	/////////////////////////////////////////////////////////////////
	// Wait for User input
	ret = (symbol)UI::UserInput();
	y2milestone( "ret %1", ret );

	/////////////////////////////////////////////////////////////////
	// Edit raid (1//dev/md) settings - not in wizard
	if( ret == `md_raid_edit && Storage::IsKeyKey( subdevraidkey )  )
	    {
	    subdevraid = RaidExpertDlg( `settings, "/dev/md" + subdevraidindex,
				        subdevraid, Stage::initial (),
					FileSystems::GetAllFileSystems(true,true),
					false, Storage::GetTargetMap());
	    
	    Storage::SetSubDev( subdevraidkey, subdevraid );
	    }

	/////////////////////////////////////////////////////////////////
	// Add a new raid 
	if( ret == `md_raid_add  )
	    {
	    if( ! check_raid_possible( Storage::GetTargetMap() ))
	    {
		ret = `again;
		continue;
	    }

	    list<integer> old = 
		maplist( map e,
			 filter( map p,
				 (list<map>)Storage::GetMainDevParam( "/dev/md", "partitions", [] ),
				 ``(!p["delete"]:false)),
			 ``(e["nr"]:-1));

	    ChooseRaidType( `settings );    
	    list<integer> new = 
		maplist( map e,
			 filter( map p,
	                         (list<map>)Storage::GetMainDevParam( "/dev/md", "partitions", [] ),
				 ``(!p["delete"]:false)),
			 ``(e["nr"]:-1));
	    integer new_num = -1;
	    foreach( integer e, new,
		``{
		if( new_num==-1 && !contains( old, e ) )
		    {
		    new_num = e;
		    }
		});
	    if( new_num!=-1 )
		{
		string newkey = sformat( "%1//dev/md", new_num );
		UI::ChangeWidget( `id(`raids), `Value, newkey );
		subdevraid = Storage::GetSubDev( newkey );
		changed = true;
		}
	    y2milestone( "old %1 new %2 new_num:%3", old, new, new_num );
	    }

	/////////////////////////////////////////////////////////////////
	// Remove a new raid (2//dev/md ) - not in wizard  
	if( ret == `md_raid_remove  && Storage::IsKeyKey( subdevraidkey ))
	    {
	    // popup text
	    if ( Popup::YesNo ( sformat(_("Do you really want to delete partition %1?"), "/dev/md"+subdevraidindex )))
		{
		Storage::DelSubDev( subdevraidkey );
		changed = true;
		}
	    }

	/////////////////////////////////////////////////////////////////
	// Update Raid ComboBox - not in wizard
	if( ret == `md_raid_add || ret == `md_raid_remove )
	    {
	    new_raid_list( get_raid_devices( subdevraid["nr"]:0 ));
	    subdevraidkey = (string)UI::QueryWidget(`id(`raids), `Value );
	    y2milestone( "subdevraidkey %1", subdevraidkey );
	    subdevraidindex = Storage::GetSubDevIndex( subdevraidkey );
	    changed = true;
	    }
	
	if( ret == `raid_table )
	    {
	    subdevpartkey = (string)UI::QueryWidget( `id(`raid_table), 
	                                             `CurrentItem );

	    if( isItemRd(subdevpartkey) && Storage::IsKeyKey(subdevraidkey) )
		{
		ret = `raid_remove;
		}
	    else
		{
		ret = `raid_add;
		}
	    }

	/////////////////////////////////////////////////////////////
	// Add a new physical volume to a raid e.g. assign 1//dev/hda 
	// to 1//dev/md
	if( ret == `raid_add )
	    {
	    subdevpartkey = (string)UI::QueryWidget( `id(`raid_table), 
	                                             `CurrentItem );

	    if( CheckItemIsNotRaid( subdevpartkey ) && 
	        Storage::IsKeyKey( subdevraidkey ) &&
		CheckRaidNumbers( subdevraidindex ) )
		{
		string device = "/dev/md"+subdevraidindex;
		if( IsRaidCreated( device, what )) // Popup!
		    {
		    addRaid( subdevpartkey, subdevraidindex );
		    changed = true;
		    }
		else
		    {
		    CannotEditExistingRaid( device );
		    ret = `again;
		    }
		}
	    }


	/////////////////////////////////////////////////////////////
	// Delete a new physical volume e.g. remove 1//dev/hda from 1//dev/md
	if( ret == `raid_remove )
	    {
	    subdevpartkey = (string)UI::QueryWidget(`id(`raid_table), `CurrentItem);

	    if( CheckItemIsRaid( subdevpartkey) && subdevraidindex != "" && 
	        subdevraidindex != nil )
		{
		string device = "/dev/md"+subdevraidindex;
		if( IsRaidCreated( device, what )) // Popup!
		    {
		    removeRaid( subdevpartkey, subdevraidindex, what );
		    changed = true;
		    }
		else
		    {
		    CannotEditExistingRaid( device );
		    ret = `again;
		    }
		}
	    }

	
	if (ret == `next)
	    {
	    ///////////////////////////////////////////////////////
	    // check if enough disks are attached to the raid
	    list<map> parts = (list<map>)Storage::GetMainDevParam( "/dev/md", 
	                                                 "partitions", [] );
	    
	    foreach( map part,  parts,
		``{
		if( !part["delete"]:false && part["create"]:false )
		    {
		    string raid_type = part["raid_type"]:"raid1";
		    integer nr_of_parts = 
			getNrOfParts( Storage::GetTargetMap(), 
			              sformat("%1", part["nr"]:0) );
		    string raid = "/dev/md" + sformat("%1", part["nr"]:0 );

		    if( raid_type == "raid0" && (nr_of_parts < 2))
			{
			// Error popup text
			Popup::Error(sformat(_("For RAID 0, add at least two partitions to %1."),  raid ));
			ret = `again;
			}

		    if( raid_type == "raid1" && (nr_of_parts < 2))
			{
			// Error popup text
			Popup::Error(sformat(_("For RAID 1, add at least two partitions to %1."), raid ));
			ret = `again;
			}

		    if( raid_type == "raid5" && (nr_of_parts < 3))
			{
			// Error popup text
			Popup::Error(sformat(_("For RAID 5, add at least three partitions to %1."), raid));
			ret = `again;
			}
		    if( raid_type == "multipath" && (nr_of_parts < 2))
			{
			// Error popup text
			Popup::Error(sformat(_("For Multipath raid, add at least two partitions to %1."), raid));
			ret = `again;
			}
		    }
		});
	    }
	if( ret == `back )
	    {
	    if( changed )
		{
		// popup text %1 will be replaces with button text
		string txt = sformat( _("You have changed the partitioning.
These changes will be lost if you exit the dialog with \"%1\".
Continue?
"), 
				      deletechars(Label::BackButton(),"&") );
		if( !Popup::ContinueCancel( txt ))
		    { 
		    ret = `again;
		    }
		}
	    }

	} until (ret == `next || ret == `back || ret == `cancel || ret == `abort );


    if( ret == `next && what == `settings )
	{
	// fix the raid size in all raid devices
	map<string,any> target = Storage::GetMainDev( "/dev/md" );

	target["partitions"] = maplist( map part, target["partitions"]:[],
	    ``({
	    integer len = raid_size_byte( Storage::GetTargetMap(), part);
	    part["region"] = [0, len / (1024 * 1024)];
	    y2milestone( "region = %1", part["region"]:[] );
	    return( part );
	    }));

	Storage::SetMainDev("/dev/md", target );
	Storage::Commit();
	}
    else if( ret == `back )
	{
	// Go back to last save settings in Storage  
	Storage::RestoreTargetBackup("raid");
	}

    return ret;
    }


/**
 * RaidExpertWizard 
 * 
 */
define symbol RaidExpertWizard()
    ``{
    string  subdevkey     	= Storage::GetWizardKey();
    map<string,any> subdev      = Storage::GetSubDev( subdevkey );
 
    subdev = RaidExpertDlg( `wizard, subdev["raid_name"]:"", subdev, 
                            Stage::initial (),
			    FileSystems::GetAllFileSystems(true, true),
			    false, Storage::GetTargetMap() );

    symbol ret = subdev["symbol"]:`next;
    // remove symbol key from new_raid
    subdev = filter( string key, any value, subdev, ``( key != "symbol" ));

    if( ret == `next || ret == `back )
	{
	/////////////////////////////////////////////////
	// fill in the new size of the raid:
	subdev["region"] = [0, raid_size_byte( Storage::GetTargetMap(),subdev ) / (1024 * 1024)];
	y2milestone( "subdevkey %1 subdev %2", subdevkey, subdev );

	Storage::SetSubDev( subdevkey, subdev );
	Storage::Commit();
	}
    return  ret;
    }



}
