suite is a replacement
co. written in v3 Python, utilising AF_RXRPC to provide the
The utilities are intended to have the following internal architecture:
|Application||AFS standard tools||User python scripting|
|Layer 4||High-level scripting interface|
|Layer 3||RPC abstraction wrappers||AFS filesystem query interface|
|Layer 2||Autogenerated RPC calls|
|Layer 1||Pluggable transport interface||Pluggable query interface|
|OpenAFS lib||AuriStor lib||kAFS lib||OpenAFS lib||AuriStor lib|
|Kernel I/F||UDP socket||
Pluggable Transport Interface
The scripting Python module will not actually have an rx protocol implementation linked in, but will rather load one dynamically. This allows third-party rx stacks to be used with the Python scripting framework. The transport module will need to provide a table of functions the framework to call.
These functions allow the framework to set up and tear down connections, security descriptors and calls, and allow data to be sent and received.
The code is still in a state of flux.
Autogenerated RPC Calls
As part of building the python module, the
that describe the various AFS RPC calls are processed to produce
method functions and constant definitions corresponding the
definitions in the protocol specification files.
Fundamental types, such as integers, strings and opaques, are translated to/from Python objects that represent equivalent data. Arrays are translated into Python sequences.
Constructed data types are translated into Python object classes and these are translated to/from raw data when the code is marshalled/unmarshalled. Generated Python object classes have members representing the members of the constructed type in the protocol.
If a call is aborted by the peer, the Python module will raise an appropriate exception that can be caught individually, for example:
try: ret = rpc.BOZO_DeleteBnode(bos_conn, i) except rpc.AbortBZBUSY: error("can't delete running instance '", i, "'\n")
All raised objects are derived from the
class and so can be caught as a group through that. There is also an
intermediate class defined for each package containing abort codes,
so, for example, there's an
rpc.BOZOAbort class that can
be caught for all the bos server aborts, such
will all catchexcept rpc.AbortBZBUSY: except rpc.BOZOAbort: except rpc.RemoteAbort:
Each function returns a Python object that contains the reply data as named members, for example:
users = "" try: i = 0 while True: ret = rpc.BOZO_ListSUsers(bos_conn, i) i += 1 users += " " + ret.name except rpc.RemoteAbort as msg: if str(msg) == "Aborted 1": pass else: raise
RPC Abstraction Wrappers
The higher level functions don't really want to call the autogenerated Python module directly for a number of reasons:
Many of the RPC calls in the AFS definition files have multiple
- Some have different options available in the parameters.
- Some return extra data.
- Some return data in linked-list form rather than array form (which shouldn't be a protocol distinction at all, but rather an interface distinction).
- AuriStor's service-upgrade capability provides the ability to have RPC calls that can pass data that can't be supported in the basic AFS definitions (IPv6 addresses, for example).
- The autogenerated interface is slightly tricky to use in the way it returns values.
Some RPC calls (such as
BOZO.LISTSUsers) have to be called multiple times to enumerate a set of objects, and give an abort when done.
The higher level stuff really want all of this hidden from it. It
wants to set out what it wants from a call and let the middle layer
sort out how to achieve that. So, for example,
BOZO.ListSUsers RPC call used above might be wrapped
user_list = kafs_rpc.list_bos_super_users(bos_conn)
user_list is a sequence of strings, each of which
is a user name.
Pluggable Query Interface
The scripting Python module will also not have a method of communicating directly with a running filesystem linked in, but rather will load this dynamically as well. This will allow third-party filesystems to be queried, possibly on the basis of just which one is behind the targetted file if it becomes possible to run different AFS filesystems side-by-side.
The method loaded will have to hide the exact method of communicating
with the kernel inside of itself, whether that involves
pioctl, opening a file in
ioctl on it or using some combination
getxattr and other calls.
AFS Filesystem Query Interface
The AFS filesystem running on the local machine can be asked to
provide information, both about specific objects and more generally,
and to manipulate specific object state and more generally. This is
typically done using the
system calls or some emulation thereof, using
Some of the utilities in the suite need access to information the filesystem has available, so the query interface provides a high-level view on this. For instance:
(vol, fileid, uniquifier) = kafs.get_fid_of_file(filename)
They also need to talk to the cache manager associated with the AFS filesystem in order to manipulate things. For instance:
High-Level Scripting Interface
The high-level scripting interface will provide functions that do the
equivalent of standard AFS commands, but rather than
argv, the arguments will be passed to the
function as predigested Python objects of the appropriate types and
the output will come back as Python objects rather than being printed
to the standard output.
So, for example, the function to examine a volume might look like:
cell_handle = kafs.get_cell_handle(cell_name, auth_handle) call_handle->set_encryption(principal_name) volume_info = kafs.examine_volume(cell_handle, volume_name_or_id, extended=False)
volume_info = kafs.vos.examine(cell=cell_name, id=volume_name_or_id, extended=False, noauth=True, ...) kafs.display_vol_information(volume_info)
cell_handle is a Python object that represents a
cell, including caches for volume and protection data, and
volume_info is the looked-up information about the