# Copyright (c) 2006..2011 - OpenSLX GmbH # # This program is free software distributed under the GPL version 2. # See http://openslx.org/COPYING # # If you have any feedback please consult http://openslx.org/feedback and # send your suggestions, praise, or complaints to feedback@openslx.org # # General information about OpenSLX can be found at http://openslx.org/ # ----------------------------------------------------------------------------- # NFS.pm # - provides NFS-specific overrides of the OpenSLX::OSExport::FileSystem API. # ----------------------------------------------------------------------------- package OpenSLX::OSExport::FileSystem::NFS; use strict; use warnings; use base qw(OpenSLX::OSExport::FileSystem::Base); use File::Basename; use OpenSLX::Basics; use OpenSLX::ConfigDB qw(:support); use OpenSLX::Utils; ################################################################################ ### interface methods ################################################################################ sub new { my $class = shift; my $self = { 'name' => 'nfs', }; return bless $self, $class; } sub initialize { my $self = shift; my $engine = shift; $self->SUPER::initialize($engine); my $exportBasePath = "$openslxConfig{'public-path'}/export"; $self->{'export-path'} = "$exportBasePath/nfs/$engine->{'vendor-os-name'}"; return; } sub exportVendorOS { my $self = shift; my $source = shift; my $target = $self->{'export-path'}; if ($self->_isTargetBindMounted($source, $target)) { warn _tr( "%s is a bind-mount to vendor-OS root - rsync step is skipped!", $target ); return; } $self->_copyViaRsync($source, $target); return; } sub purgeExport { my $self = shift; my $source = $self->{'engine'}->{'vendor-os-path'}; my $target = $self->{'export-path'}; if ($self->_isTargetBindMounted($source, $target)) { warn _tr( "%s is a bind-mount to vendor-OS root - removal step is skipped!", $target ); return; } if (system("rm -r $target")) { vlog(0, _tr("unable to remove export '%s'!", $target)); return 0; } return 1; } sub checkRequirements { my $self = shift; my $vendorOSPath = shift; # determine most appropriate kernel version ... my $kernelVer = $self->_pickKernelVersion($vendorOSPath); # ... and check if that kernel-version provides all the required modules my $nfsMod = $self->_locateKernelModule( $vendorOSPath, 'nfs.ko', [ "$vendorOSPath/lib/modules/$kernelVer/kernel/fs/nfs", "$vendorOSPath/lib/modules/$kernelVer/kernel/fs" ] ); if (!defined $nfsMod) { warn _tr("unable to find nfs-module for kernel version '%s'.", $kernelVer); return; } return 1; } sub generateExportURI { my $self = shift; my $export = shift; my $vendorOS = shift; my $serverIP = $export->{server_ip} || ''; my $server = length($serverIP) ? $serverIP : generatePlaceholderFor('serverip'); my $port = $export->{port} || ''; $server .= ":$port" if length($port); my $exportPath = "$openslxConfig{'public-path'}/export"; return "nfs://$server$exportPath/nfs/$vendorOS->{name}"; } sub requiredFSMods { my $self = shift; return qw( nfs ); } sub showExportConfigInfo { my $self = shift; my $export = shift; print (('#' x 80)."\n"); print _tr("Please make sure the following line is contained in /etc/exports\nin order to activate the NFS-export of this vendor-OS:\n\t%s\n", "$self->{'export-path'}\t*(ro,no_root_squash,async,no_subtree_check)"); print (('#' x 80)."\n"); # TODO : add something a bit more clever here... # my $exports = slurpFile("/etc/exports"); return; } ################################################################################ ### implementation methods ################################################################################ sub _copyViaRsync { my $self = shift; my $source = shift; my $target = shift; if (system("mkdir -p $target")) { die _tr("unable to create directory '%s', giving up! (%s)\n", $target, $!); } my $includeExcludeList = $self->_determineIncludeExcludeList(); vlog(1, _tr("using include-exclude-filter:\n%s\n", $includeExcludeList)); my $rsyncFH; my $additionalRsyncOptions = $ENV{SLX_RSYNC_OPTIONS} || ''; my $rsyncCmd = "rsync -av --numeric-ids --delete-excluded --exclude-from=- $additionalRsyncOptions" . " $source/ $target"; vlog(2, "executing: $rsyncCmd\n"); open($rsyncFH, '|-', $rsyncCmd) or die _tr("unable to start rsync for source '%s', giving up! (%s)", $source, $!); print $rsyncFH $includeExcludeList; close($rsyncFH) or die _tr("unable to export to target '%s', giving up! (%s)", $target, $!); return; } sub _determineIncludeExcludeList { my $self = shift; # Rsync uses a first match strategy, so we mix the local specifications # in front of the filterset given by the package (as the local filters # should always overrule the vendor filters): my $distroName = $self->{engine}->{'distro-name'}; my $localFilterFile = "$openslxConfig{'config-path'}/distro-info/$distroName/export-filter"; my $includeExcludeList = slurpFile($localFilterFile, { failIfMissing => 0 }); $includeExcludeList .= $self->{engine}->{distro}->{'export-filter'}; $includeExcludeList =~ s[^\s+][]igms; # remove any leading whitespace, as rsync doesn't like it return $includeExcludeList; } sub _isTargetBindMounted { my $self = shift; my $source = shift; my $target = shift; # For development purposes, it is very desirable to be able to take a # shortcut that avoids doing the actual copying of the folders (as that # takes a considerable amount of time). # In order to support this, we explicitly check if the OpenSLX NFS export # root folder (/srv/openslx/export/nfs) is a bind-mount of the OpenSLX # stage1 folder (/var/opt/openslx/stage1). # If that is the case, we print a notice and skip the rsync step (which # wouldn't work anyway, as source and target folder are the same). my $stage1Root = dirname($source); my $nfsRoot = dirname($target); chomp(my $canonicalStage1Root = qx{readlink -f $stage1Root} || $stage1Root); chomp(my $canonicalNFSRoot = qx{readlink -f $nfsRoot} || $nfsRoot); my @mounts = slurpFile('/etc/mtab'); for my $mount (@mounts) { if ($mount =~ m{ ^ $canonicalStage1Root # mount source \s+ $canonicalNFSRoot # mount target \s+ none # filesystem for bind mounts is 'none' \s+ \S*\bbind\b\S* # look for bind mounts only }gmsx) { return 1; } } return 0; } 1;