8.6 Central Printer Management

Now that you've moved your DNS zone data into an LDAP directory, you have the leisure to ask, "What's the big deal?" DNS already has highly effective mechanisms for distributing and replicating zone data; it's not like user account data, which needs to be kept consistent on every machine. So have you accomplished anything, aside from being able to point to a directory server that's serving the zone data to your DNS servers? Clearly, you need to be able to justify the effort you've spent, and to do so, you need to find another application that can make use of the same data.

Network printers are devices that are associated with entries in DNS and possess additional attributes used to support a non-DNS application (i.e., printing). Our next step is to design a directory-based solution for managing printer configuration information that simplifies the process of adding, deploying, and retiring printers. A printer should be accessible to its clients as soon as it has been added to the directory. The namespace shown in Figure 8-8 was designed with this philosophy in mind. All printer configuration information is stored below the ou=printers organizational unit. The immediate three children, config, global, and location, are used to group printers and maintain configuration parameters.

Figure 8-8. LDAP namespace for directory-based storage of printer configuration data
figs/ldap_0808.gif

The config organizational unit sits at the root of the actual configuration tree. Each printer has an entry containing information such as the printer's name and maximum print job size. For network printers, the entry also contains DNS information, such as IP address and hostname. The ou=config,ou=printers,dc=plainjoe,dc=org entry acts as the base suffix for the lp.plainjoe.org DNS zone used by your BIND 9 server. This means that if an administrator removes a printer's entry from the config organizational unit, it is immediately removed by DNS as well. Devices that are physically connected to a host acting as the spooler are not considered network printers for the purposes of this discussion.

Printers listed beneath the ou=global entry should be available to all clients on the network. The entries here contain only a printer's name; the actual configuration data can be accessed by querying for the attributes of cn=printername,ou=config, ou=printers,dc=plainjoe,dc=org. The ou=location tree has a similar function to the global tree. The location organizational unit is a holder for another group of organizational units, one of which is shown in Figure 8-8. Each organizational unit at this level represents a group of printers. Each client on the network can list one or more group names; the clients are then allowed to access the printers in the groups that they have listed.

The major difficulty in dealing with printers is deciding on an acceptable schema for representing printer capabilities and data. Currently, there is no standardized printer schema. The closest we have to a standard is the Internet-Draft draft-fleming-ldap-printer-schema-XX.txt. We only need to implement a subset of the schema in this document (see Figure 8-9).[5] The printer.schema file also includes a modified version of the schema presented in Network Printing, by Todd Radermacher and Matt Gast (O'Reilly). These additional object classes and attributes support the information needed to generate printcap entries for use with the Berkeley print spooler (LPD) or Patrick Powell's LPRng (http://www.lprng.com/).

[5] The printer.schema file is available online at the web site for this book (http://www.oreilly.com/catalog/ldapsa).

The printer.schema file should be viewed as an example only. While it can be used in a production directory, my hope is that a final standardized schema will soon be available and supported by printing vendors.

Figure 8-9. The abstract printerAbstract class, structural printerService class, and auxiliary classes printerLPR, nprintPortPrinterInfo, and nprintNetworkPrinterInfo included in the printer.schema file
figs/ldap_0809.gif

Begin populating the printers organizational unit with a simple network printer named hp2100. The printer-uri attribute was developed by the IETF's Printer Working Group to represent different printing systems such as ipp://, lpr://, etc. All of the printers in your directory will use a printer URI of the form lpr://<printer-name>. This printer also exists as a host in DNS under the name hp2100.lp.plainjoe.org.

Immediately, however, we have a problem: the dNSZone and the printerService objects are both structural classes. Luckily, the default Bind 9 LDAP lookups do not use the objectclass value in searches. Therefore, you can use the extensibleObject class in the place of dNSZone. The other solution would be to define a new auxiliary object class that contained all of the attributes contained in a dNSZone object. I choose to use an extensibleObject to prevent the introduction of new schema items into our discussion.

Now that the object class conflict has been resolved, we can return to our discussion of printer attributes. The printer's domain name is stored in the relativeDomainName and zoneName attributes. The printer-name and nprintHardwareQueueName represent the remote machine (rm) and remote printer (rp) printcap variables.

dn: printer-uri=lpr://hp2100,ou=config,ou=printers,dc=plainjoe,dc=org
aRecord: 192.168.1.220
printer-name: hp2100
nprintHardwareQueueName: raw
printer-uri: lpr://hp2100
relativeDomainName: hp2100
objectClass: printerService
objectClass: nprintNetworkPrinterInfo
objectClass: extensibleObject
printer-job-k-octets-supported: 10000
zoneName: lp.plainjoe.org

You get extra points if you noticed that the nprintDNSName attribute is missing. This attribute doesn't appear because the fully qualified hostname can be determined from the relativeDomainName and zoneName attributes. Because the nprintDNSName serves the same purpose, it can be left out. The script for generating a printcap entry attempts to retrieve the nprintDNSName attribute first; in its absence, the script generates the remote printer's name by concatenating the relativeDomainName and zoneName attribute values.

Your system must be able to represent nonnetworked printers in the same namespace as networked printers. Nonnetworked printers don't have the attributes associated with a dNSZone (replaced by an extensibleObject) that are required to support DNS lookups. Of course, since such an entry describes a nonnetworked device, this detail is of no concern. Here's the LDIF representation of a nonnetworked printer:

dn: printer-uri=lpr://bjc240,ou=config,ou=printers,dc=plainjoe,dc=org
printer-name: bjc240
printer-uri: lpr://bjc240
objectClass: printerService
objectClass: printerLPR
objectClass: nprintPortPrinterInfo
nprintDeviceName: /dev/lp0
printer-aliases: canon

Directory entries that exist below the ou=global and ou=location roots contain only a printer's name. The next two directory entries state that the printer hp2100 is available for all network hosts (because it is in the global organizational unit), and the printer bitsink is available only to clients within the floor-1 group (because it is in the floor-1 group, which is within the location subtree):

dn: printer-name=hp2100,ou=global,ou=printers,dc=plainjoe,dc=org
printer-name: hp2100
objectClass: printerService
      
dn: printer-name=bitsink,ou=floor-1,ou=location,ou=printers,dc=plainjoe,dc=org
printer-name: bitsink
objectClass: printerService

The nprintHostPrinter AUXILIARY object class (see Figure 8-10) allows you to extend an existing entry for a network host to define membership in a printing group, and lets you list any host-specific printers that should be available to users. The entry for workstation queso.plainjoe.org associates it with the floor-1 printing group (i.e., ou=floor-1,ou=location,ou=printers,dc=plainjoe,dc=org) and includes a reference to the specific printer named draft-printer:

Figure 8-10. nprintHostPrinter object class
figs/ldap_0810.gif
dn: relativeDomainName=queso,ou=hosts,dc=plainjoe,dc=org
aRecord: 192.168.1.74
nprintLocation: floor-1
objectClass: dNSZone
objectClass: nprintHostPrinter
relativeDomainName: queso
dNSTTL: 86400
nprintPrinterName: draft-printer
zoneName: plainjoe.org

Finally, you must be able to retrieve information from the directory and format it in a way that is usable by the local printing system. The generate_printcap.pl Perl script supports the printer.schema used in this section and generates printcap files from the directory that are compatible with the BSD printing system. This script supports the common LDAP searching options, such as the directory server's name and base suffix. The script also accepts the hostname of the client to receive the printcap file. Table 8-5 presents the complete set of parameters supported by generate_printcap.pl.

Table 8-5. Options to generate_printcap.pl

Parameter

Default

Description

--base

none

The base suffix used when searching the directory

--debug

off

Enables extra debugging output

--help

 

Summarizes command usage and parameters

--host

$HOSTNAME

The host for which you want to generate the printcap file

--printcap

printcap.$HOSTNAME

The name of the generated printcap file

--server

localhost

The hostname of the LDAP server to query

Figure 8-11. Printing information and entries for the host queso.plainjoe.org
figs/ldap_0811.gif

The following command generates a printcap file for queso.plainjoe.org, given the directory entries represented by Figure 8-11:

$ generate_printcap.pl --host=queso \
 --base="dc=plainjoe,dc=org" \
 --server=ldap.plainjoe.org
      
Searching ou=global,ou=printers,dc=plainjoe,dc=org . . . 
Using entry for "relativeDomainName=queso,ou=hosts,dc=plainjoe,dc=org"
Searching ou=floor-1,ou=location,ou=printers,dc=plainjoe,dc=org . . . 
Finished generating printcap (printcap.queso)

generate_printcap.pl uses an anonymous bind when connecting to the LDAP server. This means that the OpenLDAP server must be configured with the following access control rule, which allows read-only access to information about printers:

access to dn.children="ou=printers,dc=plainjoe,dc=org"
      by * read

The resulting printcap file is:

#
# Printcap file generated automatically on Sun Jan 20 19:33:37 2002 for host queso 
#
######################################################################
# printer-uri=lpr://hp2100,ou=config, ou=printers, dc=plainjoe,dc=org
# objectclass: nprintNetworkPrinterInfo
hp2100:\
     :sh:\
     :mx#10000:\
     :lf=/var/spool/lpd/hp2100/lpd-err:\
     :sd=/var/spool/lpd/hp2100:\
     :lp=/dev/null:\
     :rm=hp2100.lp.plainjoe.org:\
     :rp=raw:
######################################################################
      
      
######################################################################
# printer-uri=lpr://bitsink,ou=config, ou=printers, dc=plainjoe,dc=org
# objectclass: nprintNetworkPrinterInfo
bitsink:\
     :sh:\
     :mx#0:\
     :lf=/var/spool/lpd/bitsink/lpd-err:\
     :sd=/var/spool/lpd/bitsink:\
     :lp=/dev/null:\
     :rm=bitsink.lp.plainjoe.org:\
     :rp=bitsink:
######################################################################
      
      
######################################################################
# printer-uri=lpr://draft-printer,ou=config,ou=printers,dc=plainjoe,dc=org
# objectclass: nprintPortPrinterInfo
draft-printer:\
     :sh:\
     :mx#0:\
     :lf=/var/spool/lpd/draft-printer/lpd-err:\
     :sd=/var/spool/lpd/draft-printer:\
     :lp=/dev/lp0:\
     :sd=/var/spool/lpd/draft-printer:\
     :if=/opt/printers/filters/hpif.sh:
######################################################################

The details of writing Perl scripts such as generate_printcap.pl to manage information in an LDAP directory using the Net::LDAP module will be presented in Chapter 10.

Whenever you're trying to integrate network services into LDAP, remember to focus on reduction of data. Storing information in a directory has no benefit by itself; it is only worthwhile if it decreases the cost of managing the data (i.e., makes life easier for you and the people you work with). If data is used only by a single application on a single host, the information could be kept in a local database file just as easily. However, if the information is needed by several services on multiple hosts, as with user accounts or printer settings, storing the information in an LDAP directory reduces the cost of updating data by ensuring that each change needs to be made only once.