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 / include / partitioning / raid_ui.ycp < prev    next >
Text File  |  2006-11-29  |  32KB  |  1,179 lines

  1. /**
  2.  * File:
  3.  *   raid_ui.ycp
  4.  *
  5.  * Module:
  6.  *   Configuration of raid
  7.  *
  8.  * Summary:
  9.  *   User interface functions.
  10.  *
  11.  * Authors:
  12.  *   Michael Hager <mike@suse.de>
  13.  *
  14.  * $Id: raid_ui.ycp 33346 2006-10-12 10:47:02Z fehr $
  15.  *
  16.  * All user interface functions for RAID.
  17.  *
  18.  */
  19.  
  20. {
  21.  
  22.   textdomain "storage";
  23.  
  24.  
  25.   import "Wizard";
  26.   import "Storage";
  27.   import "FileSystems";
  28.   import "Partitions";
  29.   import "Label";
  30.   import "Popup";
  31.   
  32.   include "partitioning/raid_lib.ycp";
  33.   include "partitioning/partition_defines.ycp";
  34.   include "partitioning/custom_part_lib.ycp";
  35.  
  36. /**
  37.  *  AbortDialog
  38.  **/
  39. define symbol AbortDialog()
  40.     ``{
  41.     if( Popup::ReallyAbort(true))
  42.     return `yes;
  43.     else 
  44.     return `back;
  45.     }
  46.  
  47.  
  48. define boolean CheckRaidNumbers( integer raidnr )
  49.     ``{
  50.     integer max_raid = 27;
  51.     list<map> parts = get_possible_rds( Storage::GetTargetMap() );
  52.     string dev = sformat("md%1", raidnr);
  53.     parts = filter( map e, parts, ``(e["used_by"]:""==dev));
  54.     y2milestone( "CheckRaidNumbers size %1 parts %2", size(parts), parts );
  55.     if( size(parts) >= max_raid )
  56.     {
  57.     // popup text
  58.     string text = sformat(_("
  59. The maximum number of partitions in a software raid is %1.
  60. You want to add more partitions to the raid than can be
  61. handled by the current version of software raid.
  62. "), max_raid);
  63.     Popup::Error(text);
  64.     }
  65.     return( size(parts) < max_raid );
  66.     }
  67.  
  68.  
  69. /**
  70.  * Help for RaidTypeDialog
  71.  * @return string
  72.  */
  73. define string ChooseRaidTypeHelp()
  74.     ``{
  75.     // help text
  76.      string help    = _("<p><b>RAID 0:</b> This level increases your disk performance.
  77. There is <b>NO</b> redundancy in this mode. If one of the drives crashes, data recovery will not be possible.</p>
  78. ");
  79.  
  80.     // help text
  81.      help = help +  _("<p><b>RAID 1:</b> <br>This mode has the best redundancy. It can be
  82. used with two or more disks. This mode maintains an exact copy of all data on all
  83. disks. As long as at least one disk is still working, no data is lost. The partitions
  84. used for this type of RAID should have approximately the same size.</p>
  85. ");
  86.  
  87.     // help text
  88.      help = help +  _("<p><b>RAID 5:</b> <br>This mode combines management of a larger number
  89. of disks and still maintains some redundancy. This mode can be used on three disks or more.
  90. If one disk fails, all data is still intact. If two disks fail simultaneously, all data is lost</p>
  91. ");
  92.  
  93.     // help text
  94.      help = help +  _("<p><b>Multipath:</b> <br>This mode allow access to the same physical device
  95. over multiple controllers for redundancy against a fault in a controller card. This mode can be used with at least two devices<p>
  96. ");
  97.  
  98.      return help;
  99.     };
  100.  
  101.  
  102. /**
  103.  * Contents for dialog ChooseRaidType
  104.  * @return term
  105.  **/
  106. define term get_choose_raid_type_contents( string raid, symbol what )
  107.     ``{
  108.     integer space = 0;
  109.     term MpDetect = `Empty();
  110.  
  111.     if( what == `settings )
  112.     {
  113.     space = 5;
  114.     }
  115.     else
  116.     {
  117.     MpDetect = `Left( `HBox( `HSpacing(7), 
  118.                  `PushButton(`id(`mp_auto), 
  119.                              // button text
  120.                           _("Auto&detect Multipath"))));
  121.     }
  122.  
  123.     // Translators, 'Striping' is a technical term here.  Translate only if 
  124.     // you are sure!! If in doubt, leave it in English.
  125.     string str_text = _("RAID &0  (Striping)");
  126.     // Translators, 'Mirroring' is a technical term here. Translate only if 
  127.     // you are sure!! If in doubt, leave it in English.
  128.     string mir_text = _("RAID &1  (Mirroring)");
  129.     // Translators, 'Redundant Striping' is a technical term here. Translate 
  130.     // only if you are sure!! If in doubt, leave it in English.
  131.     string r5_text = _("RAID &5  (Redundant Striping)");
  132.     // label text
  133.     string mp_text = _("&Multipath (Redundant access over two controllers)");
  134.  
  135.  
  136.  
  137.  
  138.     return
  139.      `VBox(
  140.         `VSpacing(space),
  141.         // popup text
  142.         `Heading (_("What type of RAID would you like to create?")),
  143.         `VSpacing(1),
  144.         `RadioButtonGroup(
  145.             `id("raid_type"),
  146.             `HSquash(
  147.             `VBox(
  148.                 `Left(`RadioButton(`id("raid0"), str_text,
  149.                            raid=="raid0")),
  150.                 `VSpacing (1),
  151.                 `Left(`RadioButton(`id("raid1"), mir_text,
  152.                            raid=="raid1")),
  153.                 `VSpacing (1),
  154.                 `Left(`RadioButton(`id("raid5"), r5_text,
  155.                            raid=="raid5"))
  156. /*
  157.                 `VSpacing (1),
  158.                 `Left(`RadioButton(`id("multipath"), mp_text,
  159.                                    raid=="multipath")),
  160.                 MpDetect 
  161. */
  162.                  )
  163.              )
  164.             ),
  165.         `VSpacing(space)
  166.           );
  167.  
  168.  
  169. };
  170.  
  171. define string RaidMultipathAutoHelp()
  172.     ``{
  173.     // help text
  174.     string help = _("<p>The list contains the devices that could be automatically
  175. detected for multipath setup. Disable the devices not to 
  176. have activated by double-clicking the table line and continue when 
  177. finished. If you go back, none of the autodetected multipath raid devices
  178. are created.</p>
  179. <p>If you deselect lines, the names of the raid devices 
  180. after the deselected lines will be changed.</p>
  181. ");
  182.     return( help );
  183.     };
  184.  
  185.  
  186. /**
  187.  * RaidMultipathAuto Dialog
  188.  * @return symbol
  189.  */
  190. define symbol RaidMultipathAuto()
  191.     ``{
  192.     // Choose Raid Type ...
  193.     /////////////////////////////////////////////////////////////////
  194.     y2milestone( "enter" );
  195.  
  196.     // heading text
  197.     string caption = _("RAID Wizard: Multipath Autodetection");
  198.     map<string,map> tg = Storage::GetTargetMap();
  199.     list<map> raids = tg["/dev/md","partitions"]:[];
  200.     list<map> mp_raids = filter( map p, raids,
  201.                             ``(p["create"]:false && 
  202.                    p["raid_type"]:""=="multipath"));
  203.     foreach( map p, mp_raids,
  204.     ``{
  205.     foreach( string dev, map disk, tg,
  206.         ``{
  207.         list new_part = [];
  208.         foreach( map pt, disk["partitions"]:[],
  209.         ``{
  210.         if( pt["used_by"]:""==substring(p["device"]:"",5) )
  211.             {
  212.             pt = filter( string k, any value, (map<string,any>)pt, 
  213.                          ``( k != "used_by" && k != "used_by_type" ));
  214.             if( pt["change_fsid"]:false )
  215.             {
  216.             pt["fsid"] = pt["ori_fsid"]:0;
  217.             pt["fstype"] = pt["ori_fstype"]:"";
  218.             pt = filter( string key, any value, (map<string,any>)pt,  
  219.                       ``( key != "ori_fsid" && 
  220.                           key != "change_fsid" &&
  221.                       key != "ori_fstype" ));
  222.             }
  223.             }
  224.         new_part = add( new_part, pt );
  225.         });
  226.         tg[dev,"partitions"] = new_part;
  227.         });
  228.     raids = filter( map part, raids, ``(part["nr"]:0 != p["nr"]:0));
  229.     });
  230.     if( haskey( tg, "/dev/md" ))
  231.     {
  232.     tg["/dev/md","partitions"] = raids;
  233.     }
  234.     map<integer,list<string> > at = $[];
  235.     list raid_list = [];
  236.     list raid_enab = [];
  237.     integer raid_num = -1;
  238.     integer i=0;
  239.     foreach(list<string> entry, Storage::AutodetectMultipathRaid( tg ),
  240.     ``{
  241.     raid_num = get_free_raid_nr( tg, raid_num+1 );
  242.     raid_list = add( raid_list, raid_num );
  243.     raid_enab = add( raid_enab, true );
  244.     string dev = Storage::GetDeviceName( "/dev/md", raid_num );
  245.     at[i] = entry;
  246.     i = i+1;
  247.     });
  248.     y2milestone( "at=%1", at );
  249.     y2milestone( "rl=%1", raid_list );
  250.     term contents = 
  251.     // popup text
  252.     `Label(_("No automatically configurable\nmultipath devices detected"));
  253.     if( size(at)>0 )
  254.     {
  255.     integer i = 0;
  256.     list ct = maplist( integer key, list content, at,
  257.                ``{
  258.                term a = `item( `id(i) );
  259.                // label text
  260.                a = add( a, _("Yes") );
  261.                a = add( a, Storage::GetDeviceName( "/dev/md",
  262.                                                    raid_list[key]:255 ));
  263.                a = add( a, content[0]:"" );
  264.                a = add( a, content[1]:"" );
  265.                string rest = "";
  266.                if( size(content)>2 )
  267.                    {
  268.                    rest = mergestring( (list<string>)remove(remove(content,0),0),
  269.                                        " ");
  270.                    }
  271.                a = add( a, rest );
  272.                i = i + 1;
  273.                return( a );
  274.                });
  275.     term header = `header();
  276.         // heading text
  277.     header = add( header, _("Create") );
  278.         // heading text
  279.     header = add( header, _("Raid Device") );
  280.         // heading text
  281.     header = add( header, _("Partition 1") );
  282.         // heading text
  283.     header = add( header, _("Partition 2") );
  284.         // heading text
  285.     header = add( header, _("Other...") );
  286.     contents = `HBox(`HSpacing(8),
  287.                      `VBox(`VSpacing(2), 
  288.                    `Table( `id(`mp_table), `opt(`notify), 
  289.                             header, ct ),
  290.                    `VSpacing(2) ),
  291.              `HSpacing(8));
  292.     }
  293.  
  294.     Wizard::SetContentsButtons(caption, contents, RaidMultipathAutoHelp(),
  295.                    Label::BackButton(), Label::NextButton());
  296.  
  297.     symbol ret = `next;
  298.  
  299.     do
  300.     {
  301.     ret = (symbol)UI::UserInput();
  302.     y2milestone( "ret %1", ret );
  303.  
  304.     if( ret == `mp_table )
  305.         {
  306.         integer cur = (integer) UI::QueryWidget( `id(`mp_table), 
  307.                                                  `CurrentItem );
  308.         integer sel_num = 0;
  309.         integer i = 0;
  310.         while( i<cur )
  311.         {
  312.         if( raid_enab[i]:false )
  313.             {
  314.             sel_num = sel_num + 1;
  315.             }
  316.         i=i+1;
  317.         }
  318.         string dev = "";
  319.         if( !raid_enab[i]:false )
  320.         {
  321.         dev = Storage::GetDeviceName( "/dev/md", 
  322.                                       raid_list[sel_num]:255 );
  323.         sel_num = sel_num + 1;
  324.         }
  325.         raid_enab[i] = !raid_enab[i]:false;
  326.         UI::ChangeWidget(`id(`mp_table), `Item(cur, 1), dev );
  327.         UI::ChangeWidget(`id(`mp_table), `Item(cur, 0), 
  328.                  // label text
  329.                  size(dev)==0?_("No"):_("Yes") );
  330.         i = cur+1;
  331.         while( i<size(raid_enab) )
  332.         {
  333.         if( raid_enab[i]:false )
  334.             {
  335.             dev = Storage::GetDeviceName( "/dev/md", 
  336.                           raid_list[sel_num]:255 );
  337.             sel_num = sel_num + 1;
  338.             UI::ChangeWidget(`id(`mp_table), `Item(i, 1), dev );
  339.             }
  340.         i = i+1;
  341.         }
  342.         }
  343.  
  344.     if( ret == `next )
  345.         {
  346.         integer i = 0;
  347.         list md_part = [];
  348.         while( i<size(raid_enab) )
  349.         {
  350.         if( raid_enab[i]:false )
  351.             {
  352.             string dev = sformat( "/dev/md%1", raid_list[i]:255 );
  353.             map rp = $[ "nr"         : raid_list[i]:255,
  354.                     "device"     : dev,
  355.                     "raid_type"  : "multipath",
  356.                     "type"       : `sw_raid,
  357.                     "fsid"       : Partitions::fsid_native,
  358.                     "size_k"     : 0, // dummy
  359.                     "create"        : true,
  360.                     "used_fs"    : Partitions::DefaultFs()    ];
  361.             map p = $[];
  362.             foreach( string device, (list<string>)at[i]:[],
  363.             ``{
  364.             p = Storage::GetPartition( tg, device );
  365.             p["used_by_type"] = `UB_MD;
  366.             p["used_by"] = substring(dev,5);
  367.             if( p["fsid"]:0 != Partitions::fsid_raid )
  368.                 {
  369.                 if( !p["change_fsid"]:false )
  370.                 {
  371.                 p["change_fsid"] = true;
  372.                 p["ori_fsid"] = p["fsid"]:0;
  373.                 p["ori_fstype"] = p["fstype"]:"";
  374.                 }
  375.                 p["fsid"] = Partitions::fsid_raid;
  376.                 p["fstype"] = 
  377.                 Partitions::FsIdToString(Partitions::fsid_raid);
  378.                 }
  379.             y2debug( "p1 %1", p );
  380.             tg = (map<string,map>)Storage::SetPartition( tg, p );
  381.             });
  382.  
  383.             integer mdsize = raid_size_byte(rp);
  384.             y2milestone( "mdsize=%1", mdsize );
  385.             rp["size_k"] = mdsize / 1024;
  386.             md_part = add( md_part, rp );
  387.             y2milestone( "enabled %1 num:%2 entry:%3", i, 
  388.                          raid_list[i]:255, at[i]:[] );
  389.             y2milestone( "rp %1", rp );
  390.             }
  391.         i = i+1;
  392.         }
  393.         if( size(md_part)>0 )
  394.         {
  395.         tg = Storage::GetTargetMap();
  396.         tg["/dev/md","partitions"] = 
  397.             merge( tg["/dev/md","partitions"]:[], md_part );
  398.         Storage::SetTargetMap( tg );
  399.         }
  400.         }
  401.     }
  402.     while( ret != `back && ret !=`next && ret !=`abort );
  403.     y2milestone( "ret %1", ret );
  404.     return ret;
  405.     }
  406.  
  407. /**
  408.  * ChooseRaidType Dialog
  409.  * @parm what -> `settings or `wizard
  410.  * @return symbol
  411.  */
  412. define symbol ChooseRaidType(symbol  what)
  413.     ``{
  414.     integer  raidnr = 0;
  415.     map raidp = $[];
  416.  
  417.     Storage::CreateTargetBackup("raid");
  418.    
  419.     y2milestone( "ChooseRaidType what:%1", what );
  420.  
  421.     map<string,map> tg = Storage::GetTargetMap();
  422.     string rdev = Storage::GetWizardKey();
  423.     if( size(rdev)==0 )
  424.     {
  425.     raidnr = get_free_raid_nr( tg, 0 );
  426.     rdev = sformat( "/dev/md%1", raidnr );
  427.     Storage::SetWizardKey(rdev);
  428.     }
  429.     else
  430.     {
  431.     raidp = Storage::GetPartition( tg, rdev );
  432.     raidnr = raidp["nr"]:0;
  433.     }
  434.  
  435.     y2milestone( "ChooseRaidType raidp raidp=%1", raidp );
  436.     y2milestone( "ChooseRaidType nr=%1 dev:%2", raidnr, rdev );
  437.     
  438.     /////////////////////////////////////////////////////////////////
  439.     // Choose Raid Type ...
  440.     /////////////////////////////////////////////////////////////////
  441.  
  442.     // heading text
  443.     string caption = _("RAID Wizard: Step 1.");
  444.     term contents = 
  445.     get_choose_raid_type_contents(raidp["raid_type"]:"raid1", what );
  446.  
  447.     if( what == `wizard )
  448.     {
  449.     Wizard::SetContentsButtons(caption, contents,
  450.                    ChooseRaidTypeHelp(),
  451.                    Label::BackButton(),
  452.                    Label::NextButton());
  453.     }
  454.     else if ( what == `settings )
  455.     {
  456.     contents = add( contents,
  457.             `HBox(
  458.                    // popup create partition:
  459.                    `PushButton(`id(`ok), `opt(`default), 
  460.                        Label::OKButton() ),
  461.                    // popup create partition:
  462.                    `PushButton(`id(`cancel), Label::CancelButton() )
  463.                    ));
  464.  
  465.     UI::OpenDialog( `opt(`decorated ),
  466.             `HBox(
  467.                    `HWeight(50, `RichText( ChooseRaidTypeHelp() )),
  468.                    `HStretch(),
  469.                    `HSpacing(1),
  470.                    `HWeight(70, contents ),
  471.                    `HSpacing(1),
  472.                    `HStretch()
  473.                    ));
  474.         }
  475.     else
  476.     {
  477.     y2error( " Error what in ChooseRaidType must be symbol `wizard or `settings");
  478.     }
  479.  
  480.     boolean new_raid = size(raidp)==0;
  481.     symbol ret = `none;
  482.     y2milestone( "ChooseRaidType new_raid:%1", new_raid );
  483.     do
  484.     {
  485.     ret = (symbol)UI::UserInput();
  486.  
  487.     if( contains( [ `next, `ok, `accept ], ret ))
  488.         {
  489.         ////////////////////////////////////////////////////////////
  490.         //   Create initial raid datastructure, which comes to 
  491.         //   /dev/md[partitions]
  492.  
  493.         string raidtype = (string)UI::QueryWidget( `id("raid_type"), 
  494.                                `CurrentButton );
  495.         y2milestone( "ChooseRaidType raidtype:%1", raidtype );
  496.  
  497.         integer chunk_size = 4;
  498.  
  499.         if( raidtype == "raid5" ) chunk_size = 128;
  500.         if( raidtype == "raid0" ) chunk_size = 32;
  501.  
  502.         if( raidp == $[] || what == `settings )
  503.         {
  504.         string mprop = GetMountPointProposal( tg, [] );
  505.         symbol used_fs = Partitions::DefaultFs();
  506.         if( mprop == Partitions::BootMount() )
  507.             {
  508.             used_fs = Partitions::DefaultBootFs();
  509.             }
  510.         Storage::CreateMd( raidnr, raidtype );
  511.         raidp = $[ "device"   : rdev,
  512.                "format"      : true,
  513.                "mount"    : mprop,
  514.                "used_fs"  : used_fs ];
  515.         Storage::ChangeVolumeProperties( raidp );
  516.         raidp = Storage::GetPartition( Storage::GetTargetMap(), rdev );
  517.         y2milestone( "ChooseRaidType raidp:%1", raidp );
  518.         }
  519.         else
  520.         Storage::ChangeMdType( raidnr, raidtype );
  521.         Storage::ChangeMdChunk( raidnr, chunk_size );
  522.         ret = `next;
  523.         }
  524.     else if( ret == `back && what==`wizard && !new_raid )
  525.         {
  526.         // popup text %1 will be replaces with button text
  527.         string txt = sformat( _("Changes made to your RAID setup  so far will be 
  528. lost if you exit the dialog with %1.
  529. Continue?
  530. "), 
  531.                   deletechars(Label::BackButton(),"&") );
  532.         if( !Popup::ContinueCancel( txt ))
  533.         { 
  534.         ret = `again;
  535.         }
  536.         }
  537.     }
  538.     while( !contains( [`next, `back ], ret ));
  539.  
  540.     if( what == `settings)
  541.     {
  542.     UI::CloseDialog();
  543.     }
  544.  
  545.     if( ret == `back && what==`wizard )
  546.     {
  547.     y2milestone( "ChooseRaidType raidp:%1", raidp );
  548.     if( raidp["create"]:false )
  549.         {
  550.         string md = sformat( "md%1", raidp["nr"]:0 );
  551.         list<map> parts = filter( map p, all_raid_partitions(tg),
  552.                                   ``(p["used_by"]:""==md));
  553.         foreach( map p, parts,
  554.         ``{
  555.         Storage::UnchangePartitionId( p["device"]:"" );
  556.         });
  557.         y2milestone( "ChooseRaidType parts:%1", parts );
  558.         Storage::DeleteDevice( "/dev/md", raidp["device"]:"" );
  559.         }
  560.     }
  561.     y2milestone( "ChooseRaidType ret %1", ret );
  562.     return ret;
  563.     }
  564.  
  565. /**
  566.  * Help for RaidDevices Dialog
  567.  * @return string
  568.  */
  569. define string RaidDevicesHelp()
  570.     ``{
  571.     // help text, richtext format
  572.     string helptext           = _("<p><b>Add partitions to your RAID.</b> According to
  573. the RAID type, the usable disk size is the sum of these partitions (RAID0), the size
  574. of the smallest partition (RAID 1), or (N-1)*smallest partition (RAID 5).</p>
  575. ");
  576.  
  577.     // help text, richtext format
  578.     helptext = helptext + _("<p>Generally, the partitions should be on different drives,
  579. to get the redundancy and performance you want.</p>
  580. ");
  581.  
  582.     // help text, richtext format
  583.     helptext = helptext + _("<p><b>Expert options:</b><br>Here, set
  584. things like chunk size to get the best performance
  585. out of your system. These settings are used for all partitions of this RAID.</p>
  586. ");
  587.  
  588.     return helptext;
  589.     }
  590.  
  591.  
  592. /**
  593.  * Popup to display an error when changing an existing raid.
  594.  *
  595.  * in    dev: device name of the raid device
  596.  */
  597. define void CannotEditExistingRaid( string dev )
  598.     ``{
  599.     // Error popup text
  600.     Popup::Error(sformat( _("The RAID to change (%1) is already created on disk.
  601. It can no longer be changed. To change %1, 
  602. remove it and create it again.
  603. "),dev));
  604.     };
  605.  
  606.  
  607. /**
  608.  * partition add partition widget table.
  609.  * @return term
  610.  */
  611. define term get_raid_devices_content( symbol what, string current_raid_text, 
  612.                       list all_raids, string size_md )
  613.     ``{
  614.     term top = `HBox(
  615.               `Left (`Label (current_raid_text)),
  616.               // Label in raid -wf
  617.               `Label (_("Size:")),
  618.               `Label (`id(`raid_size), `opt( `outputField), "---------")
  619.             );
  620.  
  621.     if( what == `settings )
  622.     {
  623.     top = `HBox(
  624.             `Left(`VBox(
  625.                 `ReplacePoint(`id(`raids_rp), 
  626.                               `ComboBox(`id(`raids), 
  627.                                     `opt(`notify), 
  628.                             // label text
  629.                             _("RA&ID:"), all_raids)),
  630.                 `PushButton( `id(`md_raid_edit), 
  631.                          // label text
  632.                              `opt(`hstretch), _("O&ptions"))
  633.                 )),
  634.  
  635.             `HSpacing(0.5),
  636.             // label text
  637.             `Left(`Label(_("Size:"))),
  638.             `Left(`Label( `id(`raid_size),
  639.                   `opt( `hstretch, `outputField), "-------" )),
  640.             `HWeight(1,`HStretch()),
  641.             `Right(`VBox(
  642.                  `PushButton( `id(`md_raid_remove), 
  643.                                `opt(`hstretch), 
  644.                           // button text
  645.                           _("Remo&ve RAID")),
  646.                  `PushButton( `id(`md_raid_add), 
  647.                               `opt(`hstretch), 
  648.                           // button text
  649.                           _("Add RAI&D"))))
  650.             );
  651.     }
  652.  
  653.     return( `HBox(
  654.           `HSpacing(1),
  655.           `VBox(
  656.           `VSpacing (0.5),
  657.           top,
  658.           `VSpacing (0.2),
  659.           `Table(`id(`raid_table), `opt(`notify),
  660.              // table header 
  661.              `header(_("    Device    "),
  662.              // table header
  663.                  `Right(_("    Size    ")),
  664.              // table header
  665.                  `Center(_("        Type        ")),
  666.              // table header
  667.                  `Center(_("      RAID      "))),
  668.              []
  669.              ),
  670.           `HBox (
  671.              // Button in RAID-WF 
  672.              `PushButton (`id (`raid_add), _("A&dd")),
  673.              // Button in RAID-WF 
  674.              `PushButton (`id (`raid_remove), _("&Remove"))
  675.              ),
  676.           `VSpacing (0.5)
  677.           ),
  678.           `HSpacing(1)
  679.  
  680.         )
  681.         );
  682.     };
  683.  
  684.  
  685. /**
  686.  * RaidExpertDlgHelp
  687.  * @return string
  688.  */
  689. define string RaidExpertDlgHelp()
  690.     ``{
  691.     // help text
  692.     string helptext_edit_raid = _("<p><b>chunk size:</b><br>It is the smallest \"atomic\" mass
  693. of data that can be written to the devices. A reasonable chunk size for RAID 5 is 128KB. For RAID 0,
  694. 32 KB is a good starting point. For RAID 1, the chunk size does not affect the array very much.</p>
  695. ");
  696.  
  697.     // help text
  698.     helptext_edit_raid = helptext_edit_raid + _("<p><b>parity algorithm:</b><br>The parity algorithm to use with RAID5.
  699. Left-symmetric is the one that offers maximum performance on typical disks with rotating platters.</p>
  700. ");
  701.  
  702.     return helptext_edit_raid;
  703.     };
  704.  
  705. /**
  706.  *  raidExpertDlg dialog
  707.  *  edit expert options
  708.  */
  709. define map<string,any> 
  710. RaidExpertDlg( symbol what, string device, map<string,any> cur_raid,
  711.            map<symbol,map> file_systems, boolean edit_raid_type, 
  712.            map<string,map> targetMap )
  713.     ``{
  714.     string raid_type = cur_raid["raid_type"]:"raid1";
  715.     // heading text
  716.     string caption   = sformat(_("Raid settings %1"), device );
  717.     boolean created  = cur_raid["create"]:false;
  718.     
  719.     term contents = `HBox(
  720.                FormatDlg( cur_raid, file_systems ),
  721.                `HSpacing(2),
  722.                `VBox(
  723.                  `Top(    RaidOptionsDlg( cur_raid, true )),
  724.                  `Bottom( 
  725.                      `ReplacePoint( `id(`mount_dlg_rp), 
  726.                             MountDlg( cur_raid, [] )))
  727.                  )
  728.                );
  729.  
  730.  
  731.     if( what == `settings )
  732.     {
  733.     contents = `VBox(
  734.              `Heading(caption),
  735.              `VSpacing(1),
  736.              `VStretch(),
  737.              contents,
  738.              `VSpacing(1),
  739.              `VStretch(),
  740.              `HBox(
  741.                    // popup create partition:
  742.                    `PushButton(`id(`ok), `opt(`default), 
  743.                        Label::OKButton()  ),
  744.                    // popup create partition:
  745.                    `PushButton(`id(`cancel), Label::CancelButton() )
  746.                    ));
  747.  
  748.  
  749.  
  750.     /////////////////////////////////////////////////////////////
  751.     // Open main dialog for Raid
  752.     /////////////////////////////////////////////////////////////
  753.     if( ! UI::OpenDialog( `opt(`decorated ),
  754.                   `HBox(
  755.                     `HWeight(30,`RichText(RaidExpertDlgHelp())),
  756.                     `HStretch(),
  757.                     `HSpacing(1),
  758.                     `HWeight(70, contents ),
  759.                     `HSpacing(1),
  760.                     `HStretch()
  761.                     )))
  762.         return cur_raid;
  763.     }
  764.     else
  765.     {
  766.     // heading text
  767.     Wizard::SetContentsButtons(_("RAID Wizard Step 3:"),
  768.                      `HBox(
  769.                        `HSpacing(),
  770.                        `VBox(
  771.                          `VSpacing(),
  772.                          `HVCenter(`HVSquash(contents)),
  773.                          `VSpacing(),
  774.                          `VSpacing(1)
  775.                          ),
  776.                        `HSpacing()
  777.                        ),
  778.                   RaidExpertDlgHelp(),
  779.                   Label::BackButton(), Label::FinishButton());
  780.  
  781.     }
  782.  
  783.  
  784.      ////////////////////////////////////////////////////////////////
  785.      // configure main dialog for the first call
  786.  
  787.      ChangeExistingSymbolsState( [`raid_combo], false);
  788.  
  789.      UI::ChangeWidget( `id(`raid_combo), `Value, raid_type );
  790.      UI::ChangeWidget( `id(`chunk_size), `Value, cur_raid["chunk_size"]:4 );
  791.      UI::ChangeWidget( `id(`parity), `Enabled, created && raid_type=="raid5" );
  792.      UI::ChangeWidget( `id(`chunk_size), `Enabled, 
  793.                        created && raid_type!="multipath" );
  794.  
  795.      map<string,any> retval = eval(cur_raid);
  796.      symbol ret = `ok;
  797.  
  798.      retval = HandlePartWidgetChanges( true, ret, file_systems, cur_raid, retval );
  799.  
  800.      repeat
  801.      {
  802.      ret = (symbol)UI::UserInput();
  803.      y2debug( "DlgRaid %1", ret);
  804.  
  805.      ////////////////////////////////////////////////////////////
  806.      if( ret != `cancel )
  807.          {
  808.          retval = HandlePartWidgetChanges( false, ret, file_systems, 
  809.                                            cur_raid, retval );
  810.  
  811.          if( edit_raid_type )
  812.          retval["raid_type"] = 
  813.              UI::QueryWidget(`id(`raid_combo), `Value);
  814.  
  815.          retval["chunk_size"] = UI::QueryWidget(`id(`chunk_size), `Value);
  816.          retval["parity_algorithm"] = UI::QueryWidget(`id(`parity), `Value);
  817.          y2milestone( "RaidExpertDlg retval:%1", retval );
  818.          }
  819.  
  820.      if( ret == `raid_combo )
  821.          {
  822.          UI::ChangeWidget( `id(`parity), `Enabled, 
  823.                            created && raid_type=="raid5" );
  824.          UI::ChangeWidget( `id(`chunk_size), `Enabled, 
  825.                            created && raid_type!="multipath" );
  826.          }
  827.  
  828.      if( ret == `ok || ret == `next )
  829.          {
  830.          map ret_mp = CheckOkMount( retval["device"]:"", cur_raid, retval );
  831.          retval = ret_mp["map"]:$[];
  832.          if( !ret_mp["ok"]:false )
  833.         {
  834.         if( ret_mp["field"]:`none != `none )
  835.             UI::SetFocus(`id( ret_mp["field"]:`none ));
  836.         ret = `again;
  837.         continue;
  838.         }
  839.  
  840.          ret_mp = CheckDeviceFinalOk( retval );
  841.          if( !ret_mp["ok"]:false )
  842.         {
  843.         ret = `again;
  844.         }
  845.          else
  846.         {
  847.         retval = ret_mp["map"]:$[];
  848.         y2milestone( "RaidExpertDlg retval:%1", retval );
  849.         }
  850.          }
  851.  
  852.      } until( ret == `ok || ret == `cancel || ret == `next ||
  853.           ret == `back || ret == `abort );
  854.  
  855.      y2milestone( "ret:%1 retval=%2", ret, retval );
  856.      
  857.      if( ret == `back || ret == `next )
  858.      {
  859.      retval["symbol"] = ret;
  860.      }
  861.      else
  862.      {
  863.      UI::CloseDialog();
  864.      }
  865.  
  866.      if( ret == `ok  || ret == `next  || ret == `back )
  867.      {
  868.      y2milestone( "RaidExpertDlg retval:%1", retval );
  869.      Storage::ChangeVolumeProperties( retval );
  870.      if( edit_raid_type && haskey( retval, "raid_type" ))
  871.         Storage::ChangeMdType( retval["nr"]:0, retval["raid_type"]:"raid1" );
  872.      if( haskey( retval, "chunk_size" ))
  873.          Storage::ChangeMdChunk( retval["nr"]:0, retval["chunk_size"]:4 );
  874.      if( retval["raid_type"]:"raid1"=="raid5" && 
  875.          haskey( retval, "parity_algorithm" ))
  876.          Storage::ChangeMdParity( retval["nr"]:0, 
  877.                                   retval["parity_algorithm"]:"" );
  878.      return retval;
  879.      }
  880.     return cur_raid;
  881.     }
  882.  
  883. /**
  884.  * AddRaidDevices dialog
  885.  *
  886.  * Choose free partitions and add them to the raid.
  887.  * additionally you can edit the raid options like chunk size
  888.  * in -
  889.  *  out:
  890.  */
  891. define symbol RaidDevices( symbol what )
  892.     ``{
  893.     y2milestone( "RaidDevices what %1", what );
  894.     Storage::CreateTargetBackup("raid");
  895.    
  896.     string raiddev = Storage::GetWizardKey();
  897.     y2milestone( "RaidDevices Current raid; raiddev=%1", raiddev );
  898.     map<string,map> tg = Storage::GetTargetMap();
  899.     map<string,any> raidp = Storage::GetPartition( tg, raiddev );
  900.     y2milestone( "RaidDevices raidp=%1", raidp );
  901.  
  902.     /* e.g.: subdevraidindex = 1; */
  903.     integer raidnr = raidp["nr"]:0;
  904.     // heading text
  905.     string caption = _("RAID Wizard Step 2:");
  906.  
  907.     /* key for subdev that can assigned to a raid */
  908.     string pdevice = "";
  909.  
  910.     if( what == `settings )
  911.     {
  912.     // heading text
  913.     caption   = _("RAID settings");
  914.     }
  915.  
  916.     list<map> parts        =  []; 
  917.     list    table        =  [];
  918.  
  919.     Wizard::SetContentsButtons( caption,
  920.                 get_raid_devices_content(what,
  921.                              // label text
  922.                              sformat(_("Current RAID: /dev/md%1"), raidnr),
  923.                              get_raid_devices(tg,raidp["nr"]:0),
  924.                              ""
  925.                              ),
  926.                 RaidDevicesHelp(),
  927.                 Label::BackButton(), Label::NextButton() );
  928.  
  929.     UI::SetFocus( `id(`raid_table));
  930.  
  931.     //////////////////////////////////////////////////////////////////////
  932.     // mainloop
  933.     //////////////////////////////////////////////////////////////////////
  934.   
  935.     symbol ret = `start;
  936.  
  937.     tg = Storage::GetTargetMap();
  938.     repeat
  939.     {
  940.     //////////////////////////////////////////////////////////
  941.     // Raid ComboBox 
  942.     if( what == `settings )
  943.         {
  944.         raiddev = (string)UI::QueryWidget(`id(`raids), `Value );
  945.         y2milestone( "RaidDevices Selected Raid in ComboBox; raiddev=%1", 
  946.                      raiddev );
  947.         raidp = Storage::GetPartition( tg, raiddev );
  948.         raidnr = raidp["nr"]:255;
  949.         }
  950.     y2milestone( "RaidDevices Current raid; raiddev=%1", raiddev );
  951.     y2milestone( "RaidDevices ret %1", ret );
  952.  
  953.     ///////////////////////////////////////////////////////////
  954.     // Update raid widget table
  955.     if( ret != `raids || ret == `start )
  956.         {
  957.         parts  = get_possible_rds( tg );
  958.         if( what == `wizard )
  959.         {
  960.         parts = filter( map part, parts, 
  961.                         ``( part["used_by"]:"" == sformat("md%1",raidnr) ||
  962.                     size(part["used_by"]:"")==0 ));
  963.         }
  964.         y2milestone( "RaidDevices parts %1", parts );
  965.  
  966.         /////////////////////////////////////////////////////////////////
  967.         // Show the current state:
  968.         table = get_raid_widget_table( parts );
  969.  
  970.         UI::ChangeWidget( `id(`raid_table), `Items, table);
  971.         }
  972.  
  973.     UI::ChangeWidget( `id(`raid_size), `Value, raid_size_str( raidp ));
  974.  
  975.     if( pdevice != "" && (ret == `raid_add || ret == `raid_remove) )
  976.         {
  977.         UI::ChangeWidget( `id(`raid_table), `CurrentItem, pdevice);
  978.         }
  979.  
  980.  
  981.     /////////////////////////////////////////////////////////////////
  982.     // Wait for User input
  983.     ret = (symbol)UI::UserInput();
  984.     y2milestone( "RaidDevices ret %1", ret );
  985.  
  986.     /////////////////////////////////////////////////////////////////
  987.     // Edit raid settings - not in wizard
  988.     if( ret == `md_raid_edit )
  989.         {
  990.         RaidExpertDlg( `settings, raiddev, raidp, 
  991.                FileSystems::GetAllFileSystems(true,true),
  992.                false, tg );
  993.         tg = Storage::GetTargetMap();
  994.         raidp = Storage::GetPartition( tg, raiddev );
  995.         }
  996.  
  997.     /////////////////////////////////////////////////////////////////
  998.     // Add a new raid 
  999.     if( ret == `md_raid_add  )
  1000.         {
  1001.         if( !check_raid_possible( tg ))
  1002.         {
  1003.         ret = `again;
  1004.         continue;
  1005.         }
  1006.  
  1007.         ChooseRaidType( `settings );    
  1008.         tg = Storage::GetTargetMap();
  1009.         y2milestone( "RaidDevices new raid:%1", Storage::GetWizardKey() );
  1010.         if( Storage::GetWizardKey()!=raiddev )
  1011.         {
  1012.         raiddev = Storage::GetWizardKey();
  1013.         y2milestone( "RaidDevices new raid:%1", raiddev );
  1014.         UI::ChangeWidget( `id(`raids), `Value, raiddev );
  1015.         raidp = Storage::GetPartition( tg, raiddev );
  1016.         }
  1017.         }
  1018.  
  1019.     /////////////////////////////////////////////////////////////////
  1020.     // Remove a new raid (2//dev/md ) - not in wizard  
  1021.     if( ret == `md_raid_remove )
  1022.         {
  1023.         // popup text
  1024.         if ( Popup::YesNo ( sformat(_("Do you really want to delete partition %1?"), "/dev/md"+raidnr )))
  1025.         {
  1026.         Storage::DeleteDevice( "/dev/md", raiddev );
  1027.         tg = Storage::GetTargetMap();
  1028.         }
  1029.         }
  1030.  
  1031.     /////////////////////////////////////////////////////////////////
  1032.     // Update Raid ComboBox - not in wizard
  1033.     if( ret == `md_raid_add || ret == `md_raid_remove )
  1034.         {
  1035.         new_raid_list( get_raid_devices( tg, raidp["nr"]:0 ));
  1036.         raiddev = (string)UI::QueryWidget(`id(`raids), `Value );
  1037.         y2milestone( "RaidDevices raiddev %1", raiddev );
  1038.         raidp = Storage::GetPartition( tg, raiddev );
  1039.         raidnr = raidp["nr"]:0;
  1040.         }
  1041.     
  1042.     if( ret == `raid_table )
  1043.         {
  1044.         pdevice = (string)UI::QueryWidget( `id(`raid_table), `CurrentItem );
  1045.         ret = isItemRd(tg,pdevice) ? `raid_remove : `raid_add;
  1046.         }
  1047.  
  1048.     /////////////////////////////////////////////////////////////
  1049.     // Add a new physical volume to a raid e.g. assign 1//dev/hda 
  1050.     // to 1//dev/md
  1051.     if( ret == `raid_add )
  1052.         {
  1053.         pdevice = (string)UI::QueryWidget( `id(`raid_table), `CurrentItem );
  1054.  
  1055.         if( CheckItemIsNotRaid( tg, pdevice ) && 
  1056.         CheckRaidNumbers( raidnr ) )
  1057.         {
  1058.         if( raidp["create"]:false )
  1059.             {
  1060.             Storage::SetPartitionId( pdevice, Partitions::fsid_raid );
  1061.             Storage::ExtendMd( raidnr, pdevice );
  1062.             tg = Storage::GetTargetMap();
  1063.             raidp = Storage::GetPartition( tg, raiddev );
  1064.             }
  1065.         else
  1066.             {
  1067.             CannotEditExistingRaid( raiddev );
  1068.             ret = `again;
  1069.             }
  1070.         }
  1071.         }
  1072.  
  1073.  
  1074.     /////////////////////////////////////////////////////////////
  1075.     // Delete a new physical volume e.g. remove 1//dev/hda from 1//dev/md
  1076.     if( ret == `raid_remove )
  1077.         {
  1078.         pdevice = (string)UI::QueryWidget(`id(`raid_table), `CurrentItem);
  1079.  
  1080.         if( CheckItemIsRaid( tg, pdevice) )
  1081.         {
  1082.         if( raidp["create"]:false )
  1083.             {
  1084.             Storage::UnchangePartitionId( pdevice );
  1085.             Storage::ShrinkMd( raidnr, pdevice );
  1086.             tg = Storage::GetTargetMap();
  1087.             raidp = Storage::GetPartition( tg, raiddev );
  1088.             }
  1089.         else
  1090.             {
  1091.             CannotEditExistingRaid( raiddev );
  1092.             ret = `again;
  1093.             }
  1094.         }
  1095.         }
  1096.  
  1097.     
  1098.     if (ret == `next)
  1099.         {
  1100.         ///////////////////////////////////////////////////////
  1101.         // check if enough disks are attached to the raid
  1102.         list<map> parts = (list<map>)Storage::GetDisk( tg, "/dev/md" )["partitions"]:[];
  1103.         foreach( map part, parts,
  1104.         ``{
  1105.         if( part["create"]:false )
  1106.             {
  1107.             string raid_type = part["raid_type"]:"raid1";
  1108.             integer nr_of_parts = 
  1109.             getNrOfParts( tg, sformat("%1", part["nr"]:0) );
  1110.             string raid = part["device"]:"";
  1111.             boolean ok = Storage::CheckMd(part["nr"]:0)==0;
  1112.             if( !ok )
  1113.             {
  1114.             string txt = "";
  1115.             if( raid_type=="raid0" ) 
  1116.                 {
  1117.                 // Error popup text
  1118.                 txt = sformat(_("For RAID 0, add at least two partitions to %1."),  raid );
  1119.                 }
  1120.             else if( raid_type=="raid1" )
  1121.                 {
  1122.                 // Error popup text
  1123.                 txt = sformat(_("For RAID 1, add at least two partitions to %1."), raid );
  1124.                 }
  1125.             else if( raid_type == "raid5" )
  1126.                 {
  1127.                 // Error popup text
  1128.                 txt = sformat(_("For RAID 5, add at least three partitions to %1."), raid);
  1129.                 }
  1130.             else if( raid_type == "raid6" )
  1131.                 {
  1132.                 // Error popup text
  1133.                 txt = sformat(_("For RAID 6, add at least four partitions to %1."), raid);
  1134.                 }
  1135.             else if( raid_type == "raid10" )
  1136.                 {
  1137.                 // Error popup text
  1138.                 txt = sformat(_("For RAID 10, add at least two partitions to %1."), raid);
  1139.                 }
  1140.             else if( raid_type == "multipath" )
  1141.                 {
  1142.                 // Error popup text
  1143.                 txt = sformat(_("For Multipath raid, add at least two partitions to %1."), raid);
  1144.                 }
  1145.             Popup::Error(txt);
  1146.             ret = `again;
  1147.             }
  1148.             }
  1149.         });
  1150.         }
  1151.     } 
  1152.     until( ret == `next || ret == `back || ret == `cancel || ret == `abort );
  1153.     return ret;
  1154.     }
  1155.  
  1156.  
  1157. /**
  1158.  * RaidExpertWizard 
  1159.  * 
  1160.  */
  1161. define symbol RaidExpertWizard()
  1162.     ``{
  1163.     string raiddev = Storage::GetWizardKey();
  1164.     map<string,map> tg = Storage::GetTargetMap();
  1165.     map<string,any> raidp = Storage::GetPartition( tg, raiddev );
  1166.  
  1167.     raidp = RaidExpertDlg( `wizard, raidp["used_by"]:"", raidp, 
  1168.                FileSystems::GetAllFileSystems(true, true),
  1169.                false, tg );
  1170.  
  1171.     symbol ret = raidp["symbol"]:`next;
  1172.     y2milestone( "RaidExpertWizard ret:%1", ret );
  1173.     return  ret;
  1174.     }
  1175.  
  1176.  
  1177.  
  1178. }
  1179.