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 / clients / inst_vm_manage.ycp < prev    next >
Text File  |  2006-11-29  |  19KB  |  570 lines

  1. /**
  2.  * Module:    inst_vm_manage.ycp
  3.  *
  4.  * Authors:    Michael G. Fritch <mgfritch@novell.com>
  5.  *
  6.  * Purpose:    Manage Virtual Machines (Add, Delete, Start, View, Shutdown, Terminate)
  7.  *
  8.  * $Id: inst_vm_manage.ycp 29389 2006-03-26 05:40:23Z mgfritch $
  9.  *
  10.  */
  11.  
  12. {
  13.  
  14. textdomain "vm";
  15.  
  16. import "VM";
  17. import "VM_Common";
  18. import "VM_XEN";
  19. import "Wizard";
  20. import "Popup";
  21. import "Report";
  22. import "Label";
  23. import "Sequencer";
  24. import "String";
  25.  
  26.  
  27. list<map> SettingsList = [];
  28.  
  29.  
  30. boolean DeleteVirtualMachine(integer table_item) {
  31.     if (table_item != nil || table_item >= 0) {
  32.         boolean success = true;
  33.         map item = SettingsList[table_item]:$[];
  34. //        VM::TerminateVirtualMachine(item["name"]:""); // delete button should be disabled if the VM is running.
  35.  
  36.         // create a list of all known disk sources, excluding this VM (loopback only!)
  37.         list<map> tmpSettings = remove(SettingsList, table_item);
  38.         list<string> all_other_sources = [];
  39.         foreach(map tmpItem, tmpSettings, {
  40.             list<map> disks = (list<map>) tmpItem["disks"]:[];
  41.             foreach(map d, disks, {
  42.                 if (d["type"]:"phys" == "loop-use")
  43.                     all_other_sources = add(all_other_sources, d["source"]:"");
  44.             });
  45.         });
  46.         all_other_sources = filter(string src, all_other_sources, { return (src != nil && src != ""); });
  47.         all_other_sources = toset(all_other_sources);
  48.  
  49.         // create a list of known disk sources for this VM (loopback only!)
  50.         list<map> disks = (list<map>) item["disks"]:[];
  51.         list<string> sources = [];
  52.         foreach(map d, disks, {
  53.             if (d["type"]:"phys" == "loop-use")
  54.                 sources = add(sources, d["source"]:"");
  55.         });
  56.         sources = filter(string src, sources, { return (src != nil && src != ""); });
  57.         sources = toset(sources);
  58.  
  59.         // remove all disks not configured for use with another VM.
  60.         foreach(string src, sources, {
  61.             boolean remove_disk = true;
  62.             // do not remove files that have the .iso extension
  63.             if (regexpmatch(src, ".*\.iso$"))
  64.                 remove_disk = false;
  65.             // look for another configured vm using this disk
  66.             if (contains(all_other_sources, src))
  67.                 remove_disk = false;
  68.             // before deleting a disk source, make sure no other running VM is using it
  69.             VM::IsXendRunning();
  70.             string cmd = sformat("%1 list | grep %2", VM_XEN::xm, src);
  71.             integer retval = (integer) SCR::Execute(.target.bash, cmd);
  72.             if (retval == 0)
  73.                 remove_disk = false;
  74.             // remove the disk source
  75.             if (remove_disk) {
  76.                 y2milestone("removing virtual disk: %1", src);
  77.                 string cmd = sformat("rm -f %1", src);
  78.                 y2milestone("Executing: %1", cmd);
  79.                 map retmap = (map)SCR::Execute(.target.bash_output, cmd);
  80.                 y2milestone("retmap=%1", retmap);
  81.                 if (retmap["exit"]:-1 != 0) success = false;
  82.             }
  83.         });
  84.  
  85.         // remove any autoyast.img files from the vm image directory
  86.         string img_dir = VM::GetImgPrefix() + VM_Common::config_name;
  87.         SCR::Execute(.target.remove, img_dir + "/autoyast.img");
  88.  
  89.         // try to remove the vm image directory (if it is not empty, it should not be deleted).
  90.         string cmd = sformat("rmdir %1", img_dir);
  91.         y2milestone("Executing: %1", cmd);
  92.         map retmap = (map)SCR::Execute(.target.bash_output, cmd);
  93.         y2milestone("retmap=%1", retmap);
  94.  
  95.         // remove the config file link from the auto directory
  96.         SCR::Execute(.target.remove, sformat("%1%2", "/etc/xen/auto/", item["cfg_file"]:""));
  97.  
  98.         // remove the config file
  99.         boolean isremoved = (boolean) SCR::Execute(.target.remove, sformat("%1%2", VM::GetConfigFilePath(), item["cfg_file"]:""));
  100.         if ( ! isremoved ) success = false;
  101.         return success;
  102.     }
  103.     return false;
  104. }
  105.  
  106.  
  107. void UpdateSettingsList(map new_item) {
  108.     y2milestone("UpdateSettingsList(%1)", new_item);
  109.     if (new_item != nil && new_item["name"]:"" != nil && new_item["name"]:"" != "") {
  110.         boolean found = false;
  111.         integer pos = 0;
  112.         foreach(map item, SettingsList, { // look for an existing entry
  113.             if (item["name"]:"" == new_item["name"]:"") {
  114.                 found = true;
  115.                 // update the existing entry...
  116.                 if (new_item["name"]:"" != nil && new_item["name"]:"" != "")
  117.                     item["name"] = new_item["name"]:"";
  118.                 if (new_item["id"]:"" != nil && new_item["id"]:"" != "")
  119.                     item["id"] = new_item["id"]:"";
  120.                 if (new_item["memory"]:"" != nil && new_item["memory"]:"" != "")
  121.                     item["memory"] = new_item["memory"]:"";
  122.                 if (new_item["vcpus"]:"" != nil && new_item["vcpus"]:"" != "")
  123.                     item["vcpus"] = new_item["vcpus"]:"";
  124.                 if (new_item["state"]:"" != nil && new_item["state"]:"" != "")
  125.                     item["state"] = new_item["state"]:"";
  126.                 if (new_item["time"]:"" != nil && new_item["time"]:"" != "")
  127.                     item["time"] = new_item["time"]:"";
  128.                 SettingsList = remove(SettingsList, pos);
  129.                 SettingsList = add(SettingsList, item);
  130.             }
  131.             pos = pos + 1;
  132.         });
  133.         if (!found) {
  134.             // add a new entry for this item...
  135.             SettingsList = add(SettingsList, new_item);
  136.         }
  137.     }
  138. }
  139.  
  140.  
  141. void RemoveIDFromSettingsList(integer id) {
  142.     integer pos = 0;
  143.     foreach(map item, SettingsList, {
  144.         if (item["id"]:"" == tostring(id)) {
  145.             SettingsList = remove(SettingsList, pos);
  146.             break;
  147.         }
  148.         pos = pos + 1;
  149.     });
  150. }
  151.  
  152.  
  153. void Read_XM_List() {
  154.     VM::IsXendRunning();
  155.     string cmd = sformat("%1 list", VM_XEN::xm);
  156.     y2milestone("Executing: %1", cmd);
  157.     map retmap = (map) SCR::Execute(.target.bash_output, cmd);
  158.     string stdout = retmap["stdout"]:"";
  159.     if (stdout != nil && stdout != "") {
  160.         list<string> lines = splitstring(stdout, "\n");
  161.         lines = remove(lines, 0);// remove the xm list header...
  162.         string exp = sformat("[%1]*([^%1]*)[%1]*([^%1]*)[%1]*([^%1]*)[%1]*([^%1]*)[%1]*([^%1]*)[%1]*([^%1]*).*", String::CSpace()); // match items in xm list cmd.... \\1=name, \\2=id, \\3=mem, \\4=vcpus, \\5=state, \\6=time
  163.         y2milestone("lines=%1", lines);
  164.         foreach (string line, lines, {
  165.             if (line != nil && line != "") {
  166.                 map new_item = $[];
  167.                 new_item["name"] = regexpsub(line, exp, "\\1");
  168.                 new_item["id"] = regexpsub(line, exp, "\\2");
  169.                 new_item["memory"] = regexpsub(line, exp, "\\3");
  170.                 new_item["vcpus"] = regexpsub(line, exp, "\\4");
  171.                 new_item["state"] = regexpsub(line, exp, "\\5");
  172.                 new_item["time"] = regexpsub(line, exp, "\\6");
  173.                 UpdateSettingsList(new_item);
  174.             }
  175.         });
  176.     }
  177.     RemoveIDFromSettingsList(0);  // remove domain0 from SettingsList
  178. }
  179.  
  180.  
  181. boolean isSDL(integer table_item) {
  182.     boolean ret = false;
  183.     if (table_item != nil && table_item >= 0) {
  184.         map item = SettingsList[table_item]:$[];
  185.         if (tolower(item["sdl"]:"") == "1")
  186.             ret = true;
  187.     }
  188.     return ret;
  189. }
  190.  
  191.  
  192. boolean isFullVirtualization(integer table_item) {
  193.     boolean ret = false;
  194.     if (table_item != nil && table_item >= 0) {
  195.         map item = SettingsList[table_item]:$[];
  196.         if (item["builder"]:"" == "hvm")
  197.             ret = true;
  198.     }
  199.     return ret;
  200. }
  201.  
  202.  
  203. define void refresh_table() {
  204.  
  205.     SettingsList = VM::ReadAllConfigFileSettings();
  206.     y2milestone("SettingsList=%1", SettingsList);
  207.  
  208.     Read_XM_List();
  209.     y2milestone("SettingsList=%1", SettingsList);
  210.  
  211.     //sort SettingsList by name
  212.     SettingsList = sort(map x, map y, SettingsList, { return (x["name"]:"" < y["name"]:""); });
  213.  
  214.     list<term> table_contents = [];
  215.     integer pos = 0;
  216.     foreach(map item, SettingsList, {
  217.         // table item
  218.         string type = _("Paravirtualization");
  219.         if (isFullVirtualization(pos))
  220.             // table item
  221.             type = _("Full Virtualization");
  222.  
  223.         symbol vm_state = VM::GetVirtualMachineState(item["state"]:"");
  224.         // table item
  225.         string status = _("Unknown");
  226.         if (vm_state == `stopped)
  227.             // table item
  228.             status = _("Stopped");
  229.         else if (vm_state == `crashed)
  230.             // table item
  231.             status = _("Not Responding");
  232.         else if (vm_state == `running)
  233.             // table item
  234.             status = _("Running");
  235.  
  236.         // table item
  237.         string console = _("Text");
  238.         if (isFullVirtualization(pos)) { // full virt
  239.             if (isSDL(pos))
  240.                 // table item
  241.                 console = _("SDL");
  242.             else
  243.                 // table item
  244.                 console = _("VNC");
  245.         }
  246.  
  247.         table_contents = add(table_contents, `item(`id(pos), item["name"]:"", type, status, item["memory"]:"", console));
  248.         pos = pos + 1;
  249.     });
  250.     Popup::ClearFeedback(); // make sure feedback popup in not in the way of changing the table widget.
  251.     UI::ChangeWidget(`id(`vm_table), `Items, table_contents);
  252. }
  253.  
  254.  
  255. define symbol VirtualMachineOverview() {
  256.     // screen title for Virtual Machines Dialog
  257.     string caption = _("Manage Virtual Machines");
  258.     
  259.     term contents =
  260.         `VBox(
  261.             `MarginBox(1.5, 0.5,
  262.                 `VBox(
  263.                     `Table(`id(`vm_table), `opt(`notify, `immediate), `header(
  264.                             // table heading
  265.                             `Left(_("Name") + "          "),
  266.                             // table heading
  267.                             _("Virtualization Mode"),
  268.                             // table heading
  269.                             _("Status") + "       ",
  270.                             // table heading
  271.                             _("Memory (MB)"),
  272.                             // table heading
  273.                             _("Console")),
  274.                             []),
  275.                     `VBox(
  276.                         `HBox(
  277.                             `PushButton (`id (`add), Label::AddButton()),
  278.                             `PushButton (`id (`refresh), Label::RefreshButton()),
  279.                             `PushButton (`id (`delete), Label::DeleteButton())
  280.                         ),
  281.                         `HBox(
  282.                             // button label
  283.                             `PushButton (`id (`start), _("&Start")),
  284.                             // button label
  285.                             `PushButton (`id (`view), _("&View")),
  286.                             // button label
  287.                             `PushButton (`id (`shutdown), _("S&hutdown")),
  288.                             // button label
  289.                             `PushButton (`id (`terminate), _("&Terminate"))
  290.                         )
  291.                     )
  292.                 )
  293.             )
  294.         );
  295.  
  296.     // manage virtual machines - help text 1/4
  297.     string help_text = sformat(_("<p><b><big>%1</big></b></p>"), caption);
  298.     // manage virtual machines - help text 2/4
  299.     help_text = help_text + _("<p>A virtual machine (VM) is a defined instance of virtual hardware, such as CPU, memory, network card, and block devices, and the operating system that runs on it.</p>");
  300.     // manage virtual machines - help text 3/4
  301.     help_text = help_text + _("<p>The number of VMs you can create depends on the requirements for each VM and the available hardware resources.</p>");
  302.     // manage virtual machines - help text 4/4
  303.     help_text = help_text + _("<p>For the most current information on Novell VM server technology, see <tt>www.novell.com/documentation/technology/vm_server</tt>.</p>");
  304.  
  305.  
  306.     Wizard::SetContents (caption, contents, help_text, false, true);
  307.     // button label
  308.     Wizard::SetNextButton(`close, _("&Close"));
  309.     Wizard::HideBackButton();
  310.     Wizard::HideAbortButton();
  311.  
  312.     // popup feedback message
  313.     Popup::ShowFeedback(_("Reading Virtual Machine Settings"), _("Please Wait..."));
  314.     refresh_table();
  315.     Popup::ClearFeedback();
  316.  
  317.     integer selected_table_item = 0;
  318.     symbol ret = nil;
  319.     boolean isGraphicalDisplay = VM_Common::isGraphicalDisplay();
  320.  
  321.     while (true) {
  322.  
  323.         Popup::ClearFeedback(); // Always clear pop-up feedback ... just in case we forget to clear one in the loop below.
  324.  
  325.         // depending on the selected table item, change the buttons to the correct state (disabled/enabled)
  326.         UI::ChangeWidget(`id(`delete), `Enabled, false);
  327.         UI::ChangeWidget(`id(`start), `Enabled, false);
  328.         UI::ChangeWidget(`id(`view), `Enabled, false);
  329.         UI::ChangeWidget(`id(`shutdown), `Enabled, false);
  330.         UI::ChangeWidget(`id(`terminate), `Enabled, false);
  331.         if (selected_table_item != nil && selected_table_item >= 0) {
  332.             UI::ChangeWidget(`id(`vm_table), `CurrentItem, selected_table_item);
  333.             map current_item = SettingsList[selected_table_item]:$[];
  334.             symbol current_state = VM::GetVirtualMachineState(current_item["state"]:"");
  335.             if (current_state != `stopped) {
  336.                 UI::ChangeWidget(`id(`shutdown), `Enabled, true);
  337.                 UI::ChangeWidget(`id(`terminate), `Enabled, true);
  338. //                if ( isFullVirtualization(selected_table_item) && ! isSDL(selected_table_item) ) {
  339.                 if ( ( !isSDL(selected_table_item) ) && isGraphicalDisplay ) { // only enable `view button if graphical dispaly and selected vm is not configured to use SDL. (bugzilla #181011)
  340.                     UI::ChangeWidget(`id(`view), `Enabled, true);
  341.                 }
  342.             }
  343.             else if (size(SettingsList) > 0) { // vm state is not running (stopped) and at least on vm cfg file exists
  344.                 if ( isGraphicalDisplay || ( isFullVirtualization(selected_table_item) == false ) ) { // only enable `start button if graphical display or selected vm is paravirtualized. (bugzilla #181009)
  345.                     UI::ChangeWidget(`id(`start), `Enabled, true);
  346.                 }
  347.                 UI::ChangeWidget(`id(`delete), `Enabled, true);
  348.             }
  349.         }
  350.  
  351.         ret = (symbol) Wizard::UserInput ();
  352.         y2milestone("ret=%1", ret);
  353.  
  354.         selected_table_item = (integer) UI::QueryWidget (`id(`vm_table), `CurrentItem);
  355.     
  356.         if (ret == `abort || ret == `cancel) {
  357.             if (Popup::ReallyAbort(VM_Common::GetModified()))
  358.                 break;
  359.         }
  360.         else if (ret == `back) {
  361.             break;
  362.         }
  363.         else if (ret == `next || ret == `finish || ret == `close) {
  364.             break;
  365.         }
  366.         else if (ret == `vm_table) {
  367.             continue;
  368.         }
  369.         else if (ret == `refresh) {
  370.             // popup feedback message
  371.             Popup::ShowFeedback(_("Reading Virtual Machine Settings"), _("Please Wait..."));
  372.             refresh_table();
  373.             Popup::ClearFeedback();
  374.             continue;
  375.         }
  376.         else if (ret == `add) {
  377.             break;
  378.         }
  379.         else if (ret == `delete) {
  380.             if (selected_table_item != nil && selected_table_item >= 0) {
  381.                 // popup yes/no dialog
  382.                 if (Popup::AnyQuestion( _("Delete Virtual Machine"), _("You are about to completely remove the selected VM.
  383.  
  384. Are you sure you want to delete the selected VM?
  385.  
  386. "), Label::YesButton(), Label::NoButton(), `focus_no)) {
  387.                 // popup yes/no dialog
  388.                 if (Popup::AnyQuestion( _("Delete Virtual Machine"), _("The selected VM disk image and configuration file
  389. will be completely destroyed.
  390.  
  391. This action cannot be undone.
  392.  
  393. Do you wish to continue?
  394.  
  395. "), Label::YesButton(), Label::NoButton(), `focus_no)) {
  396.  
  397.                         map item = SettingsList[selected_table_item]:$[];
  398.                         if (item["name"]:nil != nil && item["name"]:"" != "") {
  399.                             // popup feedback message
  400.                             Popup::ShowFeedback(_("Deleting Virtual Machine"), _("Please Wait..."));
  401.                             boolean success = DeleteVirtualMachine(selected_table_item);
  402.                             refresh_table();
  403.                             Popup::ClearFeedback();
  404.                             // popup error message
  405.     //                        if (!success) Report::Error(_("Cannot delete the virtual machine."));
  406.                         }
  407.                         selected_table_item = 0;
  408.                     }
  409.                 }
  410.             }
  411.             else {
  412.                 y2error("delete_button: invalid table item selected");
  413.                 continue;
  414.             }
  415.         }
  416.         else if (ret == `start) {
  417.             if (selected_table_item != nil && selected_table_item >= 0) {
  418.                 map item = SettingsList[selected_table_item]:$[];
  419.                 // make sure there is enough free memory to start the VM.
  420.                 integer maxmem = (VM_Common::GetVirtualizationType() == "para") ? VM::GetMaxParaMemory() : VM::GetMaxHVMMemory();
  421.                 if (tointeger(item["memory"]:"0") > maxmem) {
  422.                     // popup error message
  423.                     // %1 = integer size in MB
  424.                     // %2 = integer size in MB
  425.                     Report::Error(sformat(_("There is not enough free memory to
  426. start the selected virtual machine.
  427.  
  428. Required memory size %1, memory available %2
  429.  
  430. "), item["memory"]:"", maxmem));
  431.                     continue;
  432.                  }
  433.                 if (item["cfg_file"]:nil != nil && item["cfg_file"]:"" != "") {
  434.                     // popup feedback message
  435.                     Popup::ShowFeedback(_("Starting Virtual Machine"), _("Please Wait..."));
  436.                     boolean success = VM::StartVirtualMachine(item["cfg_file"]:"", true);
  437.                     refresh_table();
  438.                     Popup::ClearFeedback();
  439.                 }
  440.             }
  441.             else {
  442.                 y2error("start_button: invalid table item selected");
  443.                 continue;
  444.             }
  445.         }
  446.         else if (ret == `view) {
  447.             if (selected_table_item != nil && selected_table_item >= 0) {
  448.                 map item = SettingsList[selected_table_item]:$[];
  449.                 if (item["name"]:nil != nil && item["name"]:"" != "") {
  450.                     if (isFullVirtualization(selected_table_item) && isSDL(selected_table_item)) continue; // nothing to display when using SDL 
  451.                     // popup feedback message
  452.                     Popup::ShowFeedback(_("Opening Virtual Machine Console"), _("Please Wait..."));
  453.                     
  454.                     boolean success = VM::ViewVirtualMachineConsole(
  455.                         (isFullVirtualization(selected_table_item)) ? item["id"]:"" : item["name"]:"", // use VM id # to display VNC in full virtualizaion
  456.                         isFullVirtualization(selected_table_item), // show XTerm in para virt.
  457.                         VM_Common::IsNetWareKernel(item["kernel"]:""), // check for NetWare kernel
  458.                         true, // always background this process.
  459.                         true // show any error messages
  460.                     );
  461.                     
  462.                     refresh_table();
  463.                     Popup::ClearFeedback();
  464.                     // popup error message
  465. //                    if (!success) Report::Error(_("Cannot open the virtual machine console."));
  466.                 }
  467.             }
  468.             else {
  469.                 y2error("view_button: invalid table item selected");
  470.                 continue;
  471.             }
  472.         }
  473.         else if (ret == `shutdown) {
  474.             if (selected_table_item != nil && selected_table_item >= 0) {
  475.                 // popup yes/no dialog
  476.                 if (Popup::AnyQuestion( _("Shutdown Virtual Machine"), _("Shutting down the VM stops its operating system
  477. and any running programs. 
  478.  
  479. Are you sure you want to shutdown the selected VM?
  480.  
  481. "), Label::YesButton(), Label::NoButton(), `focus_no)) {
  482.                     map item = SettingsList[selected_table_item]:$[];
  483.                     if (item["name"]:nil != nil && item["name"]:"" != "") {
  484.                         // popup feedback message
  485.                         Popup::ShowFeedback(_("Shutting Down Virtual Machine"), _("Please Wait..."));
  486.                         boolean success = VM::ShutdownVirtualMachine(item["name"]:"", true);
  487.                         refresh_table();
  488.                         Popup::ClearFeedback();
  489.                         // popup error message
  490.     //                    if (!success) Report::Error(_("Cannot shutdown the virtual machine."));
  491.                     }
  492.                 }
  493.             }
  494.             else {
  495.                 y2error("shutdown_button: invalid table item selected");
  496.                 continue;
  497.             }
  498.         }
  499.         else if (ret == `terminate) {
  500.             if (selected_table_item != nil && selected_table_item >= 0) {
  501.                 // popup yes/no dialog
  502.                 if (Popup::AnyQuestion( _("Terminate Virtual Machine"), _("Terminating a VM might result in loss of application
  503. and system data. 
  504.  
  505. The VM will not have a chance to save its state or
  506. any data before it is terminated.
  507.  
  508. Are you sure you want to terminate the selected VM?
  509.  
  510. "), Label::YesButton(), Label::NoButton(), `focus_no)) {
  511.                     map item = SettingsList[selected_table_item]:$[];
  512.                     if (item["name"]:nil != nil && item["name"]:"" != "") {
  513.                         // popup feedback message
  514.                         Popup::ShowFeedback(_("Terminating Virtual Machine"), _("Please Wait..."));
  515.                         boolean success = VM::TerminateVirtualMachine(item["name"]:"", true);
  516.                         refresh_table();
  517.                         Popup::ClearFeedback();
  518.                         // popup error message
  519. //                        if (!success) Report::Error(_("Cannot terminate the virtual machine."));
  520.                     }
  521.                 }
  522.             }
  523.             else {
  524.                 y2error("terminate_button: invalid table item selected");
  525.                 continue;
  526.             }
  527.         }
  528.         else {
  529.             y2error("unexpected retcode: %1", ret);
  530.             continue;
  531.         }
  532.     }
  533.  
  534.     Popup::ClearFeedback(); // clear pop-up again...just in case ;-)
  535.  
  536.     return ret;
  537. }
  538.  
  539.  
  540.  
  541. Wizard::OpenNextBackDialog();
  542.  
  543. map aliases = $[
  544.         "VirtualMachineOverview"    : ``(VirtualMachineOverview()),
  545.     ];
  546.  
  547. map sequence = $[
  548.         "ws_start"    : "VirtualMachineOverview",
  549.         "VirtualMachineOverview"    :
  550.         $[
  551.             `add    : `add,
  552.             `close    : `close,
  553.             `next    : `next,
  554.             `finish    : `finish,
  555.             `abort    : `abort,
  556.         ],
  557.     ];
  558.  
  559.  
  560. symbol ret = Sequencer::Run(aliases, sequence);
  561.  
  562. Wizard::CloseDialog();
  563.  
  564. return ret;
  565.  
  566.  
  567.  
  568. /* EOF */
  569. }
  570.