kAFS Utilities

The kafs-utils suite is a replacement for bos, vos, pts and co. written in v3 Python, utilising AF_RXRPC to provide the transport.

Architecture

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
Protocol AF_RXRPC
socket
OpenAFS lib AuriStor lib kAFS lib OpenAFS lib AuriStor lib
Kernel I/F UDP socket getxattr,
procfs, ...
pioctl, afs syscalls

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 .xg files 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 rpc.RemoteAbort 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 as rpc.AbortBZBUSY. So:

    except rpc.AbortBZBUSY:
    except rpc.BOZOAbort:
    except rpc.RemoteAbort:
will all catch rpc.AbortBZBUSY.

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:

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, the BOZO.ListSUsers RPC call used above might be wrapped thusly:

	  user_list = kafs_rpc.list_bos_super_users(bos_conn)

where 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 calling pioctl, opening a file in /proc and doing an ioctl on it or using some combination of 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 afs and pioctl system calls or some emulation thereof, using the VIOC* command codes.

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:

	  kafs.flush_file(filename)

High-Level Scripting Interface

The high-level scripting interface will provide functions that do the equivalent of standard AFS commands, but rather than parsing 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)

possibly and/or:

	  volume_info = kafs.vos.examine(cell=cell_name, id=volume_name_or_id, extended=False, noauth=True, ...)
	  kafs.display_vol_information(volume_info)

where 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 specified volume.