Revision: e457cb297b34
Author: Schlomo Schapiro <l...@schlomo.schapiro.org>
Date: Tue Jul 12 02:42:50 2011
Log: New Features:...
http://code.google.com/p/lml/source/detail?r=e457cb297b34
Revision: c9c319caed39
Author: Schlomo Schapiro <l...@schlomo.schapiro.org>
Date: Tue Jul 12 03:18:26 2011
Log: Rule should match only on UUID and not on MAC
http://code.google.com/p/lml/source/detail?r=c9c319caed39
Revision: aec2e608e5ee
Author: Schlomo Schapiro <l...@schlomo.schapiro.org>
Date: Tue Jul 12 03:21:08 2011
Log: config for Force Boot, minor changes to match my production config
http://code.google.com/p/lml/source/detail?r=aec2e608e5ee
==============================================================================
Revision: e457cb297b34
Author: Schlomo Schapiro <l...@schlomo.schapiro.org>
Date: Tue Jul 12 02:42:50 2011
Log: New Features:
* multi-NIC support (each gets a unique section in the DHCP config)
* force boot
* force the VM into booting a predefined configuration
* Triggered by new Custom Field which contains either a URL or a shortname
* provide configuration shortnames via lml configuration
* provide relative or full HTTP URL
Improvements:
* notice DHCP restart errors and report to VM
* support distributed vSwitch (needs completely different code :-( )
* SVN commits more informative
http://code.google.com/p/lml/source/detail?r=e457cb297b34
Modified:
/web/www/boot/lml/lib/LML/Common.pm
/web/www/boot/lml/lib/LML/Subversion.pm
/web/www/boot/lml/lib/LML/VMware.pm
/web/www/boot/lml/pxelinux.pl
=======================================
--- /web/www/boot/lml/lib/LML/Common.pm Wed May 4 06:36:09 2011
+++ /web/www/boot/lml/lib/LML/Common.pm Tue Jul 12 02:42:50 2011
@@ -32,6 +32,7 @@
use File::Glob ':glob';
use IO::Handle;
use Path::Class 'dir';
+use Cwd;
use DateTime::Format::Flexible;
=======================================
--- /web/www/boot/lml/lib/LML/Subversion.pm Wed May 4 06:36:09 2011
+++ /web/www/boot/lml/lib/LML/Subversion.pm Tue Jul 12 02:42:50 2011
@@ -15,9 +15,9 @@
sub svnCopyPath ($$) {
my ($source,$destination) = @_;
- my $result = qx(svn copy -m lml $source $destination 2>&1);
+ my $result = qx(svn copy -m "lml triggered" $source $destination 2>&1);
if ($? > 0) {
- warn "'svn copy -m lml $source $destination' failed:\n$result";
+ warn "'svn copy -m 'lml triggered' $source $destination'
failed:\n$result";
return undef;
}
#warn "copied $source -> $destination";
@@ -27,9 +27,9 @@
sub svnMovePath ($$) {
my ($source,$destination) = @_;
- my $result = qx(svn move -m lml $source $destination 2>&1);
+ my $result = qx(svn move -m "lml triggered" $source $destination 2>&1);
if ($? > 0) {
- warn "'svn move -m lml $source $destination' failed:\n$result";
+ warn "'svn move -m 'lml triggered' $source $destination'
failed:\n$result";
return undef;
}
#warn "moved $source $destination";
=======================================
--- /web/www/boot/lml/lib/LML/VMware.pm Wed May 4 06:36:09 2011
+++ /web/www/boot/lml/lib/LML/VMware.pm Tue Jul 12 02:42:50 2011
@@ -98,8 +98,42 @@
};
foreach my $vm_dev (@{$vm->config->hardware->device}) {
if ($vm_dev->can("macAddress") and defined($vm_dev->macAddress)) {
- $VM{$uuid}{"MAC"}{$vm_dev->macAddress} = $vm_dev->backing->deviceName;
-# print "MAC: ".$vm_dev->macAddress."\n";
+ if ($vm_dev->backing->can("deviceName")) {
+ $VM{$uuid}{"MAC"}{$vm_dev->macAddress} = $vm_dev->backing->deviceName;
+ # print "MAC: ".$vm_dev->macAddress."\n";
+ } else {
+ # TODO: deal with Distributed Virtual Switch:
+=pod
+ DB<34> x $vm_dev
+0 VirtualPCNet32=HASH(0xa8ca558)
+ 'addressType' => 'assigned'
+ 'backing' =>
VirtualEthernetCardDistributedVirtualPortBackingInfo=HASH(0xa86dce8)
+ 'port' => DistributedVirtualSwitchPortConnection=HASH(0xa7dad90)
+ 'connectionCookie' => 1799741339
+ 'portKey' => 257
+ 'portgroupKey' => 'dvportgroup-288'
+ 'switchUuid' => 'a4 4a 13 50 16 1d e3 48-ad 44 65 f2 fa f9 77 72'
+ 'connectable' => VirtualDeviceConnectInfo=HASH(0xa853820)
+ 'allowGuestControl' => 1
+ 'connected' => 0
+ 'startConnected' => 1
+ 'status' => 'untried'
+ 'controllerKey' => 100
+ 'deviceInfo' => Description=HASH(0xa8a75a8)
+ 'label' => 'Netzwerkadapter 1'
+ 'summary'
=> 'vm.device.VirtualPCNet32.DistributedVirtualPortBackingInfo.summary'
+ 'key' => 4000
+ 'macAddress' => '00:50:56:b7:00:0f'
+ 'unitNumber' => 7
+ 'wakeOnLanEnabled' => 1
+
+
+
+=cut
+ my $switchuuid=$vm_dev->backing->port->switchUuid;
+ my $portgroupkey=$vm_dev->backing->port->portgroupKey;
+ $VM{$uuid}{"MAC"}{$vm_dev->macAddress} = Vim::get_view(mo_ref => new
ManagedObjectReference(type=>"DistributedVirtualPortgroup",value=>$portgroupkey))->config->name;
+ }
}
}
if ($vm->customValue) {
=======================================
--- /web/www/boot/lml/pxelinux.pl Wed May 4 06:36:09 2011
+++ /web/www/boot/lml/pxelinux.pl Tue Jul 12 02:42:50 2011
@@ -33,6 +33,8 @@
# our URL base from REQUEST_URI
my $base_url = url();
$base_url =~ s/\/[^\/]+$//; # cheap basename()
+my $tftp_url = $base_url;
+$tftp_url =~ s/\/pxelinux.cfg.*$//; # strip trailing pxelinux.cfg
my $vm_name="";
my @error=();
@@ -80,6 +82,11 @@
exit 0
}
}
+
+ # check if the VM has more than one NIC on the managed network. This is
because we manage the network via DHCP
+ # and there can be only one DHCP-assigned IP so far. ISC dhcpd also fails
if a host entry has more than one hardware address field.
+ # TODO: decide and implement
+
# modify VM if configured and current setting not as it should be
(because the reconfigure VM task takes time)
if (exists($CONFIG{MODIFYVM}{FORCENETBOOT}) and
$CONFIG{MODIFYVM}{FORCENETBOOT} and
( # either the setting is not set at all or it is set but not equal
to "allow:net"
@@ -154,7 +161,7 @@
} elsif (exists($CONFIG{HOSTRULES}{DNSCHECKNEW}) and
$CONFIG{HOSTRULES}{DNSCHECKNEW} and
scalar(gethostbyname($vm_name.".".$CONFIG{DHCP}{APPENDDOMAIN}."."))) {
# if this is a brand-new machine (e.g. we have no history of it) and new
VM checking is enabled
- push(@error,"New VM name exists already
in '$LAB->{DHCP}->{APPENDDOMAIN}'");
+ push(@error,"New VM name exists already
in '$CONFIG{DHCP}{APPENDDOMAIN}'");
}
# up till here we have only checks that verify the VM.
@@ -229,12 +236,6 @@
$hosts_changed++;
}
}
-
-# dump $LAB to file
-open(LAB_CONF,">$CONFIG{lml}{datadir}/lab.conf") || die "Could not
open '$CONFIG{lml}{datadir}/lab.conf' for writing";
-flock(LAB_CONF, 2) || die;
-print LAB_CONF Data::Dumper->Dump([$LAB],[qw(LAB)]);
-close(LAB_CONF);
# dump %VM to file
open(VM_CONF, ">$CONFIG{lml}{datadir}/vm.conf") || die "Could not
open '$CONFIG{lml}{datadir}/vm.conf' for writing";
@@ -246,16 +247,26 @@
if (exists($CONFIG{DHCP}{hostsfile}) and $CONFIG{DHCP}{hostsfile} and
$hosts_changed) {
my $dhcp_hosts="";
for my $u (keys(%{$LAB->{HOSTS}})) {
- $dhcp_hosts .= "host $u { \n";
- for my $m (@{$LAB->{HOSTS}->{$u}->{MACS}}) {
- $dhcp_hosts .= "\thardware ethernet $m;\n";
- }
- $dhcp_hosts .= "\toption host-name
\"$LAB->{HOSTS}->{$u}->{HOSTNAME}".(exists($LAB->{DHCP}->{APPENDDOMAIN})?".".$LAB->{DHCP}->{APPENDDOMAIN}:"")."\";\n";
- # the following forces the dhcpd to update the DNS records even if the
client did NOT send a hostname!!!
- # took me full day to figure that out :-(
- $dhcp_hosts .= "\tddns-hostname \"$LAB->{HOSTS}->{$u}->{HOSTNAME}\";\n";
- $dhcp_hosts .= "\tfixed-address $LAB->{HOSTS}->{$u}->{IP};\n" if
(exists($LAB->{HOSTS}->{$u}->{IP}));
- $dhcp_hosts .= "}\n\n";
+ my $count=0;
+ # FIXME: Apparently sometimes we get a VM that has no MACS defined :-(
+ if (exists($LAB->{HOSTS}->{$u}->{MACS})) {
+ for my $m (sort(@{$LAB->{HOSTS}->{$u}->{MACS}})) {
+ $dhcp_hosts .= "host $u".($count>0?"-$count":"")." { \n";
+ $dhcp_hosts .= "\thardware ethernet $m;\n";
+ my $hostname = $LAB->{HOSTS}->{$u}->{HOSTNAME}.($count>0?"-$count":"");
+ $dhcp_hosts .= "\toption host-name
\"$hostname".(exists($LAB->{DHCP}->{APPENDDOMAIN})?".".$LAB->{DHCP}->{APPENDDOMAIN}:"")."\";\n";
+ # the following forces the dhcpd to update the DNS records even if the
client did NOT send a hostname!!!
+ # took me full day to figure that out :-(
+ $dhcp_hosts .= "\tddns-hostname \"$hostname\";\n";
+ $dhcp_hosts .= "\tfixed-address $LAB->{HOSTS}->{$u}->{IP};\n" if
(exists($LAB->{HOSTS}->{$u}->{IP}));
+ $dhcp_hosts .= $LAB->{HOSTS}->{$u}->{EXTRAOPTS}."\n" if
(exists($LAB->{HOSTS}->{$u}->{EXTRAOPTS}));
+ $dhcp_hosts .= "}\n\n";
+ $count++;
+ }
+ } else {
+ warn "No MACs found for VM $LAB->{HOSTS}->{$u}->{HOSTNAME} ($u)\n";
+ warn
Data::Dumper->Dump([$LAB->{HOSTS}->{$u},$VM{$search_uuid}],[qw(LAB_HOSTS_uuid
VM_uuid)]);
+ }
}
open(DHCP_HOSTS,">$CONFIG{DHCP}{hostsfile}") || die "Could not
open '$CONFIG{DHCP}{hostsfile}' for writing";
flock(DHCP_HOSTS,2) || die;
@@ -265,8 +276,12 @@
my $result = qx($CONFIG{DHCP}{TRIGGERCOMMAND} 2>&1);
if ($? > 0) {
warn "trigger command '$CONFIG{DHCP}{TRIGGERCOMMAND}' failed:\n$result";
+ push(@error,"Could not reload DHCP server, please call for help");
+ # FIXME: Rollback last change or something
}
}
+
+
if (scalar(@error)) {
# have some errors
print header('text/plain');
@@ -283,7 +298,42 @@
}
} elsif ($vm_name) {
# if the VM is found and all is fine then redirect to default PXE
configuration
- print header(-status=>"302 VM is $vm_name and all is
fine".($hosts_changed?", some hosts
changed":""),-type=>'text/plain',-location=>$base_url."/default");
+
+ # dump $LAB to file only if all is fine. This makes sure that LML stays
with the old view of the lab for some kind of
+ # hard to catch errors.
+ open(LAB_CONF,">$CONFIG{lml}{datadir}/lab.conf") || die "Could not
open '$CONFIG{lml}{datadir}/lab.conf' for writing";
+ flock(LAB_CONF, 2) || die;
+ print LAB_CONF Data::Dumper->Dump([$LAB],[qw(LAB)]);
+ close(LAB_CONF);
+
+ my $pxelinux_config_url;
+ my $bootinfo;
+ if ($CONFIG{pxelinux}{pxelinuxcfg_path} and
$CONFIG{vsphere}{forceboot_field} and
+ exists
$VM{$search_uuid}{CUSTOMFIELDS}{$CONFIG{vsphere}{forceboot_field}} and
+ $VM{$search_uuid}{CUSTOMFIELDS}{$CONFIG{vsphere}{forceboot_field}}
+ ) {
+ my
$forceboot=$VM{$search_uuid}{CUSTOMFIELDS}{$CONFIG{vsphere}{forceboot_field}};
+ # little exploit protection, could be done more professional :-)
+ $forceboot =~ s/\.{2,}//g; # remove any .. or ...
+ $forceboot =~ tr[:/A-Za-z0-9._-][]dc; # normalize to contain only valid
path characters
+ # if forceboot contains a path relative to the pxelinux TFTP prefix
+ if (-r $CONFIG{pxelinux}{pxelinuxcfg_path}."/".$forceboot) {
+ $pxelinux_config_url="$tftp_url/$forceboot";
+ $bootinfo="force boot from VM config (file)";
+ } elsif ($CONFIG{forceboot}{$forceboot}) {
+ $pxelinux_config_url=($CONFIG{forceboot}{$forceboot} =~
m(://)?"":$tftp_url."/").$CONFIG{forceboot}{$forceboot};
+ $bootinfo="force boot from LML config";
+ } elsif ($forceboot =~ m(://)) {
+ $pxelinux_config_url=$forceboot;
+ $bootinfo="force boot from VM config (URL)";
+ }
+ else {
+ warn("Invalid/Unknown force boot target '$forceboot' ignored");
+ }
+ }
+ $pxelinux_config_url=$base_url."/default" unless ($pxelinux_config_url);
+ $bootinfo="all is fine" unless ($bootinfo);
+ print header(-status=>"302 VM is $vm_name and
$bootinfo".($hosts_changed?", some hosts
changed":""),-type=>'text/plain',-location=>$pxelinux_config_url);
} else {
# if the VM is not found then also give some error text
if (exists($CONFIG{PXELINUX}{REDIRECT_UNKNOWN_TO_DEFAULT}) and
$CONFIG{PXELINUX}{REDIRECT_UNKNOWN_TO_DEFAULT}) {
==============================================================================
Revision: c9c319caed39
Author: Schlomo Schapiro <l...@schlomo.schapiro.org>
Date: Tue Jul 12 03:18:26 2011
Log: Rule should match only on UUID and not on MAC
http://code.google.com/p/lml/source/detail?r=c9c319caed39
Modified:
/web/conf.d/lml
=======================================
--- /web/conf.d/lml Wed May 4 06:36:09 2011
+++ /web/conf.d/lml Tue Jul 12 03:18:26 2011
@@ -2,14 +2,13 @@
# a UUID to the LML scripts. If this is disabled then the default pxelinux
configuration
# will be used instead
-
<Location /boot>
RewriteEngine on
# do not rewrite if the requested file actually exists. Allows putting
special configuration
# for special hosts into pxelinux.cfg/<UUID> and LML will not be used for
those hosts
RewriteCond %{REQUEST_FILENAME} !-f
# rewrite requests for non-existant UUID-based pxelinux configs to go to
pxelinux.pl
-RewriteRule pxelinux.cfg/(.*-.*-.*-.*)$ lml/pxelinux.pl?uuid=$1
+RewriteRule pxelinux.cfg/([^-]{8}-.*-.*-.*)$ lml/pxelinux.pl?uuid=$1
</Location>
# default Perl config in Ubuntu does not enable .pl globally, we do it
just for lml
==============================================================================
Revision: aec2e608e5ee
Author: Schlomo Schapiro <l...@schlomo.schapiro.org>
Date: Tue Jul 12 03:21:08 2011
Log: config for Force Boot, minor changes to match my production config
http://code.google.com/p/lml/source/detail?r=aec2e608e5ee
Modified:
/etc/lml.conf
=======================================
--- /etc/lml.conf Thu May 12 01:28:46 2011
+++ /etc/lml.conf Tue Jul 12 03:21:08 2011
@@ -21,7 +21,7 @@
error_main= <<EOF
#menu hshift 5
menu width 80
-menu background is24-boot-splash.png
+menu background menu/is24-boot-splash.png
menu color title * #FFFFFFFF *
menu color border * #00000000 #00000000 none
menu color sel 0 #ffffffff #00000000 none
@@ -31,7 +31,7 @@
menu vshift 5
#menu rows 13
-default vesamenu.c32
+ui vesamenu.c32
noescape 1
allowoptions 0
prompt 0
@@ -47,6 +47,16 @@
Please fix these issues and press ENTER to reboot and try again
endtext
EOF
+# local path to pxelinux.cfg directory, needed for forceboot autodetection
+# This is the local path for the URI given to pxelinux as TFTP path prefix
+# don't include the pxelinux.cfg directory itself!
+pxelinuxcfg_path=/var/www/boot
+
+[forceboot]
+# force boot profiles
+# each profile simply maps to a pxelinux configuration file
+server=menu/server.centos5.txt
+server6=menu/server.sl6.txt
[hostrules]
# hostnames must match this Perl pattern
@@ -95,3 +105,5 @@
expires_field=Expires
# use european date format for expiry date field
expires_european=1
+# define a field that contains info about forced PXE boots
+forceboot_field=Force Boot