1 package AFS
::Load
::Action
;
7 AFS::Load::Action - test actions for afsload
12 node * chdir "/afs/localcell/afsload"
14 node 0 creat file1 "file 1 contents"
15 node 1 creat file2 "file 2 contents"
17 node * read file1 "file 1 contents"
18 node * read file2 "file 2 contents"
25 This module and submodule defines the actions that can be specified in an
26 afsload test configuration file. The name of each action is the first thing
27 that appears after the 'node' directive and the node range specification.
28 Everything after the action name are the arguments for that action, which
29 are different for every action.
31 Each action is implemented as a small module in AFS::Load::Action::<name>,
32 where <name> is the name of the action. So, to implement a new action, simply
33 copy an existing action into a new module, and change the code.
35 Each action typically performs one filesystem operation, or a small series of
36 filesystem operations forming one logical operation. Each action may succeed
37 or fail; in the case of a failure an action provides an error code and
38 optionally an error string. In many cases the error code is the resultant
39 errno value for a filesystem operation, but that is not necessary; errno is
40 even recorded and reported separately in the case of a failure in case it is
41 relevant and different from the given error code.
43 The rest of this documentation just covers what each action does, and how to
48 sub _interpret_impl
($) {
50 my $class = "AFS::Load::Action::$name";
51 if ($class->can('new')) {
54 die("Unknown action '$name' in configuration");
62 my $impl = _interpret_impl
($implname);
64 my $ret = $impl->new(@_);
72 my $class = ref($proto) || $proto;
80 my @ret = $self->doact();
91 node * chdir /afs/localcell/afsload
95 The C<chdir> action just changes the working directory for the specified client
96 node. Using this and specifying paths in other actions as short, relative paths
97 can make the test configuration easier to read and write.
101 The only argument is the directory to chdir to.
105 The same errors as the uafs_chdir() call, which should be the same errors as
106 you might expect from a regular chdir() call.
110 package AFS
::Load
::Action
::chdir;
112 use AFS
::Load
::Action
;
114 our @ISA = ("AFS::Load::Action");
118 my $class = ref($proto) || $proto;
119 my $self = $class->SUPER::new
();
121 bless($self, $class);
125 die("wrong number of args ($args) to chdir (should be 1)");
127 $self->{DIR
} = $_[0];
134 my $code = AFS
::ukernel
::uafs_chdir
($self->{DIR
});
136 return (int($!), '');
143 return "chdir($self->{DIR})";
153 node 0 creat file1 "file1 contents"
157 Creates a file with the given filename with the given contents.
161 The first argument is the file name to create, and the second argument is the
162 contents to write to the newly-created file.
166 Any error generated by uafs_open() or uafs_write() will cause an error. An
167 error will be generated if the file already exists.
171 package AFS
::Load
::Action
::creat
;
173 use AFS
::Load
::Action
;
175 our @ISA = ("AFS::Load::Action");
179 my $class = ref($proto) || $proto;
180 my $self = $class->SUPER::new
();
182 bless($self, $class);
186 die("wrong number of args ($args) to creat (should be 2)");
188 $self->{FILE
} = $_[0];
189 $self->{DATA
} = $_[1];
196 my $fd = AFS
::ukernel
::uafs_open
($self->{FILE
},
197 POSIX
::O_CREAT
| POSIX
::O_EXCL
| POSIX
::O_WRONLY
,
200 return (int($!), 'open error');
203 my $code = AFS
::ukernel
::uafs_write
($fd, $self->{DATA
});
205 my $errno_save = int($!);
206 AFS
::ukernel
::uafs_close
($fd);
207 return ($errno_save, 'write error');
210 AFS
::ukernel
::uafs_close
($fd);
217 return "creat($self->{FILE})";
227 node 0 read file1 "file1 contents"
231 Opens and reads a file and verifies that the file contains certain contents.
235 The first argument is the file name to read, and the second argument is the
236 expected contents of the file.
240 Any error generated by the underlying filesystem ops will cause an error. An
241 error will also be generated if the file has contents different than what was
242 specified or has a different length than the given string. In which case, what
243 was actually in the file up to the length in the given string will be reported
244 in the error message.
248 package AFS
::Load
::Action
::read;
250 use AFS
::Load
::Action
;
252 our @ISA = ("AFS::Load::Action");
256 my $class = ref($proto) || $proto;
257 my $self = $class->SUPER::new
();
259 bless($self, $class);
263 die("wrong number of args ($args) to read (should be 2)");
265 $self->{FILE
} = $_[0];
266 $self->{DATA
} = $_[1];
276 my $fd = AFS
::ukernel
::uafs_open
($self->{FILE
},
280 return (int($!), 'open error');
283 ($code, @stat) = AFS
::ukernel
::uafs_fstat
($fd);
285 my $errno_save = int($!);
286 AFS
::ukernel
::uafs_close
($fd);
287 return ($errno_save, 'fstat error');
290 ($code, $str) = AFS
::ukernel
::uafs_read
($fd, length $self->{DATA
});
292 my $errno_save = int($!);
293 AFS
::ukernel
::uafs_close
($fd);
294 return ($errno_save, 'read error');
297 AFS
::ukernel
::uafs_close
($fd);
299 if ($str ne $self->{DATA
}) {
301 if ($stat[7] != length $self->{DATA
}) {
302 $lenstr = " (total length $stat[7])";
304 return (-1, "got: $str$lenstr, expected: $self->{DATA}");
307 if ($stat[7] != length $self->{DATA
}) {
308 return (-1, "got file size: $stat[7], expected: ".(length $self->{DATA
}));
316 return "read($self->{FILE})";
326 node 0 cat file1 file2
330 Opens and reads the entire contents of all specified files, discarding any
335 The argument list is a list of files to read.
339 Any error generated by the underlying filesystem ops will cause an error.
340 When an error occurs on reading one file, subsequent files will still be
341 attempted to be read, but an error will still be returned afterwards.
345 package AFS
::Load
::Action
::cat
;
347 use AFS
::Load
::Action
;
349 our @ISA = ("AFS::Load::Action");
353 my $class = ref($proto) || $proto;
354 my $self = $class->SUPER::new
();
356 bless($self, $class);
360 die("wrong number of args ($args) to cat (should be at least 1)");
362 $self->{FILES
} = [@_,];
372 my $files = $self->{FILES
};
374 for my $file (@
$files) {
376 my $fd = AFS
::ukernel
::uafs_open
($file,
383 $errstr .= "$file: open error\n";
389 ($code, $str) = AFS
::ukernel
::uafs_read
($fd, 16384);
394 $errstr .= "$file: read error\n";
399 AFS
::ukernel
::uafs_close
($fd);
403 return (-1, $errstr);
411 my $files = $self->{FILES
};
412 return "cat(".join(',', @
$files).")";
422 node 0 cp 10M /dev/urandom foo.urandom
426 Copies file data up to a certain amount.
430 The first argument is the maximum amount of data to copy. It is a number of
431 bytes, optionally followed by a size suffix: K, M, G, or T. You can specify
432 -1 or "ALL" to copy until EOF on the source is encountered.
434 The second argument is the file to copy data out of. The third argument is the
435 destination file to copy into. The destination file may or may not exist; if it
436 exists, it is truncated before copying data.
438 Either file may be a file on local disk, but at least one must be in AFS. The
439 file will be treated as a file on local disk only if it starts with a leading
440 slash, and does not start with /afs/.
444 Any error generated by the underlying filesystem ops will cause an error.
448 package AFS
::Load
::Action
::cp
;
452 use Number
::Format
qw(round unformat_number);
454 use AFS
::Load
::Action
;
456 our @ISA = ("AFS::Load::Action");
460 my $class = ref($proto) || $proto;
461 my $self = $class->SUPER::new
();
463 bless($self, $class);
467 die("wrong number of args ($args) to cp (should be 3)");
471 $self->{SRC
} = shift;
472 $self->{DST
} = shift;
478 $self->{LEN
} = round
(unformat_number
($len), 0);
479 if (not $self->{LEN
}) {
480 die("Invalid format ($len) given to cp");
489 if ($str =~ m
:^([^/]|/afs
/):) {
490 # assume relative paths are in AFS
491 # and of course anything starting with /afs/ is in AFS
497 sub _cpin_sysread
($$) {
498 my ($inh, $len) = @_;
500 my $bytes = sysread($inh, $buf, $len);
502 if (defined($bytes)) {
503 return ($bytes, $buf);
508 sub _cpout_syswrite
($$) {
509 my ($outh, $str) = @_;
511 $code = syswrite($outh, $str, length $str);
513 if (defined($code)) {
540 if (_isafs
($self->{SRC
})) {
541 $inh = AFS
::ukernel
::uafs_open
($self->{SRC
}, POSIX
::O_RDONLY
, 0644);
543 return (int($!), "input open error (AFS)");
546 $readf = \
&AFS
::ukernel
::uafs_read
;
547 $inclosef = \
&AFS
::ukernel
::uafs_close
;
549 open($inh, "< $self->{SRC}") or
550 return (int($!), "input open error (local)");
552 $readf = \
&_cpin_sysread
;
553 $inclosef = \
&_cp_close
;
556 if (_isafs
($self->{DST
})) {
557 $outh = AFS
::ukernel
::uafs_open
($self->{DST
},
558 POSIX
::O_WRONLY
| POSIX
::O_TRUNC
| POSIX
::O_CREAT
,
561 return (int($!), "output open error (AFS)");
563 $writef = \
&AFS
::ukernel
::uafs_write
;
564 $outclosef = \
&AFS
::ukernel
::uafs_close
;
566 open($outh, "> $self->{DST}") or
567 return (int($!), "output open error(local)");
568 $writef = \
&_cpout_syswrite
;
569 $outclosef = \
&_cp_close
;
573 my $remaining = $self->{LEN
};
580 if ($remaining > 0 && $remaining < $len) {
584 ($rbytes, $str) = &$readf($inh, $len);
586 my $errno_save = int($!);
591 return ($errno_save, "read error");
598 $wbytes = &$writef($outh, $str);
599 if ($wbytes != $rbytes) {
600 my $errno_save = int($!);
605 return ($errno_save, "write error ($wbytes/$rbytes)");
608 if ($remaining > 0) {
609 $remaining -= $rbytes;
614 if (&$outclosef($outh) != 0) {
615 return (int($!), "close error");
623 return "cp(".join(',', $self->{LEN
}, $self->{SRC
}, $self->{DST
}).")";
633 node 0 truncwrite file1 "different contents"
637 Opens and truncates an existing file, then writes some data to it.
641 The first argument is the file name to open and truncate, and the second
642 argument is the data to write to the file.
646 Any error generated by the underlying filesystem ops will cause an error. Note
647 that the file must already exist for this to succeed.
651 package AFS
::Load
::Action
::truncwrite
;
653 use AFS
::Load
::Action
;
655 our @ISA = ("AFS::Load::Action");
659 my $class = ref($proto) || $proto;
660 my $self = $class->SUPER::new
();
662 bless($self, $class);
666 die("wrong number of args ($args) to truncwrite (should be 2)");
668 $self->{FILE
} = $_[0];
669 $self->{DATA
} = $_[1];
676 my $fd = AFS
::ukernel
::uafs_open
($self->{FILE
},
677 POSIX
::O_WRONLY
| POSIX
::O_TRUNC
,
680 return (int($!), 'open error');
683 my ($code) = AFS
::ukernel
::uafs_write
($fd, $self->{DATA
});
685 my $errno_save = int($!);
686 AFS
::ukernel
::uafs_close
($fd);
687 return ($errno_save, 'write error');
690 AFS
::ukernel
::uafs_close
($fd);
692 if ($code eq length $self->{DATA
}) {
696 return (-1, "got: $code bytes written, expected: ".(length $self->{DATA
}));
701 return "truncwrite($self->{FILE}, $self->{DATA})";
711 node 0 append file1 "more data"
715 Opens an existing file, and appends some data to it.
719 The first argument is the file name to open, and the second argument is the
720 data to append to the file.
724 Any error generated by the underlying filesystem ops will cause an error. Note
725 that the file must already exist for this to succeed.
729 package AFS
::Load
::Action
::append
;
731 use AFS
::Load
::Action
;
733 our @ISA = ("AFS::Load::Action");
737 my $class = ref($proto) || $proto;
738 my $self = $class->SUPER::new
();
740 bless($self, $class);
744 die("wrong number of args ($args) to append (should be 2)");
746 $self->{FILE
} = $_[0];
747 $self->{DATA
} = $_[1];
754 my $fd = AFS
::ukernel
::uafs_open
($self->{FILE
},
755 POSIX
::O_WRONLY
| POSIX
::O_APPEND
,
758 return (int($!), 'open error');
761 my ($code) = AFS
::ukernel
::uafs_write
($fd, $self->{DATA
});
763 my $errno_save = int($!);
764 AFS
::ukernel
::uafs_close
($fd);
765 return ($errno_save, 'write error');
768 AFS
::ukernel
::uafs_close
($fd);
770 if ($code eq length $self->{DATA
}) {
774 return (-1, "got: $code bytes written, expected: ".(length $self->{DATA
}));
779 return "append($self->{FILE}, $self->{DATA})";
789 node 0 unlink file1 [file2] ... [fileN]
793 Unlinks the specified file(s).
797 All arguments are files to unlink.
801 Any error generated by the underlying uafs_unlink() call. An error will be
802 returned if unlinking any file generates an error, but we will attempt to
803 unlink all specified files.
807 package AFS
::Load
::Action
::unlink;
809 use AFS
::Load
::Action
;
811 our @ISA = ("AFS::Load::Action");
815 my $class = ref($proto) || $proto;
816 my $self = $class->SUPER::new
();
818 bless($self, $class);
822 die("wrong number of args ($args) to unlink (should be at least 1)");
824 $self->{FILES
} = [@_];
833 my $files = $self->{FILES
};
835 for my $file (@
$files) {
836 my $code = AFS
::ukernel
::uafs_unlink
($file);
838 if (not length(@errfiles)) {
841 push(@errfiles, $file);
846 return ($ret, join(', ', @errfiles));
853 my $files = $self->{FILES
};
854 return "unlink(".(join(',', @
$files)).")";
864 node 0 rename file1 file2
868 Renames a file within a volume.
872 The first argument is the file to move, and the second argument is the new
877 Any error generated by the underlying uafs_rename() call.
881 package AFS
::Load
::Action
::rename;
883 use AFS
::Load
::Action
;
885 our @ISA = ("AFS::Load::Action");
889 my $class = ref($proto) || $proto;
890 my $self = $class->SUPER::new
();
892 bless($self, $class);
896 die("wrong number of args ($args) to rename (should be 2)");
898 $self->{SRC
} = $_[0];
899 $self->{DST
} = $_[1];
906 my $code = AFS
::ukernel
::uafs_rename
($self->{SRC
}, $self->{DST
});
908 return (int($!), '');
915 return "rename($self->{SRC}, $self->{DST})";
925 node 0 hlink file1 file2
929 Hard-links a file within a directory.
933 The first argument is the source file, and the second argument is the name of
938 Any error generated by the underlying uafs_link() call.
942 package AFS
::Load
::Action
::hlink
;
944 use AFS
::Load
::Action
;
946 our @ISA = ("AFS::Load::Action");
950 my $class = ref($proto) || $proto;
951 my $self = $class->SUPER::new
();
953 bless($self, $class);
957 die("wrong number of args ($args) to hlink (should be 2)");
959 $self->{SRC
} = $_[0];
960 $self->{DST
} = $_[1];
967 my $code = AFS
::ukernel
::uafs_link
($self->{SRC
}, $self->{DST
});
969 return (int($!), '');
976 return "hlink($self->{SRC}, $self->{DST})";
986 node 0 slink file1 file2
990 Symlinks a file within a directory.
994 The first argument is the source file, and the second argument is the name of
999 Any error generated by the underlying uafs_symlink() call.
1003 package AFS
::Load
::Action
::slink
;
1005 use AFS
::Load
::Action
;
1007 our @ISA = ("AFS::Load::Action");
1011 my $class = ref($proto) || $proto;
1012 my $self = $class->SUPER::new
();
1014 bless($self, $class);
1018 die("wrong number of args ($args) to slink (should be 2)");
1020 $self->{SRC
} = $_[0];
1021 $self->{DST
} = $_[1];
1028 my $code = AFS
::ukernel
::uafs_symlink
($self->{SRC
}, $self->{DST
});
1030 return (int($!), '');
1037 return "slink($self->{SRC}, $self->{DST})";
1047 node 0 access_r file1
1051 Verifies that a file exists and is readable.
1055 The only argument is a file to check readability.
1059 Any error generated by the underlying uafs_open() call.
1063 package AFS
::Load
::Action
::access_r
;
1065 use AFS
::Load
::Action
;
1067 our @ISA = ("AFS::Load::Action");
1071 my $class = ref($proto) || $proto;
1072 my $self = $class->SUPER::new
();
1074 bless($self, $class);
1078 die("wrong number of args ($args) to access_r (should be 1)");
1080 $self->{FILE
} = $_[0];
1087 my $fd = AFS
::ukernel
::uafs_open
($self->{FILE
}, POSIX
::O_RDONLY
, 0644);
1089 return (int($!), '');
1091 AFS
::ukernel
::uafs_close
($fd);
1097 return "access_r($self->{FILE})";
1107 node 0 fail ENOENT access_r file1
1111 Verifies that another action fails with a specific error code. This is useful
1112 when an easy way to specify an action is to specify when another action fails,
1113 instead of needing to write a new action.
1115 For example, the above example runs the C<access_r> action on file1, and
1116 succeeds if the C<access_r> action returns with an ENOENT error.
1120 The first argument is the error code that the subsequent action should fail
1121 with. This can be a number, or an errno symbolic constant. The next argument
1122 is the name of any other action, and the remaining arguments are whatever
1123 arguments should be supplied to that action.
1127 We only raise an error if the specified action generates a different error than
1128 what was specified, or if no error was raised. In which case, the error that
1129 was generated (if any) is reported.
1133 package AFS
::Load
::Action
::fail
;
1135 use AFS
::Load
::Action
;
1138 our @ISA = ("AFS::Load::Action");
1142 my $class = ref($proto) || $proto;
1143 my $self = $class->SUPER::new
();
1145 bless($self, $class);
1150 die("wrong number of args ($args) to fail (should be at least 2)");
1153 if (!($code =~ m/^\d$/)) {
1154 my $nCode = eval("if (exists &Errno::$code) { return &Errno::$code; } else { return undef; }");
1155 if (!defined($nCode)) {
1156 die("Invalid symbolic error name $code\n");
1160 $self->{ERRCODE
} = $code;
1161 $self->{ACT
} = AFS
::Load
::Action
->parse(-1, @_);
1168 my @ret = $self->{ACT
}->doact();
1170 if ($ret[0] == $self->{ERRCODE
}) {
1174 return (-1, "got error: $ret[0] (string: $ret[1]), expected: $self->{ERRCODE}");
1179 return "fail(".$self->{ACT
}->str().")";
1189 node 0 ignore unlink file1
1193 Performs another action, ignoring any given errors and always returning
1198 The first argument is the name of any other action, and the remaining
1199 arguments are whatever arguments should be supplied to that action.
1207 package AFS
::Load
::Action
::ignore
;
1209 use AFS
::Load
::Action
;
1212 our @ISA = ("AFS::Load::Action");
1216 my $class = ref($proto) || $proto;
1217 my $self = $class->SUPER::new
();
1219 bless($self, $class);
1223 die("wrong number of args ($args) to ignore (should be at least 1)");
1226 $self->{ACT
} = AFS
::Load
::Action
->parse(-1, @_);
1233 my @ret = $self->{ACT
}->doact();
1240 return "ignore(".$self->{ACT
}->str().")";
1254 Creates a directory.
1258 The only argument is the directory to create.
1262 The same errors as the uafs_mkdir() call.
1266 package AFS
::Load
::Action
::mkdir;
1268 use AFS
::Load
::Action
;
1270 our @ISA = ("AFS::Load::Action");
1274 my $class = ref($proto) || $proto;
1275 my $self = $class->SUPER::new
();
1277 bless($self, $class);
1281 die("wrong number of args ($args) to mkdir (should be 1)");
1283 $self->{DIR
} = $_[0];
1290 my $code = AFS
::ukernel
::uafs_mkdir
($self->{DIR
}, 0775);
1292 return (int($!), '');
1299 return "mkdir($self->{DIR})";
1313 Removes a directory.
1317 The only argument is the directory to remove.
1321 The same errors as the uafs_rmdir() call.
1325 package AFS
::Load
::Action
::rmdir;
1327 use AFS
::Load
::Action
;
1329 our @ISA = ("AFS::Load::Action");
1333 my $class = ref($proto) || $proto;
1334 my $self = $class->SUPER::new
();
1336 bless($self, $class);
1340 die("wrong number of args ($args) to rmdir (should be 1)");
1342 $self->{DIR
} = $_[0];
1349 my $code = AFS
::ukernel
::uafs_rmdir
($self->{DIR
});
1351 return (int($!), '');
1358 return "rmdir($self->{DIR})";
1363 Andrew Deason E<lt>adeason@sinenomine.netE<gt>, Sine Nomine Associates.
1367 Copyright 2010-2011 Sine Nomine Associates.