#!/usr/bin/perl -w # Drakwizard PXE # release 0.3 # Copyright (C) 2004 Mandrakesoft # # Author: Antoine Ginies # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. package MDK::Wizard::Pxe; use lib qw(/usr/lib/libDrakX); use strict; use common; use services; use MDK::Wizard::Wizcommon; my $wiz = new MDK::Wizard::Wizcommon; #my $INSTALLDIR = "/var/install/pxe"; #my $IA64PATH = '/IA64PC'; my $TMPDIR = '/tmp'; my $HOSTNAME = chomp_(`hostname`); my $TFTPDIR = "/var/lib/tftpboot"; my $CLIENTPATH = '/X86PC/linux'; my $X86 = $TFTPDIR . $CLIENTPATH; my $IMGPATH = $X86 . '/images'; my $PXEHELP = $X86 . '/help.txt'; my $PXEMESSAGE = $X86 . '/messages'; my $PXEDEFAULT = $X86 . '/pxelinux.cfg/default'; my $pxeconf = '/etc/pxe.conf'; my $SYSLINUXPATH = '/usr/lib/syslinux/'; my $MEMDISK = $SYSLINUXPATH . '/memdisk'; my $XINETDDIR = "/etc/xinetd.d"; my $IPSERVER = $wiz->{net}->itf_get("IPADDR"); if (!$IPSERVER) { my $interface = 'eth0'; ($IPSERVER) = `/sbin/ip addr show dev $interface` =~ /^\s*inet\s+(\d+\.\d+\.\d+\.\d+)/m; } #my $DOMAINNAME = $wiz->{net}->network_get("DOMAINNAME"); my $DOMAINNAME = chomp_(`dnsdomainname`); my $o = { name => N("PXE Wizard"), var => { IMG => '/var/install/92/images/network.img', PXENAME => '92', DESCR => 'install 9.2', IMGTOREMOVE => '', IMGTOMODIFY => '', IP => '127.0.0.1', DIR => '/var/install/', ALLRDZ => '/mnt/cdrom/isolinux/alt0/all.rdz', VMLINUZ => '/mnt/cdrom/isolinux/alt0/vmlinuz', i => '', }, needed_rpm => [ 'pxe', 'tftp-server', 'xinetd' ], init => sub { if (member($DOMAINNAME, qw(localdomain (none)))) { return 0, N("You need to readjust your domainname. For a DNS server you need a correct domainname, not equal to localdomain or none. Launch drakconnect to adjust it.") } 1 }, }; my %level = ( 1 => N("Set PXE server"), 2 => N("Add boot image in PXE"), 3 => N("Remove boot image in PXE"), 4 => N("Modify boot image in PXE"), 5 => N("Add all.rdz image in PXE"), ); # to get list menu entry my @list_menu; foreach (cat_($PXEDEFAULT)) { my ($ent) = /# TAG: (\w+)_BEGIN/; ! $ent or push @list_menu,$ent; } #set default list for entry in syslinux.cfg my @list_method = qw(nfs http); my @list_ram = qw(32000 48000 64000 1280000); my @list_vga = qw(788 normal 785 791 792 text); my @list_acpi = qw(ht force off); my @list_apic = qw(apic noapic); my @list_eth = qw(eth0 eth1 eth2); #begin of wizard $o->{pages} = { welcome => { name => N("PXE wizard") . "\n\n" . N("Set a PXE server.") . "\n" . N("This wizard will help you configuring the PXE server. PXE (Pre-boot Execution Environment) is a protocol designed by Intel that allows computers to boot through the network. PXE is stored in the ROM of new generation network cards. When the computer boots up, the BIOS loads the PXE ROM in the memory and executes it. A menu is displayed, allowing the computer to boot an operating system loaded through the network.") . "\n" . N("This wizard will provide a pxe service, and ability to add/remove/modify boot images."), no_back => 1, pre => sub { $o->{var}{wiz_level} ||= 1; }, post => sub { -f "$X86/drakwizard_pxe" or return 'error_needpxe'; if ($o->{var}{wiz_level} == 2) { return 'addimg' } elsif ($o->{var}{wiz_level} == 3) { return 'removeimg' } elsif ($o->{var}{wiz_level} == 4) { return 'modifyimg' } elsif ($o->{var}{wiz_level} == 5) { return 'addimgrdz' } }, data => [ { label => N("What do you want to do:"), val => \$o->{var}{wiz_level}, list => [ keys %level ], format => sub { $level{$_[0]} } }, ], no_back => 1, next => 'pxeserver', }, addimg => { name => N("Add a boot image") . "\n\n" . N("Path to image: full path to image (need network boot image)") . "\n\n" . N("PXE name: name in PXE menu (one word/number, no space please)"), data => [ { label => "PXE name:", val => \$o->{var}{PXENAME} }, { label => "PXE description:", val => \$o->{var}{DESCR} }, { label => "Full path to boot image:", val => \$o->{var}{IMG} }, ], post => \&test_data, next => 'summaryadd', }, addimgrdz => { name => N("Add a boot image") . "\n\n" . N("Path to all.rdz: full path to all.rdz image") . "\n\n" . N("PXE name: name in PXE menu (one wo\rd/number, no space please)"), data => [ { label => "PXE name:", val => \$o->{var}{PXENAME} }, { label => "PXE description:", val => \$o->{var}{DESCR} }, { label => "Full path to all.rdz image:", val => \$o->{var}{ALLRDZ} }, { label => "Full path to vmlinuz:", val => \$o->{var}{VMLINUZ} }, ], post => \&test_datardz, next => 'summaryaddrdz', }, removeimg => { name => N("Remove a boot image") . "\n\n" . N("Choose PXE boot image you want to remove from PXE server."), data => [ { label => N("Boot image to remove:"), val => \$o->{var}{IMGTOREMOVE}, fixed_list => \@list_menu }, ], next => 'summaryremove', }, modifyimg => { name => N("Add option to boot image:") . "\n\n" . N("Please choose PXE boot image to modify"), data => [ { label => N("Boot image to configure:"), val => \$o->{var}{IMGTOMODIFY}, fixed_list => \@list_menu }, ], post => \&read_option, next => 'gomodify', }, gomodify => { name => N("Options to add to PXE boot disk") . "\n\n" . N("Server IP: IP address of server which contain installation directory. You can create one with MDK install server wizard.") . "\n" . N("Install directory: full path to MDK install server directory") . "\n" . N("Installation method: choose nfs/http to install via nfs/http."), data => [ { label => N("Boot image to modify:"), fixed_val => \$o->{var}{i} }, { label => N("Server IP:"), val => \$o->{var}{IP} }, { label => N("Install directory:"), val => \$o->{var}{DIR} }, { label => N("Installation method:"), val => \$o->{var}{METHOD}, fixed_list => \@list_method }, ], next => 'gomodify2', }, gomodify2 => { name => N("Network client interface: through which interface client should be installed.") . "\n" . N("Ramsize: adjust ramsize on boot disk.") . "\n" . N("VGA option: if you encounter any problem whith VGA, please adjust. ") . "\n" . N("ACPI option: Advanced Configuration and Power Interface"), data => [ { label => N("Network client interface:"), val => \$o->{var}{ETH}, fixed_list => \@list_eth }, { label => N("Ramsize:"), val => \$o->{var}{RAM}, fixed_list => \@list_ram }, { label => N("VGA option:"), val => \$o->{var}{VGA}, fixed_list => \@list_vga }, { label => N("ACPI option:"), val => \$o->{var}{ACPI}, fixed_list => \@list_acpi }, { label => N("APIC option:"), val => \$o->{var}{APIC}, fixed_list => \@list_apic }, ], next => 'summarymodify', }, pxeserver => { name => N("Set PXE server") . "\n\n" . N("We need to use a special dhcpd.conf with PXE parameter.") . "\n" . N("This will configure all needed default configurations files to set a PXE server."), next => 'summaryserver', }, error_img => { name => N("Please provide a bootable image..."), next => 'addimg', }, needimg => { name => N("Need an image. Please add one."), no_back => 1, next => 'welcome', post => sub { $o->{var}{wiz_level} = 5 }, }, error_imgrdz => { name => N("Please provide a bootable image..."), next => 'addimgrdz', }, error_path_img => { name => N("Please choose an image from a different directory than %s.", $IMGPATH), next => 'addimg', }, error_path_imgrdz => { name => N("Please choose an image from a different directory than %s.", $IMGPATH), next => 'addimgrdz', }, error_name => { name => N("Please provide a correct name in PXE entry (one word)."), next => 'addimg', }, error_needpxe => { name => N("To add/remove/modify PXE boot image, you need to run 'Set PXE server' before."), no_back => 1, next => 'pxeserver', }, simmilar_entry => { name => N("Similar name is already used in PXE menu entry") . "\n\n" . N("Please provide another PXE Menu name"), next => 'addimg', }, simmilar_entryrdz => { name => N("Similar name is already used in PXE menu entry") . "\n\n" . N("Please provide another PXE Menu name"), next => 'addimgrdz', }, summaryserver => { name => N("Now will prepare all default files to set the PXE server"), data => [ { label => N("TFTP directory:"), fixed_val => \$TFTPDIR }, { label => N("Boot image path:"), fixed_val => \$IMGPATH }, { label => N("PXE 'default' file:"), fixed_val => \$PXEDEFAULT }, { label => N("PXE 'help' file:"), fixed_val => \$PXEHELP }, ], post => \&do_it_pxe, next => 'endserver', }, summarymodify => { name => N("Now will modify boot options in image"), data => [ { label => N("Boot image to modify:"), fixed_val => \$o->{var}{i} }, { label => N("Server IP:"), fixed_val => \$o->{var}{IP} }, { label => N("Install directory:"), fixed_val => \$o->{var}{DIR} }, { label => N("Installation method:"), fixed_val => \$o->{var}{METHOD} }, { label => N("Network client interface:"), fixed_val => \$o->{var}{ETH} }, { label => N("Ramsize:"), fixed_val => \$o->{var}{RAM} }, { label => N("VGA option:"), fixed_val => \$o->{var}{VGA} }, { label => N("ACPI option:"), fixed_val => \$o->{var}{ACPI} }, { label => N("APIC option:"), fixed_val => \$o->{var}{APIC} }, ], post => \&do_it_modify, next => 'endmodify', }, summaryremove => { name => N("Now will remove your PXE boot image"), data => [ { label => N("PXE entry to remove:"), fixed_val => \$o->{var}{IMGTOREMOVE} }, ], post => \&do_it_remove, next => 'endremove', }, summaryadd => { name => N("Now will add your PXE boot image"), data => [ { label => "PXE name:", fixed_val => \$o->{var}{PXENAME} }, { label => "PXE description:", fixed_val => \$o->{var}{DESCR} }, { label => "Path to boot image:", fixed_val => \$o->{var}{IMG} }, ], post => \&do_it_add, next => 'endadd', }, summaryaddrdz => { name => N("Now will add your PXE boot image"), data => [ { label => "PXE name:", fixed_val => \$o->{var}{PXENAME} }, { label => "PXE description:", fixed_val => \$o->{var}{DESCR} }, { label => "Full path to all.rdz image:", fixed_val => \$o->{var}{ALLRDZ} }, { label => "Full path to vmlinuz:", fixed_val => \$o->{var}{VMLINUZ} }, ], post => \&do_it_addrdz, next => 'endadd', }, endadd => { name => N("Congratulations"), data => [ { label => N("The wizard successfully add a PXE boot image.") } ], post => sub { @list_menu = (); foreach (cat_($PXEDEFAULT)) { my ($ent) = /# TAG: (\w+)_BEGIN/; ! $ent or push @list_menu,$ent; } }, next => 'welcome', }, endremove => { name => N("Congratulations"), data => [ { label => N("The wizard successfully removed the PXE boot image.") } ], post => sub { @list_menu = (); foreach (cat_($PXEDEFAULT)) { my ($ent) = /# TAG: (\w+)_BEGIN/; ! $ent or push @list_menu,$ent; } }, next => 'welcome', }, endmodify => { name => N("Congratulations"), data => [ { label => N("The wizard successfully modify image(s).") } ], next => 'welcome', }, endserver => { name => N("Congratulations"), data => [ { label => N("The wizard successfully configured your PXE server.") } ], no_back => 1, end => 1, next => 0 }, error_end => { name => N("Failed"), data => [ { label => N("Relaunch drakwizard, and try to change some parameters.") } ], no_back => 1, end => 1, next => 0, }, }; #cp memlinux from syslinux sub memlinux_prep { if (!-f "$X86/memdisk") { cp_af($MEMDISK, $X86); } } # test img and similar entry in PXE sub test_data { $o->{var}{PXENAME} =~ /^\w+$/ or return 'error_name'; if (!-f $o->{var}{IMG}) { return 'error_img' } if (any { /^$o->{var}{PXENAME}/ } cat_($PXEHELP)) { return 'simmilar_entry' } } sub test_datardz { $o->{var}{PXENAME} =~ /^\w+$/ or return 'error_name'; if (!-f $o->{var}{ALLRDZ}) { return 'error_imgrdz' } if (!-f $o->{var}{VMLINUZ}) { return 'error_imgrdz' } if (any { /^$o->{var}{PXENAME}/ } cat_($PXEHELP)) { return 'simmilar_entryrdz' } } # enable tftp server sub enable_tftps { substInFile { s/disable.*/disable = no/ } "$XINETDDIR/tftp"; } # can change option in tftp server sub tftp_blksize { # $o should be with W or not N my ($o) = @_; if ($o =~ /W/) { substInFile { s/server_args.*/server_args = -r blksize -s $TFTPDIR/ } "$XINETDDIR/tftp"; } else { substInFile { s/server_args.*/server_args = -s $TFTPDIR/ } "$XINETDDIR/tftp"; } } sub crea_wdir { if (-e $TMPDIR) { rm_rf($TMPDIR); } mkdir_p($TMPDIR); } # add TAGGED entry in PXE menu sub add_in_default { my ($NAME) = @_; if (!any { /${NAME}_BEGIN/ } cat_($PXEDEFAULT)) { append_to_file($PXEDEFAULT, <{var}{PXENAME}, $o->{var}{DESCR}); add_in_default($o->{var}{PXENAME}); add_img($o->{var}{IMG}, $o->{var}{PXENAME}); } sub do_it_addrdz { return if $::testing; add_in_help($o->{var}{PXENAME}, $o->{var}{DESCR}); add_in_defaultrdz($o->{var}{PXENAME}); add_imgrdz($o->{var}{ALLRDZ}, $o->{var}{VMLINUZ}, $o->{var}{PXENAME}); } # main remove image sub do_it_remove { return if $::testing; @list_menu or return 'needimg'; remove_in_menu($o->{var}{IMGTOREMOVE}); remove_in_help($o->{var}{IMGTOREMOVE}); remove_img($o->{var}{IMGTOREMOVE}); } # get default option for an image sub read_option { my $IMG = $o->{var}{IMGTOMODIFY}; # detect if need loop or just add option to APPEND line if (-f "$IMGPATH/$IMG.img") { my $mount = "/tmp/loop_for_images"; mkdir_p($mount); system("mount $IMGPATH/$IMG.img $mount -o loop 2>/dev/null"); my $line = cat_("$mount/syslinux.cfg"); $o->{var}{i} = "$IMGPATH/$IMG.img"; ($o->{var}{METHOD}, $o->{var}{ETH}, $o->{var}{IP}, $o->{var}{DIR}, $o->{var}{RAM}, $o->{var}{VGA}, $o->{var}{ACPI}, $o->{var}{APIC}) = $line =~ m!method:(nfs|http),interface:(eth0|eth1|eth2),network:dhcp,server:(.*?),directory:(.*?) ramdisk_size=(.*?) root=/dev/ram3 rw vga=(.*?)acpi=(ht|force|off)\s(apic|noapic)!; system("umount $mount"); if (-e $mount) { rm_rf($mount); } } else { my $line = cat_($PXEDEFAULT); $o->{var}{i} = "$IMGPATH/$IMG.rdz"; ($o->{var}{METHOD}, $o->{var}{ETH}, $o->{var}{IP}, $o->{var}{DIR}, $o->{var}{RAM}, $o->{var}{VGA}, $o->{var}{ACPI}, $o->{var}{APIC}) = $line =~ m!APPEND initrd=images/$IMG.rdz automatic=method:(nfs|http),interface:(eth0|eth1|eth2),network:dhcp,server:(.*?),directory:(.*?) ramdisk_size=(.*?) root=/dev/ram3 rw vga=(.*?)acpi=(ht|force|off)\s(apic|noapic)!; } } # modify default boot option of an image sub do_it_modify { @list_menu or return 'needimg'; my $IMG = $o->{var}{IMGTOMODIFY}; my ($METHOD, $ETH, $IP, $DIR, $RAM, $VGA, $ACPI, $APIC) = ($o->{var}{METHOD}, $o->{var}{ETH}, $o->{var}{IP}, $o->{var}{DIR}, $o->{var}{RAM}, $o->{var}{VGA}, $o->{var}{ACPI}, $o->{var}{APIC}); return if $::testing; if (-f "$IMGPATH/$IMG.img") { $o->{var}{i} = "$IMGPATH/$IMG.img"; return if $::testing; #APPEND initrd=images/ken/cooker/i586/isolinux/alt0/all.rdz automatic=method:nfs,interface:eth0,network:dhcp,server:192.168.100.7,directory:/c/cooker ramdisk_size=32000 root=/dev/ram3 rw vga=788 meta_class=discovery my $mount = "/tmp/loop_for_images"; mkdir_p($mount); system("mount $o->{var}{i} $mount -o loop"); output("$mount/syslinux.cfg", <{var}{i} = "$IMGPATH/$IMG.rdz"; substInFile { s!APPEND initrd=images/$IMG.rdz!APPEND initrd=images/$IMG.rdz automatic=method:$METHOD,interface:$ETH,network:dhcp,server:$IP,directory:$DIR ramdisk_size=$RAM root=/dev/ram3 rw vga=$VGA acpi=$ACPI $APIC!m; } $PXEDEFAULT; } } sub pxe_conf { if (!-f "$pxeconf.orig") { cp_af($pxeconf, "$pxeconf.orig") } substInFile { s/default_address.*/default_address=$IPSERVER/; s/mtftp_address.*/mtftp_address=$IPSERVER/; s/domain.*/domain=$DOMAINNAME/; } $pxeconf; } # set PXE server sub do_it_pxe { return if $::testing; my $in = 'interactive'->vnew('su', 'PXE server'); my $w = $in->wait_message(N("PXE server"), N("Configuring PXE server on your system...")); output("$X86/drakwizard_pxe", < $o, }, $class; } 1;