Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add support for root filesystem image transfer via uftp, and md5sum c… #3560

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
37 changes: 37 additions & 0 deletions uftp/README
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
Implementing UFTP in xCAT

When a node boots via xCAT in stateless mode, it first receives a kernel and
an initial ramdisk from the management server. Inside the initial ramdisk is
a script named xcatroot which is responsible for copying the node's root
filesystem image from the management server with wget, uncompressing it into
RAM, and switching to it. The URL of the root filesystem image is passed to
xcatroot via a kernel boot parameter.

In order to support uftp, xcatroot needs to start uftpd, and then signal the
management server that it is ready to receive the image file. The management
server also needs to wait for some amount of time before starting the transfer,
since there could be some variability in the hardware boot process, and the
slowest node might not have started uftpd yet. In order to make sure the image
transferred correctly, the md5 checksum is checked. If the md5 check fails, or
the transfer times out, xcatroot falls back to wget.

To implement this, three kernel boot parameters were added, which are:

uftp
uftpdelay
md5sum

To use UFTP, set uftp=true. The uftpdelay parameter is for specifying the
number of seconds to wait before the transfer starts, and defaults to 30 if
not specified. The md5sum parameter should be set to the md5 checksum of the
root filesystem image. These need to be set in the addkcmdline field in the
osimage table in xCAT, so that nodeset will pick them up. The packimage
command has been extended with some options to facilitate this:

packimage [-s| --sum] set md5sum=<md5 checksum of image>
packimage [-u| --uftp] set uftp=true
packimage [-d| --delay=<uftp delay in seconds> set uftpdelay=<uftp delay in seconds>

Note that if -s is omitted, and an md5sum exists in addkcmdline, the existing
entry is removed because it would no longer be valid. Similarly, if -u is
omitted and uftp=true exists in addkcmdline, it is removed.
Binary file added uftp/uftp-4.9.3-1.ppc64le.rpm
Binary file not shown.
Binary file added uftp/uftp-4.9.3.tar.gz
Binary file not shown.
Binary file added uftp/uftp-debuginfo-4.9.3-1.ppc64le.rpm
Binary file not shown.
100 changes: 100 additions & 0 deletions uftp/uftp-listener
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
#!/bin/bash
LOGDIR=/var/log/xcat/
LOGFILE=uftp_listener.log
mkdir -p $LOGDIR

usage() {
echo "usage: uftp-listener [ -i interface ] [ -s sleep time ]"
exit 1
}

debug() {
[ "$DEBUG" ] && echo "$@"
echo "$@" >> $LOGDIR/$LOGFILE
}

start_copy() {
sleep $SLEEP
debug "copying $ROOTIMG"
debug "$UFTP -I $INTERFACE -R 950000 -p $UFTPPORT -D /rootimg.cpio.gz $ROOTIMG"
$UFTP -I $INTERFACE -R 950000 -p $UFTPPORT -D /rootimg.cpio.gz $ROOTIMG
}

while getopts ":i:s:hd" options
do
case ${options} in
i ) INTERFACE=${OPTARG};;
s ) SLEEP=${OPTARG};;
d ) DEBUG=true;;
h ) usage
exit 1;;
* ) usage
exit 1;;
esac
done

# if INTERFACE is null, try to detect
if [ -z "$INTERFACE" ]; then
MAN_NET=$(/opt/xcat/bin/lsdef -t network management | grep net | sed 's/.*=//')
INTERFACE=$(netstat -nr | grep $MAN_NET | awk '{print $8}')
fi

if [ -z "$INTERFACE" ]; then
debug "ERROR: detection of interface failed and none supplied with -i, aborting..."
exit 1
fi

ip link show $INTERFACE > /dev/null 2>&1
RC=$?

if [ "$RC" != 0 ]; then
debug "ERROR: invalid interface $INTERFACE"
exit 1
fi

# if no sleep time supplied, use the default of 30 seconds
if [ -z "$SLEEP" ]; then
SLEEP=30
fi

UFTP=$(which uftp)
if [ ! -x $UFTP ]; then
debug "ERROR: uftp binary not found, exiting!"
exit 1
fi

# make sure 1045/tcp is not already in use
nc -i 500ms -l -p 1045 2>&1 | grep "Address already in use" > /dev/null
RC=$?
if [ "$RC" = 0 ]; then
debug "ERROR: 1045/tcp is already in use, sleeping 10 seconds and exiting"
sleep 10
exit 1
fi

debug "listening for transfer request on interface $INTERFACE 1045/tcp"
declare -A copyjobs
while true
do
nc -l -p 1045 -k | while read LINE; do
VALIDATED=$(echo $LINE | grep -E "/install/.*/rootimg(\.sfs|\.cpio\.gz|\.cpio\.xz|\.tar.gz|\.tar\.xz|-statelite\.gz)")
if [ -n "$VALIDATED" ]; then
ROOTIMG=$(echo $VALIDATED | sed 's/.*GET //' | sed 's+ HTTP/1.1.*++')
if $(echo $ROOTIMG | grep uftpport > /dev/null); then
UFTPPORT=$(echo $ROOTIMG | sed 's+.*uftpport=++')
ROOTIMG=$(echo $ROOTIMG | sed 's+/uftpport=.*++')
else
UFTPPORT=1044
fi
if [ ! -f "$ROOTIMG" ]; then
debug "$ROOTIMG: file not found"
elif [ ! x${copyjobs[$ROOTIMG]} = x ] && jobs -lr | grep ${copyjobs[$ROOTIMG]} > /dev/null; then
: # if we get here, there is already a uftp copy job staged for this url, so do nothing
# debug "$ROOTIMG job detected"
else
start_copy $ROOTIMG &
copyjobs[$ROOTIMG]=$!
fi
fi
done
done
12 changes: 12 additions & 0 deletions uftp/uftp-listener.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[Unit]
Description=UFTP listener
Wants=basic.target
After=basic.target network.target

[Service]
Type=simple
ExecStart=/bin/bash /opt/xcat/bin/uftp-listener
KillMode=control-group

[Install]
WantedBy=multi-user.target
41 changes: 41 additions & 0 deletions uftp/uftp.spec
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
Name: uftp
Version: 4.9.3
Release: 1
Summary: UFTP - Encrypted UDP based FTP with multicast

Group: FTP Server
License: GPL
URL: http://uftp-multicast.sourceforge.net
Source0: http://sourceforge.net/projects/uftp-multicast/files/source-tar/uftp-4.9.3.tar.gz
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)

BuildRequires: openssl-devel

%description
UFTP is an encrypted multicast file transfer program, designed to securely, reliably, and efficiently transfer files to multiple receivers simultaneously. This is useful for distributing large files to a large number of receivers, and is especially useful for data distribution over a satellite link (with two way communication), where the inherent delay makes any TCP based communication highly inefficient. The multicast encryption scheme is based on TLS with extensions to allow multiple receivers to share a common key. UFTP also has the capability to communicate over disjoint networks separated by one or more firewalls (NAT traversal) and without full end-to-end multicast capability (multicast tunneling) through the use of a UFTP proxy server. These proxies also provide scalability by aggregating responses from a group of receivers.

%prep
%setup -q

%build
make %{?_smp_mflags}

%install
rm -rf $RPM_BUILD_ROOT
make install DESTDIR=$RPM_BUILD_ROOT

%clean
rm -rf $RPM_BUILD_ROOT

%files
%defattr(-,root,root,-)
/usr/bin/uftp
/usr/sbin/uftpd
/usr/sbin/uftpproxyd
/usr/bin/uftp_keymgt
/usr/share/man/man1/uftp.1.gz
/usr/share/man/man1/uftp_keymgt.1.gz
/usr/share/man/man1/uftpd.1.gz
/usr/share/man/man1/uftpproxyd.1.gz

%changelog
116 changes: 110 additions & 6 deletions xCAT-server/lib/xcat/plugins/packimage.pm
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
-p Profile (compute,service)
-a Architecture (ppc64,x86_64,etc)
-m Method (default cpio)
-s Compute MD5 checksum of image
-u Use uftp for copying image
-t uftp delay in seconds (default is 30)
-d uftp destination port

=cut

Expand All @@ -32,6 +36,7 @@ use Cwd;
use File::Temp;
use File::Basename;
use File::Path;
use xCAT::DBobjUtils;

#use xCAT::Utils qw(genpassword);
use xCAT::Utils;
Expand Down Expand Up @@ -82,7 +87,7 @@ sub process_request {
@ARGV = @{$args};
}
if (scalar(@ARGV) == 0) {
$callback->({ info => ["Usage:\n packimage [-m| --method=cpio|tar] [-c| --compress=gzip|pigz|xz] [--nosyncfiles] <imagename>\n packimage [-h| --help]\n packimage [-v| --version]"] });
$callback->({ info => ["Usage:\n packimage [-m| --method=cpio|tar] [-c| --compress=gzip|pigz|xz] <imagename>\n packimage [-h| --help]\n packimage [-v| --version]\n packimage [-s| --sum]\n packimage [-u| --uftp]\n packimage [-t| --delay=<uftp delay in seconds>]\n packimage [-d <uftp destination port>]"] });
return 0;
}

Expand All @@ -98,11 +103,19 @@ sub process_request {
my $nosyncfiles;
my $imagename;
my $dotorrent;
my $domd5sum;
my $douftp;
my $uftpdelay;
my $default_uftpdelay=30;
my $uftpport;
my $addkcmdline;
my $provmethod;
my $envars;
my $help;
my $version;
my $lock;
my $newaddkcmdline;
my $linuximagetab;

GetOptions(
"profile|p=s" => \$profile,
Expand All @@ -111,6 +124,10 @@ sub process_request {
"method|m=s" => \$method,
"compress|c=s" => \$compress,
"tracker=s" => \$dotorrent,
"sum|s" => \$domd5sum,
"uftp|u" => \$douftp,
"port|d=s" => \$uftpport,
"delay|t=s" => \$uftpdelay,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hi @rich350 , what is this argument "delay|t=s" => \$uftpdelay for?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an amount of time to wait to make sure all booting nodes have gone through their hardware initialization, loaded the kernel and initrd, started xcatroot, and started uftpd. For example, let's say we are booting lots of nodes at the same time to the same image. Each node will initialize, but maybe a few nodes take longer to load a particular driver for whatever reason. So, with each boot there will be a fastest node, and a slowest node. The fastest node will start uftpd and be the first to send a request to uftp-listener for the image. If uftp-listener immediately starts the transfer after the first request, the slowest node might not have started uftpd yet, and would therefore miss the transfer. So, uftpdelay is the amount of time that uftp-listener should wait before starting the transfer, once it receives the first request from the node that booted the fastest.

Since there is a timeout built into the uftp transfer logic in xcatroot, both uftp-listener and xcatroot need to be aware of uftpdelay, so it is passed as a boot parameter. I added the timeout so that if some node is really slow and doesn't get uftpd started before the transfer starts, then sends his request after the transfer has started, it will time out and fall back to wget. The default of 30 seconds is probably overkill, and could probably safely be reduced to 15 or even 10 seconds, but this will depend on the particular hardware, networking, number of nodes, etc. Since this will vary by site, I went with a safe default that can be tuned.

'nosyncfiles' => \$nosyncfiles,
"help|h" => \$help,
"version|v" => \$version
Expand All @@ -125,7 +142,7 @@ sub process_request {
return 0;
}
if ($help) {
$callback->({ info => ["Usage:\n packimage [-m| --method=cpio|tar] [-c| --compress=gzip|pigz|xz] [--nosyncfiles] <imagename>\n packimage [-h| --help]\n packimage [-v| --version]"] });
$callback->({ info => ["Usage:\n packimage [-m| --method=cpio|tar] [-c| --compress=gzip|pigz|xz] <imagename>\n packimage [-h| --help]\n packimage [-v| --version]\n packimage [-s| --sum]\n packimage [-u| --uftp]\n packimage [-t| --delay=<uftp delay in seconds>]\n packimage [-d <uftp destination port>]"] });
return 0;
}

Expand All @@ -149,7 +166,7 @@ sub process_request {
$callback->({ error => ["The osimage table cannot be opened."], errorcode => [1] });
return 1;
}
my $linuximagetab = xCAT::Table->new('linuximage', -create => 1);
$linuximagetab = xCAT::Table->new('linuximage', -create => 1);
unless ($linuximagetab) {
$callback->({ error => ["The linuximage table cannot be opened."], errorcode => [1] });
return 1;
Expand All @@ -159,7 +176,7 @@ sub process_request {
$callback->({ error => ["Cannot find image \'$imagename\' from the osimage table."], errorcode => [1] });
return 1;
}
(my $ref1) = $linuximagetab->getAttribs({ imagename => $imagename }, 'exlist', 'rootimgdir');
(my $ref1) = $linuximagetab->getAttribs({ imagename => $imagename }, 'exlist', 'rootimgdir', 'addkcmdline');
unless ($ref1) {
$callback->({ error => ["Cannot find $imagename from the linuximage table."], errorcode => [1] });
return 1;
Expand All @@ -182,8 +199,9 @@ sub process_request {
return 1;
}

$exlistloc = $ref1->{'exlist'};
$destdir = $ref1->{'rootimgdir'};
$exlistloc = $ref1->{'exlist'};
$destdir = $ref1->{'rootimgdir'};
$addkcmdline = $ref1->{'addkcmdline'};
} else {
$provmethod = "netboot";
unless ($osver) {
Expand Down Expand Up @@ -551,6 +569,92 @@ sub process_request {
system("rm -rf $xcat_packimg_tmpfile");
return 1;
}
$newaddkcmdline = $addkcmdline;
if ($domd5sum) { # if true, pass md5sum of root image as a boot parameter
my $md5 = Digest::MD5->new;
open(my $iorootimg, '<', "$destdir/rootimg.$suffix");
binmode($iorootimg);
$md5->addfile($iorootimg);
my $md5sum = $md5->hexdigest;
if ($addkcmdline =~ m/md5sum/) {
$newaddkcmdline =~ s/md5sum=\w{32}/md5sum=$md5sum/;
} else {
$newaddkcmdline = $newaddkcmdline . " md5sum=" . $md5sum;
}
$linuximagetab->setAttribs({imagename => $imagename}, {addkcmdline => $newaddkcmdline});
} else { # -s was not given, so remove any pre-existing md5sum since it will be invalid
if ($newaddkcmdline =~ m/md5sum/) {
$newaddkcmdline =~ s/ md5sum=\w{32}//;
$newaddkcmdline =~ s/^md5sum=\w{32} //;
$newaddkcmdline =~ s/^md5sum=\w{32}//;
$linuximagetab->setAttribs({imagename => $imagename}, {addkcmdline => $newaddkcmdline});
}
}

if ($douftp) { # if true, pass uftp=true as a boot parameter
if (!($newaddkcmdline =~ m/uftp=true/)) {
$newaddkcmdline = $newaddkcmdline . " uftp=true";
$linuximagetab->setAttribs({imagename => $imagename}, {addkcmdline => $newaddkcmdline});
}
} else { # -u was not given, so remove any pre-existing uftp boot parameters
if ($newaddkcmdline =~ m/uftp=true/) {
$newaddkcmdline =~ s/ uftp=true//;
$newaddkcmdline =~ s/^uftp=true //;
$newaddkcmdline =~ s/^uftp=true//;
}
if ($newaddkcmdline =~ m/uftpdelay=\d*/) {
$newaddkcmdline =~ s/ uftpdelay=\d*//;
$newaddkcmdline =~ s/^uftpdelay=\d* //;
$newaddkcmdline =~ s/^uftpdelay=\d*//;
}
if ($newaddkcmdline =~ m/uftpport=\d*/) {
$newaddkcmdline =~ s/ uftpport=\d*//;
$newaddkcmdline =~ s/^uftpport=\d* //;
$newaddkcmdline =~ s/^uftpport=\d*//;
}
$linuximagetab->setAttribs({imagename => $imagename}, {addkcmdline => $newaddkcmdline});
}

if (($uftpdelay) && ($douftp)) { # if true, pass uftpdelay as a boot parameter
if ($newaddkcmdline =~ m/uftpdelay/) { # if there is a pre-existing uftpdelay, replace
$newaddkcmdline =~ s/uftpdelay=\d*/uftpdelay=$uftpdelay/;
} else { # otherwise, add supplied value
$newaddkcmdline = $newaddkcmdline . " uftpdelay=$uftpdelay";
}
$linuximagetab->setAttribs({imagename => $imagename}, {addkcmdline => $newaddkcmdline});
} elsif ($douftp) { # no uftpdelay was given, so use the default of $default_uftpdelay
if ($newaddkcmdline =~ m/uftpdelay/) { # if there is a pre-existing uftpdelay, replace
$newaddkcmdline =~ s/uftpdelay=\d*/uftpdelay=$default_uftpdelay/;
} else { # otherwise, add default value
$newaddkcmdline = $newaddkcmdline . " uftpdelay=$default_uftpdelay";
}
$linuximagetab->setAttribs({imagename => $imagename}, {addkcmdline => $newaddkcmdline});
}

my $port_conflict;
if (($douftp) && ($uftpport)) {
$port_conflict = `lsdef -t osimage -i addkcmdline | grep $uftpport`;
if ($port_conflict) {
$callback->({ error => ["Warning: uftpport $uftpport is already in use by another image!"], errorcode => [0] });
}
} else {
while (($douftp) && !($uftpport)) { # no uftpport was given, so use a random one
$uftpport = 1025 + int(rand(64510));
$port_conflict = `lsdef -t osimage -i addkcmdline | grep $uftpport`;
if ($port_conflict) { # a conflict was found, so pick a different port
$uftpport = "";
}
}
}

if ($douftp) { # if true, pass uftpport as a boot parameter
if ($newaddkcmdline =~ m/uftpport/) { # if there is a pre-existing uftpport, replace
$newaddkcmdline =~ s/uftpport=\d*/uftpport=$uftpport/;
} else { # otherwise, add supplied value
$newaddkcmdline = $newaddkcmdline . " uftpport=$uftpport";
}
$linuximagetab->setAttribs({imagename => $imagename}, {addkcmdline => $newaddkcmdline});
}

if ($method =~ /cpio/) {
chmod 0644, "$destdir/rootimg.$suffix";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
echo $drivers
dracut_install wget tar cpio gzip modprobe touch echo cut wc xz
dracut_install grep ifconfig hostname awk egrep grep dirname expr
dracut_install mount.nfs
dracut_install mount.nfs md5sum uftpd
dracut_install parted mke2fs bc mkswap swapon chmod
dracut_install ethtool
inst "$moddir/xcat-updateflag" "/tmp/updateflag"
Expand Down
Loading