Changeset 25027 for branches/pap/Nebulous-Server
- Timestamp:
- Aug 7, 2009, 4:08:25 PM (17 years ago)
- Location:
- branches/pap
- Files:
-
- 15 deleted
- 17 edited
- 18 copied
-
. (modified) (1 prop)
-
Nebulous-Server (modified) (1 prop)
-
Nebulous-Server/Build.PL (modified) (1 diff)
-
Nebulous-Server/Changes (modified) (2 diffs)
-
Nebulous-Server/MANIFEST (modified) (1 diff)
-
Nebulous-Server/bin/neb-admin (modified) (5 diffs)
-
Nebulous-Server/bin/neb-voladd (modified) (5 diffs)
-
Nebulous-Server/bin/neb-voladm (modified) (1 diff)
-
Nebulous-Server/bin/nebdiskd (modified) (24 diffs)
-
Nebulous-Server/lib/Nebulous/Key.pm (modified) (10 diffs)
-
Nebulous-Server/lib/Nebulous/Server.pm (modified) (109 diffs)
-
Nebulous-Server/lib/Nebulous/Server.pod (modified) (4 diffs)
-
Nebulous-Server/lib/Nebulous/Server/Config.pm (modified) (4 diffs)
-
Nebulous-Server/lib/Nebulous/Server/SQL.pm (modified) (19 diffs)
-
Nebulous-Server/lib/Test/Nebulous.pm (modified) (3 diffs)
-
Nebulous-Server/scripts/dirize.pl (copied) (copied from trunk/Nebulous-Server/scripts/dirize.pl )
-
Nebulous-Server/t/02_config.t (modified) (2 diffs)
-
Nebulous-Server/t/02_server_setup.t (deleted)
-
Nebulous-Server/t/03_server_create_object.t (deleted)
-
Nebulous-Server/t/03_server_setup.t (copied) (copied from trunk/Nebulous-Server/t/03_server_setup.t )
-
Nebulous-Server/t/04_server_create_object.t (copied) (copied from trunk/Nebulous-Server/t/04_server_create_object.t )
-
Nebulous-Server/t/04_server_replicate_object.t (deleted)
-
Nebulous-Server/t/05_server_lock_object.t (deleted)
-
Nebulous-Server/t/05_server_replicate_object.t (copied) (copied from trunk/Nebulous-Server/t/05_server_replicate_object.t )
-
Nebulous-Server/t/06_server_lock_object.t (copied) (copied from trunk/Nebulous-Server/t/06_server_lock_object.t )
-
Nebulous-Server/t/06_server_unlock_object.t (deleted)
-
Nebulous-Server/t/07_server_find_instances.t (deleted)
-
Nebulous-Server/t/07_server_unlock_object.t (copied) (copied from trunk/Nebulous-Server/t/07_server_unlock_object.t )
-
Nebulous-Server/t/08_server_delete_instance.t (deleted)
-
Nebulous-Server/t/08_server_find_instances.t (copied) (copied from trunk/Nebulous-Server/t/08_server_find_instances.t )
-
Nebulous-Server/t/09_server_delete_instance.t (copied) (copied from trunk/Nebulous-Server/t/09_server_delete_instance.t )
-
Nebulous-Server/t/09_server_stat_object.t (deleted)
-
Nebulous-Server/t/10_server_is_valid_volume_name.t (deleted)
-
Nebulous-Server/t/10_server_stat_object.t (copied) (copied from trunk/Nebulous-Server/t/10_server_stat_object.t )
-
Nebulous-Server/t/11_server_is_valid_object_key.t (deleted)
-
Nebulous-Server/t/11_server_is_valid_volume_name.t (copied) (copied from trunk/Nebulous-Server/t/11_server_is_valid_volume_name.t )
-
Nebulous-Server/t/12_server_find_objects.t (deleted)
-
Nebulous-Server/t/12_server_is_valid_object_key.t (copied) (copied from trunk/Nebulous-Server/t/12_server_is_valid_object_key.t )
-
Nebulous-Server/t/13_server_find_objects.t (copied) (copied from trunk/Nebulous-Server/t/13_server_find_objects.t )
-
Nebulous-Server/t/13_server_rename_object.t (deleted)
-
Nebulous-Server/t/14_server_rename_object.t (copied) (copied from trunk/Nebulous-Server/t/14_server_rename_object.t )
-
Nebulous-Server/t/14_server_xattr.t (deleted)
-
Nebulous-Server/t/15_mounts.t (deleted)
-
Nebulous-Server/t/15_server_xattr.t (copied) (copied from trunk/Nebulous-Server/t/15_server_xattr.t )
-
Nebulous-Server/t/16_mounts.t (copied) (copied from trunk/Nebulous-Server/t/16_mounts.t )
-
Nebulous-Server/t/16_server_swap_objects.t (deleted)
-
Nebulous-Server/t/17_server_swap_objects.t (copied) (copied from trunk/Nebulous-Server/t/17_server_swap_objects.t )
-
Nebulous-Server/t/18_server_chmod_object.t (copied) (copied from trunk/Nebulous-Server/t/18_server_chmod_object.t )
-
Nebulous-Server/t/19_server_prune_object.t (copied) (copied from trunk/Nebulous-Server/t/19_server_prune_object.t )
-
Nebulous-Server/t/75_parse_neb_key.t (modified) (32 diffs)
Legend:
- Unmodified
- Added
- Removed
-
branches/pap
- Property svn:mergeinfo changed
-
branches/pap/Nebulous-Server
- Property svn:mergeinfo changed
-
branches/pap/Nebulous-Server/Build.PL
r23932 r25027 10 10 requires => { 11 11 'Apache2::Const' => 0, 12 'Cache::Memcached' => 0, 12 13 'Class::Accessor::Fast' => 0, 13 14 'Config::YAML' => '1.42', -
branches/pap/Nebulous-Server/Changes
r23932 r25027 2 2 3 3 0.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 4 8 - retry database transactions when a deadlock is detected 5 9 - add log4perl logging to nebdiskd … … 16 20 - change 'log_level' param to 'trace' 17 21 - 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! 18 39 19 40 0.16 -
branches/pap/Nebulous-Server/MANIFEST
r23932 r25027 33 33 t/01_load.t 34 34 t/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 35 t/03_server_setup.t 36 t/04_server_create_object.t 37 t/05_server_replicate_object.t 38 t/06_server_lock_object.t 39 t/07_server_unlock_object.t 40 t/08_server_find_instances.t 41 t/09_server_delete_instance.t 42 t/10_server_stat_object.t 43 t/11_server_is_valid_volume_name.t 44 t/12_server_is_valid_object_key.t 45 t/13_server_find_objects.t 46 t/14_server_rename_object.t 47 t/15_server_xattr.t 48 t/16_mounts.t 49 t/17_server_swap_objects.t 50 t/18_server_chmod_object.t 51 t/19_server_prune_object.t 50 52 t/75_parse_neb_key.t -
branches/pap/Nebulous-Server/bin/neb-admin
r23932 r25027 91 91 { 92 92 # 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"); 97 96 98 97 if (not defined $so_id_start) { $so_id_start = 0; } … … 102 101 # XXX check if so_id_start is beyond MAX(so_id): if so, exit with exit status 10 103 102 { 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; 106 105 my $answer = $query->fetchrow_arrayref; 107 106 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 116 115 my $query = $dbh->prepare( 117 " SELECT118 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 copies126 FROM storage_object127 JOIN instance128 USING(so_id)129 JOIN myvolume130 USING(vol_id)131 LEFT JOIN storage_object_xattr132 ON storage_object.so_id = storage_object_xattr.so_id133 JOIN mymountedvol134 USING(vol_id)135 WHERE mymountedvol.available = 1136 AND storage_object_xattr.name = 'user.copies'137 AND s torage_object.so_id >= $so_id_start138 AND s torage_object.so_id < $so_id_end139 GROUP BY so_id140 HAVING available_instances < instances OR instances < copies141 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" 142 141 ); 143 142 $query->execute; 144 143 145 $dbh->do("DROP TABLE IF EXISTS mymountedvol");144 # $dbh->do("DROP TABLE IF EXISTS mymountedvol"); 146 145 147 146 my @rows; … … 174 173 # if the copies xattr is unset and the object has 2 or more instances, we 175 174 # will assume a target of 2 copies 176 177 # XXX change this: there should be no default value or we will178 # have a race condition. user.copies should never get set by179 # 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. 180 179 my $copies = $obj->{copies} || 2; 181 180 … … 207 206 # use a different exit status if we hit the limit (likely more files pending) 208 207 if ($Npending == $limit) { 209 exit 1;208 exit 1; 210 209 } 211 210 exit 0; … … 225 224 226 225 $dbh->do("DROP TABLE IF EXISTS mymountedvol"); 226 $dbh->do("DROP TABLE IF EXISTS myvolume"); 227 227 228 228 my @rows; -
branches/pap/Nebulous-Server/bin/neb-voladd
r23348 r25027 19 19 use Pod::Usage qw( pod2usage ); 20 20 21 my ($db, $dbhost, $dbuser, $dbpass, $ vname, $vhost, $uri);21 my ($db, $dbhost, $dbuser, $dbpass, $mountpoint, $vname, $vhost, $uri); 22 22 23 23 $db = $ENV{'NEB_DB'} unless $db; … … 29 29 'db|d=s' => \$db, 30 30 'host=s' => \$dbhost, 31 'mountpoint|m=s' => \$mountpoint, 32 'pass|p=s' => \$dbpass, 33 'uri|u=s' => \$uri, 31 34 'user|u=s' => \$dbuser, 32 ' pass|p=s' => \$dbpass,35 'vhost=s' => \$vhost, 33 36 'vname|n=s' => \$vname, 34 'vhost=s' => \$vhost,35 'uri|u=s' => \$uri,36 37 ) || pod2usage( 2 ); 37 38 38 39 pod2usage( -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;40 pod2usage( -msg => "Required options: --db --user --pass --mountpoint --vname --vhost --uri", -exitval => 2 ) 41 unless $db && $dbuser && $dbpass && $mountpoint && $vname && $vhost && $uri; 41 42 42 43 my $dbh = DBI->connect( … … 59 60 die "path: $path dirived from URI: $uri does not exist"; 60 61 } 62 unless (-d $mountpoint) { 63 die "path: $mountpoint does not exist"; 64 } 61 65 62 66 print " OK\n"; … … 65 69 66 70 my $query = $dbh->prepare( $sql->new_volume ); 67 $query->execute( $vname, $vhost, $path );71 $query->execute( $vname, $vhost, $path, $mountpoint); 68 72 69 73 print " OK\n"; … … 128 132 Optional. Defaults to C<localhost>. 129 133 134 =item * --mountpoint <path> 135 136 Path to the mountpoint of the filesystem on which C<--uri> resides. 130 137 131 138 =back -
branches/pap/Nebulous-Server/bin/neb-voladm
r20204 r25027 91 91 my $query = $dbh->prepare($q); 92 92 $query->execute(@bind); 93 $dbh->do("call getmountedvol");94 93 $dbh->commit; 95 94 }; -
branches/pap/Nebulous-Server/bin/nebdiskd
r23675 r25027 1 1 #!/usr/bin/env perl 2 2 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 6 4 7 5 use strict; … … 35 33 $user, 36 34 $group, 35 $retry, 37 36 ); 38 37 39 38 GetOptions( 40 'dbhost| h=s' => \$dbhost,39 'dbhost|H=s' => \$dbhost, 41 40 'dbpass|p=s' => \$dbpass, 42 41 'dbuser|u=s' => \$dbuser, … … 46 45 'pidfile=s' => \$pidfile, 47 46 'restart|r' => \$restart, 47 'retry=i' => \$retry, 48 48 'stop|s' => \$stop, 49 'user =s'=> \$user,49 'user|U=s' => \$user, 50 50 'verbose|v' => \$verbose, 51 51 ) || pod2usage( 2 ); … … 61 61 $dbhost ||= $c->get_dbhost || $ENV{'NEB_DBHOST'} || 'localhost'; 62 62 $dbuser ||= $c->get_dbuser || $ENV{'NEB_USER'}; 63 $dbpass ||= $c->get_dbpass || $ENV{'NEB_PASS'} ;63 $dbpass ||= $c->get_dbpass || $ENV{'NEB_PASS'} || undef; 64 64 $pidfile ||= $c->get_pidfile || "/var/tmp/nebdiskd"; 65 65 $user ||= $c->get_user || $<; # user 66 66 $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; 69 70 my $poll_interval = $c->get_poll_interval || 5; 70 71 … … 80 81 $c->set_pidfile($pidfile); 81 82 $c->set_poll_interval($poll_interval); 83 $c->set_retry($retry); 82 84 $c->write; 83 85 84 86 pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV; 85 pod2usage( -msg => "Required options: --db -- user --pass", -exitval => 2 )86 unless $db && $dbuser && $dbpass;87 pod2usage( -msg => "Required options: --db --dbuser", -exitval => 2 ) 88 unless $db && $dbuser; 87 89 88 90 # start up logging … … 108 110 log4perl.appender.Mailer.to = ps-ipp-ops@ifa.hawaii.edu 109 111 log4perl.appender.Mailer.subject = nebdiskd alert 110 log4perl.appender.AppError.Filter= MatchWarn 112 log4perl.appender.Mailer.buffered = 0 113 log4perl.appender.Mailer.Filter= MatchWarn 111 114 log4perl.appender.Mailer.layout = Log::Log4perl::Layout::PatternLayout 112 115 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 113 120 '; 114 121 Log::Log4perl::init(\$conf); … … 136 143 poll_interval => $poll_interval, 137 144 debug => $debug, 145 retry => $retry, 138 146 ); 139 147 }; … … 155 163 my $dbhost = $p{dbhost} or return; 156 164 my $dbuser = $p{dbuser} or return; 157 my $dbpass = $p{dbpass} or return;165 # my $dbpass = $p{dbpass} or return; 158 166 my $poll_interval = $p{poll_interval} || 60; 159 167 my $debug = $p{debug} || 0; 168 my $retry = $p{retry} || 1; 160 169 161 170 # setup the db on every pass incase the database died on us … … 168 177 169 178 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 173 195 # determine valid mountpoints 174 196 foreach my $mnt (@$mounts) { 175 $log->debug("checking $mnt"); 197 my $mountpoint = $mnt->{'mountpoint'}; 198 $log->debug("checking $mountpoint"); 176 199 # this /SHOULD/ fail if the mount point is handled by the 177 200 # 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)) { 180 205 $log->warn("$mnt is not a valid mountpoint"); 181 206 } 182 207 }; 183 208 if ($@) { 209 # try is_mountpoint() again if $retry > 1 210 if ($tries < $retry) { 211 $log->warn("retrying test of $mountpoint"); 212 goto TEST; 213 } 184 214 $log->warn($@); 185 $d_query->execute($m nt);215 $d_query->execute($mountpoint); 186 216 next; 187 217 } … … 190 220 # we determine if it's a valid mountpoint incase 191 221 # is_mountpoint() invokes the automounter 192 my $dev_info = df($m nt, 1024);222 my $dev_info = df($mountpoint, 1024); 193 223 unless (defined $dev_info) { 194 $log->error("can't find device info for $m nt");224 $log->error("can't find device info for $mountpoint"); 195 225 next; 196 226 } 197 227 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"); 200 231 201 232 } 202 233 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"); 207 238 }; 208 239 if ($@) { 209 $dbh->rollback;210 $log->debug("rolledback transaction");240 # $dbh->rollback; 241 # $log->debug("rolledback transaction"); 211 242 $log->logdie($@); 212 243 } … … 235 266 my %p = @_; 236 267 268 # $p{dbpass} may be undef; 269 237 270 return unless defined $p{db} 238 271 and defined $p{dbhost} 239 and defined $p{dbuser} 240 and defined $p{dbpass}; 272 and defined $p{dbuser}; 241 273 242 274 my $sql = Nebulous::Server::SQL->new; … … 249 281 RaiseError => 1, 250 282 PrintError => 1, 251 AutoCommit => 0,283 AutoCommit => 1, # don't want this to be trasnactional 252 284 }, 253 285 ); … … 255 287 eval { 256 288 $dbh->do( $sql->set_transaction_model ); 257 $dbh->commit;289 # $dbh->commit; 258 290 }; 259 291 if ($@) { 260 $dbh->rollback;292 # $dbh->rollback; 261 293 $log->logdie($@); 262 294 } … … 344 376 =head1 SYNOPSIS 345 377 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] 347 382 348 383 =head1 DESCRIPTION … … 355 390 =over 4 356 391 357 =item * --db|- d<db name>392 =item * --db|-D <db name> 358 393 359 394 Name of database (C<namespace>) to write too. … … 362 397 variable is set. 363 398 364 =item * -- user|-u <dbusername>399 =item * --dbuser|-u <username> 365 400 366 401 Username to authenticate with. … … 369 404 variable is set. 370 405 371 =item * -- pass|-p <dbpassword>406 =item * --dbpass|-p <password> 372 407 373 408 Password to authenticate with. 409 410 Optional if defined in the F<.nebdiskdrc> file or the appropriate environment 411 variable is set or if the database does not require a password. 412 413 =item * --dbhost|-H <hostname> 414 415 Database host to connect to. 374 416 375 417 Optional if defined in the F<.nebdiskdrc> file or the appropriate environment 376 418 variable is set. 377 419 378 =item * --debug 420 =item * --debug|-d 379 421 380 422 This flag prevents the program from "daemonizing" so output can be sent to … … 397 439 instance. 398 440 399 =item * --verbose|-r 441 =item * --retry 442 443 The number of times to test a mountpoint for "mountedness" before giving up on 444 it. 445 446 =item * --verbose|-v 400 447 401 448 Turns on informational/debugging messages to be sent to the … … 403 450 with C<--debug>. 404 451 452 =item * --user|-U 453 454 user account to run daemon as. 455 456 =item * --group|-g 457 458 group to run daemon as. 459 405 460 =back 406 461 … … 415 470 =item * C<NEB_DB> 416 471 417 Equivalent to --db|- d472 Equivalent to --db|-D 418 473 419 474 =item * C<NEB_USER> 420 475 421 Equivalent to -- user|-u476 Equivalent to --dbuser|-u 422 477 423 478 =item * C<NEB_PASS> 424 479 425 Equivalent to -- pass|-p480 Equivalent to --dbpass|-p 426 481 427 482 =back … … 439 494 dbpass: '@neb@' 440 495 dbuser: nebulous 441 mounts:442 - /mnt443 - /tmp444 - /usr445 496 pidfile: /var/tmp/nebdiskd 446 497 poll_interval: 5 498 retry: 3 447 499 448 500 The values C<db>, C<dbpass>, C<dbuser>, and C<pidfile> have the same semantics … … 451 503 =over 4 452 504 453 =item * C<mounts>454 455 A list of "paths" to C<stat(2)> before mounted volumes are polled. The propose456 of this option is to attempt to keep "automounted" volumes mounted while this457 program is running.458 459 505 This value may be omitted or left blank. 460 506 … … 465 511 This value may be omitted or left blank. The default value is C<60>s. 466 512 513 =item * C<retry> 514 515 Same as C<--retry>. 516 467 517 =back 468 518 … … 481 531 =head1 COPYRIGHT 482 532 483 Copyright (C) 2007 Joshua Hoblitt. All rights reserved.533 Copyright (C) 2007-2009 Joshua Hoblitt. All rights reserved. 484 534 485 535 This program is free software; you can redistribute it and/or modify it under -
branches/pap/Nebulous-Server/lib/Nebulous/Key.pm
r23932 r25027 8 8 use warnings FATAL => qw( all ); 9 9 10 our $VERSION = '0.0 2';10 our $VERSION = '0.03'; 11 11 12 12 use base qw( Exporter Class::Accessor::Fast ); … … 14 14 use File::Spec; 15 15 use URI::file; 16 use Carp qw( croak ); 16 17 use URI; 17 18 use overload '""' => \&_stringify_key; … … 22 23 ); 23 24 24 __PACKAGE__->mk_ro_accessors(qw( path volume soft_volume ));25 __PACKAGE__->mk_ro_accessors(qw( path volume hard_volume )); 25 26 26 27 sub parse_neb_key 27 28 { 28 29 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; 30 33 31 34 # white space is not allowed 32 35 if ($key =~ qr/\s+/) { 33 die"keys and URIs may not contain whitespace";36 croak "keys and URIs may not contain whitespace"; 34 37 } 35 38 … … 39 42 40 43 my $volume_name; 41 my $ soft_volume;44 my $hard_volume; 42 45 # if this is a valid uri 43 46 if (defined $scheme) { 44 47 # 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" 46 49 unless $scheme eq 'neb'; 47 50 … … 58 61 # path component to it 59 62 unless (length $path) { 60 die"neb URI scheme requires a path component";63 croak "neb URI scheme requires a path component"; 61 64 } 62 65 … … 68 71 if (defined $volume_info->{volume} and $volume_info->{volume} ne 'any') { 69 72 $volume_name = $volume_info->{volume}; 70 $ soft_volume = $volume_info->{soft_volume};73 $hard_volume = $volume_info->{hard_volume}; 71 74 } 72 75 73 76 # require a leading slash if there is no volume name 74 77 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:/"; 76 79 } 77 80 } else { … … 81 84 if (defined $volume_info->{volume} and $volume_info->{volume} ne 'any') { 82 85 $volume_name = $volume_info->{volume}; 83 $ soft_volume = $volume_info->{soft_volume};86 $hard_volume = $volume_info->{hard_volume}; 84 87 } 85 88 } … … 96 99 return __PACKAGE__->new({ 97 100 volume => $volume_name, 98 soft_volume => $soft_volume,101 hard_volume => $hard_volume, 99 102 path => $path, 100 103 }); … … 107 110 return unless defined $volume; 108 111 109 my $ soft_volume;112 my $hard_volume; 110 113 # 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; 113 116 } 114 117 115 return({ volume => $volume, soft_volume => $soft_volume });118 return({ volume => $volume, hard_volume => $hard_volume }); 116 119 } 117 120 … … 122 125 123 126 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"; 128 131 } 129 132 -
branches/pap/Nebulous-Server/lib/Nebulous/Server.pm
r23932 r25027 13 13 use base qw( Class::Accessor::Fast ); 14 14 15 use Cache::Memcached; 15 16 use DBI; 16 17 use Digest::SHA1 qw( sha1_hex ); 18 use Fcntl ':mode'; 17 19 use File::Basename qw( basename dirname fileparse ); 18 20 use File::ExtAttr qw( setfattr ); … … 27 29 use URI::file; 28 30 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 33 use constant SUBPATH_DEPTH => 2; 34 use constant NFS_RETRIES => 100; 35 use constant NFS_RETRY_WAIT => 1; 36 use constant TRANS_RETRY_WAIT => 1; 37 38 # transaction restart/retry regex 39 my $trans_regex = qr/Deadlock Found|Lock wait timeout exceeded|try restarting transaction|Can't connect to MySQL server/i; 33 40 34 41 sub new … … 45 52 sub new_from_config 46 53 { 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() 50 59 Nebulous::Server::Log->init($config); 51 60 my $log = Log::Log4perl::get_logger( "Nebulous::Server" ); 52 61 $log->level($config->trace); 62 $log->debug( "entered - @_" ); 53 63 54 64 my $sql = Nebulous::Server::SQL->new; 55 56 $log->debug( "entered - @_" );57 65 58 66 my $self = bless {}, ref $class || $class; … … 60 68 $self->sql($sql); 61 69 $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); 62 81 63 82 $log->debug( "leaving" ); … … 70 89 sub _db_index_for_key 71 90 { 72 my ($self, $key) = @_; 91 my $self = shift; 92 93 my $log = $self->log; 94 $log->debug( "entered - @_" ); 95 96 my ($key) = @_; 73 97 74 98 my $config = $self->config; 75 99 76 100 my $db_index = 0; 77 die "key not defined"unless defined $key;101 $log->logdie("key not defined") unless defined $key; 78 102 79 103 # hash the key to select the correct database instance … … 85 109 $db_index = unpack("h8", sha1_hex($path)) % $config->n_db; 86 110 111 $log->debug("index is $db_index"); 112 $log->debug("leaving"); 113 87 114 return $db_index; 88 115 } … … 90 117 sub _db_for_index 91 118 { 92 my ($self, $db_index) = @_;119 my $self = shift; 93 120 94 121 my $log = $self->log; 122 $log->debug( "entered - @_" ); 123 124 my ($db_index) = @_; 125 95 126 my $sql = $self->sql; 96 127 my $config = $self->config; … … 108 139 # lookup database info 109 140 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") 111 142 unless $db_config; 112 143 … … 116 147 # processes and the database might have gone away on us. Apache::DBI will 117 148 # 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; 139 178 } 140 179 141 180 $self->{dbs}[$db_index] = $dbh; 142 181 182 $log->debug("leaving"); 183 143 184 return $dbh; 144 185 } … … 148 189 my $self = shift; 149 190 191 my $log = $self->log; 192 $log->debug( "entered - @_" ); 193 150 194 my ($key) = validate_pos(@_, 151 195 { … … 154 198 ); 155 199 156 my $log = $self->log;157 200 my $sql = $self->sql; 158 201 my $config = $self->config; 159 202 160 die "key not defined"unless defined $key;203 $log->logdie("key not defined") unless defined $key; 161 204 my $db_index = $self->_db_index_for_key($key); 162 205 163 206 my $dbh = $self->_db_for_index($db_index); 207 164 208 return $dbh; 165 209 } … … 168 212 { 169 213 my $self = shift; 214 215 my $log = $self->log; 216 $log->debug( "entered - @_" ); 170 217 171 218 my ($key, $vol_name) = validate_pos(@_, … … 186 233 ); 187 234 235 my $sql = $self->sql; 236 188 237 # 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 $@; 190 242 $vol_name = $key->volume; 191 243 192 my $log = $self->log;193 my $sql = $self->sql;194 244 my $db = $self->db($key); 195 196 $log->debug( "entered - @_" );197 245 198 246 # the key's volume can't be validiated on input for this method so we have … … 200 248 if (defined $vol_name 201 249 and not $self->_is_valid_volume_name($key, $key->volume)) { 202 if ($key->soft_volume) {250 unless ($key->hard_volume) { 203 251 $log->warn( "$vol_name is not a known volume name" ); 204 252 $vol_name = undef; 205 253 } else { 206 die "$vol_name is not a valid volume name"254 $log->logdie("$vol_name is not a valid volume name"); 207 255 } 208 256 } 209 257 210 258 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); 212 260 213 261 my $parent_id = $self->_resolve_dir_parent_id(key => $key, create => 1); … … 282 330 $db->rollback; 283 331 $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; 286 335 redo TRANS; 287 336 } … … 291 340 } 292 341 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 293 346 $log->debug("leaving"); 294 347 … … 300 353 { 301 354 my $self = shift; 355 356 my $log = $self->log; 357 $log->debug( "entered - @_" ); 302 358 303 359 my %p = validate(@_, … … 322 378 my $key = $p{key}; 323 379 324 my $log = $self->log;325 380 my $sql = $self->sql; 326 381 my $db = $self->db($key); 327 328 $log->debug( "entered - @_" );329 382 330 383 # no path means '/', which has a dir_id & parent_id of 1 … … 394 447 $query->finish; 395 448 } 396 $log->logdie("failed to get LAST_INSERT_ID()")449 die("failed to get LAST_INSERT_ID()") 397 450 unless $parent_id; 398 451 … … 403 456 $db->rollback; 404 457 $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; 407 462 redo TRANS; 408 463 } 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 } 409 470 $log->logdie("error: $@"); 410 471 } … … 421 482 { 422 483 my $self = shift; 484 485 my $log = $self->log; 486 $log->debug("entered - @_"); 423 487 424 488 my ($key, $newkey) = validate_pos(@_, … … 438 502 ); 439 503 504 my $sql = $self->sql; 505 440 506 # 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 446 516 my $db = $self->db($key); 447 448 $log->debug("entered - @_");449 517 450 518 # XXX this may require database migration in the future … … 464 532 unless ($rows == 1) { 465 533 $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; 468 539 469 540 $db->commit; … … 473 544 $db->rollback; 474 545 $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; 477 549 redo TRANS; 478 550 } … … 491 563 my $self = shift; 492 564 565 my $log = $self->log; 566 $log->debug("entered - @_"); 567 493 568 my ($key1, $key2) = validate_pos(@_, 494 569 { … … 506 581 ); 507 582 583 my $sql = $self->sql; 584 508 585 # 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 $@; 514 594 515 595 my $dbidx1 = $self->_db_index_for_key($key1); 516 596 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); 518 599 519 600 my $dbh1 = $self->_db_for_index($dbidx1); 520 601 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); 524 604 525 605 # order of operations for the swap with a single db is: … … 543 623 unless ($rows == 1) { 544 624 $query->finish; 545 $log->logdie("affected row count is $rows instead of 1");625 die("affected row count is $rows instead of 1"); 546 626 } 547 627 } … … 556 636 unless ($rows == 1) { 557 637 $query->finish; 558 $log->logdie("affected row count is $rows instead of 1");638 die("affected row count is $rows instead of 1"); 559 639 } 560 640 } … … 569 649 unless ($rows == 1) { 570 650 $query->finish; 571 $log->logdie("affected row count is $rows instead of 1");651 die("affected row count is $rows instead of 1"); 572 652 } 573 653 } … … 579 659 $db->rollback; 580 660 $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; 583 664 redo TRANS; 584 665 } … … 612 693 my $self = shift; 613 694 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(@_, 615 699 { 616 700 type => SCALAR, … … 633 717 ); 634 718 719 my $sql = $self->sql; 720 635 721 # if a volume name is explicity specified then we should make the 636 722 # replication onto that volume (even if there is alread an instance on that … … 640 726 # instance on it. If all avilable volume already have an instance on them 641 727 # 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 649 736 my $db = $self->db($key); 650 737 651 $log->debug("entered - @_");652 653 if (defined $ vol_name738 # 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 654 741 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"); 658 744 } 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"); 661 753 } 662 754 663 755 my ($vol_id, $vol_host, $vol_path, $vol_xattr); 664 if (defined $ vol_name) {756 if (defined $dest_vol_name) { 665 757 ($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); 667 759 } else { 668 760 ($vol_id, $vol_host, $vol_path, $vol_xattr) … … 681 773 unless ( $rows > 0 ) { 682 774 $query->finish; 683 $log->logdie( "storage object does not exist" );775 die( "storage object does not exist" ); 684 776 } 685 777 … … 725 817 $db->rollback; 726 818 # 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) { 728 820 $log->debug("retrying with 'any' volume"); 729 821 return $self->replicate_object($key->path, 'any'); 730 822 } 731 823 $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; 734 827 redo TRANS; 735 828 } … … 739 832 } 740 833 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 741 847 $log->debug("leaving"); 742 848 … … 745 851 746 852 853 sub 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; 880 TRANS: 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 747 948 sub lock_object 748 949 { 749 950 my $self = shift; 951 952 my $log = $self->log; 953 $log->debug( "entered - @_" ); 750 954 751 955 my ( $key, $type ) = validate_pos( @_, … … 764 968 ); 765 969 970 my $sql = $self->sql; 971 766 972 # 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 771 978 my $db = $self->db($key); 772 773 $log->debug( "entered - @_" );774 979 775 980 my $so_id; … … 785 990 unless ( $rows == 1 ) { 786 991 $query->finish; 787 $log->logdie( "storage object does not exist" );992 die( "storage object does not exist" ); 788 993 } 789 994 … … 800 1005 # can't set a write lock if there are read locks 801 1006 if ($write_lock) { 802 $log->logdie("can not write lock twice -- retry");1007 die("can not write lock twice -- retry"); 803 1008 } 804 1009 805 1010 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"); 807 1012 } 808 1013 … … 813 1018 # if we affected more then one row something very bad has happened. 814 1019 unless ($rows == 1) { 815 $log->logdie("affected row count is $rows instead of 1");1020 die("affected row count is $rows instead of 1"); 816 1021 } 817 1022 … … 820 1025 # can't set a read lock if there's a write lock 821 1026 if ($write_lock) { 822 $log->logdie("can not read lock after write lock -- retry");1027 die("can not read lock after write lock -- retry"); 823 1028 } 824 1029 … … 829 1034 # if we affected more then one row something very bad has happened. 830 1035 unless ($rows == 1) { 831 $log->logdie("affected row count is $rows instead of 1");1036 die("affected row count is $rows instead of 1"); 832 1037 } 833 1038 } … … 840 1045 $db->rollback; 841 1046 $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; 844 1050 redo TRANS; 845 1051 } … … 858 1064 { 859 1065 my $self = shift; 1066 1067 my $log = $self->log; 1068 $log->debug( "entered - @_" ); 860 1069 861 1070 my ( $key, $type ) = validate_pos( @_, … … 874 1083 ); 875 1084 1085 my $sql = $self->sql; 1086 876 1087 # 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 881 1093 my $db = $self->db($key); 882 883 $log->debug( "entered - @_" );884 1094 885 1095 my $so_id; … … 895 1105 unless ($rows == 1) { 896 1106 $query->finish; 897 $log->logdie("storage object does not exist");1107 die("storage object does not exist"); 898 1108 } 899 1109 … … 909 1119 # can't remove a write lock if it doesn't exist 910 1120 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"); 912 1122 } 913 1123 914 1124 unless ($write_lock) { 915 $log->logdie("can not remove non-existant write lock");1125 die("can not remove non-existant write lock"); 916 1126 } 917 1127 … … 922 1132 # if we affected more then one row something very bad has happened. 923 1133 unless ($rows == 1) { 924 $log->logdie("affected row count is $rows instead of 1");1134 die("affected row count is $rows instead of 1"); 925 1135 } 926 1136 } … … 929 1139 # can't remove a read lock if there aren't any 930 1140 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"); 932 1142 } 933 1143 934 1144 if ($read_lock == 0) { 935 $log->logdie("can not remove non-existant read lock");1145 die("can not remove non-existant read lock"); 936 1146 } 937 1147 … … 942 1152 # if we affected more then one row something very bad has happened. 943 1153 unless ($rows == 1) { 944 $log->logdie("affected row count is $rows instead of 1");1154 die("affected row count is $rows instead of 1"); 945 1155 } 946 1156 … … 953 1163 $db->rollback; 954 1164 $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; 957 1168 redo TRANS; 958 1169 } … … 971 1182 { 972 1183 my $self = shift; 1184 1185 my $log = $self->log; 1186 $log->debug("entered - @_"); 973 1187 974 1188 my ($key, $name, $value, $flags) = validate_pos(@_, … … 981 1195 { 982 1196 type => SCALAR, 1197 callbacks => { 1198 'xattr is in user. namespace' 1199 => sub { ($_[0]) =~ qr/^user\./ }, 1200 }, 983 1201 }, 984 1202 { … … 993 1211 ); 994 1212 1213 my $sql = $self->sql; 1214 995 1215 # 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 1000 1221 my $db = $self->db($key); 1001 1002 $log->debug("entered - @_");1003 1222 1004 1223 TRANS: while (1) { … … 1020 1239 if ($flags eq 'create') { 1021 1240 unless ($rows == 1) { 1022 $log->logdie( "affected row count is $rows instead of 1" );1241 die( "affected row count is $rows instead of 1" ); 1023 1242 } 1024 1243 } else { … … 1026 1245 # the case of a replace and 1 if the xattr didn't already exist. 1027 1246 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" ); 1029 1248 } 1030 1249 } … … 1036 1255 $db->rollback; 1037 1256 $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; 1040 1260 redo TRANS; 1041 1261 } … … 1054 1274 { 1055 1275 my $self = shift; 1276 1277 my $log = $self->log; 1278 $log->debug("entered - @_"); 1056 1279 1057 1280 my ($key, $name) = validate_pos(@_, … … 1064 1287 { 1065 1288 type => SCALAR, 1289 callbacks => { 1290 'xattr is in user. namespace' 1291 => sub { ($_[0]) =~ qr/^user\./ }, 1292 }, 1066 1293 }, 1067 1294 ); 1068 1295 1296 my $sql = $self->sql; 1297 1069 1298 # 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 1074 1304 my $db = $self->db($key); 1075 1076 $log->debug("entered - @_");1077 1305 1078 1306 my $value; … … 1085 1313 if ($rows == 0) { 1086 1314 $query->finish; 1087 $log->logdie( "xattr $key:$name does not exist" );1315 die( "xattr $key:$name does not exist" ); 1088 1316 } 1089 1317 # if we go more then one row bad something very bad has happened. 1090 1318 unless ($rows == 1) { 1091 1319 $query->finish; 1092 $log->logdie( "affected row count is $rows instead of 1" );1320 die( "affected row count is $rows instead of 1" ); 1093 1321 } 1094 1322 … … 1099 1327 $value = $row->{ 'value' }; 1100 1328 }; 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 } 1102 1336 1103 1337 $log->debug("leaving"); … … 1110 1344 { 1111 1345 my $self = shift; 1346 1347 my $log = $self->log; 1348 $log->debug("entered - @_"); 1112 1349 1113 1350 my ($key) = validate_pos(@_, … … 1120 1357 ); 1121 1358 1359 my $sql = $self->sql; 1360 1122 1361 # 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 1127 1367 my $db = $self->db($key); 1128 1129 $log->debug("entered - @_");1130 1368 1131 1369 my @xattrs; … … 1151 1389 my $self = shift; 1152 1390 1391 my $log = $self->log; 1392 $log->debug("entered - @_"); 1393 1153 1394 my ($key, $name) = validate_pos(@_, 1154 1395 { … … 1160 1401 { 1161 1402 type => SCALAR, 1403 callbacks => { 1404 'xattr is in user. namespace' 1405 => sub { ($_[0]) =~ qr/^user\./ }, 1406 }, 1162 1407 }, 1163 1408 ); 1164 1409 1410 my $sql = $self->sql; 1411 1165 1412 # 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 1170 1418 my $db = $self->db($key); 1171 1172 $log->debug("entered - @_");1173 1419 1174 1420 TRANS: while (1) { … … 1179 1425 $query->finish; 1180 1426 1427 # no rows affected means the xattr did not exist 1428 if ($rows == 0) { 1429 die( "xattr $key:$name does not exist" ); 1430 } 1431 1181 1432 # 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" ); 1184 1435 } 1185 1436 … … 1190 1441 $db->rollback; 1191 1442 $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; 1194 1446 redo TRANS; 1195 1447 } … … 1209 1461 my $self = shift; 1210 1462 1463 my $log = $self->log; 1464 $log->debug( "entered - @_" ); 1465 1211 1466 my ($pattern) = validate_pos( @_, 1212 1467 { … … 1216 1471 ); 1217 1472 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 $@; 1223 1478 1224 1479 unless (defined $pattern) { … … 1235 1490 $log->logdie("no keys found") unless ( scalar @keys ); 1236 1491 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 1237 1499 $log->debug( "leaving" ); 1238 1500 … … 1245 1507 1246 1508 my $self = shift; 1509 1510 my $log = $self->log; 1511 $log->debug( "entered - @_" ); 1512 1247 1513 my $index = shift; 1248 1514 my $key = shift; 1249 1515 1250 my $log = $self->log;1251 1516 my $sql = $self->sql; 1252 1517 my $db = $self->_db_for_index($index); 1253 1518 1254 $log->debug( "entered - @_" );1255 1519 1256 1520 # first check to see if the key is an exact match … … 1287 1551 } 1288 1552 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"); 1291 1576 my $query = $db->prepare_cached( $sql->find_object_by_dir_id ); 1292 1577 $query->execute( $dir_id ); … … 1301 1586 1302 1587 $log->debug( "leaving" ); 1303 1304 return \@keys;1588 1589 return [sort(@dir_keys), sort(@keys)]; 1305 1590 } 1306 1591 … … 1309 1594 { 1310 1595 my $self = shift; 1596 1597 my $log = $self->log; 1598 $log->debug("entered - @_"); 1311 1599 1312 1600 my ($key, $vol_name) = validate_pos(@_, … … 1330 1618 ); 1331 1619 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 1332 1627 # 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 $@; 1334 1632 $vol_name = $key->volume; 1335 1633 1336 my $log = $self->log;1337 my $sql = $self->sql;1338 1634 my $db = $self->db($key); 1339 1340 $log->debug("entered - @_");1341 1635 1342 1636 # the key's volume can't be validiated on input for this method so we have … … 1344 1638 if (defined $vol_name 1345 1639 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 { 1347 1643 $log->warn( "$vol_name is not a known volume name" ); 1348 1644 $vol_name = undef; 1349 } else {1350 die "$vol_name is not a valid volume name"1351 1645 } 1352 1646 } … … 1361 1655 unless ($rows > 0) { 1362 1656 $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"); 1364 1658 } 1365 1659 } else { … … 1369 1663 unless ($rows > 0) { 1370 1664 $query->finish; 1371 $log->logdie("no instances available for key: $key");1665 die("no instances available for key: $key"); 1372 1666 } 1373 1667 } … … 1381 1675 $db->rollback; 1382 1676 # handle soft volumes 1383 if (defined $vol_name and defined $key->soft_volume) {1677 if (defined $vol_name and not defined $key->hard_volume) { 1384 1678 $log->debug("retrying with 'any' volume"); 1385 1679 return $self->find_instances($key->path, 'any'); … … 1403 1697 my $self = shift; 1404 1698 1699 my $log = $self->log; 1700 $log->debug( "entered - @_" ); 1701 1405 1702 my ($key, $uri) = validate_pos( @_, 1406 1703 { … … 1415 1712 ); 1416 1713 1714 my $sql = $self->sql; 1715 1417 1716 # 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 1422 1722 my $db = $self->db($key); 1423 1424 $log->debug( "entered - @_" );1425 1723 1426 1724 TRANS: while (1) { 1427 1725 eval { 1726 my $total; 1727 my $available; 1428 1728 my $so_id; 1429 my $ins tances;1430 # get so_id1729 my $ins_id; 1730 # find so_id for key and get count of instances 1431 1731 { 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); 1435 1734 unless ( $rows > 0 ) { 1436 1735 $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' }; 1441 1743 $query->finish; 1442 1443 } 1444 1744 } 1745 1746 # find ins_id for uri 1445 1747 { 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' }; 1450 1756 $query->finish; 1451 1757 } … … 1453 1759 # remove instance 1454 1760 { 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 ); 1457 1763 $query->finish; 1458 1764 1459 # if we affected something other then two rows something very bad1460 # has happened1765 # if we affected something other then one row something very 1766 # bad has happened 1461 1767 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 1470 1785 my $query = $db->prepare_cached( $sql->delete_object ); 1471 1786 my $rows = $query->execute( $so_id ); … … 1474 1789 # TODO: this will have to be changed in order to support hardlinks 1475 1790 unless ( $rows == 1 ) { 1476 $log->logdie( "affected row count is $rows instead of 2" );1791 die( "affected row count is $rows instead of 2" ); 1477 1792 } 1478 1793 } … … 1484 1799 $db->rollback; 1485 1800 $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; 1488 1804 redo TRANS; 1489 1805 } … … 1502 1818 { 1503 1819 my $self = shift; 1820 1821 my $log = $self->log; 1822 $log->debug("entered - @_"); 1504 1823 1505 1824 my ( $key ) = validate_pos( @_, … … 1512 1831 ); 1513 1832 1833 my $sql = $self->sql; 1834 1514 1835 # 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 1519 1841 my $db = $self->db($key); 1520 1521 $log->debug("entered - @_");1522 1842 1523 1843 my $stat; … … 1527 1847 1528 1848 unless ($rows == 1) { 1529 $log->logdie("no storage object found");1849 die("no storage object found"); 1530 1850 } 1531 1851 … … 1548 1868 my $self = shift; 1549 1869 1870 my $log = $self->log; 1871 $log->debug("entered - @_"); 1872 1550 1873 validate_pos(@_); 1551 1874 1552 my $log = $self->log;1553 1875 my $sql = $self->sql; 1554 1876 my $db = $self->_db_for_index(0); # XXX fix as above 1555 1877 1556 $log->debug("entered - @_");1557 1558 1878 my $stats; 1559 1879 my $query; … … 1576 1896 } 1577 1897 1898 sub 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 } 1578 1975 1579 1976 sub _get_storage_volume … … 1581 1978 my $self = shift; 1582 1979 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; 1589 1981 no warnings qw( uninitialized ); 1590 1982 $log->debug( "entered - @_" ); 1591 1983 use warnings; 1984 1985 my ($key, $name, $hard_volume) = @_; 1986 1987 my $sql = $self->sql; 1988 my $db = $self->db($key); 1592 1989 1593 1990 my ($vol_id, $vol_host, $vol_path, $xattr); … … 1605 2002 # if a volume name was specified, and is soft, and we failed to 1606 2003 # find it, fall back to any volume 1607 if ($soft_volume) {2004 unless ($hard_volume) { 1608 2005 ($vol_id, $vol_host, $vol_path, $xattr) = $self->_get_storage_volume($key); 1609 2006 return; # this just returns out of the eval not from the subroutine 1610 2007 } 1611 $log->logdie("storage volume: $name is not available");2008 die("storage volume: $name is not available"); 1612 2009 } 1613 2010 # when matching by name we shouldn't ever match more than once 1614 2011 if ($rows > 1) { 1615 2012 $query->finish; 1616 $log->logdie("affected row count is $rows instead of 1");2013 die("affected row count is $rows instead of 1"); 1617 2014 } 1618 2015 } else { … … 1623 2020 unless ($rows > 0) { 1624 2021 $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"); 1626 2023 } 1627 2024 } … … 1631 2028 $query->finish; 1632 2029 }; 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 } 1634 2040 1635 2041 $log->logdie("failed to find a suitable volume" ) … … 1646 2052 my $self = shift; 1647 2053 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; 1654 2055 no warnings qw( uninitialized ); 1655 2056 $log->debug( "entered - @_" ); 1656 2057 use warnings; 2058 2059 my $key = shift; 2060 2061 my $sql = $self->sql; 2062 my $db = $self->db($key); 1657 2063 1658 2064 my ($vol_id, $vol_host, $vol_path, $xattr); … … 1665 2071 unless ($rows > 0) { 1666 2072 $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"); 1668 2074 } 1669 2075 # when matching by name we shouldn't ever match more than once 1670 2076 if ($rows > 1) { 1671 2077 $query->finish; 1672 $log->logdie("affected row count is $rows instead of 1");2078 die("affected row count is $rows instead of 1"); 1673 2079 } 1674 2080 … … 1690 2096 sub _is_valid_object_key 1691 2097 { 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 1697 2105 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 1698 2122 my $db = $self->db($key); 1699 2123 … … 1712 2136 1713 2137 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" ); 1714 2143 return 1; 1715 2144 } 1716 2145 2146 $log->debug( "key not found in db" ); 2147 $log->debug( "leaving" ); 2148 1717 2149 return; 1718 2150 } … … 1721 2153 sub _is_valid_volume_name 1722 2154 { 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 $@; 1726 2170 my $volume_info = parse_neb_volume($vol_name); 1727 2171 1728 my $log = $self->log;1729 my $sql = $self->sql;1730 2172 my $db = $self->db($key); 1731 2173 … … 1734 2176 # handle "any" volume 1735 2177 if ($vol_name eq 'any') { 2178 $log->debug( "found volume name $vol_name" ); 2179 $log->debug( "leaving" ); 1736 2180 return 1; 1737 2181 } … … 1748 2192 1749 2193 if (defined $vol_id and defined $vol_path) { 2194 $log->debug( "found volume name $vol_name" ); 2195 $log->debug( "leaving" ); 1750 2196 return 1; 1751 2197 } 1752 2198 2199 $log->debug( "volume name $vol_name not found" ); 2200 $log->debug( "leaving" ); 2201 1753 2202 return; 1754 2203 } … … 1759 2208 my $self = shift; 1760 2209 2210 my $log = $self->log; 2211 $log->debug( "entered - @_" ); 2212 1761 2213 my ($key, $so_id, $ins_id, $vol_path, $xattr) = @_; 1762 2214 1763 my $log = $self->log;1764 2215 my $sql = $self->sql; 1765 2216 my $db = $self->db($key); … … 1770 2221 my $storage_filename = $self->_generate_storage_filename($key->path, $ins_id); 1771 2222 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"); 1774 2225 } 1775 2226 # check to make sure at least the parent directory has the proper 1776 2227 # permissions 1777 my $mode = [ _retry(sub { stat($storage_path) } )]->[2] & 07777;2228 my $mode = [$self->_retry(sub { stat($storage_path) } )]->[2] & 07777; 1778 2229 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: $!"); 1781 2234 } 1782 2235 … … 1787 2240 }; 1788 2241 if ($@) { 2242 if (defined $uri and -e $uri->file) { 2243 unlink($uri->file) 2244 or $log->error("failed to unlink() $uri: $!"); 2245 } 1789 2246 $log->logdie($@); 1790 2247 } … … 1792 2249 if ($xattr) { 1793 2250 my $path = $uri->file; 1794 die "can not set xattr on $path: $!"2251 $log->logdie("can not set xattr on $path: $!") 1795 2252 unless (setfattr($path, 'user.nebulous_key', $key->path)); 1796 2253 } … … 1804 2261 my $self = shift; 1805 2262 2263 my $log = $self->log; 2264 $log->debug( "entered - @_" ); 2265 1806 2266 my ($path) = @_; 1807 2267 1808 2268 # 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"); 1810 2272 1811 2273 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: $!"); 1814 2276 1815 2277 # 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: $!"); 1827 2286 1828 2287 return $path; … … 1861 2320 sub _retry 1862 2321 { 2322 my $self = shift; 2323 2324 my $log = $self->log; 2325 $log->debug( "entered - @_" ); 2326 1863 2327 my $func = shift; 1864 2328 … … 1869 2333 }; 1870 2334 if ($@) { 1871 die $@;1872 sleep 1;2335 $log->logdie($@); 2336 sleep NFS_RETRY_WAIT; 1873 2337 next; 1874 2338 } … … 1879 2343 # if the loop ended and $@ is set, rethrow the error 1880 2344 if ($@) { 1881 die $@;2345 $log->logdie($@); 1882 2346 } 1883 2347 -
branches/pap/Nebulous-Server/lib/Nebulous/Server.pod
r23932 r25027 29 29 Nebulous::Server->stat_object( $key ); 30 30 Nebulous::Server->mounts(); 31 Nebulous::Server->chmod_object( $key, $mode); 32 Nebulous::Server->prune_object( $key ); 31 33 32 34 =head1 DESCRIPTION … … 181 183 epoch 182 184 mtime 183 number of instances 185 number of available instances 186 total number of instances 184 187 185 188 =item * mounts( $key ); … … 203 206 ] 204 207 208 =item * chmod_object( $key, $mode ); 209 210 Accepts 2 parameters, both mandatory. C<$key> is the nebulous key to operate 211 on and C<$mode> are the POSIX like file permissions to set on all instances of 212 C<$key>. C<$mode> must be specified in oct. Returns C<$mode> or sucess or an 213 exception on failure. 214 215 =item * prune_object( $key ); 216 217 Removes all of the inaccessible instances from an object. 218 219 Accepts 1 mandatory parameter. Returns the number of inaccessible instances actually pruned from the object. 220 205 221 =back 206 222 … … 223 239 =head1 COPYRIGHT 224 240 225 Copyright (C) 2004-200 8Joshua Hoblitt. All rights reserved.241 Copyright (C) 2004-2009 Joshua Hoblitt. All rights reserved. 226 242 227 243 This program is free software; you can redistribute it and/or modify it under -
branches/pap/Nebulous-Server/lib/Nebulous/Server/Config.pm
r23932 r25027 13 13 14 14 use Log::Log4perl qw( :levels ); 15 use Params::Validate qw( validate validate_pos SCALAR );15 use Params::Validate qw( validate validate_pos SCALAR ARRAYREF UNDEF ); 16 16 17 17 our %LEVELS = ( … … 32 32 dsn => { type => SCALAR }, 33 33 dbuser => { type => SCALAR }, 34 dbpasswd => { type => SCALAR },34 dbpasswd => { type => SCALAR | UNDEF }, 35 35 }; 36 36 … … 46 46 }, 47 47 }, 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 }, 51 52 }; 52 53 … … 61 62 62 63 # 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 }; 64 68 65 69 bless $self, $class || ref $class; -
branches/pap/Nebulous-Server/lib/Nebulous/Server/SQL.pm
r23947 r25027 67 67 WHERE parent_id = ? 68 68 AND dirname = ? 69 LOCK IN SHARE MODE 69 70 }, 70 71 new_directory => qq{ … … 82 83 stat_object => qq{ 83 84 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, 90 92 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 93 95 USING (so_id) 94 96 JOIN instance 95 97 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 98 102 }, 99 103 # Note: this sets an update lock … … 175 179 WHERE ext_id = ? 176 180 }, 177 delete_instance => qq{181 delete_instance_by_ins_id => qq{ 178 182 DELETE FROM instance 179 WHERE uri= ?183 WHERE ins_id = ? 180 184 }, 181 185 get_object_from_uri => qq{ … … 184 188 WHERE uri = ? 185 189 }, 190 get_instance_by_uri => qq{ 191 SELECT ins_id 192 FROM instance 193 WHERE 194 so_id = ? 195 AND uri = ? 196 }, 186 197 get_instance_count => qq{ 187 198 SELECT count(ins_id) 188 199 FROM instance 189 200 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) 190 234 }, 191 235 get_object_instances => qq{ … … 233 277 get_replication_volume_for_ext_id => qq{ 234 278 SELECT 235 vol_id,279 m.vol_id, 236 280 host, 237 281 path, 238 282 xattr, 239 283 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 < ? 268 295 AND available = ? 269 296 AND allocate = ? 270 ORDER BY RAND()271 LIMIT 1297 ORDER BY RAND() 298 LIMIT 1 272 299 }, 273 300 get_storage_volume => qq{ … … 287 314 }, 288 315 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, ?) 291 318 }, 292 319 get_volume_by_name => qq{ … … 310 337 }, 311 338 find_object_by_ext_id => qq{ 312 SELECT ext_id, ext_id_basename339 SELECT so_id, ext_id, ext_id_basename 313 340 FROM storage_object 314 341 WHERE ext_id = ? … … 318 345 FROM storage_object 319 346 WHERE dir_id = ? 347 }, 348 find_dir_by_parent_id => qq{ 349 SELECT dir_id, dirname 350 FROM directory 351 WHERE parent_id = ? 320 352 }, 321 353 rename_object => qq{ … … 397 429 }, 398 430 get_mounted_volumes => qq{ 399 SELECT *FROM mountedvol ORDER BY host, name431 SELECT mountpoint, total, used, vol_id, name, host, path, allocate, available, xattr FROM mountedvol ORDER BY host, name 400 432 }, 401 433 ); … … 424 456 DROP TABLE IF EXISTS lock_record; 425 457 DROP TABLE IF EXISTS volume; 426 DROP TABLE IF EXISTS mount ;458 DROP TABLE IF EXISTS mountedvol; 427 459 DROP TABLE IF EXISTS log; 428 DROP TABLE IF EXISTS mountedvol;429 460 DROP TABLE IF EXISTS directory; 430 DROP PROCEDURE IF EXISTS getmountedvol;461 DROP TABLE IF EXISTS deleted; 431 462 SET FOREIGN_KEY_CHECKS=1 432 463 END 464 #DROP PROCEDURE IF EXISTS getmountedvol; 433 465 $sql{get_db_clear} = \@clear; 434 466 } … … 447 479 448 480 1; 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 449 548 450 549 __DATA__ … … 455 554 FOREIGN KEY(parent_id) REFERENCES directory(dir_id), 456 555 PRIMARY KEY(dir_id), 457 KEY(parent_id) 556 KEY(parent_id), 557 KEY(dirname), 558 UNIQUE(dirname, parent_id) 458 559 ) ENGINE=innodb DEFAULT CHARSET=latin1; 459 560 … … 466 567 CREATE TABLE storage_object ( 467 568 so_id BIGINT NOT NULL AUTO_INCREMENT, 468 ext_id VARCHAR(255) NOT NULL UNIQUE,569 ext_id VARCHAR(255) NOT NULL, 469 570 ext_id_basename VARCHAR(255) NOT NULL, 470 571 dir_id BIGINT NOT NULL, … … 472 573 type enum('REG_FILE'), 473 574 PRIMARY KEY(so_id), 575 UNIQUE KEY(ext_id), 474 576 KEY(dir_id), 475 577 KEY(type) … … 514 616 CREATE TABLE volume ( 515 617 vol_id INT NOT NULL AUTO_INCREMENT, 516 name VARCHAR(255) UNIQUENOT NULL,618 name VARCHAR(255) NOT NULL, 517 619 host VARCHAR(255) NOT NULL, 518 620 path VARCHAR(255) NOT NULL, … … 520 622 available BOOLEAN DEFAULT FALSE, 521 623 xattr BOOLEAN DEFAULT FALSE, 624 mountpoint VARCHAR(255) NOT NULL, 522 625 PRIMARY KEY(vol_id), 626 UNIQUE KEY(name), 627 UNiQUE KEY(path), 523 628 KEY(host(16)), 524 KEY(path(255)),525 629 KEY(allocate), 526 KEY(available) 630 KEY(available), 631 KEY(mountpoint(255)) 632 ) ENGINE=innodb DEFAULT CHARSET=latin1; 633 634 ### 635 636 CREATE 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)) 527 658 ) ENGINE=innodb DEFAULT CHARSET=latin1; 528 659 … … 532 663 ins_id BIGINT NOT NULL AUTO_INCREMENT, 533 664 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, 535 666 vol_id INT NOT NULL, 536 667 FOREIGN KEY(vol_id) REFERENCES volume(vol_id), … … 546 677 ### 547 678 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 557 679 CREATE TABLE log ( 558 680 timestamp TIMESTAMP, … … 566 688 ### 567 689 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, 690 CREATE TABLE deleted ( 573 691 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), 583 696 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 11 11 use base qw( Exporter ); 12 12 13 use Cache::Memcached; 13 14 use DBI; 14 15 use File::Path qw( mkpath rmtree ); … … 16 17 use Nebulous::Server::SQL; 17 18 18 our @EXPORT = qw( $NEB_DB $NEB_USER $NEB_PASS );19 our @EXPORT = qw( $NEB_DB $NEB_USER $NEB_PASS $NEB_MEMCACHED_SERVERS); 19 20 20 21 our $NEB_DB = $ENV{'NEB_DB'} || "DBI:mysql:database=test:host=localhost"; 21 22 our $NEB_USER = $ENV{'NEB_USER'} || "test"; 22 23 our $NEB_PASS = $ENV{'NEB_PASS'} || ''; 24 our $NEB_MEMCACHED_SERVERS = ['127.0.0.1:11211']; 23 25 24 26 my $dbh = DBI->connect( $NEB_DB, $NEB_USER, $NEB_PASS ); … … 53 55 54 56 # 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); 57 60 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); 60 64 61 65 # 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); 64 69 65 70 # 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); 68 74 69 75 # 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); 72 79 73 80 # 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); 76 84 77 85 # 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); 80 89 81 $dbh->do(qq{ call getmountedvol() });90 # $dbh->do(qq{ call getmountedvol() }); 82 91 } 83 92 84 93 sub 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 85 108 foreach my $statement (@{ $sql->get_db_clear }) { 86 109 $dbh->do( $statement ); -
branches/pap/Nebulous-Server/t/02_config.t
r23932 r25027 8 8 use warnings; 9 9 10 use Test::More tests => 2 1;10 use Test::More tests => 22; 11 11 12 12 use lib qw( ./t ./lib ); … … 109 109 is($config_db1->dbpasswd, "boo2", "dbpasswd"); 110 110 } 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 10 10 use Test::More; 11 11 12 plan tests => 99;12 plan tests => 106; 13 13 14 14 use lib qw( ./t ./lib ); … … 28 28 is($key->path, 'foo/bar/baz/quix', 'path'); 29 29 is($key->volume, undef, 'volume name'); 30 is($key-> soft_volume, undef, 'soft volume name');30 is($key->hard_volume, undef, 'soft volume name'); 31 31 } 32 32 … … 36 36 is($key->path, 'foo/bar/baz/quix', 'path'); 37 37 is($key->volume, undef, 'volume name'); 38 is($key-> soft_volume, undef, 'soft volume name');38 is($key->hard_volume, undef, 'soft volume name'); 39 39 } 40 40 … … 44 44 is($key->path, 'foo/bar/baz/quix', 'path'); 45 45 is($key->volume, undef, 'volume name'); 46 is($key-> soft_volume, undef, 'soft volume name');46 is($key->hard_volume, undef, 'soft volume name'); 47 47 } 48 48 … … 52 52 is($key->path, 'foo/bar/baz/quix', 'path'); 53 53 is($key->volume, undef, 'volume name'); 54 is($key-> soft_volume, undef, 'soft volume name');54 is($key->hard_volume, undef, 'soft volume name'); 55 55 } 56 56 … … 60 60 is($key->path, 'foo/bar/baz/quix', 'path'); 61 61 is($key->volume, undef, 'volume name'); 62 is($key-> soft_volume, undef, 'soft volume name');62 is($key->hard_volume, undef, 'soft volume name'); 63 63 } 64 64 … … 68 68 is($key->path, 'foo/bar/baz/quix', 'path'); 69 69 is($key->volume, undef, 'volume name'); 70 is($key-> soft_volume, undef, 'soft volume name');70 is($key->hard_volume, undef, 'soft volume name'); 71 71 } 72 72 … … 77 77 is($key->path, 'foo/bar/baz/quix', 'path'); 78 78 is($key->volume, 'boing', 'volume name'); 79 is($key-> soft_volume, 1, 'soft volume name');79 is($key->hard_volume, undef, 'soft volume name'); 80 80 } 81 81 … … 85 85 is($key->path, 'foo/bar/baz/quix', 'path'); 86 86 is($key->volume, 'boing', 'volume name'); 87 is($key-> soft_volume, undef, 'soft volume name');87 is($key->hard_volume, 1, 'soft volume name'); 88 88 } 89 89 … … 94 94 is($key->path, 'bar/baz/quix', 'path'); 95 95 is($key->volume, 'foo', 'volume name'); 96 is($key-> soft_volume, 1, 'soft volume name');96 is($key->hard_volume, undef, 'soft volume name'); 97 97 } 98 98 … … 102 102 is($key->path, 'bar/baz/quix', 'path'); 103 103 is($key->volume, 'foo', 'volume name'); 104 is($key-> soft_volume, 1, 'soft volume name');104 is($key->hard_volume, undef, 'soft volume name'); 105 105 } 106 106 … … 110 110 is($key->path, 'bar/baz/quix', 'path'); 111 111 is($key->volume, 'foo', 'volume name'); 112 is($key-> soft_volume, 1, 'soft volume name');112 is($key->hard_volume, undef, 'soft volume name'); 113 113 } 114 114 … … 118 118 is($key->path, 'bar/baz/quix', 'path'); 119 119 is($key->volume, 'foo', 'volume name'); 120 is($key-> soft_volume, 1, 'soft volume name');120 is($key->hard_volume, undef, 'soft volume name'); 121 121 } 122 122 … … 126 126 is($key->path, 'bar/baz/quix', 'path'); 127 127 is($key->volume, 'foo', 'volume name'); 128 is($key-> soft_volume, 1, 'soft volume name');128 is($key->hard_volume, undef, 'soft volume name'); 129 129 } 130 130 … … 135 135 is($key->path, 'bar/baz/quix', 'path'); 136 136 is($key->volume, 'foo', 'volume name'); 137 is($key-> soft_volume, undef, 'soft volume name');137 is($key->hard_volume, 1, 'soft volume name'); 138 138 } 139 139 … … 144 144 is($key->path, 'bar/baz/quix', 'path'); 145 145 is($key->volume, 'boing', 'volume name'); 146 is($key-> soft_volume, 1, 'soft volume name');146 is($key->hard_volume, undef, 'soft volume name'); 147 147 } 148 148 … … 152 152 is($key->path, 'bar/baz/quix', 'path'); 153 153 is($key->volume, 'boing', 'volume name'); 154 is($key-> soft_volume, undef, 'soft volume name');154 is($key->hard_volume, 1, 'soft volume name'); 155 155 } 156 156 … … 160 160 is($key->path, 'bar/baz/quix', 'path'); 161 161 is($key->volume, 'foo', 'volume name'); 162 is($key-> soft_volume, 1, 'soft volume name');162 is($key->hard_volume, undef, 'soft volume name'); 163 163 } 164 164 … … 169 169 is($key->path, 'bar/baz/quix', 'path'); 170 170 is($key->volume, undef, 'volume name'); 171 is($key-> soft_volume, undef, 'soft volume name');171 is($key->hard_volume, undef, 'soft volume name'); 172 172 } 173 173 … … 178 178 is($key->path, 'bar/baz/quix', 'path'); 179 179 is($key->volume, undef, 'volume name'); 180 is($key-> soft_volume, undef, 'soft volume name');180 is($key->hard_volume, undef, 'soft volume name'); 181 181 } 182 182 … … 186 186 is($key->path, 'bar/baz/quix', 'path'); 187 187 is($key->volume, undef, 'volume name'); 188 is($key-> soft_volume, undef, 'soft volume name');188 is($key->hard_volume, undef, 'soft volume name'); 189 189 } 190 190 … … 195 195 is($key->path, 'bar/baz/quix', 'path'); 196 196 is($key->volume, 'foo', 'volume name'); 197 is($key-> soft_volume, undef, 'soft volume name');197 is($key->hard_volume, 1, 'soft volume name'); 198 198 } 199 199 … … 205 205 is($key->path, 'foo/bar/baz/quix', 'path'); 206 206 is($key->volume, undef, 'volume name'); 207 is($key-> soft_volume, undef, 'soft volume name');207 is($key->hard_volume, undef, 'soft volume name'); 208 208 } 209 209 … … 213 213 is($key->volume, undef, 'volume name'); 214 214 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'); 216 216 } 217 217 … … 221 221 is($key->path, 'foo/bar/baz/quix', 'path'); 222 222 is($key->volume, undef, 'volume name'); 223 is($key-> soft_volume, undef, 'soft volume name');223 is($key->hard_volume, undef, 'soft volume name'); 224 224 } 225 225 … … 230 230 is($key->path, '', 'path'); 231 231 is($key->volume, undef, 'volume name'); 232 is($key-> soft_volume, undef, 'soft volume name');232 is($key->hard_volume, undef, 'soft volume name'); 233 233 } 234 234 … … 238 238 is($key->path, '', 'path'); 239 239 is($key->volume, undef, 'volume name'); 240 is($key-> soft_volume, undef, 'soft volume name');240 is($key->hard_volume, undef, 'soft volume name'); 241 241 } 242 242 … … 246 246 is($key->path, '', 'path'); 247 247 is($key->volume, undef, 'volume name'); 248 is($key-> soft_volume, undef, 'soft volume name');248 is($key->hard_volume, undef, 'soft volume name'); 249 249 } 250 250 … … 254 254 is($key->path, '', 'path'); 255 255 is($key->volume, undef, 'volume name'); 256 is($key-> soft_volume, undef, 'soft volume name');256 is($key->hard_volume, undef, 'soft volume name'); 257 257 } 258 258 … … 262 262 is($key->path, '', 'path'); 263 263 is($key->volume, undef, 'volume name'); 264 is($key-> soft_volume, undef, 'soft volume name');264 is($key->hard_volume, undef, 'soft volume name'); 265 265 } 266 266 … … 270 270 is($key->path, '', 'path'); 271 271 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"); 273 296 } 274 297 … … 322 345 }; 323 346 like( $@, qr/requires a path/, "no path" ); 347 348 # params 349 eval { 350 my $key = parse_neb_key(); 351 }; 352 like( $@, qr/key param is not optional/, "no params" ); 353 354 eval { 355 my $key = parse_neb_key(undef); 356 }; 357 like( $@, qr/key param is not optional/, "key is undef" ); 358 359 eval { 360 my $key = parse_neb_key(undef, 'bar'); 361 }; 362 like( $@, qr/key param is not optional/, "key is undef" ); 363 364 eval { 365 my $key = parse_neb_key('foo', 'bar', 'foo'); 366 }; 367 like( $@, qr/too many params/, "too many params" );
Note:
See TracChangeset
for help on using the changeset viewer.
