IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Ignore:
Timestamp:
Aug 7, 2009, 4:08:25 PM (17 years ago)
Author:
Paul Price
Message:

Merging trunk (r25026) to get up-to-date on old branch.

Location:
branches/pap
Files:
15 deleted
17 edited
18 copied

Legend:

Unmodified
Added
Removed
  • branches/pap

  • branches/pap/Nebulous-Server

  • branches/pap/Nebulous-Server/Build.PL

    r23932 r25027  
    1010    requires            => {
    1111        'Apache2::Const'        => 0,
     12        'Cache::Memcached'      => 0,
    1213        'Class::Accessor::Fast' => 0,
    1314        'Config::YAML'          => '1.42',
  • branches/pap/Nebulous-Server/Changes

    r23932 r25027  
    22
    330.17
     4    - add basic valid key caching via memcached
     5    - add params checking to Nebulous::Key::parse_neb_key()
     6    - restrict creation/modification/remove of xattrs to the user.* namespace
     7    - add chmod_object() method
    48    - retry database transactions when a deadlock is detected
    59    - add log4perl logging to nebdiskd
     
    1620    - change 'log_level' param to 'trace'
    1721    - refactor ->find_objects() functionality
     22    - rename Nebulous::Key->soft_volume Nebulous::Key->hard_volume and invert
     23      the semantics
     24    - add nebdiskd mountpoint test retyring
     25    - rework delete_instance() to avoid requiring an index on instance.uri
     26    - nebdiskd bug fixes: fix debug mode outpoint going to mail, remove db
     27      passwd requirement (not all dbs require a password param)
     28    - completely rework how mountedvol is populated, drop mount table
     29    - infinitely try to get a db handle if the connection fails
     30    - add --mountpoint param to neb-voladd
     31    - change Nebulous::Server->find_objects() to return dirs and to sort it's
     32      output
     33    - add the ability to delete a storage object when it has instances that are
     34      offline
     35    - change Nebulous::Server->stat_object() to return both the total number of
     36      instance and just those that are available
     37    - add Nebulous::Server->prune_object() API
     38    - don't replicate to the volume being implied by the key being replicated!
    1839     
    19400.16
  • branches/pap/Nebulous-Server/MANIFEST

    r23932 r25027  
    3333t/01_load.t
    3434t/02_config.t
    35 t/02_server_setup.t
    36 t/03_server_create_object.t
    37 t/04_server_replicate_object.t
    38 t/05_server_lock_object.t
    39 t/06_server_unlock_object.t
    40 t/07_server_find_instances.t
    41 t/08_server_delete_instance.t
    42 t/09_server_stat_object.t
    43 t/10_server_is_valid_volume_name.t
    44 t/11_server_is_valid_object_key.t
    45 t/12_server_find_objects.t
    46 t/13_server_rename_object.t
    47 t/14_server_xattr.t
    48 t/15_mounts.t
    49 t/16_server_swap_objects.t
     35t/03_server_setup.t
     36t/04_server_create_object.t
     37t/05_server_replicate_object.t
     38t/06_server_lock_object.t
     39t/07_server_unlock_object.t
     40t/08_server_find_instances.t
     41t/09_server_delete_instance.t
     42t/10_server_stat_object.t
     43t/11_server_is_valid_volume_name.t
     44t/12_server_is_valid_object_key.t
     45t/13_server_find_objects.t
     46t/14_server_rename_object.t
     47t/15_server_xattr.t
     48t/16_mounts.t
     49t/17_server_swap_objects.t
     50t/18_server_chmod_object.t
     51t/19_server_prune_object.t
    5052t/75_parse_neb_key.t
  • branches/pap/Nebulous-Server/bin/neb-admin

    r23932 r25027  
    9191{
    9292# so_id, ext_id, instances, available_instances, need_recovery, recoverable
    93     $dbh->do("CREATE TEMPORARY TABLE mymountedvol LIKE mountedvol");
    94     $dbh->do("INSERT INTO mymountedvol SELECT * FROM mountedvol");
    95     $dbh->do("CREATE TEMPORARY TABLE myvolume LIKE volume");
    96     $dbh->do("INSERT INTO myvolume SELECT * FROM volume");
     93# XXX don't remove the temp table code -- testing w/o it
     94#    $dbh->do("CREATE TEMPORARY TABLE mymountedvol LIKE mountedvol");
     95#    $dbh->do("INSERT INTO mymountedvol SELECT * FROM mountedvol");
    9796
    9897    if (not defined $so_id_start) { $so_id_start = 0; }
     
    102101    # XXX check if so_id_start is beyond MAX(so_id): if so, exit with exit status 10
    103102    {
    104         my $query = $dbh->prepare("SELECT MAX(so_id) from storage_object");
    105         $query->execute;
     103        my $query = $dbh->prepare("SELECT MAX(so_id) from storage_object");
     104        $query->execute;
    106105        my $answer = $query->fetchrow_arrayref;
    107106        my $max_so_id = $$answer[0];
    108         $query->finish;
    109 
    110         if ($so_id_start > $max_so_id) {
    111             print STDERR "at end of so_id range, reset please\n";
    112             exit 10;
    113         }
    114     }
    115        
     107        $query->finish;
     108
     109        if ($so_id_start > $max_so_id) {
     110            print STDERR "at end of so_id range, reset please\n";
     111            exit 10;
     112        }
     113    }
     114       
    116115    my $query = $dbh->prepare(
    117     "        SELECT
    118                 storage_object.so_id,
    119                 ext_id,
    120                 count(ins_id) as instances,
    121                 myvolume.name as volume_name,
    122                 myvolume.host as volume_host,
    123                 count(mymountedvol.vol_id) as available_instances,
    124                 count(mymountedvol.vol_id) > 0 as recoverable,
    125                 storage_object_xattr.value as copies
    126             FROM storage_object
    127             JOIN instance
    128                 USING(so_id)
    129             JOIN myvolume
    130                 USING(vol_id)
    131             LEFT JOIN storage_object_xattr
    132                 ON storage_object.so_id = storage_object_xattr.so_id
    133             JOIN mymountedvol
    134                 USING(vol_id)
    135             WHERE mymountedvol.available = 1
    136             AND storage_object_xattr.name = 'user.copies'
    137             AND storage_object.so_id >= $so_id_start
    138             AND storage_object.so_id <  $so_id_end
    139             GROUP BY so_id
    140             HAVING available_instances < instances OR instances < copies
    141             LIMIT $limit"
     116    "
     117        SELECT
     118            so.so_id,
     119            so.ext_id,
     120            count(ins_id) as instances,
     121            mv.name as volume_name,
     122            mv.host as volume_host,
     123            count(mv.vol_id) as available_instances,
     124            count(mv.vol_id) > 0 as recoverable,
     125            xattr.value as copies
     126        FROM storage_object AS so
     127        LEFT JOIN storage_object_xattr AS xattr
     128            ON so.so_id = xattr.so_id
     129            AND xattr.name = 'user.copies'
     130        JOIN instance AS i
     131            ON so.so_id = i.so_id
     132        JOIN mountedvol AS mv
     133            USING(vol_id)
     134        WHERE
     135            mv.available = 1
     136            AND so.so_id >= $so_id_start
     137            AND so.so_id <  $so_id_end
     138        GROUP BY so_id
     139        HAVING available_instances < instances OR instances < copies
     140        LIMIT $limit"
    142141    );
    143142    $query->execute;
    144143
    145     $dbh->do("DROP TABLE IF EXISTS mymountedvol");
     144#    $dbh->do("DROP TABLE IF EXISTS mymountedvol");
    146145
    147146    my @rows;
     
    174173        # if the copies xattr is unset and the object has 2 or more instances, we
    175174        # will assume a target of 2 copies
    176        
    177         # XXX change this: there should be no default value or we will
    178         # have a race condition.  user.copies should never get set by
    179         # any client until AFTER a file is no longer in use.
     175       
     176        # XXX change this: there should be no default value or we will
     177        # have a race condition.  user.copies should never get set by
     178        # any client until AFTER a file is no longer in use.
    180179        my $copies = $obj->{copies} || 2;
    181180
     
    207206    # use a different exit status if we hit the limit (likely more files pending)
    208207    if ($Npending == $limit) {
    209         exit 1;
     208        exit 1;
    210209    }
    211210    exit 0;
     
    225224
    226225    $dbh->do("DROP TABLE IF EXISTS mymountedvol");
     226    $dbh->do("DROP TABLE IF EXISTS myvolume");
    227227
    228228    my @rows;
  • branches/pap/Nebulous-Server/bin/neb-voladd

    r23348 r25027  
    1919use Pod::Usage qw( pod2usage );
    2020
    21 my ($db, $dbhost, $dbuser, $dbpass, $vname, $vhost, $uri);
     21my ($db, $dbhost, $dbuser, $dbpass, $mountpoint, $vname, $vhost, $uri);
    2222
    2323$db     = $ENV{'NEB_DB'} unless $db;
     
    2929    'db|d=s'            => \$db,
    3030    'host=s'            => \$dbhost,
     31    'mountpoint|m=s'    => \$mountpoint,
     32    'pass|p=s'          => \$dbpass,
     33    'uri|u=s'           => \$uri,
    3134    'user|u=s'          => \$dbuser,
    32     'pass|p=s'          => \$dbpass,
     35    'vhost=s'           => \$vhost,
    3336    'vname|n=s'         => \$vname,
    34     'vhost=s'           => \$vhost,
    35     'uri|u=s'           => \$uri,
    3637) || pod2usage( 2 );
    3738
    3839pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
    39 pod2usage( -msg => "Required options: --db --user --pass --vname --vhost --uri", -exitval => 2 )
    40     unless $db && $dbuser && $dbpass && $vname && $vhost && $uri;
     40pod2usage( -msg => "Required options: --db --user --pass --mountpoint --vname --vhost --uri", -exitval => 2 )
     41    unless $db && $dbuser && $dbpass && $mountpoint && $vname && $vhost && $uri;
    4142
    4243my $dbh = DBI->connect(
     
    5960    die "path: $path dirived from URI: $uri does not exist";
    6061}
     62unless (-d $mountpoint) {
     63    die "path: $mountpoint does not exist";
     64}
    6165
    6266print " OK\n";
     
    6569
    6670my $query = $dbh->prepare( $sql->new_volume );
    67 $query->execute( $vname, $vhost, $path );
     71$query->execute( $vname, $vhost, $path, $mountpoint);
    6872
    6973print " OK\n";
     
    128132Optional.  Defaults to C<localhost>.
    129133
     134=item * --mountpoint <path>
     135
     136Path to the mountpoint of the filesystem on which C<--uri> resides.
    130137
    131138=back
  • branches/pap/Nebulous-Server/bin/neb-voladm

    r20204 r25027  
    9191        my $query = $dbh->prepare($q);
    9292        $query->execute(@bind);
    93         $dbh->do("call getmountedvol");
    9493        $dbh->commit;
    9594    };
  • branches/pap/Nebulous-Server/bin/nebdiskd

    r23675 r25027  
    11#!/usr/bin/env perl
    22
    3 # Copyright (C) 2007  Joshua Hoblitt
    4 #
    5 # $Id: nebdiskd,v 1.14 2008-10-16 22:51:34 jhoblitt Exp $
     3# Copyright (C) 2007-2009  Joshua Hoblitt
    64
    75use strict;
     
    3533    $user,
    3634    $group,
     35    $retry,
    3736);
    3837
    3938GetOptions(
    40     'dbhost|h=s'    => \$dbhost,
     39    'dbhost|H=s'    => \$dbhost,
    4140    'dbpass|p=s'    => \$dbpass,
    4241    'dbuser|u=s'    => \$dbuser,
     
    4645    'pidfile=s'     => \$pidfile,
    4746    'restart|r'     => \$restart,
     47    'retry=i'       => \$retry,
    4848    'stop|s'        => \$stop,
    49     'user=s'        => \$user,
     49    'user|U=s'      => \$user,
    5050    'verbose|v'     => \$verbose,
    5151) || pod2usage( 2 );
     
    6161$dbhost     ||= $c->get_dbhost  || $ENV{'NEB_DBHOST'} || 'localhost';
    6262$dbuser     ||= $c->get_dbuser  || $ENV{'NEB_USER'};
    63 $dbpass     ||= $c->get_dbpass  || $ENV{'NEB_PASS'};
     63$dbpass     ||= $c->get_dbpass  || $ENV{'NEB_PASS'} || undef;
    6464$pidfile    ||= $c->get_pidfile || "/var/tmp/nebdiskd";
    6565$user       ||= $c->get_user    || $<; # user
    6666$group      ||= $c->get_group   || $); # group
    67 
    68 my $mounts = $c->get_mounts;
     67$retry      ||= $c->get_retry   || 1;
     68
     69#my $mounts = $c->get_mounts;
    6970my $poll_interval = $c->get_poll_interval || 5;
    7071
     
    8081$c->set_pidfile($pidfile);
    8182$c->set_poll_interval($poll_interval);
     83$c->set_retry($retry);
    8284$c->write;
    8385
    8486pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
    85 pod2usage( -msg => "Required options: --db --user --pass", -exitval => 2 )
    86     unless $db && $dbuser && $dbpass;
     87pod2usage( -msg => "Required options: --db --dbuser", -exitval => 2 )
     88    unless $db && $dbuser;
    8789
    8890# start up logging
     
    108110    log4perl.appender.Mailer.to      = ps-ipp-ops@ifa.hawaii.edu
    109111    log4perl.appender.Mailer.subject = nebdiskd alert
    110     log4perl.appender.AppError.Filter= MatchWarn
     112    log4perl.appender.Mailer.buffered = 0
     113    log4perl.appender.Mailer.Filter= MatchWarn
    111114    log4perl.appender.Mailer.layout = Log::Log4perl::Layout::PatternLayout
    112115    log4perl.appender.Mailer.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} | %H | %p | %M - %m%n
     116
     117    log4perl.appender.Limiter              = Log::Log4perl::Appender::Limit
     118    log4perl.appender.Limiter.appender     = Mailer
     119    log4perl.appender.Limiter.block_period = 300
    113120';
    114121Log::Log4perl::init(\$conf);
     
    136143                poll_interval   => $poll_interval,
    137144                debug           => $debug,
     145                retry           => $retry,
    138146        );
    139147    };
     
    155163    my $dbhost          = $p{dbhost} or return;
    156164    my $dbuser          = $p{dbuser} or return;
    157     my $dbpass          = $p{dbpass} or return;
     165#    my $dbpass          = $p{dbpass} or return;
    158166    my $poll_interval   = $p{poll_interval} || 60;
    159167    my $debug           = $p{debug} || 0;
     168    my $retry           = $p{retry} || 1;
    160169
    161170    # setup the db on every pass incase the database died on us
     
    168177
    169178    eval {
    170         my $r_query = $dbh->prepare_cached("REPLACE INTO mount VALUES(?, ?, ?)");
    171         my $d_query = $dbh->prepare_cached("DELETE FROM mount WHERE mountpoint = ?");
    172 
     179        my $r_query = $dbh->prepare_cached("REPLACE INTO mountedvol SELECT *, ?, ? FROM volume WHERE mountpoint = ?");
     180        my $d_query = $dbh->prepare_cached("DELETE FROM mountedvol WHERE mountpoint = ?");
     181
     182        # get list of mount points
     183        my $mounts = [];
     184        {
     185            # there may be multiple vol_ids per mountpoint but we only need to
     186            # check each mointpont once
     187            my $query = $dbh->prepare_cached("SELECT DISTINCT mountpoint FROM volume WHERE available = 1");
     188            $query->execute;
     189            while (my $row = $query->fetchrow_hashref) {
     190                push @$mounts, $row;
     191            }
     192            $query->finish;
     193        }
     194       
    173195        # determine valid mountpoints
    174196        foreach my $mnt (@$mounts) {
    175             $log->debug("checking $mnt");
     197            my $mountpoint = $mnt->{'mountpoint'};
     198            $log->debug("checking $mountpoint");
    176199            # this /SHOULD/ fail if the mount point is handled by the
    177200            # automounter and it fails to mount
    178             eval {
    179                 unless (is_mountpoint($mnt)) {
     201            my $tries = 0;
     202            TEST: eval {
     203                $tries++;
     204                unless (is_mountpoint($mountpoint)) {
    180205                    $log->warn("$mnt is not a valid mountpoint");
    181206                }
    182207            };
    183208            if ($@) {
     209                # try is_mountpoint() again if $retry > 1
     210                if ($tries < $retry) {
     211                    $log->warn("retrying test of $mountpoint");
     212                    goto TEST;
     213                }
    184214                $log->warn($@);
    185                 $d_query->execute($mnt);
     215                $d_query->execute($mountpoint);
    186216                next;
    187217            }
     
    190220            # we determine if it's a valid mountpoint incase
    191221            # is_mountpoint() invokes the automounter
    192             my $dev_info = df($mnt, 1024);
     222            my $dev_info = df($mountpoint, 1024);
    193223            unless (defined $dev_info) {
    194                 $log->error("can't find device info for $mnt");
     224                $log->error("can't find device info for $mountpoint");
    195225                next;
    196226            }
    197227
    198             $r_query->execute($mnt, @$dev_info{qw( blocks used )});
    199             $log->debug("adding $mnt to db");
     228            # find vol_id(s) for mountpoint
     229            $r_query->execute(@$dev_info{qw( blocks used )}, $mountpoint);
     230            $log->debug("adding $mountpoint to db");
    200231
    201232        }
    202233
    203         $dbh->do("call getmountedvol()");
    204 
    205         $dbh->commit;
    206         $log->debug("commited to database");
     234#        $dbh->do("call getmountedvol()");
     235
     236#        $dbh->commit;
     237#        $log->debug("commited to database");
    207238    };
    208239    if ($@) {
    209         $dbh->rollback;
    210         $log->debug("rolledback transaction");
     240#        $dbh->rollback;
     241#        $log->debug("rolledback transaction");
    211242        $log->logdie($@);
    212243    }
     
    235266    my %p = @_;
    236267
     268    # $p{dbpass} may be undef;
     269
    237270    return unless defined $p{db}
    238271              and defined $p{dbhost}
    239               and defined $p{dbuser}
    240               and defined $p{dbpass};
     272              and defined $p{dbuser};
    241273
    242274    my $sql = Nebulous::Server::SQL->new;
     
    249281            RaiseError => 1,
    250282            PrintError => 1,
    251             AutoCommit => 0,
     283            AutoCommit => 1, # don't want this to be trasnactional
    252284        },
    253285    );
     
    255287    eval {
    256288        $dbh->do( $sql->set_transaction_model );
    257         $dbh->commit;
     289#        $dbh->commit;
    258290    };
    259291    if ($@) {
    260         $dbh->rollback;
     292#        $dbh->rollback;
    261293        $log->logdie($@);
    262294    }
     
    344376=head1 SYNOPSIS
    345377
    346     nebdiskd [--db <db name>] [--user <db username>] [--pass <db password>] [--debug] [--pidfile <filename>] [--stop] [--restart] [--verbose]
     378    nebdiskd [--db|-D <db name>] [--dbhost|-H <db hostname>
     379    [--dbuser|-u <db username>] [--dbpass|-p <db password>] [--debug|-d]
     380    [--user|-U <username>] [--group|-g <groupname>] [--pidfile <filename>]
     381    [--retry <n>] [--stop|-s] [--restart|-r] [--verbose|-v]
    347382
    348383=head1 DESCRIPTION
     
    355390=over 4
    356391
    357 =item * --db|-d <db name>
     392=item * --db|-D <db name>
    358393
    359394Name of database (C<namespace>) to write too.
     
    362397variable is set.
    363398
    364 =item * --user|-u <db username>
     399=item * --dbuser|-u <username>
    365400
    366401Username to authenticate with.
     
    369404variable is set.
    370405
    371 =item * --pass|-p <db password>
     406=item * --dbpass|-p <password>
    372407
    373408Password to authenticate with.
     409
     410Optional if defined in the F<.nebdiskdrc> file or the appropriate environment
     411variable is set or if the database does not require a password.
     412
     413=item * --dbhost|-H <hostname>
     414
     415Database host to connect to.
    374416
    375417Optional if defined in the F<.nebdiskdrc> file or the appropriate environment
    376418variable is set.
    377419
    378 =item * --debug
     420=item * --debug|-d
    379421
    380422This flag prevents the program from "daemonizing" so output can be sent to
     
    397439instance.
    398440
    399 =item * --verbose|-r
     441=item * --retry
     442
     443The number of times to test a mountpoint for "mountedness" before giving up on
     444it.
     445
     446=item * --verbose|-v
    400447
    401448Turns on informational/debugging messages to be sent to the
     
    403450with C<--debug>.
    404451
     452=item * --user|-U
     453
     454user account to run daemon as.
     455
     456=item * --group|-g
     457
     458group to run daemon as.
     459
    405460=back
    406461
     
    415470=item * C<NEB_DB>
    416471
    417 Equivalent to --db|-d
     472Equivalent to --db|-D
    418473
    419474=item * C<NEB_USER>
    420475
    421 Equivalent to --user|-u
     476Equivalent to --dbuser|-u
    422477
    423478=item * C<NEB_PASS>
    424479
    425 Equivalent to --pass|-p
     480Equivalent to --dbpass|-p
    426481
    427482=back
     
    439494    dbpass: '@neb@'
    440495    dbuser: nebulous
    441     mounts:
    442       - /mnt
    443       - /tmp
    444       - /usr
    445496    pidfile: /var/tmp/nebdiskd
    446497    poll_interval: 5
     498    retry: 3
    447499
    448500The values C<db>, C<dbpass>, C<dbuser>, and C<pidfile> have the same semantics
     
    451503=over 4
    452504
    453 =item * C<mounts>
    454 
    455 A list of "paths" to C<stat(2)> before mounted volumes are polled.  The propose
    456 of this option is to attempt to keep "automounted" volumes mounted while this
    457 program is running.
    458 
    459505This value may be omitted or left blank.
    460506
     
    465511This value may be omitted or left blank.  The default value is C<60>s.
    466512
     513=item * C<retry>
     514
     515Same as C<--retry>.
     516
    467517=back
    468518
     
    481531=head1 COPYRIGHT
    482532
    483 Copyright (C) 2007  Joshua Hoblitt.  All rights reserved.
     533Copyright (C) 2007-2009  Joshua Hoblitt.  All rights reserved.
    484534
    485535This program is free software; you can redistribute it and/or modify it under
  • branches/pap/Nebulous-Server/lib/Nebulous/Key.pm

    r23932 r25027  
    88use warnings FATAL => qw( all );
    99
    10 our $VERSION = '0.02';
     10our $VERSION = '0.03';
    1111
    1212use base qw( Exporter Class::Accessor::Fast );
     
    1414use File::Spec;
    1515use URI::file;
     16use Carp qw( croak );
    1617use URI;
    1718use overload '""' => \&_stringify_key;
     
    2223);
    2324
    24 __PACKAGE__->mk_ro_accessors(qw( path volume soft_volume ));
     25__PACKAGE__->mk_ro_accessors(qw( path volume hard_volume ));
    2526
    2627sub parse_neb_key
    2728{
    2829    my ($key, $volume) = @_;
    29     return unless defined $key;
     30
     31    croak "key param is not optional" unless defined $key;
     32    croak "too many params" if scalar @_ > 2;
    3033
    3134    # white space is not allowed
    3235    if ($key =~ qr/\s+/) {
    33         die "keys and URIs may not contain whitespace";
     36        croak "keys and URIs may not contain whitespace";
    3437    }
    3538
     
    3942   
    4043    my $volume_name;
    41     my $soft_volume;
     44    my $hard_volume;
    4245    # if this is a valid uri
    4346    if (defined $scheme) {
    4447        # if so, does it use the neb scheme?
    45         die "URI does not use the 'neb' scheme"
     48        croak "URI does not use the 'neb' scheme"
    4649            unless $scheme eq 'neb';
    4750
     
    5861        # path component to it
    5962        unless (length $path) {
    60             die "neb URI scheme requires a path component";
     63            croak "neb URI scheme requires a path component";
    6164        }
    6265       
     
    6871        if (defined $volume_info->{volume} and $volume_info->{volume} ne 'any') {
    6972            $volume_name = $volume_info->{volume};
    70             $soft_volume = $volume_info->{soft_volume};
     73            $hard_volume = $volume_info->{hard_volume};
    7174        }
    7275
    7376        # require a leading slash if there is no volume name
    7477        if ((not defined $volume_name) and (not $path =~ m|^/|)) {
    75             die "neb URI scheme requires a leading slash, eg. neb:/";
     78            croak "neb URI scheme requires a leading slash, eg. neb:/";
    7679        }
    7780    } else {
     
    8184        if (defined $volume_info->{volume} and $volume_info->{volume} ne 'any') {
    8285            $volume_name = $volume_info->{volume};
    83             $soft_volume = $volume_info->{soft_volume};
     86            $hard_volume = $volume_info->{hard_volume};
    8487        }
    8588    }
     
    9699    return __PACKAGE__->new({
    97100        volume      => $volume_name,
    98         soft_volume => $soft_volume,
     101        hard_volume => $hard_volume,
    99102        path        => $path,
    100103    });
     
    107110    return unless defined $volume;
    108111
    109     my $soft_volume;
     112    my $hard_volume;
    110113    # check to see if there is a tilde and remove it if found
    111     unless (defined $volume and $volume =~ s/^~//) {
    112         $soft_volume = 1;
     114    if (defined $volume and $volume =~ s/^~//) {
     115        $hard_volume = 1;
    113116    }
    114117
    115     return({ volume => $volume, soft_volume => $soft_volume });
     118    return({ volume => $volume, hard_volume => $hard_volume });
    116119}
    117120
     
    122125
    123126    my $path        = $self->path;
    124     my $volume      = $self->volume || "";
    125     my $soft_volume = $self->soft_volume ? '~' : "";
    126 
    127     return "neb://${soft_volume}${volume}/$path";
     127    my $volume      = $self->volume || '';
     128    my $hard_volume = $self->hard_volume ? '~' : '';
     129
     130    return "neb://${hard_volume}${volume}/$path";
    128131}
    129132
  • branches/pap/Nebulous-Server/lib/Nebulous/Server.pm

    r23932 r25027  
    1313use base qw( Class::Accessor::Fast );
    1414
     15use Cache::Memcached;
    1516use DBI;
    1617use Digest::SHA1 qw( sha1_hex );
     18use Fcntl ':mode';
    1719use File::Basename qw( basename dirname fileparse );
    1820use File::ExtAttr qw( setfattr );
     
    2729use URI::file;
    2830
    29 __PACKAGE__->mk_accessors(qw( log sql config ));
    30 
    31 use constant SUBPATH_DEPTH  => 2;
    32 use constant NFS_RETRIES    => 100;
     31__PACKAGE__->mk_accessors(qw( log sql config cache ));
     32
     33use constant SUBPATH_DEPTH      => 2;
     34use constant NFS_RETRIES        => 100;
     35use constant NFS_RETRY_WAIT     => 1;
     36use constant TRANS_RETRY_WAIT   => 1;
     37
     38# transaction restart/retry regex
     39my $trans_regex = qr/Deadlock Found|Lock wait timeout exceeded|try restarting transaction|Can't connect to MySQL server/i;
    3340
    3441sub new
     
    4552sub new_from_config
    4653{
    47     my ($class, $config) = @_;
    48 
    49     # log4perl is not avaliable until we call init()
     54    my $class = shift;
     55
     56    my ($config) = @_;
     57
     58    # log4perl is not available until we call init()
    5059    Nebulous::Server::Log->init($config);
    5160    my $log = Log::Log4perl::get_logger( "Nebulous::Server" );
    5261    $log->level($config->trace);
     62    $log->debug( "entered - @_" );
    5363
    5464    my $sql = Nebulous::Server::SQL->new;
    55 
    56     $log->debug( "entered - @_" );
    5765
    5866    my $self = bless {}, ref $class || $class;
     
    6068    $self->sql($sql);
    6169    $self->config($config);
     70    $self->cache(
     71        Cache::Memcached->new({
     72            servers => $config->memcached_servers,
     73       })
     74    );
     75#    $self->cache->set("foo", "bar") or die "set failed";
     76#    $self->cache->get("foo") or die "get failed";
     77    $log->logdie("at least one database must be defined") unless $config->n_db;
     78
     79    # cause a db session to be started
     80    $self->_db_for_index(0);
    6281
    6382    $log->debug( "leaving" );
     
    7089sub _db_index_for_key
    7190{
    72     my ($self, $key) = @_;
     91    my $self = shift;
     92
     93    my $log     = $self->log;
     94    $log->debug( "entered - @_" );
     95
     96    my ($key) = @_;
    7397
    7498    my $config  = $self->config;
    7599
    76100    my $db_index = 0;
    77     die "key not defined" unless defined $key;
     101    $log->logdie("key not defined") unless defined $key;
    78102
    79103    # hash the key to select the correct database instance
     
    85109    $db_index = unpack("h8", sha1_hex($path)) % $config->n_db;
    86110
     111    $log->debug("index is $db_index");
     112    $log->debug("leaving");
     113
    87114    return $db_index;
    88115}
     
    90117sub _db_for_index
    91118{
    92     my ($self, $db_index) = @_;
     119    my $self = shift;
    93120
    94121    my $log     = $self->log;
     122    $log->debug( "entered - @_" );
     123
     124    my ($db_index) = @_;
     125
    95126    my $sql     = $self->sql;
    96127    my $config  = $self->config;
     
    108139    # lookup database info
    109140    my $db_config = $config->db($db_index);
    110     die "can't find database configuration info for database # $db_index"
     141    $log->logdie("can't find database configuration info for db # $db_index")
    111142        unless $db_config;
    112143
     
    116147    # processes and the database might have gone away on us.  Apache::DBI will
    117148    # take care of getting a valid dbh back.
    118     eval {
    119         $dbh = DBI->connect_cached(
    120             $db_config->dsn,
    121             $db_config->dbuser,
    122             $db_config->dbpasswd,
    123             {
    124                 RaiseError => 1,
    125                 PrintError => 0,
    126                 AutoCommit => 0,
    127             },
    128         );
    129 
    130         $dbh->do( $sql->set_transaction_model );
    131         $log->debug( "connected to database: ", sub { $dbh->data_sources; } );
    132         $dbh->commit;
    133         $log->debug("commit");
    134     };
    135     if ( $@ ) {
    136         $dbh->rollback if $dbh;
    137         $log->debug("rollback");
    138         $log->logdie( "database error: $@" );
     149    TRANS: while (1) {
     150        eval {
     151            $dbh = DBI->connect_cached(
     152                $db_config->dsn,
     153                $db_config->dbuser,
     154                $db_config->dbpasswd,
     155                {
     156                    RaiseError => 1,
     157                    PrintError => 0,
     158                    AutoCommit => 0,
     159                },
     160            );
     161
     162            $dbh->do( $sql->set_transaction_model );
     163            $log->debug( "connected to database: ", sub { $dbh->data_sources; } );
     164            $dbh->commit;
     165            $log->debug("commit");
     166        };
     167        if ($@) {
     168            $dbh->rollback if $dbh;
     169            $log->debug("rollback") if $dbh;
     170            if ($@ =~ qr/Can't connect to MySQL server/) {
     171                $log->warn("database error, retrying transaction: $@");
     172                sleep TRANS_RETRY_WAIT;
     173                redo TRANS;
     174            }
     175            $log->logdie( "database error: $@" );
     176        }
     177        last;
    139178    }
    140179
    141180    $self->{dbs}[$db_index] = $dbh;
    142181
     182    $log->debug("leaving");
     183
    143184    return $dbh;
    144185}
     
    148189    my $self = shift;
    149190
     191    my $log     = $self->log;
     192    $log->debug( "entered - @_" );
     193
    150194    my ($key) = validate_pos(@_,
    151195        {
     
    154198    );
    155199
    156     my $log     = $self->log;
    157200    my $sql     = $self->sql;
    158201    my $config  = $self->config;
    159202
    160     die "key not defined" unless defined $key;
     203    $log->logdie("key not defined") unless defined $key;
    161204    my $db_index = $self->_db_index_for_key($key);
    162205
    163206    my $dbh = $self->_db_for_index($db_index);
     207
    164208    return $dbh;
    165209}
     
    168212{
    169213    my $self = shift;
     214
     215    my $log = $self->log;
     216    $log->debug( "entered - @_" );
    170217
    171218    my ($key, $vol_name) = validate_pos(@_,
     
    186233    );
    187234
     235    my $sql = $self->sql;
     236
    188237    # vol_name overrides the key implied volume
    189     $key = parse_neb_key($key, $vol_name);
     238    eval {
     239        $key = parse_neb_key($key, $vol_name);
     240    };
     241    $log->logdie("$@") if $@;
    190242    $vol_name = $key->volume;
    191243
    192     my $log = $self->log;
    193     my $sql = $self->sql;
    194244    my $db  = $self->db($key);
    195 
    196     $log->debug( "entered - @_" );
    197245
    198246    # the key's volume can't be validiated on input for this method so we have
     
    200248    if (defined $vol_name
    201249        and not $self->_is_valid_volume_name($key, $key->volume)) {
    202         if ($key->soft_volume) {
     250        unless ($key->hard_volume) {
    203251            $log->warn( "$vol_name is not a known volume name" );
    204252            $vol_name = undef;
    205253        } else {
    206             die "$vol_name is not a valid volume name"
     254           $log->logdie("$vol_name is not a valid volume name");
    207255        }
    208256    }
    209257       
    210258    my ($vol_id, $vol_host, $vol_path, $vol_xattr)
    211         = $self->_get_storage_volume($key, $vol_name, $key->soft_volume);
     259        = $self->_get_storage_volume($key, $vol_name, $key->hard_volume);
    212260
    213261    my $parent_id = $self->_resolve_dir_parent_id(key => $key, create => 1);
     
    282330            $db->rollback;
    283331            $log->debug("rollback");
    284             if ($@ =~ /Deadlock found/) {
    285                 $log->warn("database deadlock retrying transaction: $@");
     332            if ($@ =~ $trans_regex) {
     333                $log->warn("database error, retrying transaction: $@");
     334                sleep TRANS_RETRY_WAIT;
    286335                redo TRANS;
    287336            }
     
    291340    }
    292341
     342    # add new key to the cache
     343    $self->cache->set($key->path, 1) if defined $self->cache;
     344    $log->debug( "key added to cache" );
     345
    293346    $log->debug("leaving");
    294347
     
    300353{
    301354    my $self = shift;
     355
     356    my $log = $self->log;
     357    $log->debug( "entered - @_" );
    302358
    303359    my %p = validate(@_,
     
    322378    my $key = $p{key};
    323379
    324     my $log = $self->log;
    325380    my $sql = $self->sql;
    326381    my $db  = $self->db($key);
    327 
    328     $log->debug( "entered - @_" );
    329382
    330383    # no path means '/', which has a dir_id & parent_id of 1
     
    394447                    $query->finish;
    395448                }
    396                 $log->logdie("failed to get LAST_INSERT_ID()")
     449                die("failed to get LAST_INSERT_ID()")
    397450                    unless $parent_id;
    398451
     
    403456            $db->rollback;
    404457            $log->debug("rollback");
    405             if ($@ =~ /Deadlock found/) {
    406                 $log->warn("database deadlock retrying transaction: $@");
     458            if ($@ =~ $trans_regex) {
     459                $log->warn("database error, retrying transaction: $@");
     460                $parent_id = 1;
     461                sleep TRANS_RETRY_WAIT;
    407462                redo TRANS;
    408463            }
     464            if ($@ =~ qr/Duplicate entry/) {
     465                $log->warn("Duplicate database entry, retrying transaction: $@");
     466                $parent_id = 1;
     467                sleep TRANS_RETRY_WAIT;
     468                redo TRANS;
     469            }
    409470            $log->logdie("error: $@");
    410471        }
     
    421482{
    422483    my $self = shift;
     484
     485    my $log = $self->log;
     486    $log->debug("entered - @_");
    423487
    424488    my ($key, $newkey) = validate_pos(@_,
     
    438502    );
    439503
     504    my $sql = $self->sql;
     505
    440506    # ignore volumes
    441     $key    = parse_neb_key($key);
    442     $newkey = parse_neb_key($newkey);
    443 
    444     my $log = $self->log;
    445     my $sql = $self->sql;
     507    eval {
     508        $key = parse_neb_key($key);
     509    };
     510    $log->logdie("$@") if $@;
     511    eval {
     512        $newkey = parse_neb_key($newkey);
     513    };
     514    $log->logdie("$@") if $@;
     515
    446516    my $db  = $self->db($key);
    447 
    448     $log->debug("entered - @_");
    449517
    450518    # XXX this may require database migration in the future
     
    464532            unless ($rows == 1) {
    465533                $query->finish;
    466                 $log->logdie("affected row count is $rows instead of 1");
    467             }
     534                die("affected row count is $rows instead of 1");
     535            }
     536
     537            $self->cache->delete($key->path) if defined $self->cache;
     538            $self->cache->set($newkey->path, 1) if defined $self->cache;
    468539
    469540            $db->commit;
     
    473544            $db->rollback;
    474545            $log->debug("rollback");
    475             if ($@ =~ /Deadlock found/) {
    476                 $log->warn("database deadlock retrying transaction: $@");
     546            if ($@ =~ $trans_regex) {
     547                $log->warn("database error, retrying transaction: $@");
     548                sleep TRANS_RETRY_WAIT;
    477549                redo TRANS;
    478550            }
     
    491563    my $self = shift;
    492564
     565    my $log = $self->log;
     566    $log->debug("entered - @_");
     567
    493568    my ($key1, $key2) = validate_pos(@_,
    494569        {
     
    506581    );
    507582
     583    my $sql = $self->sql;
     584
    508585    # ignore volumes
    509     $key1 = parse_neb_key($key1);
    510     $key2 = parse_neb_key($key2);
    511 
    512     my $log = $self->log;
    513     my $sql = $self->sql;
     586    eval {
     587        $key1 = parse_neb_key($key1);
     588    };
     589    $log->logdie("$@") if $@;
     590    eval {
     591        $key2 = parse_neb_key($key2);
     592    };
     593    $log->logdie("$@") if $@;
    514594
    515595    my $dbidx1 = $self->_db_index_for_key($key1);
    516596    my $dbidx2 = $self->_db_index_for_key($key2);
    517     die "cannot swap keys not stored on the same database" unless ($dbidx1 == $dbidx2);
     597    $log->logdie("cannot swap keys not stored on the same database")
     598        unless ($dbidx1 == $dbidx2);
    518599
    519600    my $dbh1 = $self->_db_for_index($dbidx1);
    520601    my $dbh2 = $self->_db_for_index($dbidx2);
    521     die "different db handles for the same db?" unless ($dbh1 == $dbh2);
    522 
    523     $log->debug("entered - @_");
     602    $log->logdie("different db handles for the same db?")
     603        unless ($dbh1 == $dbh2);
    524604
    525605    # order of operations for the swap with a single db is:
     
    543623              unless ($rows == 1) {
    544624                  $query->finish;
    545                   $log->logdie("affected row count is $rows instead of 1");
     625                  die("affected row count is $rows instead of 1");
    546626              }
    547627          }
     
    556636              unless ($rows == 1) {
    557637                  $query->finish;
    558                   $log->logdie("affected row count is $rows instead of 1");
     638                  die("affected row count is $rows instead of 1");
    559639              }
    560640          }
     
    569649              unless ($rows == 1) {
    570650                  $query->finish;
    571                   $log->logdie("affected row count is $rows instead of 1");
     651                  die("affected row count is $rows instead of 1");
    572652              }
    573653          }
     
    579659            $db->rollback;
    580660            $log->debug("rollback");
    581             if ($@ =~ /Deadlock found/) {
    582                 $log->warn("database deadlock retrying transaction: $@");
     661            if ($@ =~ $trans_regex) {
     662                $log->warn("database error, retrying transaction: $@");
     663                sleep TRANS_RETRY_WAIT;
    583664                redo TRANS;
    584665            }
     
    612693    my $self = shift;
    613694
    614     my ($key, $vol_name) = validate_pos(@_,
     695    my $log = $self->log;
     696    $log->debug("entered - @_");
     697
     698    my ($key, $dest_vol_name) = validate_pos(@_,
    615699        {
    616700            type        => SCALAR,
     
    633717    );
    634718
     719    my $sql = $self->sql;
     720
    635721    # if a volume name is explicity specified then we should make the
    636722    # replication onto that volume (even if there is alread an instance on that
     
    640726    # instance on it.  If all avilable volume already have an instance on them
    641727    # then we should throw an error
    642 
    643     # vol_name overrides the key implied volume
    644     $key = parse_neb_key($key, $vol_name);
    645     $vol_name = $key->volume;
    646 
    647     my $log = $self->log;
    648     my $sql = $self->sql;
     728    # volume names implied as part of the key are *IGNORED* as the source and
     729    # *SHOULD NOT* be used as the destination either
     730
     731    eval {
     732        $key = parse_neb_key($key);
     733    };
     734    $log->logdie("$@") if $@;
     735
    649736    my $db  = $self->db($key);
    650737
    651     $log->debug("entered - @_");
    652 
    653     if (defined $vol_name
     738    # puke if the source volume is bogus, we may want to actually use this as
     739    # the instance to be copied later
     740    if (defined $key->volume
    654741        and not $self->_is_valid_volume_name($key, $key->volume)) {
    655         if ($key->soft_volume) {
    656             $log->warn( "$vol_name is not a known volume name" );
    657             $vol_name = undef;
     742        unless ($key->hard_volume) {
     743            $log->warn($key->volume . " not a known volume name");
    658744        } else {
    659             die "$vol_name is not a valid volume name"
    660         }
     745           $log->logdie("$key is not a valid volume name");
     746        }
     747    }
     748    # puke if the source volume is bogus, we may want to actually use this as
     749    # the instance to be copied later
     750    if (defined $dest_vol_name
     751        and not $self->_is_valid_volume_name($key, $dest_vol_name)) {
     752           $log->logdie($key->volume . " is not a valid volume name");
    661753    }
    662754       
    663755    my ($vol_id, $vol_host, $vol_path, $vol_xattr);
    664     if (defined $vol_name) {
     756    if (defined $dest_vol_name) {
    665757        ($vol_id, $vol_host, $vol_path, $vol_xattr)
    666             = $self->_get_storage_volume($key, $vol_name);
     758            = $self->_get_storage_volume($key, $dest_vol_name);
    667759    } else {
    668760        ($vol_id, $vol_host, $vol_path, $vol_xattr)
     
    681773                unless ( $rows > 0 ) {
    682774                    $query->finish;
    683                     $log->logdie( "storage object does not exist" );
     775                    die( "storage object does not exist" );
    684776                }
    685777
     
    725817            $db->rollback;
    726818            # handle soft volumes
    727             if (defined $vol_name and defined $key->soft_volume) {
     819            if (defined $dest_vol_name and not defined $key->hard_volume) {
    728820                $log->debug("retrying with 'any' volume");
    729821                return $self->replicate_object($key->path, 'any');
    730822            }
    731823            $log->debug("rollback");
    732             if ($@ =~ /Deadlock found/) {
    733                 $log->warn("database deadlock retrying transaction: $@");
     824            if ($@ =~ $trans_regex) {
     825                $log->warn("database error, retrying transaction: $@");
     826                sleep TRANS_RETRY_WAIT;
    734827                redo TRANS;
    735828            }
     
    739832    }
    740833
     834    # check to see if the user.mode xattr exists
     835    eval {
     836        my $mode = $self->getxattr_object("$key", 'user.mode');
     837        if (defined $mode) {
     838            $self->chmod_object("$key", $mode);
     839        }
     840    };
     841    if ($@) {
     842        unless ($@ =~ qr/user.mode does not exist/) {
     843            $log->logdie("error: $@");
     844        }
     845    }
     846
    741847    $log->debug("leaving");
    742848
     
    745851
    746852
     853sub prune_object
     854{
     855    my $self = shift;
     856
     857    my $log = $self->log;
     858    $log->debug("entered - @_");
     859
     860    my ($key) = validate_pos(@_,
     861        {
     862            type        => SCALAR,
     863            callbacks   => {
     864                'is valid object key'
     865                    => sub { $self->_is_valid_object_key($_[0]) },
     866            },
     867        },
     868    );
     869
     870    my $sql = $self->sql;
     871
     872    eval {
     873        $key = parse_neb_key($key);
     874    };
     875    $log->logdie("$@") if $@;
     876
     877    my $db  = $self->db($key);
     878
     879    my $rows_removed = 0;
     880TRANS: while (1) {
     881        eval {
     882            # remove key from cache
     883            $self->cache->delete($key->path) if defined $self->cache;
     884
     885            my $so_id;
     886            {
     887                my $query = $db->prepare_cached( $sql->find_object_by_ext_id );
     888                $query->execute( $key->path );
     889                $so_id = $query->fetchrow_hashref->{'so_id'};
     890                $query->finish;
     891            }
     892
     893            # record the path of the innaccesible files for deferred
     894            # deletion
     895            my $rows_copied;
     896            {
     897                my $query = $db->prepare_cached( $sql->copy_dead_instances_to_deleted );
     898                $rows_copied = $query->execute( $so_id );
     899            }
     900
     901            # check to see if there is anything to be done
     902            unless ($rows_copied > 0) {
     903                $db->rollback;
     904                return;
     905            }
     906
     907            # In MySQL you can't select from a table your deleting rows from so
     908            # we first have to get a list of instances to be removed, and then
     909            # removed them.
     910            my $rows_found;
     911            {
     912                my $query = $db->prepare_cached( $sql->find_dead_instances_by_so_id );
     913                $rows_found = $query->execute( $so_id );
     914                foreach my $row ($query->fetchrow_hashref) {
     915                    # remove dead instances
     916                    my $query = $db->prepare_cached( $sql->delete_instance_by_ins_id);
     917                    $rows_removed += $query->execute( $row->{ins_id} );
     918                }
     919                $query->finish;
     920            }
     921
     922            # sanity check
     923            die("instances inaccessible ($rows_copied) != instances removed ($rows_removed)")
     924                unless $rows_copied == $rows_removed;
     925           
     926            $db->commit;
     927            $log->debug("commit");
     928        };
     929        if ($@) {
     930            $db->rollback;
     931            $log->debug("rollback");
     932            if ($@ =~ $trans_regex) {
     933                $log->warn("database error, retrying transaction: $@");
     934                sleep TRANS_RETRY_WAIT;
     935                redo TRANS;
     936            }
     937            $log->logdie("error: $@");
     938        }
     939        last;
     940    }
     941
     942    $log->debug("leaving");
     943
     944    return $rows_removed;
     945}
     946
     947
    747948sub lock_object
    748949{
    749950    my $self = shift;
     951
     952    my $log = $self->log;
     953    $log->debug( "entered - @_" );
    750954
    751955    my ( $key, $type ) = validate_pos( @_,
     
    764968    );
    765969
     970    my $sql = $self->sql;
     971
    766972    # ignore volume
    767     $key = parse_neb_key($key);
    768 
    769     my $log = $self->log;
    770     my $sql = $self->sql;
     973    eval {
     974        $key = parse_neb_key($key);
     975    };
     976    $log->logdie("$@") if $@;
     977
    771978    my $db  = $self->db($key);
    772 
    773     $log->debug( "entered - @_" );
    774979
    775980    my $so_id;
     
    785990                unless ( $rows == 1 ) {
    786991                    $query->finish;
    787                     $log->logdie( "storage object does not exist" );
     992                    die( "storage object does not exist" );
    788993                }
    789994
     
    8001005                # can't set a write lock if there are read locks
    8011006                if ($write_lock) {
    802                     $log->logdie("can not write lock twice -- retry");
     1007                    die("can not write lock twice -- retry");
    8031008                }
    8041009               
    8051010                if ($read_lock > 0) {
    806                     $log->logdie("can not write lock after read lock -- retry");
     1011                    die("can not write lock after read lock -- retry");
    8071012                }
    8081013
     
    8131018                    # if we affected more then one row something very bad has happened.
    8141019                    unless ($rows == 1) {
    815                         $log->logdie("affected row count is $rows instead of 1");
     1020                        die("affected row count is $rows instead of 1");
    8161021                    }
    8171022
     
    8201025                # can't set a read lock if there's a write lock
    8211026                if ($write_lock) {
    822                     $log->logdie("can not read lock after write lock -- retry");
     1027                    die("can not read lock after write lock -- retry");
    8231028                }
    8241029
     
    8291034                    # if we affected more then one row something very bad has happened.
    8301035                    unless ($rows == 1) {
    831                         $log->logdie("affected row count is $rows instead of 1");
     1036                        die("affected row count is $rows instead of 1");
    8321037                    }
    8331038                }
     
    8401045            $db->rollback;
    8411046            $log->debug("rollback");
    842             if ($@ =~ /Deadlock found/) {
    843                 $log->warn("database deadlock retrying transaction: $@");
     1047            if ($@ =~ $trans_regex) {
     1048                $log->warn("database error, retrying transaction: $@");
     1049                sleep TRANS_RETRY_WAIT;
    8441050                redo TRANS;
    8451051            }
     
    8581064{
    8591065    my $self = shift;
     1066
     1067    my $log = $self->log;
     1068    $log->debug( "entered - @_" );
    8601069
    8611070    my ( $key, $type ) = validate_pos( @_,
     
    8741083    );
    8751084
     1085    my $sql = $self->sql;
     1086
    8761087    # ignore volume
    877     $key = parse_neb_key($key);
    878 
    879     my $log = $self->log;
    880     my $sql = $self->sql;
     1088    eval {
     1089        $key = parse_neb_key($key);
     1090    };
     1091    $log->logdie("$@") if $@;
     1092
    8811093    my $db  = $self->db($key);
    882 
    883     $log->debug( "entered - @_" );
    8841094
    8851095    my $so_id;
     
    8951105                unless ($rows == 1) {
    8961106                    $query->finish;
    897                     $log->logdie("storage object does not exist");
     1107                    die("storage object does not exist");
    8981108                }
    8991109
     
    9091119                # can't remove a write lock if it doesn't exist
    9101120                if ($read_lock) {
    911                     $log->logdie("can not have a write lock under a read lock");
     1121                    die("can not have a write lock under a read lock");
    9121122                }
    9131123
    9141124                unless ($write_lock) {
    915                     $log->logdie("can not remove non-existant write lock");
     1125                    die("can not remove non-existant write lock");
    9161126                }
    9171127
     
    9221132                    # if we affected more then one row something very bad has happened.
    9231133                    unless ($rows == 1) {
    924                         $log->logdie("affected row count is $rows instead of 1");
     1134                        die("affected row count is $rows instead of 1");
    9251135                    }
    9261136                }
     
    9291139                # can't remove a read lock if there aren't any
    9301140                if ($write_lock) {
    931                     $log->logdie("can not have a read lock under a write lock");
     1141                    die("can not have a read lock under a write lock");
    9321142                }
    9331143                   
    9341144                if ($read_lock == 0) {
    935                     $log->logdie("can not remove non-existant read lock");
     1145                    die("can not remove non-existant read lock");
    9361146                }
    9371147
     
    9421152                    # if we affected more then one row something very bad has happened.
    9431153                    unless ($rows == 1) {
    944                         $log->logdie("affected row count is $rows instead of 1");
     1154                        die("affected row count is $rows instead of 1");
    9451155                    }
    9461156
     
    9531163            $db->rollback;
    9541164            $log->debug("rollback");
    955             if ($@ =~ /Deadlock found/) {
    956                 $log->warn("database deadlock retrying transaction: $@");
     1165            if ($@ =~ $trans_regex) {
     1166                $log->warn("database error, retrying transaction: $@");
     1167                sleep TRANS_RETRY_WAIT;
    9571168                redo TRANS;
    9581169            }
     
    9711182{
    9721183    my $self = shift;
     1184
     1185    my $log = $self->log;
     1186    $log->debug("entered - @_");
    9731187
    9741188    my ($key, $name, $value, $flags) = validate_pos(@_,
     
    9811195        {
    9821196            type        => SCALAR,
     1197            callbacks   => {
     1198                'xattr is in user. namespace'
     1199                    => sub { ($_[0]) =~ qr/^user\./ },
     1200            },
    9831201        },
    9841202        {
     
    9931211    );
    9941212
     1213    my $sql = $self->sql;
     1214
    9951215    # ignore volume
    996     $key = parse_neb_key($key);
    997 
    998     my $log = $self->log;
    999     my $sql = $self->sql;
     1216    eval {
     1217        $key = parse_neb_key($key);
     1218    };
     1219    $log->logdie("$@") if $@;
     1220
    10001221    my $db  = $self->db($key);
    1001 
    1002     $log->debug("entered - @_");
    10031222
    10041223TRANS: while (1) {
     
    10201239            if ($flags eq 'create') {
    10211240                unless ($rows == 1) {
    1022                     $log->logdie( "affected row count is $rows instead of 1" );
     1241                    die( "affected row count is $rows instead of 1" );
    10231242                }
    10241243            } else {
     
    10261245                # the case of a replace and 1 if the xattr didn't already exist.
    10271246                unless ($rows == 1 or $rows == 2) {
    1028                     $log->logdie( "affected row count is $rows instead of 2" );
     1247                    die( "affected row count is $rows instead of 2" );
    10291248                }
    10301249            }
     
    10361255            $db->rollback;
    10371256            $log->debug("rollback");
    1038             if ($@ =~ /Deadlock found/) {
    1039                 $log->warn("database deadlock retrying transaction: $@");
     1257            if ($@ =~ $trans_regex) {
     1258                $log->warn("database error, retrying transaction: $@");
     1259                sleep TRANS_RETRY_WAIT;
    10401260                redo TRANS;
    10411261            }
     
    10541274{
    10551275    my $self = shift;
     1276
     1277    my $log = $self->log;
     1278    $log->debug("entered - @_");
    10561279
    10571280    my ($key, $name) = validate_pos(@_,
     
    10641287        {
    10651288            type        => SCALAR,
     1289            callbacks   => {
     1290                'xattr is in user. namespace'
     1291                    => sub { ($_[0]) =~ qr/^user\./ },
     1292            },
    10661293        },
    10671294    );
    10681295
     1296    my $sql = $self->sql;
     1297
    10691298    # ignore volume
    1070     $key = parse_neb_key($key);
    1071 
    1072     my $log = $self->log;
    1073     my $sql = $self->sql;
     1299    eval {
     1300        $key = parse_neb_key($key);
     1301    };
     1302    $log->logdie("$@") if $@;
     1303
    10741304    my $db  = $self->db($key);
    1075 
    1076     $log->debug("entered - @_");
    10771305
    10781306    my $value;
     
    10851313        if ($rows == 0) {
    10861314            $query->finish;
    1087             $log->logdie( "xattr $key:$name does not exist" );
     1315            die( "xattr $key:$name does not exist" );
    10881316        }
    10891317        # if we go more then one row bad something very bad has happened.
    10901318        unless ($rows == 1) {
    10911319            $query->finish;
    1092             $log->logdie( "affected row count is $rows instead of 1" );
     1320            die( "affected row count is $rows instead of 1" );
    10931321        }
    10941322
     
    10991327        $value = $row->{ 'value' };
    11001328    };
    1101     $log->logdie("database error: $@") if $@;
     1329    if ($@) {
     1330        if ($@ =~ /user\..*? does not exist/) {
     1331            # do not log xattr does not exist messages
     1332            die $@;
     1333        }
     1334        $log->logdie("database error: $@") if $@;
     1335    }
    11021336
    11031337    $log->debug("leaving");
     
    11101344{
    11111345    my $self = shift;
     1346
     1347    my $log = $self->log;
     1348    $log->debug("entered - @_");
    11121349
    11131350    my ($key) = validate_pos(@_,
     
    11201357    );
    11211358
     1359    my $sql = $self->sql;
     1360
    11221361    # ignore volume
    1123     $key = parse_neb_key($key);
    1124 
    1125     my $log = $self->log;
    1126     my $sql = $self->sql;
     1362    eval {
     1363        $key = parse_neb_key($key);
     1364    };
     1365    $log->logdie("$@") if $@;
     1366
    11271367    my $db  = $self->db($key);
    1128 
    1129     $log->debug("entered - @_");
    11301368
    11311369    my @xattrs;
     
    11511389    my $self = shift;
    11521390
     1391    my $log = $self->log;
     1392    $log->debug("entered - @_");
     1393
    11531394    my ($key, $name) = validate_pos(@_,
    11541395        {
     
    11601401        {
    11611402            type        => SCALAR,
     1403            callbacks   => {
     1404                'xattr is in user. namespace'
     1405                    => sub { ($_[0]) =~ qr/^user\./ },
     1406            },
    11621407        },
    11631408    );
    11641409
     1410    my $sql = $self->sql;
     1411
    11651412    # ignore volume
    1166     $key = parse_neb_key($key);
    1167 
    1168     my $log = $self->log;
    1169     my $sql = $self->sql;
     1413    eval {
     1414        $key = parse_neb_key($key);
     1415    };
     1416    $log->logdie("$@") if $@;
     1417
    11701418    my $db  = $self->db($key);
    1171 
    1172     $log->debug("entered - @_");
    11731419
    11741420TRANS: while (1) {
     
    11791425            $query->finish;
    11801426
     1427            # no rows affected means the xattr did not exist
     1428            if ($rows == 0) {
     1429                die( "xattr $key:$name does not exist" );
     1430            }
     1431
    11811432            # if we affected more then one row something very bad has happened.
    1182             unless ($rows == 1) {
    1183                 $log->logdie( "affected row count is $rows instead of 1" );
     1433            if ($rows > 1) {
     1434                die( "affected row count is $rows instead of 1" );
    11841435            }
    11851436
     
    11901441            $db->rollback;
    11911442            $log->debug("rollback");
    1192             if ($@ =~ /Deadlock found/) {
    1193                 $log->warn("database deadlock retrying transaction: $@");
     1443            if ($@ =~ $trans_regex) {
     1444                $log->warn("database error, retrying transaction: $@");
     1445                sleep TRANS_RETRY_WAIT;
    11941446                redo TRANS;
    11951447            }
     
    12091461    my $self = shift;
    12101462
     1463    my $log = $self->log;
     1464    $log->debug( "entered - @_" );
     1465
    12111466    my ($pattern) = validate_pos( @_,
    12121467        {
     
    12161471    );
    12171472
    1218     $pattern = parse_neb_key($pattern);
    1219 
    1220     my $log = $self->log;
    1221 
    1222     $log->debug( "entered - @_" );
     1473
     1474    eval {
     1475        $pattern = parse_neb_key($pattern) if defined $pattern;
     1476    };
     1477    $log->logdie("$@") if $@;
    12231478
    12241479    unless (defined $pattern) {
     
    12351490    $log->logdie("no keys found") unless ( scalar @keys );
    12361491
     1492    if (defined $self->cache) {
     1493        foreach my $path (@keys) {
     1494            $self->cache->set($path, 1);
     1495                $log->debug("key added to cache as: $path");
     1496        }
     1497    }
     1498
    12371499    $log->debug( "leaving" );
    12381500
     
    12451507
    12461508    my $self    = shift;
     1509
     1510    my $log = $self->log;
     1511    $log->debug( "entered - @_" );
     1512
    12471513    my $index   = shift;
    12481514    my $key     = shift;
    12491515
    1250     my $log = $self->log;
    12511516    my $sql = $self->sql;
    12521517    my $db  = $self->_db_for_index($index);
    12531518
    1254     $log->debug( "entered - @_" );
    12551519
    12561520    # first check to see if the key is an exact match
     
    12871551    }
    12881552
    1289     eval {
    1290         $log->debug("trying for a directory match under dir: $dir_id");
     1553    # find dirs under dir
     1554    my @dir_keys;
     1555    eval {
     1556        $log->debug("looking for directories under dir: $dir_id");
     1557        my $query = $db->prepare_cached( $sql->find_dir_by_parent_id);
     1558        $query->execute( $dir_id );
     1559
     1560        while ( my $row = $query->fetchrow_hashref ) {
     1561            next if $row->{'dir_id'} == 1;
     1562            my $dir = $row->{'dirname'};
     1563            if ($dir_id == 1) {
     1564                push @dir_keys, $dir . '/' if $dir;
     1565            } else {
     1566                push @dir_keys, $key->path . '/' . $dir . '/' if $dir;
     1567            }
     1568            $log->debug( "matched $dir" ) if $dir;
     1569        }
     1570    };
     1571    $log->logdie("database error: $@") if $@;
     1572
     1573    # find files under dir
     1574    eval {
     1575        $log->debug("looking for objects under dir: $dir_id");
    12911576        my $query = $db->prepare_cached( $sql->find_object_by_dir_id );
    12921577        $query->execute( $dir_id );
     
    13011586
    13021587    $log->debug( "leaving" );
    1303 
    1304     return \@keys;
     1588   
     1589    return [sort(@dir_keys), sort(@keys)];
    13051590}
    13061591
     
    13091594{
    13101595    my $self = shift;
     1596
     1597    my $log = $self->log;
     1598    $log->debug("entered - @_");
    13111599
    13121600    my ($key, $vol_name) = validate_pos(@_,
     
    13301618    );
    13311619
     1620    my $sql = $self->sql;
     1621
     1622#    unless ($key) {
     1623#        $log->warn("key was undefined after validate_pos(), trying again...");
     1624#        return $self->find_instances(@_);
     1625#    }
     1626
    13321627    # vol_name overrides the key implied volume
    1333     $key = parse_neb_key($key, $vol_name);
     1628    eval {
     1629        $key = parse_neb_key($key, $vol_name);
     1630    };
     1631    $log->logdie("$@") if $@;
    13341632    $vol_name = $key->volume;
    13351633
    1336     my $log = $self->log;
    1337     my $sql = $self->sql;
    13381634    my $db  = $self->db($key);
    1339 
    1340     $log->debug("entered - @_");
    13411635
    13421636    # the key's volume can't be validiated on input for this method so we have
     
    13441638    if (defined $vol_name
    13451639        and not $self->_is_valid_volume_name($key, $key->volume)) {
    1346         if ($key->soft_volume) {
     1640        if ($key->hard_volume) {
     1641            $log->logdie("$vol_name is not a valid volume name");
     1642        } else {
    13471643            $log->warn( "$vol_name is not a known volume name" );
    13481644            $vol_name = undef;
    1349         } else {
    1350             die "$vol_name is not a valid volume name"
    13511645        }
    13521646    }
     
    13611655            unless ($rows > 0) {
    13621656                $query->finish;
    1363                 $log->logdie("no instances on storage volume or volume is not avaiable for key: $key volume: $vol_name");
     1657                die("no instances on storage volume or volume is not avaiable for key: $key volume: $vol_name");
    13641658            }
    13651659        } else {
     
    13691663            unless ($rows > 0) {
    13701664                $query->finish;
    1371                 $log->logdie("no instances available for key: $key");
     1665                die("no instances available for key: $key");
    13721666            }
    13731667        }
     
    13811675        $db->rollback;
    13821676        # handle soft volumes
    1383         if (defined $vol_name and defined $key->soft_volume) {
     1677        if (defined $vol_name and not defined $key->hard_volume) {
    13841678            $log->debug("retrying with 'any' volume");
    13851679            return $self->find_instances($key->path, 'any');
     
    14031697    my $self = shift;
    14041698
     1699    my $log = $self->log;
     1700    $log->debug( "entered - @_" );
     1701
    14051702    my ($key, $uri) = validate_pos( @_,
    14061703        {
     
    14151712    );
    14161713
     1714    my $sql = $self->sql;
     1715
    14171716    # ignore volume
    1418     $key = parse_neb_key($key);
    1419 
    1420     my $log = $self->log;
    1421     my $sql = $self->sql;
     1717    eval {
     1718        $key = parse_neb_key($key);
     1719    };
     1720    $log->logdie("$@") if $@;
     1721
    14221722    my $db  = $self->db($key);
    1423 
    1424     $log->debug( "entered - @_" );
    14251723
    14261724TRANS: while (1) {
    14271725        eval {
     1726            my $total;
     1727            my $available;
    14281728            my $so_id;
    1429             my $instances;
    1430             # get so_id
     1729            my $ins_id;
     1730            # find so_id for key and get count of instances
    14311731            {
    1432                 my $query = $db->prepare_cached( $sql->get_object_from_uri );
    1433                 my $rows = $query->execute( $uri );
    1434 
     1732                my $query = $db->prepare_cached( $sql->get_instance_count_by_ext_id );
     1733                my $rows = $query->execute($key->path);
    14351734                unless ( $rows > 0 ) {
    14361735                    $query->finish;
    1437                     $log->logdie( "no instance is associated with uri" );
    1438                 }
    1439 
    1440                 $so_id = $query->fetchrow_hashref->{ 'so_id' };
     1736                    die( "$key has no associated instances - this should not happen" );
     1737                }
     1738
     1739                my $record = $query->fetchrow_hashref;
     1740                $so_id      = $record->{ 'so_id' };
     1741                $total      = $record->{ 'total' };
     1742                $available  = $record->{ 'available' };
    14411743                $query->finish;
    1442 
    1443             }
    1444 
     1744            }
     1745
     1746            # find ins_id for uri
    14451747            {
    1446                 my $query = $db->prepare_cached( $sql->get_instance_count );
    1447                 $query->execute( $so_id );
    1448 
    1449                 $instances = $query->fetchrow_hashref->{ 'count(ins_id)' };
     1748                my $query = $db->prepare_cached( $sql->get_instance_by_uri );
     1749                my $rows = $query->execute($so_id, $uri);
     1750                unless ( $rows > 0) {
     1751                    $query->finish;
     1752                    die( "no instance is associated with uri" );
     1753                }
     1754
     1755                $ins_id = $query->fetchrow_hashref->{ 'ins_id' };
    14501756                $query->finish;
    14511757            }
     
    14531759            # remove instance
    14541760            {
    1455                 my $query = $db->prepare_cached( $sql->delete_instance );
    1456                 my $rows = $query->execute( $uri );
     1761                my $query = $db->prepare_cached( $sql->delete_instance_by_ins_id );
     1762                my $rows = $query->execute( $ins_id );
    14571763                $query->finish;
    14581764               
    1459                 # if we affected something other then two rows something very bad
    1460                 # has happened
     1765                # if we affected something other then one row something very
     1766                # bad has happened
    14611767                unless ( $rows == 1 ) {
    1462                     $log->logdie( "affected row count is $rows instead of 1" );
    1463                 }
    1464             }
    1465 
    1466             # if we just deleted the last instance associated with a storage object
    1467             # remove it too
    1468             if ( $instances == 1 ) {
    1469                 # we just removed the last instance
     1768                    die( "affected row count is $rows instead of 1" );
     1769                }
     1770               
     1771            }
     1772
     1773            # if we just deleted the last 'available' instance associated with
     1774            # a storage object remove it too
     1775            if ( $available == 1 ) {
     1776                # remove key from cache
     1777                $self->cache->delete($key->path) if defined $self->cache;
     1778               
     1779                if ($total > $available) {
     1780                    $self->prune_object("$key");
     1781                }
     1782               
     1783                # delete the storage object, remaining instances should be
     1784                # removed via cascading delete
    14701785                my $query = $db->prepare_cached( $sql->delete_object );
    14711786                my $rows = $query->execute( $so_id );
     
    14741789                # TODO: this will have to be changed in order to support hardlinks
    14751790                unless ( $rows == 1 ) {
    1476                     $log->logdie( "affected row count is $rows instead of 2" );
     1791                    die( "affected row count is $rows instead of 2" );
    14771792                }
    14781793            }
     
    14841799            $db->rollback;
    14851800            $log->debug("rollback");
    1486             if ($@ =~ /Deadlock found/) {
    1487                 $log->warn("database deadlock retrying transaction: $@");
     1801            if ($@ =~ $trans_regex) {
     1802                $log->warn("database error, retrying transaction: $@");
     1803                sleep TRANS_RETRY_WAIT;
    14881804                redo TRANS;
    14891805            }
     
    15021818{
    15031819    my $self = shift;
     1820
     1821    my $log = $self->log;
     1822    $log->debug("entered - @_");
    15041823
    15051824    my ( $key ) = validate_pos( @_,
     
    15121831    );
    15131832
     1833    my $sql = $self->sql;
     1834
    15141835    # ignore volume
    1515     $key = parse_neb_key($key);
    1516 
    1517     my $log = $self->log;
    1518     my $sql = $self->sql;
     1836    eval {
     1837        $key = parse_neb_key($key);
     1838    };
     1839    $log->logdie("$@") if $@;
     1840
    15191841    my $db  = $self->db($key);
    1520 
    1521     $log->debug("entered - @_");
    15221842
    15231843    my $stat;
     
    15271847
    15281848        unless ($rows == 1) {
    1529             $log->logdie("no storage object found");
     1849            die("no storage object found");
    15301850        }
    15311851
     
    15481868    my $self = shift;
    15491869
     1870    my $log = $self->log;
     1871    $log->debug("entered - @_");
     1872
    15501873    validate_pos(@_);
    15511874
    1552     my $log = $self->log;
    15531875    my $sql = $self->sql;
    15541876    my $db  = $self->_db_for_index(0); # XXX fix as above
    15551877
    1556     $log->debug("entered - @_");
    1557 
    15581878    my $stats;
    15591879    my $query;
     
    15761896}
    15771897
     1898sub chmod_object
     1899{
     1900    my $self = shift;
     1901
     1902    my $log = $self->log;
     1903    $log->debug("entered - @_");
     1904
     1905    my ($key, $mode) = validate_pos( @_,
     1906        {
     1907            type        => SCALAR,
     1908            callbacks   => {
     1909                'is valid object key' => sub { $self->_is_valid_object_key($_[0]) },
     1910            },
     1911        },
     1912        {
     1913            type        => SCALAR,
     1914            regex       => qr/\d{3,4}/,
     1915            callbacks   => {
     1916                'is allowable mode' => sub {
     1917                    $_[0] == (S_IRUSR | S_IRGRP)
     1918                },
     1919            },
     1920        },
     1921    );
     1922
     1923    my $sql = $self->sql;
     1924
     1925    # ignore volume
     1926    eval {
     1927        $key = parse_neb_key($key);
     1928    };
     1929    $log->logdie("$@") if $@;
     1930
     1931    my $db  = $self->db($key);
     1932
     1933    # find all instances of this object
     1934    my $locations;
     1935    eval {
     1936        $locations = $self->find_instances("$key");
     1937    };
     1938    $log->logdie("error: $@") if $@;
     1939
     1940    # update each instances
     1941    foreach my $inst (@$locations) {
     1942        my $path = URI->new($inst)->path;
     1943
     1944        $self->_retry(sub { chmod($mode, $path) })
     1945            or $log->logdie("can not chmod() $path: $!");
     1946
     1947        # XXX I'm assuming that it's OK to fsync() a filehandle that's only
     1948        # open for reading?  Opening as w/rw here can fail if the chmod removes
     1949        # write permissions.
     1950        my $fh;
     1951        $self->_retry(sub { open($fh, '<', $path) })
     1952            or $log->logdie("can not open() $path: $!");
     1953
     1954        # fsync(3c)
     1955        $self->_retry(sub { $fh->sync() })
     1956            or $log->logdie("can not sync() $path: $!");
     1957
     1958        $self->_retry(sub { close($fh) })
     1959            or $log->logdie("can not close() $path: $!");
     1960    }
     1961
     1962    # stick an xattr on this object with the mode
     1963    # XXX this would probably be better as a field in the storage_object_attr
     1964    # table but since we're not planning to use this for very many objects (as
     1965    # a %) it may not be worth adding the extra field at this time.
     1966    eval {
     1967        $self->setxattr_object("$key", 'user.mode', $mode, 'replace');
     1968    };
     1969    $log->logdie("error: $@") if $@;
     1970
     1971    $log->debug("leaving");
     1972
     1973    return $mode;
     1974}
    15781975
    15791976sub _get_storage_volume
     
    15811978    my $self = shift;
    15821979
    1583     my ($key, $name, $soft_volume) = @_;
    1584 
    1585     my $log = $self->log;
    1586     my $sql = $self->sql;
    1587     my $db  = $self->db($key);
    1588 
     1980    my $log = $self->log;
    15891981    no warnings qw( uninitialized );
    15901982    $log->debug( "entered - @_" );
    15911983    use warnings;
     1984
     1985    my ($key, $name, $hard_volume) = @_;
     1986
     1987    my $sql = $self->sql;
     1988    my $db  = $self->db($key);
    15921989
    15931990    my ($vol_id, $vol_host, $vol_path, $xattr);
     
    16052002                # if a volume name was specified, and is soft, and we failed to
    16062003                # find it, fall back to any volume
    1607                 if ($soft_volume) {
     2004                unless ($hard_volume) {
    16082005                    ($vol_id, $vol_host, $vol_path, $xattr) = $self->_get_storage_volume($key);
    16092006                    return; # this just returns out of the eval not from the subroutine
    16102007                }
    1611                 $log->logdie("storage volume: $name is not available");
     2008                die("storage volume: $name is not available");
    16122009            }
    16132010            # when matching by name we shouldn't ever match more than once
    16142011            if ($rows > 1) {
    16152012                $query->finish;
    1616                 $log->logdie("affected row count is $rows instead of 1");
     2013                die("affected row count is $rows instead of 1");
    16172014            }
    16182015        } else {
     
    16232020            unless ($rows > 0) {
    16242021                $query->finish;
    1625                 $log->logdie("no storage volume is available");
     2022                die("no storage volume is available for key: $key volume: $name hard_volume: $hard_volume");
    16262023            }
    16272024        }
     
    16312028        $query->finish;
    16322029    };
    1633     $log->logdie("database error: $@") if $@;
     2030    if ($@) {
     2031        if ($@ =~ qr/no storage volume is available/) {
     2032            # this should not happen unless all volumes are full
     2033            $log->error($@);
     2034            $log->debug("retrying...");
     2035            return $self->_get_storage_volume(@_);
     2036        }
     2037        # else
     2038        $log->logdie("database error: $@");
     2039    }
    16342040
    16352041    $log->logdie("failed to find a suitable volume" )
     
    16462052    my $self = shift;
    16472053
    1648     my $key = shift;
    1649 
    1650     my $log = $self->log;
    1651     my $sql = $self->sql;
    1652     my $db  = $self->db($key);
    1653 
     2054    my $log = $self->log;
    16542055    no warnings qw( uninitialized );
    16552056    $log->debug( "entered - @_" );
    16562057    use warnings;
     2058
     2059    my $key = shift;
     2060
     2061    my $sql = $self->sql;
     2062    my $db  = $self->db($key);
    16572063
    16582064    my ($vol_id, $vol_host, $vol_path, $xattr);
     
    16652071        unless ($rows > 0) {
    16662072            $query->finish;
    1667             $log->logdie("can't find a suitable storage volume to replicate $key to");
     2073            die("can't find a suitable storage volume to replicate $key to");
    16682074        }
    16692075        # when matching by name we shouldn't ever match more than once
    16702076        if ($rows > 1) {
    16712077            $query->finish;
    1672             $log->logdie("affected row count is $rows instead of 1");
     2078            die("affected row count is $rows instead of 1");
    16732079        }
    16742080
     
    16902096sub _is_valid_object_key
    16912097{
    1692     my ($self, $key) = @_;
    1693 
    1694     $key = parse_neb_key($key);
    1695 
    1696     my $log = $self->log;
     2098    my $self = shift;
     2099
     2100    my $log = $self->log;
     2101    $log->debug( "entered - @_" );
     2102
     2103    my ($key) = @_;
     2104
    16972105    my $sql = $self->sql;
     2106
     2107    eval {
     2108        $key = parse_neb_key($key);
     2109    };
     2110    $log->logdie("$@") if $@;
     2111
     2112    # check cache first
     2113    my $cached = $self->cache->get($key->path) if defined $self->cache;
     2114    if (defined $cached) {
     2115        $log->debug( "key $key found in cache as ", $key->path );
     2116        $log->debug( "leaving" );
     2117        return 1;
     2118    } else {
     2119        $log->debug( "key $key not found in cache" );
     2120    }
     2121
    16982122    my $db  = $self->db($key);
    16992123
     
    17122136
    17132137    if (defined $ext_id) {
     2138        $log->debug( "key found in db" );
     2139        # add key to cache
     2140        $self->cache->set($key->path, 1) if defined $self->cache;
     2141        $log->debug( "key added to cache as ", $key->path );
     2142        $log->debug( "leaving" );
    17142143        return 1;
    17152144    }
    17162145
     2146    $log->debug( "key not found in db" );
     2147    $log->debug( "leaving" );
     2148
    17172149    return;
    17182150}
     
    17212153sub _is_valid_volume_name
    17222154{
    1723     my ($self, $key, $vol_name) = @_;
    1724 
    1725     $key = parse_neb_key($key);
     2155    my $self = shift;
     2156
     2157    my $log = $self->log;
     2158    $log->debug( "entered - @_" );
     2159
     2160    my ($key, $vol_name) = @_;
     2161
     2162    my $sql = $self->sql;
     2163
     2164    # the volume name implied by the key is ignored.  $key is only needed to
     2165    # select a database connection
     2166    eval {
     2167        $key = parse_neb_key($key);
     2168    };
     2169    $log->logdie("$@") if $@;
    17262170    my $volume_info = parse_neb_volume($vol_name);
    17272171
    1728     my $log = $self->log;
    1729     my $sql = $self->sql;
    17302172    my $db  = $self->db($key);
    17312173
     
    17342176    # handle "any" volume
    17352177    if ($vol_name eq 'any') {
     2178        $log->debug( "found volume name $vol_name" );
     2179        $log->debug( "leaving" );
    17362180        return 1;
    17372181    }
     
    17482192
    17492193    if (defined $vol_id and defined $vol_path) {
     2194        $log->debug( "found volume name $vol_name" );
     2195        $log->debug( "leaving" );
    17502196        return 1;
    17512197    }
    17522198
     2199    $log->debug( "volume name $vol_name not found" );
     2200    $log->debug( "leaving" );
     2201
    17532202    return;
    17542203}
     
    17592208    my $self = shift;
    17602209
     2210    my $log = $self->log;
     2211    $log->debug( "entered - @_" );
     2212
    17612213    my ($key, $so_id, $ins_id, $vol_path, $xattr) =  @_;
    17622214
    1763     my $log = $self->log;
    17642215    my $sql = $self->sql;
    17652216    my $db  = $self->db($key);
     
    17702221        my $storage_filename = $self->_generate_storage_filename($key->path, $ins_id);
    17712222        unless (-d $storage_path) {
    1772             _retry(sub { mkpath(@_) }, $storage_path, 0, 0775)
    1773                 or die "can't create storage path: $storage_path";
     2223            $self->_retry(sub { mkpath([$storage_path], 0, 0775) })
     2224                or die("can't create storage path: $storage_path");
    17742225        }
    17752226        # check to make sure at least the parent directory has the proper
    17762227        # permissions
    1777         my $mode = [_retry(sub { stat($storage_path) } )]->[2] & 07777;
     2228        my $mode = [$self->_retry(sub { stat($storage_path) } )]->[2] & 07777;
    17782229        unless ($mode == 0775) {
    1779             $log->error("$storage_path has the wrong permissions of: %04x", $mode);
    1780             _retry(sub { chmod(0775, $storage_path) }) or die "can't chmod $storage_path: $!";
     2230            # XXX: this problem is so common that it's flooding the logs
     2231            $log->debug("$storage_path has the wrong permissions of: 0", sprintf("%o", $mode));
     2232            $self->_retry(sub { chmod(0775, $storage_path) })
     2233                or die("can not chmod() $storage_path: $!");
    17812234        }
    17822235
     
    17872240    };
    17882241    if ($@) {
     2242        if (defined $uri and -e $uri->file) {
     2243            unlink($uri->file)
     2244                or $log->error("failed to unlink() $uri: $!");
     2245        }
    17892246        $log->logdie($@);
    17902247    }
     
    17922249    if ($xattr) {
    17932250        my $path = $uri->file;
    1794         die "can not set xattr on $path: $!"
     2251        $log->logdie("can not set xattr on $path: $!")
    17952252            unless (setfattr($path, 'user.nebulous_key', $key->path));
    17962253    }
     
    18042261    my $self = shift;
    18052262
     2263    my $log = $self->log;
     2264    $log->debug( "entered - @_" );
     2265
    18062266    my ($path) = @_;
    18072267
    18082268    # perl's open() can't do an O_CREAT | O_EXCL
    1809     die "file $path already exists" if (-e $path);
     2269    # XXX is it possible to tell if this system call failed?
     2270    -e $path
     2271        and $log->logdie("file $path already exists");
    18102272
    18112273    my $fh;
    1812     die "can not open $path: $!"
    1813         unless (_retry(sub { open($fh, '>', $path) }));
     2274    $self->_retry(sub { open($fh, '>', $path) })
     2275        or $log->logdie("can not open() $path: $!");
    18142276
    18152277    # chmod before fsync() to make sure the changed perms hit the disk too
    1816     die "can not chmod $path: $!"
    1817         unless (_retry(sub { chmod(0664, $path) }));
    1818 
    1819     die "can not flush $path: $!"
    1820         unless ($fh->flush);
    1821 
    1822     die "can not sync $path: $!"
    1823         unless ($fh->sync);
    1824 
    1825     die "can not close $path: $!"
    1826         unless (_retry(sub { close($fh) }));
     2278    $self->_retry(sub { chmod(0664, $path) })
     2279        or $log->logdie("can not chmod() $path: $!");
     2280
     2281    $self->_retry(sub { $fh->sync() })
     2282        or $log->logdie("can not sync() $path: $!");
     2283
     2284    $self->_retry(sub { close($fh) })
     2285        or $log->logdie("can not close() $path: $!");
    18272286
    18282287    return $path;
     
    18612320sub _retry
    18622321{
     2322    my $self = shift;
     2323
     2324    my $log = $self->log;
     2325    $log->debug( "entered - @_" );
     2326
    18632327    my $func = shift;
    18642328
     
    18692333        };
    18702334        if ($@) {
    1871             die $@;
    1872             sleep 1;
     2335            $log->logdie($@);
     2336            sleep NFS_RETRY_WAIT;
    18732337            next;
    18742338        }
     
    18792343    # if the loop ended and $@ is set, rethrow the error
    18802344    if ($@) {
    1881         die $@;
     2345        $log->logdie($@);
    18822346    }
    18832347
  • branches/pap/Nebulous-Server/lib/Nebulous/Server.pod

    r23932 r25027  
    2929    Nebulous::Server->stat_object( $key );
    3030    Nebulous::Server->mounts();
     31    Nebulous::Server->chmod_object( $key, $mode);
     32    Nebulous::Server->prune_object( $key );
    3133
    3234=head1 DESCRIPTION
     
    181183    epoch
    182184    mtime
    183     number of instances
     185    number of available instances
     186    total number of instances
    184187
    185188=item * mounts( $key );
     
    203206    ]
    204207
     208=item * chmod_object( $key, $mode );
     209
     210Accepts 2 parameters, both mandatory.  C<$key> is the nebulous key to operate
     211on and C<$mode> are the POSIX like file permissions to set on all instances of
     212C<$key>.  C<$mode> must be specified in oct.  Returns C<$mode> or sucess or an
     213exception on failure.
     214
     215=item * prune_object( $key );
     216
     217Removes all of the inaccessible instances from an object.
     218
     219Accepts 1 mandatory parameter.  Returns the number of inaccessible instances actually pruned from the object.
     220
    205221=back
    206222
     
    223239=head1 COPYRIGHT
    224240
    225 Copyright (C) 2004-2008  Joshua Hoblitt.  All rights reserved.
     241Copyright (C) 2004-2009  Joshua Hoblitt.  All rights reserved.
    226242
    227243This program is free software; you can redistribute it and/or modify it under
  • branches/pap/Nebulous-Server/lib/Nebulous/Server/Config.pm

    r23932 r25027  
    1313
    1414use Log::Log4perl qw( :levels );
    15 use Params::Validate qw( validate validate_pos SCALAR );
     15use Params::Validate qw( validate validate_pos SCALAR ARRAYREF UNDEF );
    1616
    1717our %LEVELS = (
     
    3232    dsn         => { type => SCALAR },
    3333    dbuser      => { type => SCALAR },
    34     dbpasswd    => { type => SCALAR },
     34    dbpasswd    => { type => SCALAR | UNDEF },
    3535};
    3636
     
    4646        },
    4747    },
    48     dsn         => { type => SCALAR, optional => 1 },
    49     dbuser      => { type => SCALAR, optional => 1 },
    50     dbpasswd    => { type => SCALAR, optional => 1 },
     48    dsn                 => { type => SCALAR, optional => 1 },
     49    dbuser              => { type => SCALAR, optional => 1 },
     50    dbpasswd            => { type => SCALAR | UNDEF, optional => 1 },
     51    memcached_servers   => { type => ARRAYREF, optional => 1 },
    5152};
    5253
     
    6162
    6263    # normalize log levels to lower-case
    63     my $self = { trace => $LEVELS{lc($p{trace})} };
     64    my $self = {
     65        trace             => $LEVELS{lc($p{trace})},
     66        memcached_servers => $p{memcached_servers},
     67    };
    6468
    6569    bless $self, $class || ref $class;
  • branches/pap/Nebulous-Server/lib/Nebulous/Server/SQL.pm

    r23947 r25027  
    6767        WHERE parent_id = ?
    6868            AND dirname = ?
     69        LOCK IN SHARE MODE
    6970    },
    7071    new_directory       => qq{
     
    8283    stat_object          => qq{
    8384        SELECT
    84             storage_object.so_id,
    85             storage_object.ext_id,
    86             read_lock,
    87             write_lock,
    88             storage_object_attr.epoch,
    89             storage_object_attr.mtime,
     85            so.so_id,
     86            so.ext_id,
     87            attr.read_lock,
     88            attr.write_lock,
     89            attr.epoch,
     90            attr.mtime,
     91            SUM(available) as available,
    9092            COUNT(instance.so_id) as instances
    91         FROM storage_object
    92         JOIN storage_object_attr
     93        FROM storage_object as so
     94        JOIN storage_object_attr as attr
    9395            USING (so_id)
    9496        JOIN instance
    9597            USING (so_id)
    96         WHERE ext_id = ?
    97         GROUP BY storage_object.so_id
     98        LEFT JOIN mountedvol
     99            USING(vol_id)
     100        WHERE ext_id = ?
     101        GROUP BY so.so_id
    98102    },
    99103    # Note: this sets an update lock
     
    175179        WHERE ext_id = ?
    176180    },
    177     delete_instance    => qq{
     181    delete_instance_by_ins_id => qq{
    178182        DELETE FROM instance
    179         WHERE uri = ?
     183        WHERE ins_id = ?
    180184    },
    181185    get_object_from_uri   => qq{
     
    184188        WHERE uri = ?
    185189    },
     190    get_instance_by_uri   => qq{
     191        SELECT ins_id
     192        FROM instance
     193        WHERE
     194            so_id = ?
     195            AND uri = ?
     196    },
    186197    get_instance_count   => qq{
    187198        SELECT count(ins_id)
    188199        FROM instance
    189200        WHERE so_id = ?
     201    },
     202    get_instance_count_by_ext_id   => qq{
     203        SELECT
     204            so_id,
     205            count(ins_id) as total,
     206            sum(available) as available
     207        FROM instance
     208        JOIN storage_object
     209            USING(so_id)
     210        LEFT JOIN mountedvol
     211            USING(vol_id)
     212        WHERE
     213            ext_id = ?
     214        GROUP BY so_id
     215    },
     216    copy_dead_instances_to_deleted => qq{
     217        INSERT INTO deleted
     218        SELECT vol_id, uri, NULL
     219        FROM instance
     220        LEFT JOIN mountedvol
     221            USING(vol_id)
     222        WHERE
     223            so_id = ?
     224            AND (mountedvol.available IS NULL || mountedvol.available = 0)
     225    },
     226    find_dead_instances_by_so_id => qq{
     227        SELECT so_id, ins_id, vol_id, uri
     228        FROM instance
     229        LEFT JOIN mountedvol
     230            USING(vol_id)
     231        WHERE
     232            so_id = ?
     233            AND (mountedvol.available IS NULL || mountedvol.available = 0)
    190234    },
    191235    get_object_instances    => qq{
     
    233277    get_replication_volume_for_ext_id   => qq{
    234278        SELECT
    235             vol_id,
     279            m.vol_id,
    236280            host,
    237281            path,
    238282            xattr,
    239283            total - used as free
    240         FROM (
    241 -- This query works but is slow...
    242 --             SELECT
    243 --                  mountedvol.*
    244 --             FROM mountedvol
    245 --             WHERE
    246 --                 vol_id NOT IN(
    247 --                     SELECT vol_id
    248 --                     FROM storage_object
    249 --                     JOIN instance
    250 --                         USING(so_id)
    251 --                     WHERE ext_id = --
    252 --                  )
    253                 SELECT
    254                     m.*
    255                 FROM mountedvol AS m
    256                 LEFT JOIN instance AS i
    257                     ON m.vol_id = i.vol_id
    258                     AND i.so_id = (
    259                         SELECT so_id
    260                         FROM storage_object
    261                         WHERE ext_id = ?
    262                     )
    263                 WHERE
    264                     i.vol_id IS NULL
    265         ) as Foo
    266         WHERE
    267             used / total < ?
     284        FROM mountedvol AS m
     285        LEFT JOIN instance AS i
     286            ON m.vol_id = i.vol_id
     287            AND i.so_id = (
     288                SELECT so_id
     289                FROM storage_object
     290                WHERE ext_id = ?
     291            )
     292        WHERE
     293            i.vol_id IS NULL
     294            AND used / total < ?
    268295            AND available = ?
    269296            AND allocate = ?
    270         ORDER BY RAND()
    271         LIMIT 1
     297            ORDER BY RAND()
     298            LIMIT 1
    272299    },
    273300    get_storage_volume          => qq{
     
    287314    },
    288315    new_volume          => qq{
    289         INSERT INTO volume (name, host, path, allocate, available, xattr)
    290         VALUES (?, ?, ?, TRUE, TRUE, FALSE)
     316        INSERT INTO volume (name, host, path, allocate, available, xattr, mountpoint)
     317        VALUES (?, ?, ?, TRUE, TRUE, FALSE, ?)
    291318    },
    292319    get_volume_by_name => qq{
     
    310337    },
    311338    find_object_by_ext_id => qq{
    312         SELECT ext_id, ext_id_basename
     339        SELECT so_id, ext_id, ext_id_basename
    313340        FROM storage_object
    314341        WHERE ext_id = ?
     
    318345        FROM storage_object
    319346        WHERE dir_id = ?
     347    },
     348    find_dir_by_parent_id => qq{
     349        SELECT dir_id, dirname
     350        FROM directory
     351        WHERE parent_id = ?
    320352    },
    321353    rename_object => qq{
     
    397429    },
    398430    get_mounted_volumes => qq{
    399         SELECT * FROM mountedvol ORDER BY host, name
     431        SELECT mountpoint, total, used, vol_id, name, host, path, allocate, available, xattr FROM mountedvol ORDER BY host, name
    400432    },
    401433);
     
    424456DROP TABLE IF EXISTS lock_record;
    425457DROP TABLE IF EXISTS volume;
    426 DROP TABLE IF EXISTS mount;
     458DROP TABLE IF EXISTS mountedvol;
    427459DROP TABLE IF EXISTS log;
    428 DROP TABLE IF EXISTS mountedvol;
    429460DROP TABLE IF EXISTS directory;
    430 DROP PROCEDURE IF EXISTS getmountedvol;
     461DROP TABLE IF EXISTS deleted;
    431462SET FOREIGN_KEY_CHECKS=1
    432463END
     464#DROP PROCEDURE IF EXISTS getmountedvol;
    433465    $sql{get_db_clear} = \@clear;
    434466}
     
    447479
    4484801;
     481
     482# ###
     483#
     484# CREATE PROCEDURE getmountedvol() DETERMINISTIC
     485# BEGIN
     486#     DECLARE done BOOLEAN DEFAULT FALSE;
     487#     DECLARE vol_idvar INT;
     488#     DECLARE namevar VARCHAR(255);
     489#     DECLARE hostvar VARCHAR(255);
     490#     DECLARE pathvar VARCHAR(255);
     491#     DECLARE allocatevar BOOLEAN;
     492#     DECLARE availablevar BOOLEAN;
     493#     DECLARE xattrvar BOOLEAN;
     494#     DECLARE trans_level VARCHAR(255);
     495#     DECLARE key_checks BOOLEAN;
     496#     DECLARE cur1 CURSOR FOR SELECT vol_id, name, host, path, allocate, available, xattr FROM myvolume;
     497#     DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = TRUE;
     498#
     499#     -- store the okey checking state
     500# --    SELECT @@FOREIGN_KEY_CHECKS INTO key_checks;
     501#     -- disable foregin check checks to prevent deadlocks on the mountedvol table
     502# --    SET FOREIGN_KEY_CHECKS=0;
     503#
     504#     -- make sure the temp table does not already exist... this can happy if the
     505#     -- stored proc fails for some reason
     506#     DROP TABLE IF EXISTS myvolume;
     507#     CREATE TEMPORARY TABLE myvolume LIKE volume;
     508#     INSERT INTO myvolume SELECT * FROM volume;
     509#
     510#     -- store the current transaction level
     511# --    SELECT @@session.tx_isolation INTO trans_level;
     512#     -- set trans level to repeatable-read so the volume table does not change
     513#     -- out from under our cursor
     514# --    SET @@session.tx_isolation = 'REPEATABLE-READ';
     515#
     516#     -- iterate over the volume table finding the coresponding entry in the
     517#     -- mount table and inserting union of the volume & mount row into the
     518#     -- mountedvol table
     519#     OPEN cur1;
     520#
     521#     myloop: LOOP
     522#         FETCH cur1 INTO vol_idvar, namevar, hostvar, pathvar, allocatevar, availablevar, xattrvar;
     523#         IF `done` THEN LEAVE myloop; END IF;
     524#         REPLACE INTO mountedvol
     525#             SELECT mountpoint, total, used, vol_idvar, namevar, hostvar, pathvar, allocatevar, availablevar, xattrvar
     526#             FROM
     527#                 (SELECT *, INSTR(pathvar, mountpoint) = 1 as substring
     528#                 FROM mount
     529#                 HAVING substring = 1
     530#                 ORDER BY substring DESC, LENGTH(mountpoint) DESC
     531#                 LIMIT 1) as bar;
     532#     END LOOP myloop;
     533#     
     534#     CLOSE cur1;
     535#
     536#     -- restore the original transaction level
     537# --    SET @@session.tx_isolation = trans_level;
     538#
     539#     -- restore the original key checking state
     540# --    SET @@FOREIGN_KEY_CHECKS = key_checks;
     541#
     542#     DROP TABLE IF EXISTS myvolume;
     543#
     544# --    SET FOREIGN_KEY_CHECKS=1;
     545#
     546#     COMMIT;
     547# END
    449548
    450549__DATA__
     
    455554    FOREIGN KEY(parent_id) REFERENCES directory(dir_id),
    456555    PRIMARY KEY(dir_id),
    457     KEY(parent_id)
     556    KEY(parent_id),
     557    KEY(dirname),
     558    UNIQUE(dirname, parent_id)
    458559) ENGINE=innodb DEFAULT CHARSET=latin1;
    459560
     
    466567CREATE TABLE storage_object (
    467568    so_id BIGINT NOT NULL AUTO_INCREMENT,
    468     ext_id VARCHAR(255) NOT NULL UNIQUE,
     569    ext_id VARCHAR(255) NOT NULL,
    469570    ext_id_basename VARCHAR(255) NOT NULL,
    470571    dir_id BIGINT NOT NULL,
     
    472573    type enum('REG_FILE'),
    473574    PRIMARY KEY(so_id),
     575    UNIQUE KEY(ext_id),
    474576    KEY(dir_id),
    475577    KEY(type)
     
    514616CREATE TABLE volume (
    515617    vol_id INT NOT NULL AUTO_INCREMENT,
    516     name VARCHAR(255) UNIQUE NOT NULL,
     618    name VARCHAR(255) NOT NULL,
    517619    host VARCHAR(255) NOT NULL,
    518620    path VARCHAR(255) NOT NULL,
     
    520622    available BOOLEAN DEFAULT FALSE,
    521623    xattr BOOLEAN DEFAULT FALSE,
     624    mountpoint VARCHAR(255) NOT NULL,
    522625    PRIMARY KEY(vol_id),
     626    UNIQUE KEY(name),
     627    UNiQUE KEY(path),
    523628    KEY(host(16)),
    524     KEY(path(255)),
    525629    KEY(allocate),
    526     KEY(available)
     630    KEY(available),
     631    KEY(mountpoint(255))
     632) ENGINE=innodb DEFAULT CHARSET=latin1;
     633
     634###
     635
     636CREATE TABLE mountedvol(
     637    vol_id INT NOT NULL,
     638    FOREIGN KEY(vol_id) REFERENCES volume(vol_id) ON DELETE CASCADE,
     639    name VARCHAR(255) NOT NULL,
     640    host VARCHAR(255) NOT NULL,
     641    path VARCHAR(255) NOT NULL,
     642    FOREIGN KEY(path) REFERENCES volume(path) ON DELETE CASCADE,
     643    allocate BOOLEAN DEFAULT FALSE,
     644    available BOOLEAN DEFAULT FALSE,
     645    xattr BOOLEAN DEFAULT FALSE,
     646    mountpoint VARCHAR(255) NOT NULL,
     647    FOREIGN KEY(mountpoint) REFERENCES volume(mountpoint) ON DELETE CASCADE,
     648    total BIGINT NOT NULL,
     649    used BIGINT NOT NULL,
     650    PRIMARY KEY(vol_id),
     651    KEY(name),
     652    KEY(host),
     653    KEY(path),
     654    KEY(allocate),
     655    KEY(available),
     656    KEY(xattr),
     657    KEY(mountpoint(255))
    527658) ENGINE=innodb DEFAULT CHARSET=latin1;
    528659
     
    532663    ins_id BIGINT NOT NULL AUTO_INCREMENT,
    533664    so_id BIGINT NOT NULL,
    534     FOREIGN KEY(so_id) REFERENCES storage_object(so_id),
     665    FOREIGN KEY(so_id) REFERENCES storage_object(so_id) ON DELETE CASCADE,
    535666    vol_id INT NOT NULL,
    536667    FOREIGN KEY(vol_id) REFERENCES volume(vol_id),
     
    546677###
    547678
    548 CREATE TABLE mount (
    549     mountpoint VARCHAR(255) NOT NULL,
    550     total BIGINT NOT NULL,
    551     used BIGINT NOT NULL,
    552     PRIMARY KEY(mountpoint)
    553 ) ENGINE=innodb DEFAULT CHARSET=latin1;
    554 
    555 ###
    556 
    557679CREATE TABLE log (
    558680    timestamp TIMESTAMP,
     
    566688###
    567689
    568 CREATE TABLE mountedvol(
    569     mountpoint VARCHAR(255) NOT NULL,
    570     FOREIGN KEY(mountpoint) REFERENCES mount(mountpoint) ON DELETE CASCADE,
    571     total BIGINT NOT NULL,
    572     used BIGINT NOT NULL,
     690CREATE TABLE deleted (
    573691    vol_id INT NOT NULL,
    574     FOREIGN KEY(vol_id) REFERENCES volume(vol_id) ON DELETE CASCADE,
    575     name VARCHAR(255) NOT NULL,
    576     host VARCHAR(255) NOT NULL,
    577     path VARCHAR(255) NOT NULL,
    578     FOREIGN KEY(path) REFERENCES volume(path) ON DELETE CASCADE,
    579     allocate BOOLEAN DEFAULT FALSE,
    580     available BOOLEAN DEFAULT FALSE,
    581     xattr BOOLEAN DEFAULT FALSE,
    582     PRIMARY KEY(mountpoint),
     692    FOREIGN KEY(vol_id) REFERENCES volume(vol_id),
     693    uri VARCHAR(255) NOT NULL,
     694    timestamp TIMESTAMP,
     695    PRIMARY KEY(vol_id, uri),
    583696    KEY(vol_id),
    584     KEY(name),
    585     KEY(host),
    586     KEY(path),
    587     KEY(allocate),
    588     KEY(available),
    589     KEY(xattr)
    590 ) ENGINE=innodb DEFAULT CHARSET=latin1;
    591 
    592 ###
    593 
    594 CREATE PROCEDURE getmountedvol() DETERMINISTIC
    595 BEGIN
    596     DECLARE done BOOLEAN DEFAULT FALSE;
    597     DECLARE vol_idvar INT;
    598     DECLARE namevar VARCHAR(255);
    599     DECLARE hostvar VARCHAR(255);
    600     DECLARE pathvar VARCHAR(255);
    601     DECLARE allocatevar BOOLEAN;
    602     DECLARE availablevar BOOLEAN;
    603     DECLARE xattrvar BOOLEAN;
    604     DECLARE trans_level VARCHAR(255);
    605     DECLARE key_checks BOOLEAN;
    606     DECLARE cur1 CURSOR FOR SELECT vol_id, name, host, path, allocate, available, xattr FROM myvolume;
    607     DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = TRUE;
    608 
    609     -- store the okey checking state
    610     SELECT @@FOREIGN_KEY_CHECKS INTO key_checks;
    611     -- disable foregin check checks to prevent deadlocks on the mountedvol table
    612     SET FOREIGN_KEY_CHECKS=0;
    613 
    614     -- make sure the temp table does not already exist... this can happy if the
    615     -- stored proc fails for some reason
    616     DROP TABLE IF EXISTS myvolume;
    617     CREATE TEMPORARY TABLE myvolume LIKE volume;
    618     INSERT INTO myvolume SELECT * FROM volume;
    619 
    620     -- store the current transaction level
    621 --    SELECT @@session.tx_isolation INTO trans_level;
    622     -- set trans level to repeatable-read so the volume table does not change
    623     -- out from under our cursor
    624 --    SET @@session.tx_isolation = 'REPEATABLE-READ';
    625 
    626     -- iterate over the volume table finding the coresponding entry in the
    627     -- mount table and inserting union of the volume & mount row into the
    628     -- mountedvol table
    629     OPEN cur1;
    630 
    631     DELETE FROM mountedvol;
    632 
    633     myloop: LOOP
    634         FETCH cur1 INTO vol_idvar, namevar, hostvar, pathvar, allocatevar, availablevar, xattrvar;
    635         IF `done` THEN LEAVE myloop; END IF;
    636         INSERT INTO mountedvol
    637             SELECT mountpoint, total, used, vol_idvar, namevar, hostvar, pathvar, allocatevar, availablevar, xattrvar
    638             FROM
    639                 (SELECT *, INSTR(pathvar, mountpoint) = 1 as substring
    640                 FROM mount
    641                 HAVING substring = 1
    642                 ORDER BY substring DESC, LENGTH(mountpoint) DESC
    643                 LIMIT 1) as bar;
    644     END LOOP myloop;
    645    
    646     CLOSE cur1;
    647 
    648     -- restore the original transaction level
    649 --    SET @@session.tx_isolation = trans_level;
    650 
    651     -- restore the original key checking state
    652     SET @@FOREIGN_KEY_CHECKS = key_checks;
    653 
    654     DROP TABLE IF EXISTS myvolume;
    655 
    656     SET FOREIGN_KEY_CHECKS=1;
    657 
    658     COMMIT;
    659 END
     697    KEY(uri)
     698) ENGINE=innodb DEFAULT CHARSET=latin1;
  • branches/pap/Nebulous-Server/lib/Test/Nebulous.pm

    r19493 r25027  
    1111use base qw( Exporter );
    1212
     13use Cache::Memcached;
    1314use DBI;
    1415use File::Path qw( mkpath rmtree );
     
    1617use Nebulous::Server::SQL;
    1718
    18 our @EXPORT = qw( $NEB_DB $NEB_USER $NEB_PASS );
     19our @EXPORT = qw( $NEB_DB $NEB_USER $NEB_PASS $NEB_MEMCACHED_SERVERS);
    1920
    2021our $NEB_DB     = $ENV{'NEB_DB'}   || "DBI:mysql:database=test:host=localhost";
    2122our $NEB_USER   = $ENV{'NEB_USER'} || "test";
    2223our $NEB_PASS   = $ENV{'NEB_PASS'} || '';
     24our $NEB_MEMCACHED_SERVERS = ['127.0.0.1:11211'];
    2325
    2426my $dbh = DBI->connect( $NEB_DB, $NEB_USER, $NEB_PASS );
     
    5355
    5456    # node01/node02: allocate = TRUE
    55     $dbh->do(qq{ INSERT INTO volume (vol_id, name, host, path, allocate, available) VALUES (1, 'node01', 'node01', ?, TRUE, TRUE) }, undef, $dir1);
    56     $dbh->do(qq{ INSERT INTO mount VALUES (?, 10e10, 10e7) }, undef, $dir1);
     57    $dbh->do(qq{ INSERT INTO volume (vol_id, name, host, path, mountpoint, allocate, available) VALUES (1, 'node01', 'node01', ?, '/', TRUE, TRUE) }, undef, $dir1);
     58    $dbh->do(qq{ INSERT INTO mountedvol SELECT *, 10e10, 10e7 FROM volume WHERE vol_id = ? }, undef, 1);
     59#    $dbh->do(qq{ INSERT INTO mount VALUES (?, 10e10, 10e7) }, undef, $dir1);
    5760
    58     $dbh->do(qq{ INSERT INTO volume (vol_id, name, host, path, allocate, available) VALUES (2, 'node02', 'node02', ?, TRUE, TRUE) }, undef, $dir2);
    59     $dbh->do(qq{ INSERT INTO mount VALUES (?, 10e10, 10e8) }, undef, $dir2);
     61    $dbh->do(qq{ INSERT INTO volume (vol_id, name, host, path, mountpoint, allocate, available) VALUES (2, 'node02', 'node02', ?, '/', TRUE, TRUE) }, undef, $dir2);
     62    $dbh->do(qq{ INSERT INTO mountedvol SELECT *, 10e10, 10e8 FROM volume WHERE vol_id = ? }, undef, 2);
     63#    $dbh->do(qq{ INSERT INTO mount VALUES (?, 10e10, 10e8) }, undef, $dir2);
    6064
    6165    # node03: allocate = TRUE
    62     $dbh->do(qq{ INSERT INTO volume (vol_id, name, host, path, allocate, available) VALUES (3, 'node03', 'node03', ?, TRUE, TRUE) }, undef, $dir3);
    63     $dbh->do(qq{ INSERT INTO mount VALUES (?, 10e10, 10e8) }, undef, $dir3);
     66    $dbh->do(qq{ INSERT INTO volume (vol_id, name, host, path, mountpoint, allocate, available) VALUES (3, 'node03', 'node03', ?, '/', TRUE, TRUE) }, undef, $dir3);
     67    $dbh->do(qq{ INSERT INTO mountedvol SELECT *, 10e10, 10e8 FROM volume WHERE vol_id = ? }, undef, 3);
     68#    $dbh->do(qq{ INSERT INTO mount VALUES (?, 10e10, 10e8) }, undef, $dir3);
    6469
    6570    # node04: allocate = FALSE, available = FALSE
    66     $dbh->do(qq{ INSERT INTO volume (vol_id, name, host, path, allocate, available) VALUES (4, 'node04', 'node04', ?, FALSE, FALSE) }, undef, $dir4);
    67     $dbh->do(qq{ INSERT INTO mount VALUES (?, 10e10, 10e7) }, undef, $dir4);
     71    $dbh->do(qq{ INSERT INTO volume (vol_id, name, host, path, mountpoint, allocate, available) VALUES (4, 'node04', 'node04', ?, '/', FALSE, FALSE) }, undef, $dir4);
     72    $dbh->do(qq{ INSERT INTO mountedvol SELECT *, 10e10, 10e7 FROM volume WHERE vol_id = ? }, undef, 4);
     73#    $dbh->do(qq{ INSERT INTO mount VALUES (?, 10e10, 10e7) }, undef, $dir4);
    6874
    6975    # node05: allocate = FALSE, available = TRUE
    70     $dbh->do(qq{ INSERT INTO volume (vol_id, name, host, path, allocate, available) VALUES (5, 'node05', 'node05', ?, FALSE, TRUE) }, undef, $dir5);
    71     $dbh->do(qq{ INSERT INTO mount VALUES (?, 10e10, 10e7) }, undef, $dir5);
     76    $dbh->do(qq{ INSERT INTO volume (vol_id, name, host, path, mountpoint, allocate, available) VALUES (5, 'node05', 'node05', ?, '/', FALSE, TRUE) }, undef, $dir5);
     77    $dbh->do(qq{ INSERT INTO mountedvol SELECT *, 10e10, 10e7 FROM volume WHERE vol_id = ? }, undef, 5);
     78#    $dbh->do(qq{ INSERT INTO mount VALUES (?, 10e10, 10e7) }, undef, $dir5);
    7279
    7380    # node06: allocate = TRUE, available = FALSE
    74     $dbh->do(qq{ INSERT INTO volume (vol_id, name, host, path, allocate, available) VALUES (6, 'node06', 'node06', ?, TRUE, FALSE) }, undef, $dir6);
    75     $dbh->do(qq{ INSERT INTO mount VALUES (?, 10e10, 10e7) }, undef, $dir6);
     81    $dbh->do(qq{ INSERT INTO volume (vol_id, name, host, path, mountpoint, allocate, available) VALUES (6, 'node06', 'node06', ?, '/', TRUE, FALSE) }, undef, $dir6);
     82    $dbh->do(qq{ INSERT INTO mountedvol SELECT *, 10e10, 10e7 FROM volume WHERE vol_id = ? }, undef, 6);
     83#    $dbh->do(qq{ INSERT INTO mount VALUES (?, 10e10, 10e7) }, undef, $dir6);
    7684
    7785    # node07: full
    78     $dbh->do(qq{ INSERT INTO volume (vol_id, name, host, path, allocate, available) VALUES (7, 'node07', 'node07', ?, TRUE, TRUE) }, undef, $dir7);
    79     $dbh->do(qq{ INSERT INTO mount VALUES (?, 10e10, 10e10) }, undef, $dir7);
     86    $dbh->do(qq{ INSERT INTO volume (vol_id, name, host, path, mountpoint, allocate, available) VALUES (7, 'node07', 'node07', ?, '/', TRUE, TRUE) }, undef, $dir7);
     87    $dbh->do(qq{ INSERT INTO mountedvol SELECT *, 10e10, 10e10 FROM volume WHERE vol_id = ? }, undef, 7);
     88#    $dbh->do(qq{ INSERT INTO mount VALUES (?, 10e10, 10e10) }, undef, $dir7);
    8089
    81     $dbh->do(qq{ call getmountedvol() });
     90#    $dbh->do(qq{ call getmountedvol() });
    8291}
    8392
    8493sub cleanup {
     94    # memcached needs to be emptied between test runs
     95        Cache::Memcached->new(servers => $NEB_MEMCACHED_SERVERS)->flush_all;
     96    # if flush_all() turns out to be useless as feared
     97#   my $memd = Cache::Memcached->new(servers => $NEB_MEMCACHED_SERVERS);
     98#   my $query = $dbh->prepare("SELECT ext_id FROM storage_object");
     99#   if ($query->execute) {
     100#        while (my $row = $query->fetchrow_hashref) {
     101#            my $ext_id = $row->{ext_id};
     102#            warn "found key $ext_id\n" if $memd->get($ext_id);
     103#            warn "deleted $ext_id\n" if $memd->delete($ext_id);
     104#        }
     105#   }
     106#   $query->finish;
     107
    85108    foreach my $statement (@{ $sql->get_db_clear }) {
    86109        $dbh->do( $statement );
  • branches/pap/Nebulous-Server/t/02_config.t

    r23932 r25027  
    88use warnings;
    99
    10 use Test::More tests => 21;
     10use Test::More tests => 22;
    1111
    1212use lib qw( ./t ./lib );
     
    109109    is($config_db1->dbpasswd, "boo2", "dbpasswd");
    110110}
     111
     112# memcached_servers
     113{
     114    my $config = Nebulous::Server::Config->new(
     115            memcached_servers => ['127.0.0.1:11211'],
     116        );
     117
     118    is_deeply($config->memcached_servers, ['127.0.0.1:11211'], "memcached_servers");
     119}
  • branches/pap/Nebulous-Server/t/75_parse_neb_key.t

    r23932 r25027  
    1010use Test::More;
    1111
    12 plan tests => 99;
     12plan tests => 106;
    1313
    1414use lib qw( ./t ./lib );
     
    2828    is($key->path, 'foo/bar/baz/quix', 'path');
    2929    is($key->volume, undef, 'volume name');
    30     is($key->soft_volume, undef, 'soft volume name');
     30    is($key->hard_volume, undef, 'soft volume name');
    3131}
    3232
     
    3636    is($key->path, 'foo/bar/baz/quix', 'path');
    3737    is($key->volume, undef, 'volume name');
    38     is($key->soft_volume, undef, 'soft volume name');
     38    is($key->hard_volume, undef, 'soft volume name');
    3939}
    4040
     
    4444    is($key->path, 'foo/bar/baz/quix', 'path');
    4545    is($key->volume, undef, 'volume name');
    46     is($key->soft_volume, undef, 'soft volume name');
     46    is($key->hard_volume, undef, 'soft volume name');
    4747}
    4848
     
    5252    is($key->path, 'foo/bar/baz/quix', 'path');
    5353    is($key->volume, undef, 'volume name');
    54     is($key->soft_volume, undef, 'soft volume name');
     54    is($key->hard_volume, undef, 'soft volume name');
    5555}
    5656
     
    6060    is($key->path, 'foo/bar/baz/quix', 'path');
    6161    is($key->volume, undef, 'volume name');
    62     is($key->soft_volume, undef, 'soft volume name');
     62    is($key->hard_volume, undef, 'soft volume name');
    6363}
    6464
     
    6868    is($key->path, 'foo/bar/baz/quix', 'path');
    6969    is($key->volume, undef, 'volume name');
    70     is($key->soft_volume, undef, 'soft volume name');
     70    is($key->hard_volume, undef, 'soft volume name');
    7171}
    7272
     
    7777    is($key->path, 'foo/bar/baz/quix', 'path');
    7878    is($key->volume, 'boing', 'volume name');
    79     is($key->soft_volume, 1, 'soft volume name');
     79    is($key->hard_volume, undef, 'soft volume name');
    8080}
    8181
     
    8585    is($key->path, 'foo/bar/baz/quix', 'path');
    8686    is($key->volume, 'boing', 'volume name');
    87     is($key->soft_volume, undef, 'soft volume name');
     87    is($key->hard_volume, 1, 'soft volume name');
    8888}
    8989
     
    9494    is($key->path, 'bar/baz/quix', 'path');
    9595    is($key->volume, 'foo', 'volume name');
    96     is($key->soft_volume, 1, 'soft volume name');
     96    is($key->hard_volume, undef, 'soft volume name');
    9797}
    9898
     
    102102    is($key->path, 'bar/baz/quix', 'path');
    103103    is($key->volume, 'foo', 'volume name');
    104     is($key->soft_volume, 1, 'soft volume name');
     104    is($key->hard_volume, undef, 'soft volume name');
    105105}
    106106
     
    110110    is($key->path, 'bar/baz/quix', 'path');
    111111    is($key->volume, 'foo', 'volume name');
    112     is($key->soft_volume, 1, 'soft volume name');
     112    is($key->hard_volume, undef, 'soft volume name');
    113113}
    114114
     
    118118    is($key->path, 'bar/baz/quix', 'path');
    119119    is($key->volume, 'foo', 'volume name');
    120     is($key->soft_volume, 1, 'soft volume name');
     120    is($key->hard_volume, undef, 'soft volume name');
    121121}
    122122
     
    126126    is($key->path, 'bar/baz/quix', 'path');
    127127    is($key->volume, 'foo', 'volume name');
    128     is($key->soft_volume, 1, 'soft volume name');
     128    is($key->hard_volume, undef, 'soft volume name');
    129129}
    130130
     
    135135    is($key->path, 'bar/baz/quix', 'path');
    136136    is($key->volume, 'foo', 'volume name');
    137     is($key->soft_volume, undef, 'soft volume name');
     137    is($key->hard_volume, 1, 'soft volume name');
    138138}
    139139
     
    144144    is($key->path, 'bar/baz/quix', 'path');
    145145    is($key->volume, 'boing', 'volume name');
    146     is($key->soft_volume, 1, 'soft volume name');
     146    is($key->hard_volume, undef, 'soft volume name');
    147147}
    148148
     
    152152    is($key->path, 'bar/baz/quix', 'path');
    153153    is($key->volume, 'boing', 'volume name');
    154     is($key->soft_volume, undef, 'soft volume name');
     154    is($key->hard_volume, 1, 'soft volume name');
    155155}
    156156
     
    160160    is($key->path, 'bar/baz/quix', 'path');
    161161    is($key->volume, 'foo', 'volume name');
    162     is($key->soft_volume, 1, 'soft volume name');
     162    is($key->hard_volume, undef, 'soft volume name');
    163163}
    164164
     
    169169    is($key->path, 'bar/baz/quix', 'path');
    170170    is($key->volume, undef, 'volume name');
    171     is($key->soft_volume, undef, 'soft volume name');
     171    is($key->hard_volume, undef, 'soft volume name');
    172172}
    173173
     
    178178    is($key->path, 'bar/baz/quix', 'path');
    179179    is($key->volume, undef, 'volume name');
    180     is($key->soft_volume, undef, 'soft volume name');
     180    is($key->hard_volume, undef, 'soft volume name');
    181181}
    182182
     
    186186    is($key->path, 'bar/baz/quix', 'path');
    187187    is($key->volume, undef, 'volume name');
    188     is($key->soft_volume, undef, 'soft volume name');
     188    is($key->hard_volume, undef, 'soft volume name');
    189189}
    190190
     
    195195    is($key->path, 'bar/baz/quix', 'path');
    196196    is($key->volume, 'foo', 'volume name');
    197     is($key->soft_volume, undef, 'soft volume name');
     197    is($key->hard_volume, 1, 'soft volume name');
    198198}
    199199
     
    205205    is($key->path, 'foo/bar/baz/quix', 'path');
    206206    is($key->volume, undef, 'volume name');
    207     is($key->soft_volume, undef, 'soft volume name');
     207    is($key->hard_volume, undef, 'soft volume name');
    208208}
    209209
     
    213213    is($key->volume, undef, 'volume name');
    214214    is($key->path, 'foo/bar/baz/quix', 'path');
    215     is($key->soft_volume, undef, 'soft volume name');
     215    is($key->hard_volume, undef, 'soft volume name');
    216216}
    217217
     
    221221    is($key->path, 'foo/bar/baz/quix', 'path');
    222222    is($key->volume, undef, 'volume name');
    223     is($key->soft_volume, undef, 'soft volume name');
     223    is($key->hard_volume, undef, 'soft volume name');
    224224}
    225225
     
    230230    is($key->path, '', 'path');
    231231    is($key->volume, undef, 'volume name');
    232     is($key->soft_volume, undef, 'soft volume name');
     232    is($key->hard_volume, undef, 'soft volume name');
    233233}
    234234
     
    238238    is($key->path, '', 'path');
    239239    is($key->volume, undef, 'volume name');
    240     is($key->soft_volume, undef, 'soft volume name');
     240    is($key->hard_volume, undef, 'soft volume name');
    241241}
    242242
     
    246246    is($key->path, '', 'path');
    247247    is($key->volume, undef, 'volume name');
    248     is($key->soft_volume, undef, 'soft volume name');
     248    is($key->hard_volume, undef, 'soft volume name');
    249249}
    250250
     
    254254    is($key->path, '', 'path');
    255255    is($key->volume, undef, 'volume name');
    256     is($key->soft_volume, undef, 'soft volume name');
     256    is($key->hard_volume, undef, 'soft volume name');
    257257}
    258258
     
    262262    is($key->path, '', 'path');
    263263    is($key->volume, undef, 'volume name');
    264     is($key->soft_volume, undef, 'soft volume name');
     264    is($key->hard_volume, undef, 'soft volume name');
    265265}
    266266
     
    270270    is($key->path, '', 'path');
    271271    is($key->volume, undef, 'volume name');
    272     is($key->soft_volume, undef, 'soft volume name');
     272    is($key->hard_volume, undef, 'soft volume name');
     273}
     274
     275# stringification
     276
     277{
     278    my $txt = 'neb:///bar/baz';
     279    my $key = parse_neb_key($txt);
     280
     281    is("$key", $txt, "stringified");
     282}
     283
     284{
     285    my $txt = 'neb://foo.0/bar/baz';
     286    my $key = parse_neb_key($txt);
     287
     288    is("$key", $txt, "stringified");
     289}
     290
     291{
     292    my $txt = 'neb://~foo.0/bar/baz';
     293    my $key = parse_neb_key($txt);
     294
     295    is("$key", $txt, "stringified");
    273296}
    274297
     
    322345};
    323346like( $@, qr/requires a path/, "no path" );
     347
     348# params
     349eval {
     350    my $key = parse_neb_key();
     351};
     352like( $@, qr/key param is not optional/, "no params" );
     353
     354eval {
     355    my $key = parse_neb_key(undef);
     356};
     357like( $@, qr/key param is not optional/, "key is undef" );
     358
     359eval {
     360    my $key = parse_neb_key(undef, 'bar');
     361};
     362like( $@, qr/key param is not optional/, "key is undef" );
     363
     364eval {
     365    my $key = parse_neb_key('foo', 'bar', 'foo');
     366};
     367like( $@, qr/too many params/, "too many params" );
Note: See TracChangeset for help on using the changeset viewer.