How to use the Amd automounter to provide uniform and easily controlled access to all your file servers from anywhere.
Administrators today manage large sites with many Network File System (NFS) file servers and clients. Users like to be able to log in to any host and access the same files from any remote server. The naïve way of providing this access to all users is to hand mount all file servers on each client. Not only is this unwieldy to manage, but any unreachable server can cause all the clients to hang. Furthermore, users have to know what name to use to access files from certain servers.
The solution to these problems is an automounter. An automounter is configured with the knowledge of all file servers, such that administrators need only maintain the automounter's configuration in one place. Moreover, an automounter provides access to remote file servers on demand when users first try to access a pathname that leads to that server; this ensures that only servers in use and reachable are mounted on clients, minimizing the chances for hangs. Finally, the automounter provides a uniform namespace for the resources. For example, the pathname /src/kernel can hide the fact that the actual location of those files is server1:/n/raid/l/ksrc/v2.4/21preX.
Most commercial vendors and Linux distributions include an automounter. However, such automounters often work on only a single platform, use incompatible configurations or provide a limited feature set. The Amd Automounter, also known as the Berkeley Automounter, was developed with portability and a rich feature set in mind. It runs identically on numerous UNIX systems, provides a superset of features found in other automounters and supports a large set of features for even the most demanding administrator. If you are an administrator of a site with heterogeneous UNIX systems and want to make your life easier, Amd is the way to go.
This article explains how Amd works and provides a set of examples to demonstrate Amd's capabilities. The article is aimed at site administrators and anyone with a perversion for filesystems. We assume basic familiarity with Linux, NFS and filesystems. It is impossible to cover the Amd Automounter in detail in only a few pages; it would take a whole book to do that. Nevertheless, our examples are designed to start with the simpler, more common uses of Amd and gradually increase in complexity.
Amd is a user-level dæmon that attaches itself to a directory, called an automount point. Amd receives requests from the kernel whenever users try to access that automount directory. Amd then ensures the actual resource the user is requesting is available and responds to the kernel's request. Finally, the kernel returns the appropriate status code to the user, and the user magically sees the desired files in the pathname requested.
Let's follow an example. Suppose Amd is started and attaches itself to the directory /src. A user runs ls -l /src/kernel. The kernel knows that the Amd dæmon is the valid file server for the /src directory, so the kernel suspends the user's ls process and sends a message to Amd asking it to resolve the name kernel within the automount point /src. When Amd starts, it loads automounter maps for each automount point. These automounter maps can be read from plain files, NIS/NIS+, LDAP, N/DBM and more. The map that Amd loads for /src is a list of key-value pairs. The key represents the name that Amd should provide within the automounted directory, and the value contains the information that Amd requires to resolve access to the named key. For our example, the map might contain:
kernel type:=nfs;rhost:=server1;\ rfs:=/n/raid/l/ksrc/v2.4/21preX
In this example, the key is kernel and it is separated from its value by whitespace. The value consists of three variable assignments, delimited by a semicolon. We use a back slash as a multiline continuation character. Variables are assigned values using the := Pascal-style syntax. Here, the type variable says the map entry describes an NFS mount. The rhost variable gives the name of the remote NFS server. Finally, the rfs variable describes the pathname of the exported directory on that remote host.
Going back to the suspended ls process, when Amd receives a lookup request from the kernel for the name kernel, Amd consults its map and finds the entry with that name. Amd mounts the /n/raid/l/ksrc/v2.4/21preX directory from the remote host server1. It then returns enough information back to the kernel about the type and actual location of that NFS-mounted directory for the kernel to resume the ls process. The ls process resumes listing the contents of the directory /src/kernel, unaware of the flurry of activity that transpired between Amd and the kernel, let alone where the actual location of the files for /src/kernel is.
Amd also checks to see when was the last time an automounted entry was used and automatically unmounts unused entries. This ensures that a system mounts only entries actively in use. In case you're wondering if you could control the timeout period, yes, you can. In fact, you can control dozens of parameters. Now you're wondering if you have to spend days configuring Amd. No, you don't; most parameters have been tuned carefully with appropriate defaults.
Amd uses a configuration file, often stored in /etc/amd.conf. The syntax of this file is similar to the smb.conf configuration file. Consider:
[global] log_file = /var/log/amd debug_options = all,noreaddir [/net] map_type = file map_name = /etc/amd.net mount_type = nfs [/home] map_type = nis map_name = amd.users mount_type = autofs
This amd.conf file first specifies global options that are applicable to all automounted directories. All options are simple key=value pairs. The first global option (log_file) specifies the pathname to a file for Amd to log information such as errors and trace activity. The second global option (debug_options) asks to turn on all verbose debugging other than the debugging associated with directory-reading operations. Next, we define two automounted directories. Here, Amd attaches and manages the directory /net, the entries for which come from the file /etc/amd.net. Amd also manages a /home automounted directory whose entries are read from the site's NIS (YP) server.
The mount_type parameter requires some background explanation. By default, Amd appears to the kernel as a user-level NFSv2/UDP server. That is, when the kernel has to inform Amd that a user has asked to look up an entry (for example, /src/kernel), the kernel sends RPC messages to Amd, encoding the NFS_LOOKUP request in the same manner that the kernel would contact any other remote NFS server. The only differences here are that Amd is a user-level process not a kernel-based NFS server, and Amd runs on the local host, so the kernel sends its NFS RPCs to 127.0.0.1. As a user-level NFS server, Amd is portable and works the same on every UNIX host. However, user-level NFS servers incur extra context switches and data copies with the kernel, slowing performance. Worse, if the Amd process were to die unexpectedly—which never happens, as our code is 100% bug-free—it can hang every process on the host that accesses an automounted directory, sometimes requiring a system reboot to clear.
A decade ago, Sun Microsystems realized these automounter deficiencies and devised a special in-kernel automounter helper filesystem called Autofs. Autofs provides most of the critical functionality that an automounter needs in the kernel, where the work can be done more reliably and quickly. Autofs often works in conjunction with a user-level automounter whose job is reduced to map lookup and parsing. Amd is flexible enough, as you can see from the above amd.conf example, to work concurrently as both a user-level NFS server and an Autofs-compliant automounter. All you have to do is set the mount_type parameter to the right value. So why not use Autofs all the time? Autofs unfortunately is not available on all operating systems, and on those systems where it is available (Linux, Solaris and a handful of others), it uses incompatible implementations that behave differently. For those reasons, not all administrators like to use Autofs. Nevertheless, with Amd you have the choice of which one to use.
In almost every large site, user home directories are distributed over multiple file servers. Users find it particularly annoying when their home directories first exist in, say, /u/staff/serv1/ezk, and then—when new file servers are installed or data is migrated—the directories are moved to, say, /u/newraid3/ezk. A much better approach is to provide a uniform naming convention for all home directories, such that /home/ezk always points to the most current location of the user's home directory. Administrators could migrate a user's home directory to a new, larger file server and simply change the definition of the ezk entry in the amd.users map. Here's an example of a small amd.users map that mounts three users' home directories from two different servers:
#comment: amd.home map /defaults type:=nfs ezk rhost:=serv1;rfs:=/staffdisk/ezk joe rhost:=raid3;rfs:=/newdisk/joe dan rhost:=raid3;rfs:=/newdisk/dan
This example starts with a special entry called /defaults that defines values common to all entries in the map; here, all mounts in this map are NFS mounts. The subsequent three lines specify the user's name, plus the remote host and partition to mount to resolve the user's home directory. Although the pathname for each user's home directory, such as /home/joe, can remain fixed for a long time, the actual remote host and remote filesystem for Joe's home directory can change often without inconveniencing Joe.
As with Perl, there are several ways in Amd to achieve the same goal, and some ways are better than others. The above map is not the most optimal map for several reasons. So here are a couple of tips for optimizing Amd maps. First, consider what happens when you access /home/dan and are running on host raid3: Amd tries to perform an NFS mount of raid3 (as an NFS client) from raid3 as an NFS server. This is rather silly, going through the entire networking stack and the overhead of the NFS protocol, simply to get to a pathname local to the host. For that reason, Amd defines a different type of mount, a link type (using a symlink). Dan's map entry thus can be rewritten as:
dan -rhost:=raid3;rfs:=/newdisk/dan \ host!=${rhost};type:=nfs \ host==${rhost};type:=link
This revised map entry introduces several new features of Amd maps. First, the back slashes are preceded by whitespace. Amd ignores whitespace after the back slash but not before; Dan's map entry essentially is broken into three distinct whitespace-delimited components called locations. The first location starts with the hyphen character and defines defaults for the map entry itself, overriding anything in /defaults. The second and third locations start with selectors. Amd map selectors are dynamic variables whose values could be compared at runtime by Amd. As you might expect from the mother of all automounters, Amd supports dozens of selectors. Amd evaluates Dan's map entry one location at a time until it finds one for which the selectors evaluate to true; Amd then mounts the given location. In order, Amd first compares whether the current running host's name does not equal the predefined value of rhost. On any host other than raid3, then, Amd performs an NFS-type mount. On raid3 itself, Amd uses a faster and simpler symlink-type mount.
The amd.home map contains a second inefficiency: it mounts /newdisk/joe and /newdisk/dan from the same NFS server, although they most likely are subdirectories of the same physically exported filesystem. This is slow and wastes kernel resources. A better way uses the same rfs but returns pathnames that are subdirectories of actual mountpoints (sublink is appended to returned pathnames automatically):
/defaults type:=nfs;sublink:=${key} joe rhost:=raid3;rfs:=/newdisk dan rhost:=raid3;rfs:=/newdisk
A common use for automounter maps is to allow the mounting of any filesystem from any host, often called a net map. This map entry provides a comprehensive and uniform way of accessing all file servers:
/defaults fs:=/a/${rhost}/root/${rfs} * rhost:=${key};type:=host;rfs:=/
This short example packs a lot of punch. First, we define the default fs variable's value to be a pathname that uniquely identifies the remote NFS server and filesystem being mounted. We can do this because the fs variable defines the pathname on the local host where Amd mounts remote hosts' filesystems. This pathname must be unique to avoid conflicts.
Second, the actual map entry's key, an asterisk, is a wild-card entry that matches anything and sets the key variable's value to the value of the name being looked up inside /net. This wild-card key value becomes the remote host's name (rhost). Next, we specify a special Amd mount entry of the type host, and we set the rfs variable to request mounting of all exported filesystems from that remote host (starting with /). The host mount type in Amd works by querying the remote host's rpc.mountd dæmon for the list of all exported filesystems. It then mounts each of them in turn. For example, suppose host foo exports two filesystems named /homes and /proj/X11. If you chdir to /net/foo, Amd mounts foo:/homes in /a/foo/root/home and mounts foo:/proj/X11 in /a/foo/root/proj/X11. An ls in /net/foo conveniently shows these two mounted directories.
In large sites with many subgroups (sometimes with partial administrative control over the main group), there often are scenarios in which what you can mount depends on where you are. For efficiency, you may be limited on distributing binaries for different architectures to different subnets. With a single centralized set of Amd maps, it is possible to accommodate local needs:
/defaults type:=link lbin in_network(eng);fs:=/local/${arch}/bin \ in_network(10.0.1.0/24);fs:=/x/beta/bin
This map uses the in_network function selector. Function selectors evaluate to true or false based on the current system conditions and the parameters passed to these functions. This selector compares whether the current hostname is part of the eng network, often defined in /etc/networks. If so, Amd expands the value of the arch variable to the current running architecture. It also resolves the lbin entry to /local/i386/bin on IA-32 systems, to /local/sparc/bin on SPARC systems and so on. The next location in this map shows that the in_network selector can match against network/netmask pairs. In fact, not only can this selector match using several forms, but it can match against any local interface up and running on your host. This capability offers the benefit of optimizing network routes on multihome hosts.
Many people keep ISO-9660 CD-ROM images handy, but accessing their content requires burning them onto a CD-ROM or mounting the images on Linux using a special loop driver. The cdfs mount type knows how to mount ISO-9660 CDs. But if you list a filename in the dev parameter and specify the loop mount option, Amd can mount those ISO image files directly. You then can browse them without copying the files out of each ISO image:
/defaults type:=cdfs;opts:=loop shrike1 dev:=/iso/rh9/shrike-disc1.iso shrike2 dev:=/iso/rh9/shrike-disc2.iso shrike3 dev:=/iso/rh9/shrike-disc3.iso
Every self-respecting tool should have built-in extensibility mechanisms to accommodate the unexpected. For Amd to mount a given filesystem, it has to know how to mount it ahead of time (read: yours truly hacks the C sources). Using the program type mounts, you can define a custom method for mounting and unmounting any filesystem your native host knows about but which Amd doesn't:
r2 type:=program;dev:=/dev/sda1;\ mount:="/sbin/dohans dohans ${dev} ${fs}";\ unmount:="/sbin/undohans undohans ${fs}"
The above example passes the predefined dev parameter, as well as the automatically determined fs parameter to, say, a shell script named dohans that can perform whatever is needed to mount /dev/sda1 as a ReiserFS.
The Amd automounter is a powerful tool with many features to support numerous sites' needs. With proper care, site administrators can provide many useful features to users while reducing system administration efforts. Amd is part of the Am-utils distribution, which comes prebuilt with most Linux distributions. For more information, the latest sources, access to mailing lists and on-line documentation, visit www.am-utils.org. Happy automounting.