Skip to content

Commit

Permalink
factor out QCOW and mount into plugins
Browse files Browse the repository at this point in the history
  • Loading branch information
jaybuff committed Feb 10, 2011
1 parent e8b0d26 commit d549644
Show file tree
Hide file tree
Showing 10 changed files with 420 additions and 265 deletions.
5 changes: 3 additions & 2 deletions bin/joot_image
Expand Up @@ -3,7 +3,8 @@
use strict;
use warnings;

use Joot::Util qw( mkpath bin run sudo nbd_connect nbd_disconnect );
use Joot::Util qw( mkpath bin run sudo );
use Joot::Plugin::QCOW qw( nbd_connect nbd_disconnect is_disk_connected );
use Getopt::Long ();
use File::Spec ();
use Log::Log4perl qw(:easy);
Expand All @@ -21,7 +22,7 @@ if ( $options{mount} ) {
run( bin("mount"), $device, $mnt );
}
elsif ( $options{umount} ) {
if ( my $device = Joot::Util::is_disk_connected($image) ) {
if ( my $device = is_disk_connected($image) ) {
run( bin("umount"), $device );
nbd_disconnect($device);
}
Expand Down
2 changes: 2 additions & 0 deletions joot.cfg
Expand Up @@ -24,6 +24,8 @@
"pass_thru_env": [ "SSH_AUTH_SOCK", "JOOT_CONFIG" ],

"plugins": [
"QCOW",
"BindMount",
"Users",
"SudoUsers"
]
Expand Down
122 changes: 19 additions & 103 deletions lib/Joot.pm
Expand Up @@ -6,6 +6,7 @@ use warnings;
use vars '$VERSION';
$VERSION = "0.0.1";

use Cwd ();
use English '-no_match_vars';
use File::Copy ();
use File::Spec ();
Expand Down Expand Up @@ -214,68 +215,9 @@ sub mount { ## no critic qw(Subroutines::RequireArgUnpacking)
die "Joot \"$joot_name\" does not exist\n";
}

# connect and mount the joot first
# this is a no op if it's already connected/mounted
my $mnt = $self->mount_point();
mkpath($mnt);

my $device = nbd_connect( $self->disk() );
my $conf = $self->get_config();

# partition is always > 0 if it exists
if ( exists( $conf->{image} ) && $conf->{image}->{root_partition} ) {
my $part = $conf->{image}->{root_partition};
$device = "${device}p$part";

# when the device is first connected, it takes a bit for the /dev
# device to be created. we'll give it 5 seconds before giving up
local $SIG{ALRM} = sub { die "config for $joot_name says root_partition is $part, but $device doesn't exist\n"; };
alarm(5);
while (1) {
if ( -e $device ) {
alarm(0); # cancel alarm
last;
}
}
}

#TODO some images have partitions (mount ${device}p1 etc)
if ( !is_mounted($mnt) ) {
run( bin('mount'), $device, $mnt );
}
else {
DEBUG "$mnt is already mounted";
}

if ( !@dirs && !$args->{'no-automount'} ) {
return $self->automount();
}

# if user passes in /.//foo and /foo/bar we need to
# mount /foo then /foo/bar
foreach my $dir ( sort map { Cwd::abs_path($_) } @dirs ) {
my $target = Cwd::abs_path("$mnt/$dir");

if ( is_mounted($target) ) {
DEBUG "$target is already mounted";
next;
}

if ( !-e $dir ) {
WARN "$dir doesn't exist. Not trying to mount";
next;
}

mkpath($target);
run( bin('mount'), '--bind', $dir, $target );

# can't bind mount readonly in one mount command
# see http://lwn.net/Articles/281157/
if ( $args->{'read-only'} ) {
run( bin('mount'), '-o', 'remount,ro', $target );
}
}
$self->run_hook( "mount", $args, @dirs );

my $conf = $self->get_config();
if ( $args->{always} ) {
delete $args->{always};
foreach my $dir (@dirs) {
Expand Down Expand Up @@ -330,41 +272,11 @@ sub umount { ## no critic qw(Subroutines::RequireArgUnpacking)
die "Joot \"" . $self->name() . "\" does not exist\n";
}

my $mnt = Cwd::abs_path( $self->mount_point() );
if ( !@dirs ) {
DEBUG "unmounting all mounts for this joot";
foreach my $dir ( grep {/^$mnt/x} get_mounts() ) {
run( bin("umount"), $dir );
}

# by now $mnt is unmounted, so we can disconnect the disk from nbd
my $disk = $self->disk();
if ( my $device = Joot::Util::is_disk_connected($disk) ) {
nbd_disconnect($device);
}
else {
DEBUG "$disk wasn't connect to a nbd device; not trying to disconnect";
}
return;
if ( $self->run_hook( "umount", $args, @dirs ) == 0 ) {
die "There are no plugins registered that implement the umount hook\n";
}

# if the joot itself isn't mounted, there can't be anything mounted under it
if ( !is_mounted($mnt) ) {
DEBUG "joot isn't mounted, nothing to do";
return;
}

# if user passes in /.//foo and /foo/bar we need to
# umount /foo/bar then /foo
foreach my $dir ( reverse sort map { Cwd::abs_path($_) } @dirs ) {
my $target = Cwd::abs_path("$mnt/$dir");
if ( is_mounted($target) ) {
run( bin("umount"), $target );
}
else {
DEBUG "$dir isn't mounted in " . $self->name();
}
}
$self->run_hook( "post_umount", $args, @dirs );

return;
}
Expand Down Expand Up @@ -413,7 +325,11 @@ sub create {
}

mkpath($joot_dir);
run( bin("qemu-img"), qw(create -f qcow2 -o), "backing_file=" . $image->path(), $self->disk() );
$self->run_hook( "pre_create", $image );

if ( $self->run_hook( "create", $image ) == 0 ) {
die "There are no plugins registered that implement the create hook\n";
}

my $conf = {
image => $image->config(),
Expand Down Expand Up @@ -452,13 +368,6 @@ sub create {
return 1;
}

sub disk {
my $self = shift;

my $joot_dir = $self->joot_dir();
return "$joot_dir/disk.qcow2";
}

#TODO also list images that are downloaded, but not referenced in any index
sub images {
my $self = shift;
Expand Down Expand Up @@ -582,23 +491,30 @@ sub load_plugins {
}
}

return;
}

# returns number of hooks run;
sub run_hook {
my $self = shift;
my $hook = shift or die "missing hook name\n";
my @args = @_;

my $count = 0;
foreach my $plugin ( @{ config("plugins") } ) {
my $class = "Joot::Plugin::$plugin";
if ( $class->can($hook) ) {
my $function = "${class}::$hook";
DEBUG "Dispatching to $function";
{
no strict 'refs';
$function->($self);
$function->($self, @args);
$count++;
}
}
}

return $count;
}

1;
Expand Down
107 changes: 107 additions & 0 deletions lib/Joot/Plugin/BindMount.pm
@@ -0,0 +1,107 @@
package Joot::Plugin::BindMount;

use strict;
use warnings;

use Cwd ();
use Joot ();
use Joot::Util qw(get_mounts is_mounted bin run mkpath);
use Log::Log4perl ':easy';

sub mount {
my $joot = shift;
my $args = shift;
my @dirs = @_;

if ( !@dirs && !$args->{'no-automount'} ) {
return $joot->automount();
}

# if user passes in /.//foo and /foo/bar we need to
# mount /foo then /foo/bar
my $mnt = $joot->mount_point();
foreach my $dir ( sort map { Cwd::abs_path($_) } @dirs ) {
my $target = Cwd::abs_path("$mnt/$dir");

if ( is_mounted($target) ) {
DEBUG "$target is already mounted";
next;
}

if ( !-e $dir ) {
WARN "$dir doesn't exist. Not trying to mount";
next;
}

mkpath($target);
run( bin('mount'), '--bind', $dir, $target );

# can't bind mount readonly in one mount command
# see http://lwn.net/Articles/281157/
if ( $args->{'read-only'} ) {
run( bin('mount'), '-o', 'remount,ro', $target );
}
}

return;
}

# unmount specified dirs or everything if no dirs passed in
sub umount {
my $joot = shift;
my $args = shift;
my @dirs = @_;

my $mnt = Cwd::abs_path( $joot->mount_point() );

# if the joot itself isn't mounted, there can't be anything mounted under it
if ( !is_mounted($mnt) ) {
DEBUG "joot isn't mounted, nothing to do";
return;
}

if ( !@dirs ) {
DEBUG "unmounting mounted dirs for this joot";
foreach my $dir ( grep {/^$mnt/x} get_mounts() ) {
run( bin("umount"), $dir );
}

return;
}

# if user passes in /.//foo and /foo/bar we need to
# umount /foo/bar then /foo
foreach my $dir ( reverse sort map { Cwd::abs_path($_) } @dirs ) {
my $target = Cwd::abs_path("$mnt/$dir");
if ( is_mounted($target) ) {
run( bin("umount"), $target );
}
else {
DEBUG "$dir isn't mounted in " . $joot->name();
}
}

return;
}

1;

__END__
=head1 AUTHOR
Jay Buffington (jaybuffington@gmail.com)
=head1 COPYRIGHT
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this software except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

0 comments on commit d549644

Please sign in to comment.