1 <?php
3 class InstallRecipe extends plugin
4 {
6 public $rpcErrorMessage = "";
7 public $deviceUUID = "";
9 // Template options
10 public $installTemplate = "";
11 public $installationTemplateNames = array();
12 public $templateToRelease = array();
13 public $installationTemplates = array();
15 // The selected installation release
16 public $installRelease = "";
18 // Selected config management variant
19 public $installConfigManagement = '';
20 public $installConfigManagementList = array();
22 // Bootstrap method
23 public $installBaseMethod = '';
24 public $installBaseMethodList = array();
26 // Timezone settings
27 public $installTimezone = "";
28 public $installTimeUTC = "";
30 // Kernel packages
31 public $installKernelPackage = "";
32 public $installKernelPackageList = array();
34 // System Locale
35 public $installSystemLocale = "";
36 public $installSystemLocaleList = array();
38 // Selected NTP Server
39 public $installNTPServer = array();
40 public $installNTPServerList = NULL;
42 // Root password
43 public $installRootEnabled = "";
44 public $installRootPasswordHash = "";
45 public $setPasswordHash = FALSE;
47 // Partition table variables
48 public $installPartitionTable = "";
49 private $partitionEdit = NULL;
51 // Keyboard-layout attribute
52 public $installKeyboardlayout = "";
53 public $installKeyboardlayoutList = array();
55 // Mirror related attribute
56 public $installMirror = "";
57 public $installMirrorList = array();
59 public $objectclasses = array();
60 public $attributes = array("installRelease", "installTemplate","installKeyboardlayout","installSystemLocale",
61 "installTimezone","installTimeUTC","installNTPServer","installMirror",
62 "installRootEnabled","installRootPasswordHash","installKernelPackage",
63 "installPartitionTable","installConfigManagement","installBaseMethod");
66 /* Some plugin management related attributes
67 * See class plugin for details.
68 */
69 public $view_logged = FALSE;
70 public $ignore_account = FALSE;
71 public $parent = NULL;
73 /*! \brief Initialize the plugin and its widgets.
74 * Finally call init() to load values from the backend.
75 */
76 function __construct(&$config, $dn, $parent)
77 {
78 plugin::plugin($config, $dn);
80 $this->is_account = $this->initially_was_account = FALSE;
82 $this->_device = &$parent;
83 $this->deviceUUID = $parent->deviceUUID;
85 // Set default password hash
86 $this->hash = $this->config->get_cfg_value("core","passwordDefaultHash");
88 // Get list of password hashes
89 $tmp = passwordMethod::get_available_methods();
90 $this->hashes = array();
91 foreach($tmp['name'] as $name){
92 $this->hashes[$name] = $name;
93 }
95 // Initialize plugin
96 $this->init_static_info();
97 $this->init($initially = TRUE);
98 }
100 function init($initially = FALSE)
101 {
102 // Init the object values.
103 $this->init_client_info();
105 // Prepare NTP servers list
106 $this->installNTPServerList= new sortableListing($this->installNTPServer);
107 $this->installNTPServerList->setDeleteable(true);
108 $this->installNTPServerList->setEditable(false);
109 $this->installNTPServerList->setColspecs(array('*'));
110 $this->installNTPServerList->setWidth("100%");
111 $this->installNTPServerList->setHeight("70px");
113 // Preset values for new devices
114 if(!$this->is_account){
115 $this->installTemplate = key($this->installationTemplates);
116 $this->installRelease = key($this->templateToRelease[$this->installTemplate]);
117 }else{
119 if(!isset($this->installationTemplates[$this->installTemplate])){
121 // The selected template is no longer available, select the next selectable template.
122 $new = key($this->installationTemplates);
124 if($initially){
125 $message = sprintf(_("The selected template '%s' is no longer available. Selecting '%s' as replacement!"),
126 $this->installTemplate, $new);
127 msg_dialog::display(_("Warning"), $message, WARNING_DIALOG);
128 }
129 $this->installTemplate = $new;
130 $installRelease = key($this->templateToRelease[$this->installTemplate]);
132 }elseif(!isset($this->installationTemplates[$this->installTemplate]['method'])){
134 // Not necessary to warn the user here, the input fields will be empty and thus
135 // avoid saving.
137 }elseif(!in_array_strict($this->installRelease, $this->templateToRelease[$this->installTemplate])){
139 // The selected release is no longer available for the current template
140 // Warn the user and select the next selectable release.
141 $new = key($this->templateToRelease[$this->installTemplate]);
142 if($initially){
143 $message = sprintf(_("The selected release '%s' is no longer available. Selecting '%s' as replacement!"),
144 $this->installRelease, $new);
145 }
146 $this->installRelease = $new;
147 msg_dialog::display(_("Warning"), $message, WARNING_DIALOG);
148 }
149 }
151 // Update selectable items like kernels and mirrors depending on the current setup (relase)
152 $this->installBaseMethod = $this->installationTemplates[$this->installTemplate]['method'];
153 $this->reloadInstallationKernelPackages($initially);
154 $this->reloadInstallationMirrors($initially);
156 // Preset the config management method.
157 if(!isset($this->installConfigManagementList[$this->installBaseMethod][$this->installConfigManagement])){
158 if(isset($this->installConfigManagementList[$this->installBaseMethod]) &&
159 count($this->installConfigManagementList[$this->installBaseMethod])){
160 $this->installConfigManagement = key($this->installConfigManagementList[$this->installBaseMethod]);
161 }
162 }
163 }
166 /*! \brief Loads client-information values from the GOsa-backend using jsonRPC
167 */
168 function init_client_info()
169 {
170 if(empty($this->deviceUUID)){
171 return;
172 }
174 // Load base install Method
175 $rpc = $this->config->getRpcHandle();
176 $bMethod = $rpc->systemGetBaseInstallMethod($this->deviceUUID);
177 if(!$rpc->success()){
178 $this->rpcErrorMessage = $rpc->get_error();
179 msg_dialog::display(_("Error"), msgPool::rpcError($this->rpcErrorMessage), ERROR_DIALOG);
180 return(NULL);
181 }
183 // Do nothing while no install method is defined.
184 if(!$bMethod){
185 return;
186 }
188 // Set the bootstrap method.
189 $this->installBaseMethod = $bMethod;
191 $res = $rpc->systemGetBaseInstallParameters($this->deviceUUID);
193 // Failed to load backend values, keep reported error and return with FALSE;
194 $this->rpcError = !$rpc->success();
195 if(!$rpc->success()){
196 $this->rpcErrorMessage = $rpc->get_error();
197 msg_dialog::display(_("Error"), msgPool::rpcError($this->rpcErrorMessage), ERROR_DIALOG);
198 return(NULL);
199 }
201 // Apply received options
202 $this->is_account = $res == True;
203 if($this->is_account){
205 $map = array(
206 'ntp-servers' => "installNTPServer",
207 'template' => "installTemplate",
208 'keyboard-layout' => "installKeyboardlayout");
210 // We receive an array for these attributes, but require a string instead
211 // we just use the first element of the array.
212 $mapArray = array(
213 'release' => "installRelease",
214 'timezone' => "installTimezone",
215 'utc' => "installTimeUTC",
216 'kernel' => "installKernelPackage",
217 'system-locale' => "installSystemLocale",
218 'root-user' => "installRootEnabled",
219 'root-hash' => "installRootPasswordHash",
220 'disk-setup' => "installPartitionTable");
222 foreach($res as $name => $data){
223 if(isset($map[$name])){
224 $target = $map[$name];
225 $value = $res[$name];
226 }elseif(isset($mapArray[$name])){
227 $target = $mapArray[$name];
228 $value = $res[$name][0];
229 }
230 $this->$target = $value;
231 }
233 // Convert Bool values to Bool.
234 $boolean = array("installRootEnabled", "installTimeUTC");
235 foreach($boolean as $attr){
236 $this->$attr = ($this->$attr != FALSE && !preg_match("/false/i", $this->$attr));
237 }
238 }
240 return;
241 }
244 function init_static_info()
245 {
246 // Load static values.
247 foreach(array(
248 "timeozones" => "getTimezones",
249 "distributions" => "loadDistributions",
250 "keyboardLayouts" => "loadInstallationKeyboardLayouts",
251 "installationTemplates" => "loadInstallationTemplates",
252 "installationMethods" => "loadInstallationMethods",
253 "locales" => "getSystemLocales" ) as $target => $func){
255 if(!session::is_set("InstallRecipe::{$func}")){
256 $tmp = $this->$func();
257 if($tmp == NULL){
258 $this->initFailed = TRUE;
259 msg_dialog::display(_("Error"), msgPool::rpcError($this->rpcErrorMessage), ERROR_DIALOG);
260 return;
261 }else{
262 session::set("InstallRecipe::{$func}", $tmp);
263 }
264 }
265 $$target = session::get("InstallRecipe::{$func}");
266 }
268 // Assign fetchted values
269 $this->timezones = $timeozones;
270 $this->installBaseMethodList = $installationMethods['installBaseMethodList'];
271 $this->installConfigManagementList = $installationMethods['installConfigManagementList'];
272 $this->installSystemLocaleList = $locales;
273 $this->installKeyboardlayoutList = $keyboardLayouts;
274 $this->distributions = $distributions;
276 # Create template to relase map
277 $this->templateToRelease = array();
279 // Walk through installation templates and use the 'repositories' attribute
280 // to supported releases.
281 foreach($installationTemplates as $name => $tdata){
283 $mName = isset($this->installBaseMethodList[$tdata['method']]) ?
284 $this->installBaseMethodList[$tdata['method']]: $tdata['method'];
286 $this->installationTemplateNames[$name] = $tdata['description']." (".$mName.")";
287 $this->installationTemplates[$name] = $tdata;
288 $this->templateToRelease[$name] = array();
290 // Check if the 'repository'-category of the template can be found in any
291 // received release list.
292 if(isset($installationMethods[$tdata['method']]['repositories'])){
293 $repos = $installationMethods[$tdata['method']]['repositories'];
294 foreach($distributions as $dist){
295 if(in_array_strict($dist['type']['name'],$repos) && isset($dist['releases'])){
296 foreach($dist['releases'] as $release){
297 $rname = $dist['name'].'/'.$release['name'];
298 $this->templateToRelease[$name][$rname] = $rname;
299 }
300 }
301 }
302 }
303 }
304 }
307 function check()
308 {
309 // Check if the parents deviceUUID has changed
310 if($this->deviceUUID != $this->_device->deviceUUID){
311 $this->deviceUUID = $this->_device->deviceUUID;
312 print "The device uuid has changed.";
313 }
314 }
317 function execute()
318 {
319 // Log account access
320 if($this->is_account && !$this->view_logged){
321 $this->view_logged = TRUE;
322 new log("view","Device/".get_class($this),$this->dn);
323 }
325 /***************
326 Handle account state
327 ***************/
329 // Allow to add or remove the distribution list extension
330 if(isset($_POST['modify_state'])){
331 if($this->is_account && $this->acl_is_removeable()){
332 $this->is_account= FALSE;
333 }elseif(!$this->is_account && $this->acl_is_createable()){
334 $this->is_account= TRUE;
335 }
336 }
338 // Show account status-changer
339 $display = "";
340 if ($this->parent !== NULL){
341 if ($this->is_account){
342 $display= $this->show_disable_header(_("Remove base installation"),
343 msgPool::featuresEnabled(_("Base installation")));
344 } else {
345 $display= $this->show_enable_header(_("Add base installation"),
346 msgPool::featuresDisabled(_("Base installation")));
347 return ($display);
348 }
349 }
351 /***************
352 Root password hash dialog
353 ***************/
355 $this->dialog = FALSE;
356 if($this->setPasswordHash){
357 $this->dialog = TRUE;
358 $smarty = get_smarty();
359 $smarty->assign('hashes', set_post($this->hashes));
360 $smarty->assign('hash', set_post($this->hash));
361 return($smarty->fetch(get_template_path('goto/Device/SetPassword.tpl', TRUE)));
362 }
364 /***************
365 Partition dialog
366 ***************/
368 if(isset($_POST['partition_finish']) && $this->partitionEdit){
369 $this->installPartitionTable = $this->partitionEdit->save();
370 $this->partitionEdit = NULL;
371 }
372 if(isset($_POST['partition_cancel']) && $this->partitionEdit){
373 $this->partitionEdit = NULL;
374 }
375 if(isset($_POST['edit_installPartitionTable'])){
376 $this->partitionEdit = new DevicePartition($this->config, $this->installPartitionTable);
377 }
378 if($this->partitionEdit){
379 $this->partitionEdit->save_object();
380 $this->dialog = TRUE;
381 return($this->partitionEdit->execute());
382 }
386 /***************
387 Generate HTML content
388 ***************/
390 $this->installNTPServerList->setAcl($this->getacl('installNTPServer'));
391 $this->installNTPServerList->update();
393 plugin::execute();
394 $smarty = get_smarty();
396 // Assign ACLs
397 foreach($this->attributes as $attr){
398 $smarty->assign("{$attr}ACL", $this->getacl($attr));
399 }
401 $smarty->assign('installTemplateList', $this->installationTemplateNames);
402 $smarty->assign('installReleaseList', $this->templateToRelease[$this->installTemplate]);
403 $smarty->assign('timezones', $this->timezones);
404 $smarty->assign('installKeyboardlayoutList', $this->installKeyboardlayoutList);
405 $smarty->assign('installKernelPackageList', $this->installKernelPackageList);
406 $smarty->assign('installMirrorList', $this->installMirrorList);
407 $smarty->assign('installSystemLocaleList', $this->installSystemLocaleList);
408 $smarty->assign('installNTPServerList', $this->installNTPServerList->render());
410 $smarty->assign('installBaseMethod', $this->installBaseMethod);
411 $smarty->assign('installBaseMethodList', $this->installBaseMethodList);
412 $smarty->assign('installConfigManagement', $this->installConfigManagement);
414 // Assign list of available config-management types.
415 if(isset($this->installConfigManagementList[$this->installBaseMethod])){
416 $smarty->assign('installConfigManagementList', $this->installConfigManagementList[$this->installBaseMethod]);
417 }else{
418 $smarty->assign('installConfigManagementList', array());
419 }
421 foreach($this->attributes as $attr){
422 $smarty->assign($attr, $this->$attr);
423 }
424 $this->dialog = false;
425 return($display.$smarty->fetch(get_template_path('goto/Device/InstallRecipe.tpl', TRUE)));
426 }
429 function save_object()
430 {
431 if(isset($_POST['InstallRecipePosted'])){
433 $currentInstallMethod = $this->installConfigManagement;
435 $oldRelease = $this->installRelease;
436 plugin::save_object();
437 $this->installRootEnabled = isset($_POST['installRootEnabled']);
438 $this->installTimeUTC = isset($_POST['installTimeUTC']);
441 if(isset($_POST['setPasswordHash'])){
442 $this->setPasswordHash = true;
443 }
445 $this->installNTPServerList->save_object();
446 $this->installNTPServer = $this->installNTPServerList->getMaintainedData();
447 if(isset($_POST['installNTPServer_Input']) && isset($_POST['installNTPServer_Add'])){
448 $add = get_post('installNTPServer_Input');
449 if(!in_array_strict($add, $this->installNTPServer) && !empty($add)){
450 $this->installNTPServer[] = $add;
451 }
452 }
453 $this->installNTPServerList->setListData($this->installNTPServer);
454 $this->installBaseMethod = $this->installationTemplates[$this->installTemplate]['method'];
456 // The selected release is no longer available for the current template
457 if(!in_array_strict($this->installRelease, $this->templateToRelease[$this->installTemplate])){
458 $this->installRelease = key($this->templateToRelease[$this->installTemplate]);
459 }
461 // Reload list of kernel packages and mirrors
462 if($oldRelease != $this->installRelease){
463 $this->reloadInstallationKernelPackages();
464 $this->reloadInstallationMirrors();
465 }
466 }
468 if(isset($_POST['cancelPassword'])) $this->setPasswordHash =false;
469 if(isset($_POST['setPassword'])) {
470 $this->setPasswordHash =false;
471 $hash = get_post('passwordHash');
473 // Not sure, why this is here, but maybe some encryption methods require it.
474 mt_srand((double) microtime()*1000000);
476 // Generate the requested hash
477 $methods = new passwordMethod($this->config, $this->dn);
478 $available = $methods->get_available_methods();
479 $test = new $available[$hash]($this->config,$this->dn);
480 $this->installRootPasswordHash = @$test->generate_hash(get_post('rootPassword'));
481 }
482 }
485 function save()
486 {
487 if(!$this->installRootEnabled) $this->installRootPasswordHash = "";
489 $map = array(
490 "installTemplate"=>'template',
491 "installRelease"=>'release',
492 "installTimezone"=>'timezone',
493 "installTimeUTC"=>'utc',
494 "installKernelPackage"=>'kernel',
495 "installSystemLocale"=>'system-locale',
496 "installRootEnabled"=>'root-user',
497 "installRootPasswordHash"=>'root-hash',
498 "installPartitionTable"=>'disk-setup',
499 "installKeyboardlayout"=>'keyboard-layout',
500 "installBaseMethod"=>'method',
501 );
503 // Collect values to be saved.
504 $data = array();
505 foreach($map as $source => $dest){
506 if(!empty($this->$source)){
507 $data[$dest] = $this->$source;
508 }
509 }
511 // Enforce an array for ntp-servers
512 if(!empty($this->installNTPServer)){
513 $data['ntp-servers'] = array_values($this->installNTPServer);
514 }
516 // Save Boolean like a String
517 $boolean = array("root-user" => "installRootEnabled", "utc" => "installTimeUTC");
518 foreach($boolean as $attr => $source){
519 $data[$attr] = ($this->$source) ? $data[$attr] = "TRUE" : $data[$attr] = "FALSE";
520 }
522 // Set the base installation parameters
523 $rpc = $this->config->getRpcHandle();
524 $res = $rpc->systemSetBaseInstallParameters($this->deviceUUID, $data);
526 // Handle errors
527 $this->rpcError = !$rpc->success();
528 if(!$rpc->success()){
529 $this->rpcErrorMessage = $rpc->get_error();
530 msg_dialog::display(_("Error"), msgPool::rpcError($this->rpcErrorMessage), ERROR_DIALOG);
531 return(NULL);
532 }
533 }
536 function remove_from_parent()
537 {
538 // Try to remove the base install extension.
539 $rpc = $this->config->getRpcHandle();
540 $bMethod = $rpc->removeBaseInstallParameters($this->deviceUUID);
541 if(!$rpc->success()){
542 $this->rpcErrorMessage = $rpc->get_error();
543 msg_dialog::display(_("Error"), msgPool::rpcError($this->rpcErrorMessage), ERROR_DIALOG);
544 return(NULL);
545 }
546 }
549 /*! \brief Refreshes the list of selectable kernel packages.
550 */
551 function reloadInstallationKernelPackages($initially = FALSE)
552 {
553 // Do nothing if no release is given.
554 if(empty($this->installRelease)){
555 $this->installKernelPackageList = array();
556 return;
557 }
559 $release = preg_replace("/^[^\/]*\//","", $this->installRelease);
560 $rpc = $this->config->getRpcHandle();
561 $res = $rpc->getKernelPackages($release);
563 // Failed to load backend values, keep reported error and return with FALSE;
564 $this->rpcError = !$rpc->success();
565 if(!$rpc->success()){
566 $this->rpcErrorMessage = $rpc->get_error();
567 msg_dialog::display(_("Error"), msgPool::rpcError($this->rpcErrorMessage), ERROR_DIALOG);
568 }else{
569 $this->installKernelPackageList = array();
570 foreach($res as $data){
571 $name = $data['name'];
572 $this->installKernelPackageList[$name] = $name;
573 }
575 if(!isset($this->installKernelPackageList[$this->installKernelPackage])){
576 $new = key($this->installKernelPackageList);
577 if($initially && $this->is_account){
578 msg_dialog::display(_("Warning"), sprintf(_("The selected kernel package '%s' is no longer available! The kernel package '%s' was selected as replacement!"), $this->installKernelPackage, $new), WARNING_DIALOG);
579 }
580 $this->installKernelPackage = $new;
581 }
582 }
583 }
586 /*! \brief Returns a list of distributions on success.
587 * In case of an error, it returns NULL.
588 */
589 function loadDistributions()
590 {
591 $rpc = $this->config->getRpcHandle();
592 $res = $rpc->getDistributions();
594 // Failed to load backend values, keep reported error and return with FALSE;
595 $this->rpcError = !$rpc->success();
596 if(!$rpc->success()){
597 $this->rpcErrorMessage = $rpc->get_error();
598 return(NULL);
599 }
600 return($res);
601 }
604 /*! \brief Returns a list of timezones on success.
605 * In case of an error, it returns NULL.
606 */
607 function getTimezones()
608 {
609 $rpc = $this->config->getRpcHandle();
610 $res = $rpc->getTimezones();
612 // Failed to load backend values, keep reported error and return with FALSE;
613 $this->rpcError = !$rpc->success();
614 if(!$rpc->success()){
615 $this->rpcErrorMessage = $rpc->get_error();
616 return(NULL);
617 }
618 return($res);
619 }
622 /*! \brief Returns a list of installation mirrors on success.
623 * In case of an error, it returns NULL.
624 */
625 function reloadInstallationMirrors($initially = FALSE)
626 {
627 $res = array(
628 'dc=intranet,dc=gonicus,dc=de' => 'Intranet',
629 'ou=systems,dc=intranet,dc=gonicus,dc=de' => 'System');
630 $this->installMirrorList =$res;
631 }
634 /*! \brief Returns a list of installation templates on success.
635 * In case of an error, it returns NULL.
636 */
637 function loadInstallationTemplates()
638 {
639 $rpc = $this->config->getRpcHandle();
640 $res = $rpc->installListTemplates();
642 // Failed to load backend values, keep reported error and return with FALSE;
643 $this->rpcError = !$rpc->success();
644 if(!$rpc->success()){
645 $this->rpcErrorMessage = $rpc->get_error();
646 return(NULL);
647 }
648 return($res);
649 }
652 /*! \brief Returns a list of keyboard layouts on success.
653 * In case of an error, it returns NULL.
654 */
655 function loadInstallationKeyboardLayouts()
656 {
657 $rpc = $this->config->getRpcHandle();
658 $res = $rpc->getKeyboardModels();
660 // Failed to load backend values, keep reported error and return with FALSE;
661 $this->rpcError = !$rpc->success();
662 if(!$rpc->success()){
663 $this->rpcErrorMessage = $rpc->get_error();
664 return(NULL);
665 }
666 $lay = array();
667 foreach($res as $key => $data){
668 $lay[$key] = "{$data[0]} - {$data[2]}";
669 }
670 asort($lay);
671 return($lay);
672 }
675 /*! \brief Returns a list of system locales on success.
676 * In case of an error, it returns NULL.
677 */
678 function getSystemLocales()
679 {
680 $rpc = $this->config->getRpcHandle();
681 $res = $rpc->getSystemLocales();
683 // Failed to load backend values, keep reported error and return with FALSE;
684 $this->rpcError = !$rpc->success();
685 if(!$rpc->success()){
686 $this->rpcErrorMessage = $rpc->get_error();
687 msg_dialog::display(_("Error"), msgPool::rpcError($this->rpcErrorMessage), ERROR_DIALOG);
688 return(NULL);
689 }
691 return($res);
692 }
695 /*! \brief Try to load install and bootstrap methods from the GOsa-backend
696 * This is done via jsonRPC, if calling the backend method fails,
697 * this method returns FALSE, in case of success it return TRUE;
698 */
699 function loadInstallationMethods()
700 {
701 $rpc = $this->config->getRpcHandle();
702 $res = $rpc->getSupportedBaseInstallMethods();
704 // Failed to load backend values, keep reported error and return with FALSE;
705 $this->rpcError = !$rpc->success();
706 if(!$rpc->success()){
707 $this->rpcErrorMessage = $rpc->get_error();
708 return(NULL);
709 }
711 // Walk through result and set bootstrap methods.
712 foreach($res as $name => $method){
713 $res['installBaseMethodList'][$name] = $method['name'];
714 foreach($method['methods'] as $m){
715 $res['installConfigManagementList'][$name][$m] = $m;
716 }
717 }
718 return($res);
719 }
722 static function plInfo()
723 {
724 return (array(
725 "plShortName" => _("Device"),
726 "plDescription" => _("Registered device"),
727 "plSelfModify" => FALSE,
728 "plDepends" => array(),
729 "plPriority" => 1,
730 "plSection" => array("administration"),
731 "plCategory" => array("Device"),
732 "plProvidedAcls" => array(
734 "installRelease" => _("Release"),
735 "installTemplate" => _("Template"),
736 "installKeyboardlayout" => _("Keyboard layout"),
737 "installSystemLocale" => _("Locale"),
738 "installTimezone" => _("Timezone"),
739 "installTimeUTC" => _("Utc"),
740 "installNTPServer" => _("Ntp server"),
741 "installMirror" => _("Mirror"),
742 "installRootEnabled" => _("Root login enabled"),
743 "installRootPasswordHash" => _("Root password hash"),
744 "installKernelPackage" => _("Kernel package"),
745 "installConfigManagement" => _("Config mangement"),
746 "installBaseMethod" => _("Bootstrap method"),
747 "installPartitionTable" => _("Partition table")
748 )
749 )
750 );
751 }
752 }
753 ?>