# Copyright (c) 2006, 2007 - 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/
# -----------------------------------------------------------------------------
# NBD_Squash.pm
# - provides NBD+Squashfs-specific overrides of the OpenSLX::OSExport::ExportType API.
# -----------------------------------------------------------------------------
package OpenSLX::OSExport::ExportType::NBD_Squash;
use vars qw(@ISA $VERSION);
@ISA = ('OpenSLX::OSExport::ExportType::Base');
$VERSION = 1.01; # API-version . implementation-version
use strict;
use Carp;
use File::Basename;
use OpenSLX::Basics;
use OpenSLX::ConfigDB qw(:support);
use OpenSLX::OSExport::ExportType::Base 1.01;
################################################################################
### interface methods
################################################################################
sub new
{
my $class = shift;
my $self = {
'name' => 'NBD_Squash',
};
return bless $self, $class;
}
sub exportVendorOS
{
my $self = shift;
my $source = shift;
my $target = shift;
my $includeExcludeList = $self->determineIncludeExcludeList();
# in order to do the filtering as part of mksquashfs, we need to map
# our internal (rsync-)filter format to regexes:
$includeExcludeList
= mapRsyncFilter2Regex($source, $includeExcludeList);
vlog 1, _tr("using include-exclude-filter:\n%s\n", $includeExcludeList);
$self->createSquashFS($source, $target, $includeExcludeList);
}
sub purgeExport
{
my $self = shift;
my $target = shift;
if (system("rm $target")) {
vlog 0, _tr("unable to remove export '%s'!", $target);
return 0;
}
1;
}
sub checkRequirements
{
my $self = shift;
my $vendorOSPath = shift;
my $kernel = shift || 'vmlinuz';
while (-l "$vendorOSPath/boot/$kernel") {
$kernel = readlink "$vendorOSPath/boot/$kernel";
}
if ($kernel !~ m[^vmlinuz-(.+)$]) {
die _tr("unable to determine version of kernel '%s'!", $kernel);
}
my $kernelVer = $1;
if (!locateKernelModule(
$vendorOSPath,
'nbd.ko',
["$vendorOSPath/lib/modules/$kernelVer/kernel/drivers/block"])
) {
warn _tr("unable to find nbd-module for kernel version '%s'.",
$kernelVer);
return 0;
}
if (!locateKernelModule(
$vendorOSPath,
'squashfs.ko',
["$vendorOSPath/lib/modules/$kernelVer/kernel/fs/squashfs",
"$vendorOSPath/lib/modules/$kernelVer/kernel/fs"])
) {
warn _tr("unable to find squashfs-module for kernel version '%s'.",
$kernelVer);
return 0;
}
1;
}
sub addExportToConfigDB
{
my $self = shift;
my $export = shift;
my $openslxDB = shift;
$export->{port}
= $openslxDB->incrementGlobalCounter('next-nbd-server-port');
my $res = $openslxDB->addExport($export);
$self->showNbdParams($export) if $res;
return $res;
}
sub generateExportURI
{
my $self = shift;
my $export = shift;
my $server
= length($export->{server_ip})
? $export->{server_ip}
: generatePlaceholderFor('serverip');
$server .= ":$export->{port}" if length($export->{port});
return "nbd://$server/squashfs";
}
sub requiredFSMods
{
my $self = shift;
return 'nbd squashfs';
}
################################################################################
### implementation methods
################################################################################
sub createSquashFS
{
my $self = shift;
my $source = shift;
my $target = shift;
my $includeExcludeList = shift;
system("rm -f $target");
# mksquasfs isn't significantly faster if fs already exists, but it
# causes the filesystem to grow somewhat, so we remove it in order to
# get the smallest FS-file possible.
my $baseDir = dirname($target);
if (!-e $baseDir) {
if (system("mkdir -p $baseDir")) {
die _tr("unable to create directory '%s', giving up! (%s)\n",
$baseDir, $!);
}
}
# dump filter to a file ...
my $filterFile = "/tmp/slx-nbdsquash-filter-$$";
open(FILTERFILE,"> $filterFile")
or die _tr("unable to create tmpfile '%s' (%s)", $filterFile, $!);
print FILTERFILE $includeExcludeList;
close(FILTERFILE);
# ... invoke mksquashfs ...
vlog 0, _tr("invoking mksquashfs...");
my $mksquashfsBinary
= "$openslxConfig{'share-path'}/squashfs/mksquashfs";
my $res = system("$mksquashfsBinary $source $target -ff $filterFile");
unlink($filterFile);
# ... remove filter file if done
if ($res) {
die _tr("unable to create squashfs for source '%s' as target '%s', giving up! (%s)",
$source, $target, $!);
}
}
sub showNbdParams
{
my $self = shift;
my $export = shift;
print (('#' x 80)."\n");
print _tr("Please make sure you start a corresponding nbd-server:\n\t%s\n",
"nbd-server $export->{port} $self->{engine}->{'export-path'} -r");
print (('#' x 80)."\n");
}
sub mapRsyncFilter2Regex
{
my $sourcePath = shift;
return
join "\n",
map {
if ($_ =~ m[^([-+]\s*)(.+?)\s*$]) {
my $action = $1;
my $regex = $2;
$regex =~ s[\*\*][.+]g;
# '**' matches everything
$regex =~ s[\*][[^/]+]g;
# '*' matches anything except slashes
$regex =~ s[\?][[^/]?]g;
# '*' matches any single char except slash
$regex =~ s[\?][[^/]?]g;
# '*' matches any single char except slash
$regex =~ s[\.][\\.]g;
# escape any dots
if (substr($regex, 0, 1) eq '/') {
# absolute path given, need to extend by source-path:
"$action^$sourcePath$regex\$";
} else {
# filename pattern given, need to anchor to the end only:
"$action$regex\$";
}
} else {
$_;
}
}
split "\n", shift;
}
sub locateKernelModule
{
my $vendorOSPath = shift;
my $moduleName = shift;
my $defaultPaths = shift;
vlog 1, _tr("locating kernel-module '%s'", $moduleName);
# check default paths first:
foreach my $defPath (@$defaultPaths) {
vlog 2, "trying $defPath/$moduleName";
return "$defPath/$moduleName" if -e "$defPath/$moduleName";
}
# use brute force to search for the newest incarnation of the module:
use File::Find;
my $location;
my $locationAge = 9999999;
vlog 2, "searching in $vendorOSPath/lib/modules";
find sub {
return unless $_ eq $moduleName;
if (-M _ < $locationAge) {
$locationAge = -M _;
$location = $File::Find::name;
vlog 2, "located at $location (age=$locationAge days)";
}
}, "$vendorOSPath/lib/modules";
return $location;
}
1;