view release on metacpan or search on metacpan
# We will need to create a pts group and user, and if these names are
# already taken, then change these lines. The code will *remove* them
# when its done.
#
AFS_COMMAND_PTS_GROUP = afscmdgroup
AFS_COMMAND_PTS_USER = afscmduser
#
# If the AFS client on which the tests run does NOT match that
# specified above, then the pts group/user we create will NOT be
# visible to the fs setacl command. Therefore, we need the name of an
# existing pts user or group to use for that test.
#
# Yes, I promise the tests will NOT remove this one, 'cause I did
# all the tests using my own ID. ;-)
#
AFS_COMMAND_PTS_EXISTING = wpm
#
# If you don't have either gzip/gunzip, of bzip2/bunzip2, then comment
# the appropriate line to disable the tests using these compression
# something in the pipeline that reads from stdin and writes to
# stdout, and it can't break the volume dump format that passes
# through it.
#
# Tweak this at your own peril.
#
AFS_COMMAND_DUMP_FILTER = cat
AFS_COMMAND_RESTORE_FILTER = cat
#
# By default, we'll get the AFS commands from your PATH, but you can
# test with explicit versions by specifying these variables.
#
#AFS_COMMAND_BINARY_VOS = /some/path/to/vos
#AFS_COMMAND_BINARY_BOS = /some/path/to/bos
#AFS_COMMAND_BINARY_PTS = /some/path/to/pts
#AFS_COMMAND_BINARY_FS = /some/path/to/fs
id => 'somevol',
cell => 'somecell',
) || die $vos->errors();
When this works, the $vos->errors() will have the verbose output,
which can be logged even in the successful case, for diagnostics.
Here's an example for a failure:
[2004-11-18 17:20:36] Could not lock the VLDB entry for the volume 536998569.
[2004-11-18 17:20:36] VLDB: no permission access for call
[2004-11-18 17:20:36] Error in vos release command.
[2004-11-18 17:20:36] VLDB: no permission access for call
=head1 Changes in 1.8
=head1 Bug Fixes
=head2 vos examine did not pick up the LOCKED flag
The code to parse the VLDB header was missing the LOCKED flag, if it
was present, so this attribute was not being set properly. It is now.
=head2 pts membership error checking was bogus
Well, it still is bogus, actually, since the code has to deal with the
fact that pts has never produced meaningful return codes, so a failed
pts membership command can still exit 0, and we have to figure out if
it failed by other means. This is done by looking for the known error
messages that pts prints out, which is a good example of why parsing
the ASCII test output of commands like this is a weak architecture.
=head1 Changes in 1.7
=head1 Enhancements
=head2 Boolean flags can be turned off, as well as on
If an argument to a method (and its corresponding command line
argument) doesn't take a value, it is treated like a Boolean flag.
However, the code used to assume that the existence of a Boolean key
in the argument list implied the Boolean argument was always true.
Now, the truth of the arguments I<value> is tested to determine if the
flag should be set on or off. This makes it easy to have subroutines
that just blindly pass certain arguments along, without haing to test
them, and allows for much cleaner code.
For example:
my $result = $vos->release
(
name => $name,
force => $force,
);
Will work as you probably expected it to, namely if $force is zero, it
will NOT be passed to the "vos release" command. In previous
releases, regardless of the value of $force, the mere existence of the
force key in the argument hash would have caused the -force option to
be used.
=head1 Bugs
=head2 vos examine by numeric ID did not parse Volume Headers
The code to parse the volume headers in the output from "vos examine"
was looking for a pattern match based on the "id" argument. However,
getUserByName(), they will be lower-cased before looking up the user,
or group. Since PTS is a case-insensitive database, this will avoid
the need for applications to lc() some strings, although not in all
cases.
For example, you can pass a mixed case string to pts->creategroup(),
and then use the same string to query the resulting AFS::Object::Group
object using getGroupByName().
The object's "group" attribute, however, will be in lower case, since
that will be the value returned by the pts command itself.
=head2 AFS::Command::VOS->restore arguments added
Documentation for two important new vos restore arguments was added to
the documentation (-creation and -lastupdate). These options are only
available in a patch to vos, which should be in one the next two major
OpenAFS 1.2.X releases.
=head1 Bugs
=head1 Changes in 1.2
=head1 Enhancements
=head2 AFS::Command::VOS->offline() and ->online()
Both of these "hidden" vos commands are now supported by the API.
These commands are part of the vos command suite, but they do not show
up in the output of "vos help".
=head1 Bugs
=head1 Changes in 1.1
=head1 Enhancements
Changes.html view on Meta::CPAN
When this works, the $vos->errors() will have the verbose output, which
can be logged even in the successful case, for diagnostics. Here's an
example for a failure:
<P>
<PRE> [2004-11-18 17:20:36] Could not lock the VLDB entry for the volume 536998569.
[2004-11-18 17:20:36] VLDB: no permission access for call
[2004-11-18 17:20:36] Error in vos release command.
[2004-11-18 17:20:36] VLDB: no permission access for call
</PRE>
<P>
<P>
<HR>
<H1><A NAME="Changes_in_1_8">Changes in 1.8
</A></H1>
Changes.html view on Meta::CPAN
<P>
<P>
<HR>
<H2><A NAME="pts_membership_error_checking_wa">pts membership error checking was bogus
</A></H2>
Well, it still is bogus, actually, since the code has to deal with the fact
that pts has never produced meaningful return codes, so a failed pts
membership command can still exit 0, and we have to figure out if it failed
by other means. This is done by looking for the known error messages that
pts prints out, which is a good example of why parsing the ASCII test
output of commands like this is a weak architecture.
<P>
<P>
<HR>
<H1><A NAME="Changes_in_1_7">Changes in 1.7
</A></H1>
<P>
<HR>
<H1><A NAME="Enhancements">Enhancements
</A></H1>
<P>
<HR>
<H2><A NAME="Boolean_flags_can_be_turned_off_">Boolean flags can be turned off, as well as on
</A></H2>
If an argument to a method (and its corresponding command line argument)
doesn't take a value, it is treated like a Boolean flag. However, the code
used to assume that the existence of a Boolean key in the argument list
implied the Boolean argument was always true.
<P>
Now, the truth of the arguments <EM>value</EM> is tested to determine if the flag should be set on or off. This makes it
easy to have subroutines that just blindly pass certain arguments along,
without haing to test them, and allows for much cleaner code.
Changes.html view on Meta::CPAN
<PRE> my $result = $vos->release
(
name => $name,
force => $force,
);
</PRE>
<P>
Will work as you probably expected it to, namely if <CODE>$force</CODE> is
zero, it will NOT be passed to the ``vos release'' command. In previous
releases, regardless of the value of $force, the mere existence of the
force key in the argument hash would have caused the -force option to be
used.
<P>
<P>
<HR>
<H1><A NAME="Bugs">Bugs
Changes.html view on Meta::CPAN
<P>
For example, you can pass a mixed case string to pts->creategroup(), and
then use the same string to query the resulting AFS::Object::Group object
using <CODE>getGroupByName().</CODE>
<P>
The object's ``group'' attribute, however, will be in lower case, since
that will be the value returned by the pts command itself.
<P>
<P>
<HR>
<H2><A NAME="AFS_Command_VOS_restore_argum">AFS::Command::VOS->restore arguments added
</A></H2>
Documentation for two important new vos restore arguments was added to the
Changes.html view on Meta::CPAN
<P>
<HR>
<H1><A NAME="Enhancements">Enhancements
</A></H1>
<P>
<HR>
<H2><A NAME="AFS_Command_VOS_offline_and">AFS::Command::VOS->offline() and ->online()
</A></H2>
Both of these ``hidden'' vos commands are now supported by the API. These
commands are part of the vos command suite, but they do not show up in the
output of ``vos help''.
<P>
<P>
<HR>
<H1><A NAME="Bugs">Bugs
</A></H1>
#-*-cperl-*-
#
# $Id: README,v 7.1 2004/01/13 19:01:11 wpm Exp $
#
# (c) 2003-2004 Morgan Stanley and Co.
# See ..../src/LICENSE for terms of distribution.
#
=head1 INTRODUCTION
AFS::Command -- An OO wrapper API for the AFS command line utilities (vos, bos, pts, fs)
=head1 SUMMARY
This suite of perl modules implements an API for the command line
utilities for managing and using the AFS distributed file system. AFS
is available as both an Open Source product (http://www.openafs.org),
as well as a commercial version from IBM
(http://www.ibm.com/software/stormgmt/afs/).
For example, AFS volumes are managed using the command line utility
'vos', for which there is currently no programmatic API, in any
language. This API is a pure perl wrapper around the command line
utility, that implements an OO API that allows for the easy
development of systems management applications for AFS.
=head1 PREREQUISITES
This module requires perl5.6 or later, as well as an installed AFS
infrastructure to work with. The code is pure perl, with no compiled
components, do it should work on any variant of UNIX (sorry, but this
code makes aggressive use of pipe() and fork(), so porting it to
Windows is gonna be painful... but then, why anyone would want to
README.html view on Meta::CPAN
<LI><A HREF="#DOCUMENTATION">DOCUMENTATION</A>
<LI><A HREF="#AUTHOR">AUTHOR</A>
</UL>
<!-- INDEX END -->
<HR>
<P>
<H1><A NAME="INTRODUCTION">INTRODUCTION
</A></H1>
AFS::Command -- An OO wrapper API for the AFS command line utilities (vos,
bos, pts, fs)
<P>
<P>
<HR>
<H1><A NAME="SUMMARY">SUMMARY
</A></H1>
This suite of perl modules implements an API for the command line utilities
for managing and using the AFS distributed file system. AFS is available as
both an Open Source product (http://www.openafs.org), as well as a
commercial version from IBM (http://www.ibm.com/software/stormgmt/afs/).
<P>
For example, AFS volumes are managed using the command line utility 'vos',
for which there is currently no programmatic API, in any language. This API
is a pure perl wrapper around the command line utility, that implements an
OO API that allows for the easy development of systems management
applications for AFS.
<P>
<P>
<HR>
<H1><A NAME="PREREQUISITES">PREREQUISITES
Look for this in version 1.1
=head2 stderr handling
stderr processing needs to be handled in the _exec_cmds() method, and
for that matter, _reap_cmds() needs to be folded into _exec_cmds().
The problem is that each API method calls _save_stderr(), and then
later calls _restore_stderr(), and we leave stderr redirected for
longer than necessary. The contents of the redirected output should
be *only* the output from the commands we run, and right now, some of
our own carping can creep in there.
Worse, its possible that a failure in the API can leave stderr
redirected, resulting in a lot of confusion.
Its possible we should just suck in *ALL* of the output, both
stdout/stderr, and drop that data into a couple of arrays. Then,
method calls on the command object get gets individual rows of
stdout/stderr output.
return unless $self->_exec_cmds();
#
# Process stdout
#
while ( defined($_ = $self->_stdout() ) ) {
}
#
# Process stderr (in some cases, there's interesting data in here.
# see the fs examine/diskfree and similar api calls)
#
while ( defined($_ = $self->_stderr() ) ) {
}
Maybe something like that. By the time _exec_cmds returns, we have
reaped the commands, and collected *ALL* of the output into arrays in
the object.
=head2 Test Suite: fs lsmount using multiple dirs
We should create several mount points and then query then all with one
lsmount method call, to verify we can parse output for multiple dirs.
We should pass in some bogus paths, too, to verify the error handling
is correct as well (that code feels dubious to me).
=head1 Bugs
=head2 stdout/stderr buffering will break the fs examine/diskfree commands
Actually, all of the commands that parse per-path output, really.
Currently the code assumes the stderr output will appear first, which
is a side effect of the buffering. Some attempts to turn of buffering
didn't change this, and in any case, we don't want to be sensitive to
this (we currently are).
We need to process stderr first, to determine which paths had errors,
and then parse stdout. This will require the change descibred above
for how we handle stderr.
=cut
<UL>
<LI><A HREF="#Accept_AFS_Object_objects_as_ar">Accept AFS::Object objects as arguments</A>
<LI><A HREF="#stderr_handling">stderr handling</A>
<LI><A HREF="#Test_Suite_fs_lsmount_using_mul">Test Suite: fs lsmount using multiple dirs</A>
</UL>
<LI><A HREF="#Bugs">Bugs</A>
<UL>
<LI><A HREF="#stdout_stderr_buffering_will_bre">stdout/stderr buffering will break the fs examine/diskfree commands</A>
</UL>
</UL>
<!-- INDEX END -->
<HR>
<P>
<H1><A NAME="NAME">NAME
</A></H1>
stderr processing needs to be handled in the <CODE>_exec_cmds()</CODE>
method, and for that matter, <CODE>_reap_cmds()</CODE> needs to be folded
into <CODE>_exec_cmds().</CODE>
<P>
The problem is that each API method calls <CODE>_save_stderr(),</CODE> and
then later calls <CODE>_restore_stderr(),</CODE> and we leave stderr
redirected for longer than necessary. The contents of the redirected output
should be *only* the output from the commands we run, and right now, some
of our own carping can creep in there.
<P>
Worse, its possible that a failure in the API can leave stderr redirected,
resulting in a lot of confusion.
<P>
Its possible we should just suck in *ALL* of the output, both
stdout/stderr, and drop that data into a couple of arrays. Then, method
calls on the command object get gets individual rows of stdout/stderr
output.
<P>
<PRE> return unless $self->_exec_cmds();
</PRE>
<P>
</PRE>
<P>
<PRE> }
</PRE>
<P>
Maybe something like that. By the time _exec_cmds returns, we have reaped
the commands, and collected *ALL* of the output into arrays in the object.
<P>
<P>
<HR>
<H2><A NAME="Test_Suite_fs_lsmount_using_mul">Test Suite: fs lsmount using multiple dirs
</A></H2>
We should create several mount points and then query then all with one
<P>
<P>
<HR>
<H1><A NAME="Bugs">Bugs
</A></H1>
<P>
<HR>
<H2><A NAME="stdout_stderr_buffering_will_bre">stdout/stderr buffering will break the fs examine/diskfree commands
</A></H2>
Actually, all of the commands that parse per-path output, really. Currently
the code assumes the stderr output will appear first, which is a side
effect of the buffering. Some attempts to turn of buffering didn't change
this, and in any case, we don't want to be sensitive to this (we currently
are).
<P>
We need to process stderr first, to determine which paths had errors, and
then parse stdout. This will require the change descibred above for how we
lib/AFS/Command.pod view on Meta::CPAN
=head1 INTRODUCTION
Welcome to the OO Perl API for the AFS Command Line Utilities. OK,
enough with formalities....
This set of classes implements a perl API for developing systems
administration applications for AFS, and is essentially a complete
rewrite of a similar API developed for perl4 almost a decade ago.
The API is designed to be as intuitive as the underlying command
themselves. One of the strengths of the AFS CLI is its consistent
command line parsing, since all of the utilities share a common
library, and this API leverages this fact. The methods available, and
their available arguments, are determined dynamically by parsing the
command help strings.
This API is also designed to complement the existing perl5 AFS::*
namespace, and co-exist with it.
=head1 OBJECT ARCHITECTURE
WARNING: This is an early design phase (1.x) of this API, and it is in
its infancy, so expect it to change in future releases, and expect to
change your code to accomodate it.
The entire API is designed to be pure OO, with the following classes:
=head2 AFS::Command::(VOS|BOS|PTS|FS)
These are the primary classes used by applications directly. All of
the other objects are used internally to encapsulate the data parsed
from the underlying commands, and returned by the primary methods.
Each of these classes has a shared constructor (new), and methods that
correspond to each of the underlying commands. For example, the "vos"
utility has a command called "listvldb", and the AFS::Command::VOS
objects have a "listvldb" method. Each of the methods such as
"listvldb" take a list of key/value pairs that correspond to the
command line options for the "vos listvldb" command.
These classes implement the externally supported interface to the
entire API. If you use anything else, you're mucking with internals,
and you get what you deserver when your code implodes.
For details, see the module documentation for each of the above.
Also, the only reason there is no AFS::Command::KAS is that the
author's AFS infrastructure is an MIT Kerberos site, and since we
don't use the kas utility, there has never been a need for such a
module.
The author would welcome a contributed module to support KAS via the
same interface, but note that the AFS::KAS module exists as part of
the existing AFS module suite, although the API is very different.
=head2 AFS::Command::Base
This is the base class for the command suite, which implements the
shared constructor, and a couple of other useful class and object
methods.
=head2 AFS::Object
This is the base class for the objects returned from the command
methods that encapsulate some form of structured data. Many of the
methods return simple boolean true/false values (either the command
worked, or it failed), with no need for any special objects, so we
don't create them. Anything that has to return interesting data uses
this class, or one of its base classes.
The subclasses are associated with data structures such as volume
headers, a VLDB entry, or a partition on a server. Each of these
classes has methods to retrieve the objects they "contain", such as a
method to query the list of volume names on a partition object, and a
method to get a list of VLDB entries from a VLDB site.
The data structures, and their varying relationships, are documented
in details of the methods for each of the commands, and the specific
interfaces for each object type are documented in the corresponding
class documentation.
The subclasses for encapsulating the VLDB data are:
AFS::Object::VLDB
AFS::Object::VLDBEntry
AFS::Object::VLDBSite
The subclasses for encapsulating the volume headers are:
lib/AFS/Command.pod view on Meta::CPAN
AFS::Object::Volume
AFS::Object::VolumeHeader
The subclasses for encapsulating the bosserver data are:
AFS::Object::BosServer
AFS::Object::Instance
Note that none of these classes are specifically documented, since the
structure of the classes is subject to change. The API for accessin
the results of any given AFS command (eg. vos listvol) is considered
reasonably stable, but the encapsulation may change radically.
Don't get too attached to these class names, because they will likely
be rearranged in a future release.
=cut
lib/AFS/Command/BOS.pm view on Meta::CPAN
$errors++ unless $self->_reap_cmds();
$errors++ unless $self->_restore_stderr();
return if $errors;
return $result;
}
#
# XXX -- we might want to provide parsing of the bos salvage output,
# but for now, this is a non-parsed command.
#
# sub salvage {
# my $self = shift;
# my (%args) = @_;
# my $result = AFS::Object::BosServer->new();
# $self->{operation} = "salvage";
lib/AFS/Command/BOS.pm view on Meta::CPAN
$instance->_setAttribute
(
errorexitdue => 'code',
errorexitcode => $1,
);
}
}
if ( /Command\s+(\d+)\s+is\s+\'(.*)\'/ ) {
my $command = AFS::Object->new
(
index => $1,
command => $2,
);
$instance->_addCommand($command);
}
if ( /Notifier\s+is\s+\'(.*)\'/ ) {
$instance->_setAttribute( notifier => $1 );
}
}
if ( defined $instance ) {
$result->_addInstance($instance);
lib/AFS/Command/BOS.pod view on Meta::CPAN
#
# $Id: BOS.pod,v 7.1 2004/01/13 19:01:12 wpm Exp $
#
# (c) 2003-2004 Morgan Stanley and Co.
# See ..../src/LICENSE for terms of distribution.
#
=head1 NAME
AFS::Command::BOS - OO API to the AFS bos command
=head1 SYNOPSIS
use AFS::Command::BOS;
my $bos = AFS::Command::BOS->new();
my $bos = AFS::Command::BOS->new
(
command => $path_to_your_bos_binary,
);
my $bos = AFS::Command::BOS->new
(
localauth => 1,
);
=head1 DESCRIPTION
This module implements an OO API wrapper around the AFS 'bos' command.
The supported methods depend on the version of the bos binary used,
and are determined automagically.
=head1 METHODS -- Inherited
All of the following methods are inherited from the AFS::Command::Base
class. See that documentation for details.
=over
lib/AFS/Command/BOS.pod view on Meta::CPAN
my $result = $bos->status
(
server => $server,
long => 1,
) || die $bos->errors();
foreach my $instanceobj ( $result->getInstances() ) {
my $instance = $instanceobj->instance();
my $status = $instanceobj->status();
print "Instance $instance has status $status\n";
foreach my $commandobj ( $instance->getCommands() ) {
my $index = $commandobj->index();
my $command = $commandobj->command();
print "\tCmd $index is '$command'\n";
}
}
The objects have the following attributes and methods:
B<AFS::Object::BosServer>
The following attribute is only present when "bos status" reports
inappropriate access on directories:
lib/AFS/Command/BOS.pod view on Meta::CPAN
"stopped for too many errors"
core Boolean, indicating the instance has a core file
errorexitdate Date when the process last exited with an error
errorexitdue "shutdown", or "signal", or "code" (present only when
"errorexitdate" attribute is present)
errorexitsignal Signal that cause the error exit (present only when
"errorexitdue" eq "signal")
errorexitcode Exit code from last error exit (present only when
"errorexitdue" eq "code")
The following methods can be used to extract the command objects,
which are also only present when the 'long' argument is specified.
Methods Returns
------- -------
getCommandIndexes() list of numeric indexes for the commands
getCommands() list of AFS::Object objects for all commands
getCommand($index) the AFS::Object object for the command with index $index
B<AFS::Object> (Commands)
The following pair of attributes are always present:
Attributes Values
---------- ------
index Numerical index of the command
command Command string
=back
=head1 METHODS (with simple return values)
All of the following commands return a simple Boolean (true/false)
value, if they succeed or fail.
=head2 addhost
The bos help string is:
bos addhost: add host to cell dbase
Usage: bos addhost -server <machine name> -host <host name>+
[-clone] [-cell <cell name>] [-noauth] [-localauth]
Where: -clone vote doesn't count
lib/AFS/Command/BOS.pod view on Meta::CPAN
noauth => 1,
localauth => 1,
);
=head2 create
The bos help string is:
bos create: create a new server instance
Usage: bos create -server <machine name> -instance <server process name>
-type <server type> -cmd <command lines>+ [-notifier <Notifier program>]
[-cell <cell name>] [-noauth] [-localauth]
The corresponding method invocation looks like:
my $result = $bos->create
(
# Required arguments
server => $server,
instance => $instance,
type => $type,
lib/AFS/Command/BOS.pod view on Meta::CPAN
# Optional arguments
cell => $cell,
noauth => 1,
localauth => 1,
);
=head2 exec
The bos help string is:
bos exec: execute shell command on server
Usage: bos exec -server <machine name> -cmd <command to execute>
[-cell <cell name>] [-noauth] [-localauth]
The corresponding method invocation looks like:
my $result = $bos->exec
(
# Required arguments
server => $server,
cmd => $cmd,
# Optional arguments
lib/AFS/Command/Base.pm view on Meta::CPAN
my $class = ref($proto) || $proto;
my %args = @_;
my $self = {};
foreach my $key ( qw( localtime noauth localauth encrypt quiet timestamps ) ) {
$self->{$key}++ if $args{$key};
}
# AFS::Command::VOS -> vos
if ( $args{command} ) {
my @commands = (split /\s+/,$args{command});
push (@{$self->{command}},@commands);
} else {
@{$self->{command}} = lc((split(/::/,$class))[2]);
}
bless $self, $class;
return $self;
}
sub errors {
my $self = shift;
lib/AFS/Command/Base.pm view on Meta::CPAN
my $operation = shift;
my $class = ref $self;
unless ( $self->{_operations} ) {
my %operations = ();
#
# This hack is necessary to support the offline/online "hidden"
# vos commands. These won't show up in the normal help output,
# so we have to check for them individually. Since offline and
# online are implemented as a pair, we can just check one of
# them, and assume the other is there, too.
#
foreach my $type ( qw(default hidden) ) {
if ( $type eq 'hidden' ) {
next unless $self->isa("AFS::Command::VOS");
}
lib/AFS/Command/Base.pm view on Meta::CPAN
}
if ( $pid == 0 ) {
STDERR->fdopen( STDOUT->fileno(), "w" ) ||
$self->_Croak("Unable to redirect stderr: $ERRNO\n");
STDOUT->fdopen( $pipe->writer()->fileno(), "w" ) ||
$self->_Croak("Unable to redirect stdout: $ERRNO\n");
if ( $type eq 'default' ) {
exec @{$self->{command}}, 'help';
} else {
exec @{$self->{command}}, 'offline', '-help';
}
die "Unable to exec @{$self->{command}} help: $ERRNO\n";
} else {
$pipe->reader();
while ( defined($_ = $pipe->getline()) ) {
if ( $type eq 'default' ) {
next if /Commands are:/;
my ($command) = split;
next if $command =~ /^(apropos|help)$/;
$operations{$command}++;
} else {
if ( /^Usage:/ ) {
$operations{offline}++;
$operations{online}++;
}
}
}
}
unless ( waitpid($pid,0) ) {
$self->_Carp("Unable to get status of child process ($pid)");
return;
}
if ( $? ) {
$self->_Carp("Error running @{$self->{command}} help. Unable to configure $class");
return;
}
}
$self->{_operations} = \%operations;
}
return $self->{_operations}->{$operation};
lib/AFS/Command/Base.pm view on Meta::CPAN
my $self = shift;
my $operation = shift;
my $arguments =
{
optional => {},
required => {},
aliases => {},
};
my @command;
push (@command, @{$self->{command}});
unless ( $self->_operations($operation) ) {
$self->_Carp("Unsupported @command operation '$operation'\n");
return;
}
return $self->{_arguments}->{$operation}
if ref $self->{_arguments}->{$operation} eq 'HASH';
my $pipe = IO::Pipe->new() || do {
$self->_Carp("Unable to create pipe: $ERRNO");
return;
};
lib/AFS/Command/Base.pm view on Meta::CPAN
$self->_Carp("Unable to fork: $ERRNO");
return;
}
if ( $pid == 0 ) {
STDERR->fdopen( STDOUT->fileno(), "w" ) ||
die "Unable to redirect stderr: $ERRNO\n";
STDOUT->fdopen( $pipe->writer()->fileno(), "w" ) ||
die "Unable to redirect stdout: $ERRNO\n";
exec @command, $operation, '-help';
die "Unable to exec @command help $operation: $ERRNO\n";
} else {
$pipe->reader();
while ( <$pipe> ) {
if ( /Unrecognized operation '$operation'/ ) {
$self->_Carp("Unsupported @command operation '$operation'\n");
$errors++;
last;
}
next unless s/^Usage:.*\s+$operation\s+//;
while ( $_ ) {
if ( s/^\[\s*-(\w+?)\s*\]\s*// ) {
$arguments->{optional}->{$1} = 0
unless $1 eq 'help'; # Yeah, skip it...
lib/AFS/Command/Base.pm view on Meta::CPAN
$arguments->{optional}->{$1} = [];
} elsif ( s/^\[\s*-(\w+?)\s+<[^>]*?>\s*]\s*// ) {
$arguments->{optional}->{$1} = 1;
} elsif ( s/^\s*-(\w+?)\s+<[^>]*?>\+\s*// ) {
$arguments->{required}->{$1} = [];
} elsif ( s/^\s*-(\w+?)\s+<[^>]*?>\s*// ) {
$arguments->{required}->{$1} = 1;
} elsif ( s/^\s*-(\w+?)\s*// ) {
$arguments->{required}->{$1} = 0;
} else {
$self->_Carp("Unable to parse @command help for $operation\n" .
"Unrecognized string: '$_'");
$errors++;
last;
}
}
last;
}
lib/AFS/Command/Base.pm view on Meta::CPAN
$arguments->{aliases}->{f} = 'force';
}
}
unless ( waitpid($pid,0) ) {
$self->_Carp("Unable to get status of child process ($pid)");
$errors++;
}
if ( $? ) {
$self->_Carp("Error running @command $operation -help. Unable to configure @command $operation");
$errors++;
}
return if $errors;
return $self->{_arguments}->{$operation} = $arguments;
}
sub _save_stderr {
my $self = shift;
$self->{olderr} = IO::File->new(">&STDERR") || do {
$self->_Carp("Unable to dup stderr: $ERRNO");
return;
};
my $command = basename((split /\s+/,@{$self->{command}})[0]);
$self->{tmpfile} = "/tmp/.$command.$self->{operation}.$$";
my $newerr = IO::File->new(">$self->{tmpfile}") || do {
$self->_Carp("Unable to open $self->{tmpfile}: $ERRNO");
return;
};
STDERR->fdopen( $newerr->fileno(), "w" ) || do {
$self->_Carp("Unable to reopen stderr: $ERRNO");
return;
};
lib/AFS/Command/Base.pm view on Meta::CPAN
$self->{errors} = "";
$self->{cmds} = [];
if ( $args{inputfile} ) {
push( @{$self->{cmds}}, [ 'cat', $args{inputfile} ] );
} else {
my @argv = ( @{$self->{command}}, $self->{operation} );
foreach my $key ( keys %args ) {
next unless $arguments->{aliases}->{$key};
$args{$arguments->{aliases}->{$key}} = delete $args{$key};
}
foreach my $key ( qw( noauth localauth encrypt ) ) {
next unless $self->{$key};
$args{$key}++ if exists $arguments->{required}->{$key};
$args{$key}++ if exists $arguments->{optional}->{$key};
lib/AFS/Command/Base.pod view on Meta::CPAN
# See ..../src/LICENSE for terms of distribution.
#
=head1 NAME
AFS::Command::Base -- Base OO Class for AFS::Command::* API
=head1 SYNOPSIS
This module is B<NOT> used directly by applications, but indirectly by
the various command wrapper classes:
use AFS::Command::VOS;
use AFS::Command::BOS;
use AFS::Command::PTS;
use AFS::Command::FS;
All of these classes inherit from this base class, and the inherited
methods are documented here.
=head1 DESCRIPTION
The AFS::Command suite provides an programmatic API to several of the
key AFS command line utilities, namely: vos, bos, pts and fs. This
module is <NOT> a replacement for the AFS::* modules, which are an XS
interface to the lower level AFS C programming APIs.
This module attempts to fill a huge void in the APIs available for
developing AFS systems management applications in perl. Norbert
Gruener's AFS module implements wrapper classes for the various C
programming APIs available for AFS, however most of the systems
management for AFS is implemented in the command line utilities
themselves, not in any of the C libraries. Far too much of the logic
in vos, by far the most important utility, is in the application
itself, not the underlying libraries. This makes it very difficult
for an XS-only approach to support the vos functionality.
These classes take an entirely different approach, since they are
simply wrappers around the command line executables themselves, and
implemented entirely in pure perl (no XS code at all). This has some
advantages and disadvantages, of course. The primary advantage is
that we are not forced to re-implement the complex logic in the vos
command using the low level primitive C API calls. The primary
disadvantage is that we are tightly coupled with the format of the
text output of each of these commands, but this is a problem space
that perl excels in.
This document covers general programming with the API, and the details
of the specific wrapper classes are documented separately
(eg. AFS::Command::VOS).
=head1 CLASS METHODS
=head2 new
All of the AFS::Command subclasses use this inherited method as an
object constructor. In its simplest usage, it can be called with no
arguments, but there are several which control global behavior of the
commands, simplifying subsequent coding a bit.
=over
=item command
This key has the pathname to the command to be used for execution. By
default, this is the simple command name "vos", "bos", etc, and the
command will simply be found in your $PATH by exec().
If you want to run a specific version of vos not found in the $PATH,
then for example:
my $vos = AFS::Command::VOS->new
(
command => "/ms/dist/openafs/PROJ/core/1.2.9/bin/vos",
);
If the path given is invalid, then expect the API to implode on itself
when it can't be found, or it isn't an AFS vos command.
=item localauth, noauth, encrypt
All of these arguments correspond to command line arguments common
across the entire command line suite. Typically, if an application
uses this flag once, it will be using it for B<all> subsequent calls as
well. Therefore, the state of these flags can be set globally by
setting them when creating the command object.
my $vos = AFS::Command::VOS->new
(
localauth => 1,
encrypt => 1,
);
NOTE: The encrypt option is only available in more recent versions of
AFS, and may be unsupported by the underlying commands.
XXX: What should the default behavior be? Croak or carp? we can
figure out dynamically if the command supports it, and have the
constructor fail, or we can be lazy and let the first command fail.
=item quiet
The default behavior for the common -verbose flag is inverted. By
default, all commands are run with the -verbose flag, in order to
capture maximum diagnostics when an error occurs. Normally, the
chatty output is all trapped by the API anyway, so there is no
application visible noise, just more verbose errors.
There should be no need to disable verbosity, but for completeness,
specifying 'quiet' will turn off the default verbose output.
=item timestamps
If this argument is given, then the output collected from the commands
will be prepended with the date formatted using Date::Format with:
%Y/%m/%d %H:%M:%S
This is primarily useful for debugging and timing of commands such as
vos release, which can be very time consuming. Since we enable
-verbose by default, this option will let us determine the relative
time required for each step in these complex operations.
This only applies to commands that return simple return values, eg:
release, restore, etc. Commands that return complex structures of
objects, such as listvldb, listvol, etc will not be affected.
=back
=head2 setCarp
This class method configures the carp and/or croak subroutines used
throughout the API. By default, the obviously sensible thing is done:
the carp an croak subroutines exported by the Carp module are used.
lib/AFS/Command/Base.pod view on Meta::CPAN
This method returns true of false, depending on whether or not the
carp and/or croak subroutines were properly configured. If the values
are not CODE references, then this method will itself croak.
=head1 INSTANCE METHODS
=head2 errors
This method takes no arguments, and it returns a string, containing the
unparsed errors from the last command method invoked. This string is
reset with each subsequent command method invocation. The string is
normally the output written to stderr by the process, but in the case
of unparsed boolean commands, it contains both the stdout as well as
the stderr output.
my $result = $vos->examine
(
id => $volname,
cell => $cell,
);
unless ( $result ) {
die "Unable to examine volname '$volname' in cell '$cell':" .
$vos->errors();
}
=head2 supportsOperation
This class method allows the developer to test whether or not any
given operation is supported by the underlying command line utility.
For example, the "vos changeloc" operation is not supported in older
release of vos.
unless ( $vos->supportsOperation('changeloc') {
die "Unable to continue -- 'vos changeloc' is unsupported.\n";
}
The return value is simply true or false.
=head2 supportsArgument
Similar to supportsOperation, supportsArgument will test whether or
not a given argument is a support command line argument for the
specified operation. For example, the -encrypt argument is only
supported in more recent versions of vos, so that support can be
tested for easily.
unless ( $vos->supportsArgument('listvldb','encrypt') ) {
warn "Encryption is not support by your version of vos.\n";
}
The return value is simply true or false.
lib/AFS/Command/FS.pod view on Meta::CPAN
#
# $Id: FS.pod,v 8.1 2004/05/17 13:05:47 wpm Exp $
#
# (c) 2003-2004 Morgan Stanley and Co.
# See ..../src/LICENSE for terms of distribution.
#
=head1 NAME
AFS::Command::FS - OO API to the AFS fs command
=head1 SYNOPSIS
use AFS::Command::FS;
my $fs = AFS::Command::FS->new();
my $fs = AFS::Command::FS->new
(
command => $path_to_your_fs_binary,
);
=head1 DESCRIPTION
This module implements an OO API wrapper around the AFS 'fs' command.
The supported methods depend on the version of the fs binary used,
and are determined automagically.
=head1 METHODS -- Inherited
All of the following methods are inherited from the AFS::Command::Base
class. See that documentation for details.
=over
lib/AFS/Command/FS.pod view on Meta::CPAN
=item errors
=item supportsOperation
=item supportsArgument
=back
=head1 METHODS (with complex return values)
=head2 NOTE: Error checking for commands that accept a list of paths
A number of these methods accept a list of paths, and will return
information for each path, individually. If you specify a
non-existent path, or one which is not in AFS, then the fs command
returns a non-zero exist status, which normally would mean the command
failed.
If you specify a list of paths to this API, and one or more of them
result in errors, the API call is still considered to succeed, as long
as we can determine the error for each path specified. The API will
still return an AFS::Object::CacheManager object, which contains a set
of AFS::Object::Path object, for each path specified in the arguments,
as long as we saw some kind of output from the fs commands for each
path.
Each AFS::Object::Path object must be examined to determine the
success of failure for that individual path. When errors were
encountered for any given path, then the objects will have an "error"
attribute, and nothing else (no other data attributes, except the path
itself).
This holds true for the following API methods: diskfree, examine,
listquota, quota, storebehind, whereis, whichcell, and listacl.
lib/AFS/Command/FS.pod view on Meta::CPAN
acl => [ <<see below>> ],
# Optional arguments
clear => 1,
negative => 1,
id => 1,
if => 1,
);
NOTE: The values passed to the 'acl' argument has to be constructed
with care. Unlike many of the other arguments, this has to be a seen
by the 'fs' command as an even number of additional command line
arguments immediately after the -acl flag.
If you construct a single string, such as "user read group write",
then the method will fail. There is no shell involved in exec'ing fs,
so there will be no splitting of this string on whitespace before we
construct the arguments to fs, so it will look like a single argument,
not four distinct arguments.
Therefore, there are two ways to construct an ACL to pass to setacl():
lib/AFS/Command/PTS.pm view on Meta::CPAN
if ( $user ) {
$user->_addOwned($1);
} else {
$group->_addOwned($2);
}
} elsif ( /unable to get owner list/ ) {
#
# pts still (as of OpenAFS 1.2.8) doesn't have proper exit codes.
# If we see this string, then let the command fail, even
# though we might have partial data.
#
$self->{errors} .= $_;
$errors++;
}
}
$result->_addUser($user) if $user;
lib/AFS/Command/PTS.pm view on Meta::CPAN
} else {
$group->_addMembership($1);
}
} elsif ( /unable to get membership/ ||
/User or group doesn't exist/ ||
/membership list for id \d+ exceeds display limit/ ) {
#
# pts still (as of OpenAFS 1.2.8) doesn't have proper exit codes.
# If we see this string, then let the command fail, even
# though we might have partial data.
#
$self->{errors} .= $_;
$errors++;
}
}
$result->_addUser($user) if $user;
lib/AFS/Command/PTS.pod view on Meta::CPAN
#
# $Id: PTS.pod,v 7.1 2004/01/13 19:01:13 wpm Exp $
#
# (c) 2003-2004 Morgan Stanley and Co.
# See ..../src/LICENSE for terms of distribution.
#
=head1 NAME
AFS::Command::PTS - OO API to the AFS pts command
=head1 SYNOPSIS
use AFS::Command::PTS;
my $pts = AFS::Command::PTS->new();
my $pts = AFS::Command::PTS->new
(
command => $path_to_your_pts_binary,
);
my $pts = AFS::Command::PTS->new
(
noauth => 1,
force => 1,
);
=head1 DESCRIPTION
This module implements an OO API wrapper around the AFS 'pts' command.
The supported methods depend on the version of the pts binary used,
and are determined automagically.
=head1 METHODS -- Inherited
All of the following methods are inherited from the AFS::Command::Base
class. See that documentation for details.
=over
lib/AFS/Command/PTS.pod view on Meta::CPAN
Methods Returns
------- -------
getMembership() For a user, the list of group to which the user belongs,
for a group, the members of the group
=back
=head1 METHODS (with simple return values)
All of the following commands return a simple Boolean (true/false)
value, if they succeed or fail.
=head2 adduser
The pts help string is:
pts adduser: add a user to a group
Usage: pts adduser -user <user name>+ -group <group name>+
[-cell <cell name>] [-noauth] [-force]
lib/AFS/Command/VOS.pod view on Meta::CPAN
#
# $Id: VOS.pod,v 7.2 2004/05/11 15:55:17 wpm Exp $
#
# (c) 2003-2004 Morgan Stanley and Co.
# See ..../src/LICENSE for terms of distribution.
#
=head1 NAME
AFS::Command::VOS - OO API to the AFS vos command
=head1 SYNOPSIS
use AFS::Command::VOS;
my $vos = AFS::Command::VOS->new();
my $vos = AFS::Command::VOS->new
(
command => $path_to_your_vos_binary,
);
my $vos = AFS::Command::VOS->new
(
localauth => 1,
encrypt => 1,
);
=head1 DESCRIPTION
This module implements an OO API wrapper around the AFS 'vos' command.
The supported methods depend on the version of the vos binary used,
and are determined automagically.
=head1 METHODS -- Inherited
All of the following methods are inherited from the AFS::Command::Base
class. See that documentation for details.
=over
lib/AFS/Command/VOS.pod view on Meta::CPAN
=head1 METHODS (dump, restore)
Both the 'dump' and 'restore' methods are special, since this API
supports compression to and from the filesystem when dumping or
restoring the volume. Normally, "vos dump -file" will just wrote the
uncompressed volume dump to the file, but this API can compress it.
This is a huge cost savings in disk space, assuming you can afford the
CPU time to perform the compression (this is the 21st century -- you
probably can).
Both of these commands return simply boolean true/false values, but
they have some special case handling for the -file argument, and
support several special arguments that are extensions implemented in
this API only.
=head2 dump
The vos help string is:
vos dump: dump a volume
Usage: vos dump -id <volume name or ID> [-time <dump from time>] [-file <dump file>]
lib/AFS/Command/VOS.pod view on Meta::CPAN
server => $server,
partition => $partition,
cell => $cell,
noauth => 1,
localauth => 1,
verbose => 1,
encrypt => 1,
# Enhanced arguments
gzip => $gzip,
bzip2 => $bzip2,
filterout => [ @command ], # OR [ [ @cmd1 ], [ @cmd2 ], ... ]
);
The first thing to notice is that 'file' is optional to the vos dump
command itself, but required in this API. The second thing to notice
is the addition of three new arguments: gzip, bzip2 and filterout.
=over
=item file
This argument specifies the file to which the vos dump output should
be written. If this file ends in '.gz' or '.bz2', then gzip or bzip2
will be used to compress the output before it is written to the
filesystem. This is accomplished using a pipe, so there is no
lib/AFS/Command/VOS.pod view on Meta::CPAN
The value of these arguments specifies the degree of compression used,
an should be a single numeric digit, from 0 to 9. See the gzip and
bzip2 man pages for more information.
These arguments are also mutually exclusive.
=item filterout
This is an advanced feature, and one that allows the volume dump to be
filtered through any arbitrary number of commands before it is
compressed (optionally) and written to the filesystem. The value of
this argument is either an ARRAY reference to a list of command line
arguments, suitable for passing to exec(), or an ARRAY or such ARRAYS,
when more than one filter command is being used.
For example, the author has a requirement to pass all volume dumps
through a simple filter called 'newversion', which reads a volume
dump, changes the directory version numbers to the current utime
value, and writes the volume dump to stdout. Trust me, you really
don't want to know why.
my $result = $vos->dump
(
...
filterout => [ 'newversion' ],
...
);
If there were command line arguments for this command, then they must
be given as follows:
my $result = $vos->dump
(
...
filterout => [ 'newversion', '-arg1', '-value1' ],
...
);
These args are passed directly to exec, with no shell involved. When
more than one command is given, then an ARRAY or ARRAYs must be
specified as follows:
my $result = $vos->dump
(
...
filterout => [
[ 'command1', '-arg1', '-value1' ],
[ 'command2', '-arg2', '-value2' ],
[ 'command3', '-arg3', '-value3' ],
],
...
);
If B<ANY> of the filterout commands exits with a non-zero status, then
the entire dump method invocation is considered to fail. You may or
may not get a valid volume dump file, compressed or otherwise,
depending on the behavior of the commands you specify.
=back
=head2 restore
The vos help string is:
vos restore: restore a volume
Usage: vos restore -server <machine name> -partition <partition name>
-name <name of volume to be restored> [-file <dump file>]
lib/AFS/Command/VOS.pod view on Meta::CPAN
creation => 'dump' | 'keep' | 'new',
lastupdate => 'dump' | 'keep' | 'new',
cell => $cell,
noauth => 1,
localauth => 1,
verbose => 1,
encrypt => 1,
# Enhanced arguments
gunzip => 1,
bunzip2 => 1,
filterin => [ @command ], # OR [ [ @cmd1 ], [ @cmd2 ], ... ]
);
NOTE: The 'creation' and 'lastupdate' options are available only in a
very recent patch to the vos command, which should be available in the
OpenAFS 1.2.11 or 1.2.12 releases. These options control how the
Creation and LastUpdate timestamps are set on the restored volume.
The 3 values these options can take, and their meanings, are:
=over
=item dump
Use the timestamp from the volume dump file being restored to the
lib/AFS/Command/VOS.pod view on Meta::CPAN
=item new
Set the timestamp to the current time. This is the default behavior
for the Creation timestamp.
=back
Note that the default behavior creates the condition where the
Creation time is newer than the LastUpdate time, and when this is
true, "vos examine" (or any command that display the timestamps in the
volume header, really) will show the Creation time as the LastUpdate
time, presumably because it would be confusing to show the volume as
having been updated before it was created.
Similar to 'vos dump', the 'file' argument is optional to 'vos
restore', but required in this API. There are also three new
arguments: gunzip, bunzip2, and filterin. The analogy with 'vos dump'
is by design entirely symmetrical.
=over
lib/AFS/Command/VOS.pod view on Meta::CPAN
compressed, but lack the proper extension ('.gz' or '.bz2'), or if the
compressed input is being read from stdin, then uncompression must be
specified explicitly.
These arguments have boolean values, since uncompression is either on
or off. They are mutually exclusive as well.
=item filterin
This is an advanced feature, and one that allows the volume dump to be
filtered through any arbitrary number of commands after it is
uncompressed (optionally) and read by vos restore. The value of this
argument is either an ARRAY reference to a list of command line
arguments, suitable for passing to exec(), or an ARRAY or such ARRAYS,
when more than one filter command is being used.
Lacking a better example, let's assume the author's 'newversion'
utility is being applied to the restore process, rather than the dump.
my $result = $vos->restore
(
...
filterin => [ 'newversion' ],
...
);
If there were command line arguments for this command, then they must
be given as follows:
my $result = $vos->restore
(
...
filterin => [ 'newversion', '-arg1', '-value1' ],
...
);
These args are passed directly to exec, with no shell involved. When
more than one command is given, then an ARRAY or ARRAYs must be
specified as follows:
my $result = $vos->restore
(
...
filterin => [
[ 'command1', '-arg1', '-value1' ],
[ 'command2', '-arg2', '-value2' ],
[ 'command3', '-arg3', '-value3' ],
],
...
);
If B<ANY> of the filterin commands exits with a non-zero status, then
the entire restore method invocation is considered to fail. You may
or may not get a valid volume restored to your fileserver, depending
on the behavior of the commands you specify.
=back
=head1 METHODS (with complex return values)
=head2 examine
=over
=item Arguments
lib/AFS/Command/VOS.pod view on Meta::CPAN
associated type in the VLDB entry:
Attributes Values
---------- ------
ronly Numeric Volume ID for the RO volume
backup Numeric Volume ID for the BK volume
rclone Numeric Volume ID for the RClone volume, if present
Note that the 'rclone' attribute is only present if the volume was
actively being cloned while being examined. This is true when a 'vos
release' command is actively updating the RO volumes.
The following methods are available:
Methods Returns
------- -------
getVLDBSites() list of AFS::Object::VLDBSite objects
B<AFS::Object::VLDBSite>
This object is created by parsing the individual VLDB sites in the
lib/AFS/Command/VOS.pod view on Meta::CPAN
The following attributes are always available:
Attributes Values
---------- ------
server Fileserver hostname
partition Fileserver /vice partition name
type "RO" | "RW" | "BK"
status Site status.
Note that the status is the field indicating the state of the volume
during a 'vos release' command, and this will be an empty string for
VLDB entries which are completely in sync.
B<AFS::Object::VolumeHeader>
This object is created by parsing the volume header stanza, such as:
root.afs 536908042 RW 23 K Off-line
npiafa3 /viceph
RWrite 536908042 ROnly 536908046 Backup 0
MaxQuota 0 K
lib/AFS/Command/VOS.pod view on Meta::CPAN
access Volume Last Access date (also in ctime format)
update Volume update date (also in ctime format)
accesses Number of volume accesses since the last reset
rwrite Numeric Volume ID for the RW volume
ronly Numeric Volume ID for the RO volume
backup Numeric Volume ID for the BK volume
rclone Numeric Volume ID for the RClone volume, if present
Note that the 'rclone' attribute is only present if the volume was
actively being cloned while being examined. This is true when a 'vos
release' command is actively updating the RO volumes.
The following attributes are only present if the 'extended' argument
was specified (see below for details on access the raw and author
stats):
Attributes Values
---------- ------
files Number of files in the volume
raw Generic AFS::Object object
author Generic AFS::Object object
lib/AFS/Command/VOS.pod view on Meta::CPAN
=item Return Values
This method returns an AFS::Object::VLDB object, which has a
few attributes, and contains one or more
AFS::Object::VLDBEntry objects, which in turn contain
AFS::Object::VLDBSite objects, as well as their own
attributes.
NOTE: the VLDBEntry and VLDBSite objects are the same as those used by
the 'examine' method, since that command also queries the VLDB for
part of its return values. See that discussion above for some
relevant details on the parsing of those objects, which will no be
repeated here.
my $result = $vos->listvldb
(
cell => $cell,
) || die $vos->errors();
print("VLDB contains " . $result->total() " volumes, " .
lib/AFS/Command/VOS.pod view on Meta::CPAN
To understand the meaning of these various fields (which to the
author's knowledge are not documented anywhere), see the OpenAFS
source code. Some of these values are obvious, or intuitive, but
others are not.
=back
=head1 METHODS (with simple return values)
All of the following commands return a simple Boolean (true/false)
value, if they succeed or fail.
=head2 addsite
The vos help string is:
vos addsite: add a replication site
Usage: vos addsite -server <machine name for new site> -partition <partition name for new site>
-id <volume name or ID> [-cell <cell name>]
[-noauth] [-localauth] [-verbose] [-encrypt]
lib/AFS/Object.pod view on Meta::CPAN
AFS::Object - Base class for encapsulating AFS::Command results
=head1 SYNOPSIS
This module is not used directly by applications, only indirectly
through the return values of the various AFS::Command::* methods.
=head1 DESCRIPTION
This class is used by the various AFS::Command classes to encapsulate
data returned from any command that has structured return values. In
the simplest case, this object just encapsulates a simple list of
key/value pairs. Each distinct key is represented as an object
attribute, and is query-able via one of several methods documented
below.
There are numerous subclasses of this class, which are used when
objects must contains other objects, and these subclasses just
implement special methods for querying the embedded objects.
=head1 METHODS
lib/AFS/Object/Instance.pm view on Meta::CPAN
package AFS::Object::Instance;
use strict;
our @ISA = qw(AFS::Object);
our $VERSION = '1.99';
sub getCommandIndexes {
my $self = shift;
return unless ref $self->{_commands};
return sort keys %{$self->{_commands}};
}
sub getCommands {
my $self = shift;
return unless ref $self->{_commands};
return values %{$self->{_commands}};
}
sub getCommand {
my $self = shift;
my $index = shift;
return unless ref $self->{_commands};
return $self->{_commands}->{$index};
}
sub _addCommand {
my $self = shift;
my $command = shift;
unless ( ref $command && $command->isa("AFS::Object") ) {
$self->_Croak("Invalid argument: must be an AFS::Object object");
}
return $self->{_commands}->{$command->index()} = $command;
}
1;
t/00vos_basic.t view on Meta::CPAN
push(@servers,$server);
push(@partitions,$partition);
}
#
# If the constructor fails, we're doomed.
#
my $vos = AFS::Command::VOS->new
(
command => $binary,
);
if ( ref $vos && $vos->isa("AFS::Command::VOS") ) {
print "ok $TestCounter\n";
$TestCounter++;
} else {
print "not ok $TestCounter..$TestTotal\n";
die "Unable to instantiate AFS::Command::VOS object\n";
}
#
t/00vos_basic.t view on Meta::CPAN
partition => $partition_primary,
name => $volname,
cell => $cell,
);
if ( $result ) {
print "ok $TestCounter\n";
$TestCounter++;
} else {
print "not ok $TestCounter..$TestTotal\n";
die("Unable to create volume '$volname' on server '$server_primary:$partition_primary' " .
"in cell '$cell'\n" . "Errors from vos command:\n" . $vos->errors());
}
#
# Examine it.
#
$result = $vos->examine
(
id => $volname,
cell => $cell,
);
t/01vos_dumprestore.t view on Meta::CPAN
push(@servers,$server);
push(@partitions,$partition);
}
#
# If the constructor fails, we're doomed.
#
my $vos = AFS::Command::VOS->new
(
command => $binary,
);
if ( ref $vos && $vos->isa("AFS::Command::VOS") ) {
print "ok $TestCounter\n";
$TestCounter++;
} else {
print "not ok $TestCounter..$TestTotal\n";
die "Unable to instantiate AFS::Command::VOS object\n";
}
#
t/01vos_dumprestore.t view on Meta::CPAN
name => $volname,
cell => $cell,
);
if ( $result ) {
print "ok $TestCounter\n";
$TestCounter++;
$Volnames{$volname}++;
} else {
print "not ok $TestCounter..$TestTotal\n";
die("Unable to create volume '$volname' on server '$server_primary:$partition_primary'" .
"in cell '$cell'\n" . "Errors from vos command:\n" . $vos->errors());
}
#
# OK, let's create a few dump files, in different ways.
#
# First, a vanilla dump, nothing special.
#
my %files =
(
raw => "$tmproot/$volname.dump",
t/02vos_volserver.t view on Meta::CPAN
push(@servers,$server);
push(@partitions,$partition);
}
#
# If the constructor fails, we're doomed.
#
my $vos = AFS::Command::VOS->new
(
command => $binary,
);
if ( ref $vos && $vos->isa("AFS::Command::VOS") ) {
print "# AFS::Command::VOS->new()\n";
print "ok $TestCounter\n";
$TestCounter++;
} else {
print "not ok $TestCounter..$TestTotal\n";
die "Unable to instantiate AFS::Command::VOS object\n";
}
t/10bos_basic.t view on Meta::CPAN
my $binary = $AFS::Command::Tests::Config{AFS_COMMAND_BINARY_BOS} || 'bos';
exit 0 unless $TestTotal;
#
# First, test the constructor
#
my $bos = AFS::Command::BOS->new
(
command => $binary,
);
if ( ref $bos && $bos->isa("AFS::Command::BOS") ) {
print "ok $TestCounter\n";
$TestCounter++;
} else {
print "not ok $TestCounter..$TestTotal\n";
die "Unable to instantiate AFS::Command::BOS object\n";
}
#
t/10bos_basic.t view on Meta::CPAN
foreach my $attr ( qw(status type startdate startcount) ) {
if ( $instance->$attr() ) {
print "ok $TestCounter\n";
} else {
print "not ok $TestCounter\n";
warn("No attribute '$attr' for instance '$name' from getInstance()\n");
}
$TestCounter++;
}
my @commands = $instance->getCommands();
if ( $#commands == 0 ) {
print "ok $TestCounter\n";
} else {
print "not ok $TestCounter\n";
warn("Instance '$name' has more than one command\n");
}
$TestCounter++;
my $command = $commands[0];
if ( $command->index() == 1 ) {
print "ok $TestCounter\n";
} else {
print "not ok $TestCounter\n";
warn("Command should have index == 1, but has index == " .
$command->index() . "\n");
}
$TestCounter++;
if ( $command->command() =~ /$name/) {
print "ok $TestCounter\n";
} else {
print "not ok $TestCounter\n";
warn("Command should have command attr matching '/$name/', but is " .
$command->command() . "\n");
}
$TestCounter++;
} else {
print "not ok $TestCounter.." . ($TestCounter+7) . "\n";
$TestCounter += 7;
warn("Unable to get instance '$name' from getInstance()\n");
}
}
t/20fs_basic.t view on Meta::CPAN
my $pathbogus = "/this/does/not/exist";
my $binary = $AFS::Command::Tests::Config{AFS_COMMAND_BINARY_FS} || 'fs';
#
# If the constructor fails, we're doomed.
#
my $fs = AFS::Command::FS->new
(
command => $binary,
);
if ( ref $fs && $fs->isa("AFS::Command::FS") ) {
print "ok $TestCounter\n";
$TestCounter++;
} else {
print "not ok $TestCounter..$TestTotal\n";
die "Unable to instantiate AFS::Command::FS object\n";
}
#
t/30pts_basic.t view on Meta::CPAN
die "Missing configuration variable AFS_COMMAND_PTS_USER\n";
};
my $binary = $AFS::Command::Tests::Config{AFS_COMMAND_BINARY_PTS} || 'pts';
#
# If the constructor fails, we're doomed.
#
my $pts = AFS::Command::PTS->new
(
command => $binary,
);
if ( ref $pts && $pts->isa("AFS::Command::PTS") ) {
print "ok $TestCounter\n";
$TestCounter++;
} else {
print "not ok $TestCounter..$TestTotal\n";
die "Unable to instantiate AFS::Command::PTS object\n";
}
#
t/40fs_complex.t view on Meta::CPAN
my %binary =
(
pts => ($AFS::Command::Tests::Config{AFS_COMMAND_BINARY_PTS} || 'pts'),
vos => ($AFS::Command::Tests::Config{AFS_COMMAND_BINARY_VOS} || 'vos'),
fs => ($AFS::Command::Tests::Config{AFS_COMMAND_BINARY_FS} || 'fs'),
);
my $pts = AFS::Command::PTS->new
(
command => $binary{pts},
);
if ( ref $pts && $pts->isa("AFS::Command::PTS") ) {
print "ok $TestCounter\n";
$TestCounter++;
} else {
print "not ok $TestCounter..$TestTotal\n";
die "Unable to instantiate AFS::Command::PTS object\n";
}
my $vos = AFS::Command::VOS->new
(
command => $binary{vos},
);
if ( ref $vos && $vos->isa("AFS::Command::VOS") ) {
print "ok $TestCounter\n";
$TestCounter++;
} else {
print "not ok $TestCounter..$TestTotal\n";
die "Unable to instantiate AFS::Command::VOS object\n";
}
my $fs = AFS::Command::FS->new
(
command => $binary{fs},
);
if ( ref $fs && $fs->isa("AFS::Command::FS") ) {
print "ok $TestCounter\n";
$TestCounter++;
} else {
print "not ok $TestCounter..$TestTotal\n";
die "Unable to instantiate AFS::Command::FS object\n";
}
#
t/40fs_complex.t view on Meta::CPAN
partition => $partition,
name => $volname,
cell => $cell,
);
if ( $result ) {
print "ok $TestCounter\n";
$TestCounter++;
} else {
print "not ok $TestCounter..$TestTotal\n";
die("Unable to create volume '$volname' on server '$server:$partition'" .
"in cell '$cell'\n" . "Errors from vos command:\n" . $vos->errors());
}
#
# Mount it (several different ways)
#
my %mtpath =
(
rw => "$pathafs/$volname-rw",
cell => "$pathafs/$volname-cell",
plain => "$pathafs/$volname-plain",
t/40fs_complex.t view on Meta::CPAN
print "$ok $TestCounter\n";
$TestCounter++;
}
}
}
#
# Sadly, if the localhost is not in the same AFS cell as that being
# tested, the setacl command is guaranteed to fail, because the test
# pts entries will not be defined.
#
# Thus, we use a different, existing pts entry for these tests, and
# not the ones we created above.
#
my %entries =
(
$ptsexisting => 'rlidwk',
);
t/40fs_complex.t view on Meta::CPAN
partition => $partition,
id => $volname,
cell => $cell,
);
if ( $result ) {
print "ok $TestCounter\n";
$TestCounter++;
} else {
print "not ok $TestCounter..$TestTotal\n";
die("Unable to remove volume '$volname' from server '$server:$partition'" .
"in cell '$cell'\n" . "Errors from vos command:\n" . $vos->errors());
}
delete $Volnames{$volname};
END {
#$TestCounter--;
#warn "Total number of tests == $TestCounter\n";
if ( %Volnames ) {
warn("The following temporary volumes were created, and may be left over:\n\t" .
t/99pts_cleanup.t view on Meta::CPAN
die "Missing configuration variable AFS_COMMAND_PTS_USER\n";
};
my $binary = $AFS::Command::Tests::Config{AFS_COMMAND_BINARY_PTS} || 'pts';
#
# If the constructor fails, we're doomed.
#
my $pts = AFS::Command::PTS->new
(
command => $binary,
);
if ( ref $pts && $pts->isa("AFS::Command::PTS") ) {
print "ok $TestCounter\n";
$TestCounter++;
} else {
print "not ok $TestCounter..$TestTotal\n";
die "Unable to instantiate AFS::Command::PTS object\n";
}
my $result = $pts->delete