Amanda::Device - interact with Amanda data-storage devices
use Amanda::Device qw( :constants );
my $dev = Amanda::Device->new($device_name);
if ($dev->read_label() == $DEVICE_STATUS_SUCCESS) {
print "Label on $device_name is '$dev->volume_label'\n";
}
A volume is a container for data which can be "loaded" into a particular device. For tape devices, a volume is a tape, but most other devices do not deal with such physical objects. Each volume has a volume header giving, among other things, the label of the volume and the timestamp on which it was written. The header may also indicate that the volume is not an Amanda volume. Aside from the header, a volume contains a sequence of files, numbered starting at 1. While writing, devices number files sequentially, but devices that support partial volume recycling may have "holes" in the sequence of file numbers where files have been deleted. The seek_file
method, below, describes how the API represents this situation. Each file has a header, too, which contains lots of information about the file. See Amanda::Header for the full list. After the header, a file is just a sequence of bytes.
Reads and writes to devices take place in blocks. Unlike a typical operating-system file, in which any block boundaries are lost after the file is written, devices must be read back with the block sizes that were used to read. See amanda-devices(7)
for more in block sizes, and the read_block and write_block sections, below, for more information.
The Device API is object-oriented, so the first task in using the API is to make a Device object:
$dev = Amanda::Device->new("tape:/dev/nst0");
This function takes a device name (possibly a device alias) and returns a device object. This function always returns a Device, although it may be a Null device with an error condition. Any new
call should be followed by a check of the device's status:
$dev = Amanda::Device->new($device_name);
if ($dev->status() != $Amanda::Device::DEVICE_STATUS_SUCCESS) {
die "Could not open '$device_name': " . $dev->error();
}
This function does not access the underlying hardware or any other external systems in any way: that doesn't happen until read_label
or start
. An Amanda configuration must be loaded when this function is called, as it searches the configuation for device definitions. The member variable device_name
is set when this function has returned.
It is unusual for higher-level code to call Amanda::Device->new
. Intead, use Amanda::Changer to load a volume and reserve access to it; the resulting reservation will contain an already-created Device object.
While Amanda proivdes multiple implementations of the Device class, they are not distinguishable via the usual Perl methods (ref
or $dev->isa
).
Device users generally call device methods in the following order for reading:
read_label (optional)
start
init_seek_file (optional)
seek_file (optional)
read_block (repeated)
or, when writing or appending:
read_label (optional)
start
start_file
write_block (repeated)
finish_file
finish
To create a new RAIT device from a collection of device objects, call Amanda::Device->new_rait_from_children($child1, $child2, ..)
. If one of the child objects is undef
, the resulting RAIT device will operate in degraded mode.
Device methods return a particular value to signal the presence of an error condition. In many cases, this is simply false (exceptions are listed below).
When a device signals an error, $dev->status
and $dev->error
contain details of what went wrong. Status is a bitfield where each bit that is set indicates a possible problem. Unfortunately, some devices are unable to distinguish states due to limitations of an external system. For example, the tape device often cannot distinguish an empty drive ($DEVICE_STATUS_VOLUME_MISSING
) from a hard device error ($DEVICE_STATUS_DEVICE_ERROR
), leading to the status $DEVICE_STATUS_VOLUME_MISSING
|$DEVICE_STATUS_DEVICE_ERROR
. To be clear: as few as one of the status bits may represent a actual problem. If $DEVICE_STATUS_VOLUME_UNLABELED
is set along with other bits, it is not safe to assume that an unlabeled volume is available. However, if the bit is not set, then it is safe to assume there is no unlabeled volume present.
$DEVICE_STATUS_SUCCESS
All OK (no bits set)
$DEVICE_STATUS_DEVICE_ERROR
The device is in an unresolvable error state, and further retries are unlikely to change the status
$DEVICE_STATUS_DEVICE_BUSY
The device is in use, and should be retried later
$DEVICE_STATUS_VOLUME_MISSING
The device itself is OK, but has no media loaded. This may change if media is loaded by the user or a changer
$DEVICE_STATUS_VOLUME_UNLABELED
The device is OK and media is laoded, but there is no Amanda header or an invalid header on the media.
$DEVICE_STATUS_VOLUME_ERROR
The device is OK, but there was an unresolvable error loading the header from the media, so subsequent reads or writes will probably fail.
At the risk of being repetitive, never test a device's status with ==
, unless it is to $DEVICE_STATUS_SUCCESS
. Furthermore, never try to parse the device error messages -- they are only for user consumption, and may differ from device to device.
In addition to the status bitfield, a Device also provides a user-comprehensible error message, available from the methods error
(returning the error message), status_error
(returning the string form of the status), or error_or_status
(returning the error message if one is set, otherwise the string form of the status). None of these functions will ever return undef
.
Device properties provide a bidirectional means of communication between devices and their users. A device provides values for some properties, which other parts of Amanda can use to adjust their behavior to suit the device. For example, Amanda will only attempt to append to a volume if the device's properties indicate that it supports this activity. Some devices have additional properties that can be set to control its activity. For example, the S3 Device requires that the users' keys be given via properties.
See amanda-devices(7)
for more information on device properties and their meanings.
The methods property_get
and property_set
are used to get and set properties, respectively. If the indicated property simply does not exist, these functions return an error indication (FALSE), but the device's status remains $DEVICE_STATUS_SUCCESS
. If a more serious error occurs, then the device's status is set appropriately.
Device properties are easy to handle, as the Perl-to-C glue takes care of all necessary type conversions:
$success = $device->property_set("BLOCK_SIZE", $blocksize);
$blocksize = $device->property_get("BLOCK_SIZE");
If property_get
is called in an array context, it returns the property value, its surety, and its source, in that order. If there is an error fetching the property, property_get
returns undef
.
The property_list()
method returns a list of all properties:
my @props = $device->property_list();
its return is an array of hashes:
( { 'access' => $access_flags,
'name' => $property_name,
'description' => $property_description },
...
)
All properties have a source - where the value came from - and surety - a level of confidence in the value. This can be used to decide which of two potentially contradictory properties to believe. For example, the RAIT device examines the source and surety of child devices' block sizes, prefering properties set by the user ($PROPERTY_SOURCE_USER
) over others.
Set a property's source and surety with property_set_ex
: $dev->property_set_ex("my_prop", 13, $PROPERTY_SURETY_BAD, $PROPERTY_SOURCE_DEFAULT); The surety and source are returned after the property value in list context: my ($val, $sur, $sou) = $dev->property_get("my_prop");
The available sureties are:
$PROPERTY_SURETY_GOOD
$PROPERTY_SURETY_BAD
and the sources are:
$PROPERTY_SOURCE_DEFAULT
$PROPERTY_SOURCE_DETECTED
$PROPERTY_SOURCE_USER
Some devices can perform more than one operation simultaneously, while others are more limited. For example, a tape device is exclusive to a single process while it is in use, while a VFS device can support concurrent reads and writes on the same volume.
As of this writing, device locking is not correctly implemented in many devices; consult the source code and check with the Amanda developers before depending on concurrent operation of devices.
When writing to a volume, an EOM (end-of-media) condition occurs when no more space is available on the volume. Some devices (currently only those supporting DirectTCP) distinguish a logical EOM (LEOM) from a physical EOM (PEOM). The logical EOM comes some distance before the physical EOM, with enough space left to finish a data block and write any additional bookkeeping data before PEOM.
In such devices, the is_eom
attribute is set once LEOM is detected. Such detection can happen in any method that writes to the volume, including start
, start_file
, finish_file
, and finish
. API users that understand LEOM should take this as a signal to complete writing to the device and move on before hitting PEOM.
Devices which do not support LEOM simply return a VOLUME_ERROR when the volume is full. If this occurs during a write_block
operation, then the volume may or may not contain the block - the situation is indeterminate.
Devices indicate their support for LEOM with the LEOM property.
Some device types have a "locking" mechanism that prevents other parts of the system from accessing the underlying resource while an operation is in progress. For example, a typical UNIX tape driver cannot be opened by two processes at once.
Amanda Devices will lock the underlying resource when start
or read_label
is called, and unlock the resource either when the Device object is garbage-collected or in the finish
method. Thus in a calling sequence such as
read_label
start
init_seek_file
seek_file
...
finish
the underlying resource remains locked for the entire sequence, even between read_label and finish.
It is unwise to rely on Perl's garbage-collection to automatically release resources. Instead, always explicitly release resources with a finish
call. The Changer API is careful to do this in its release
method.
All member variables are implemented using accessor methods, rather than the more common hashref technique. Use
print $dev->device_name, "\n";
instead of
print $dev->{'device_name'}, "\n";
The member variables are:
file
the current file number, if any
block
the current block number, if any
in_file
true if the device is in the middle of reading or writing a file
device_name
the name with which the device was constructed; note that this is not set until after open_device is finished -- it is an error to access this variable in an open_device implementation
access_mode
the current access mode ($ACCESS_NULL
, or that supplied to start)
is_eof
true if an EOF occurred while reading; also used by write_from_connection
is_eom
true if a write operation reached the end of the volume (end-of-medium)
volume_label
the label of the current volume, set by start and read_label
volume_time
the timestamp of the current volume, set by start and read_label
volume_header
the header of the current volume, set by read_label
status
the device's error status (bit flags) as an integer
status_error
the device's error status (bit flags) as a string
error
the device's error message
error_or_status
the device's error message, if set, otherwise the same as status_error
-- use this to display error messages from devices
block_size
the device's currently configured block size. This is also available via the BLOCK_SIZE property. Writers should use block_size-byte blocks, and readers should initially use block_size, and expand buffers as directed by read_block
.
min_block_size
minimum allowed block size for this device
max_block_size
maximum allowed block size for this device
$dev->configure(1);
Once you have a new device, you should configure it. This sets properties on the device based on the user's configuation. If $use_global_config
is true, then any global device_property
parameters are processed, along with tapetype and other relevant parameters. Otherwise, only parameters from the device definition (if the device was opened via an alias) are processed.
This method is deprecated. All access to Devices should be via the Changer API (see Amanda::Changer), which implements its own, more advanced method of configuring devices. The configure
method may be removed in a future release.
$status = $dev->read_label();
This function reads the tape header of the current volume, returning the Device's status (see "Error Handling", above). Since this is often the first function to accses the underlying hardware, its error status is the one most often reported to the user. In fact, amdevcheck(8)
is little more than a wrapper around read_label.
The method sets the following member variables:
volume_header
if any header data was read from the volume, it is represented here. The header's type may be F_WEIRD if the header was not recognized by Amanda.
volume_label
if read_label read the header successfully, then volume_label contains the label
volume_time
smililarly, if read_label read the header successfully, then volume_time contains the timestamp from the header
$succss = $dev->start($ACCESS_WRITE, $label, $timestamp);
Start starts the device and prepares it for the use described by its second parameter. This function can be called regardless of whether read_label
has already been called.
If the access mode is $ACCESS_WRITE
, then the label and timestamp must be supplied (although leaving the timestamp undef will use the current time), and they will be used to write a new volume header. Otherwise, these parameters should be undef.
On completion, start leaves the device's access_mode
, volume_label
and volume_time
member variables set, by reading the tape header if necessary. Note that in mode $ACCESS_APPEND
, the file
member variable is not set until after start_file
has been called.
$success = $dev->start_file($header);
This method prepares the device to write data into a file, beginning by writing the supplied header to the volume. On successful completion, the device's file
is set to the current file number, block
is zero, and in_file
is true. If the volume is out of space, the is_eom
member is set to true and the method returns false with status DEVICE_STATUS_VOLUME_ERROR
.
# (not available from Perl)
success = device_write_block(dev, blocksize, buf);
This method writes a single block of data to the volume. It is only available from C -- Perl code should not be handling raw data, as it is far too slow. Use the transfer architecture (Amanda::Xfer) for that purpose.
The blocksize
must be the device's block size, unless this is a short write. A short write must be the last block of a file. Some devices will zero-pad a short write to a full blocksize. This method returns false on error. If the volume is out of space, is_eom
is set and the method returns false with status DEVICE_STATUS_VOLUME_ERROR
. Note that not all devices can differentiate an EOM condition from other errors; these devices will set is_eom
whenever the situation is ambiguous.
This function ensures that block
is correct on exit. Even in an error condition, it does not finish the current file for the caller.
$success = $dev->finish_file();
Once an entire file has been written, finish_file performs any cleanup required on the volume, such as writing filemarks. On exit, in_file
is false. If the device runs out of space while finishing (e.g., the filemark does not fit), then this method returns false with status DEVICE_STATUS_VOLUME_ERROR
and is_eom
is set.
This function should not be used while reading -- instead, just seek to the next file.
$dev->init_seek_file($fileno);
Lauch file retrieval, so they are ready when seek_file
is called. Useful lonly with the glacier device.
$header = $dev->seek_file($fileno);
In $ACCESS_READ
, seek_file
sets up the device to read from file $fileno
. This function is not available in $ACCESS_WRITE
and $ACCESS_APPEND
. It returns the header from the requested file on success, or undef on error.
If the requested file doesn't exist, as might happen when a volume has had files recycled, then seek_file
will seek to the next file that does exist. The file this function selected is indicated by the file
member variable on exit. If the requested file number is exactly one more than the last valid file, this function returns a $F_TAPEEND
header.
As an example, on a volume with only files 1 and 3:
$dev->seek_file(1) returns header for file 1, $dev->file == 1
$dev->seek_file(2) returns header for file 3, $dev->file == 3
$dev->seek_file(3) returns header for file 3, $dev->file == 3
$dev->seek_file(4) returns a tapend header, $dev->file == 4
$dev->seek_file(5) returns NULL/undef
On exit, is_eof
is false, in_file
is true unless no file was found (tapeend or NULL), file
is the discovered file, and block
is zero.
$success = $dev->seek_block($block);
After seeking to a file, the caller can optionally seek to a particular block in the file. This function will set block
appropriately. Note that it may not be possible to detect EOF, so this function may fail to set is_eof
even though a subsequent read_block
will return no data.
# (not available from Perl)
bytes_read = device_read_block(dev, buffer, *blocksize);
This method is the complement of write_block
, and reads the next block from the device, or returns -1 on error. Pass a buffer and its size. If the buffer is not big enough, no read is performed, the parameter blocksize
is set to the required blocksize, and the method returns 0. As a special case, passing a NULL
buffer and *blocksize == 0
is treated as a request for the required block size. It is not an error to pass a buffer that is too large (and, in fact, this is precisely the effect of setting the read_block_size
configuration parameter).
On EOF, this method returns -1, but sets is_eof
and leaves the device's status set to $DEVICE_STATUS_SUCCESS
. Some devices may be able to detect EOF while reading the last block, and will set is_eof
at that time. Others must wait for the next read to fail. It is never an error to call read_block
after an EOF, so there is no need to check is_eof
except when read_block
returns -1.
$success = $dev->finish();
This undoes the effects of start, returning the device to a neutral state ($ACCESS_NULL
). It will also release any resources acquired by read_label
, even if start
was not called. After finish
, it is not an error to call start
again, even with a different mode.
$success = $dev->recycle_file(fileno);
On devices that support it, this removes the indicated file from the volume, presumably freeing its space to be used for other files. File numbers of existing files will not change, so this operation may leave "holes" in the sequence of file numbers. See seek_file
to see how this is handled.
This method cannot be called while in a file, nor while in $ACCESS_READ
mode.
$success = $dev->erase(fileno);
On devices that support it, this erases all data from the volume, presumably freeing the space. This method must be called before start and after finish -- that is, while the device is in a neutral state ($ACCESS_NULL
). You can detect whether or not this operation is supported using the full_deletion
property.
$success = $dev->eject();
On devices that support it, this eject the volume. This method can be called before start and after finish.
$supp = $dev->directtcp_supported();
This method returns TRUE if the DirectTCP-related methods (listen
, accept
, write_from_connection
, and read_to_connection
) are implemented by this device.
$addrs = $dev->listen($for_writing);
The listen
method starts the device listening for an incoming DirectTCP connection. The method returns a set of IP:PORT pairs to which a TCP connection can be made. The boolean for_writing
is TRUE if this connection will be used to write to the device.
This method can be called at any time, but between the time listen
is called and when accept
returns, no other methods of the device should be called.
The return value might look like:
$addrs = [ [ "127.0.0.1", 9382 ] ]
In C, the memory for these addresses remains the responsibility of the device, and will remain unchanged until accept
returns.
$conn = $dev->accept();
This method accepts a connection to one of the addresses returned by listen
, returning an established DirectTCPConnection object (see below). It returns undef
on failure. Note that this method may block indefinitely if no connection ever occurs. The C implementation returns an already-referenced connection object, so the caller should call g_object_unref
when the connection is no longer needed.
$conn = $dev->connect($for_writing, $addrs);
This method initiates a connection to one of the addresses in $addrs
, returning an established DirectTCPConnection object (see below). The $for_writing
parameter is TRUE if the connection will be used to write to the device. It returns undef
on failure. Note that this method may block indefinitely if no connection ever occurs. The C implementation returns an already-referenced connection object, so the caller should call g_object_unref
when the connection is no longer needed.
my $ok = $dev->use_connection($conn);
Call this method to use a DirectTCPConnection object created with another device. The method must be called before the device is started (so access_mode
is $ACCESS_NULL
), as some devices cannot support switching connections without rewinding. Any subsequent read_to_connection
or write_from_connection
calls will use this connection.
($ok, $actual_size) = $dev->write_from_connection($size);
This method reads data from the DirectTCPConnection specified with use_connection
or returned from accept
or connect
and writes it to the volume. It writes at most $size
bytes, and returns the number of bytes written in $actual_size
. If $size
is zero, it will write until EOF, EOM, or a device error. On error, $ok
is false.
When an EOF is received over the connection, signalling the end of the data stream, then this method returns without error ($ok
is true), with $actual_size
indicating the number of bytes written to the device (which may be zero). In this case, the is_eof
attribute is true on return.
Similarly, when the device encounters logical EOM in this method, it returns the total bytes transferred in $actual_size
, with $ok
true, and the is_eom
attribute true. No data is lost. If writes continue until physical EOM, data may be lost.
($ok, $actual_size) = $dev->read_to_connection($size);
This method is similar to write_from_connection
but the data flows in the opposite direction. It reads at most $size
bytes, and returns the total number of bytes read in $actual_size
.
When the method encounters an EOF, it stops early and returns successfully with the number of bytes actually read (which may be zero).
Get a property value, where the property is specified by name. See "Properties", above.
Set a simple property value. See "Properties", above.
Set a property value with surety and source. See "Properties", above.
This module defines a large number of constant scalars. These constants are available from the package namespace (e.g., $Amanda::Device::ACCESS_WRITE
), or imported with the :constant
import tag.
The accept
and connect
methods return an object to represent the ongoing DirectTCP connection. This object is mostly useful as a "token" to be passed to write_from_connection
and read_to_connection
. In particular, a connection created by one device can be used with another device; this is how DirectTCP dumps are spanned over multiple volumes.
The class does have one critical method, though:
$conn->close();
This method closes the connection, releasing all resources allocated to it. It can be called at any time, whether the remote side has closed the connection already or not.
This page was automatically generated Tue Mar 19 07:08:16 2019 from the Amanda source tree, and documents the most recent development version of Amanda. For documentation specific to the version of Amanda on your system, use the 'perldoc' command.