Skip Navigation Links | |
Exit Print View | |
Remote Administration Daemon Developer Guide Oracle Solaris 11.1 Information Library |
This section contains specific guidance on how to use rad.
Designing a rad API requires judgement and the application of domain knowledge.
The users of the API fall into two broad categories:
Administrators
Developers
Unfortunately, accommodating the desires of consumers in these two categories within one interface is difficult. The first group desire task-based APIs which match directly onto well-understood and defined administrative activities. The second group desire detailed, operation-based interfaces which may be aggregated to better support unusual or niche administrative activities.
For any given subsystem, you can view existing command-line utilities (CLIs) and libraries (APIs) as expressions of the rad APIs which are required. The CLIs represent the task-based administrative interfaces and the APIs represent the operation-based developer interfaces.
The goal in using rad is to provide interfaces that address the lowest-level objectives of the target audience. If targeting administrators (task-based), this effort could translate to matching existing CLIs. If targeting developers, this effort could mean significantly less aggregation of the lower-level APIs.
Many subsystems present incomplete interfaces to the world. Some CLIs contain processing capabilities that are not accessible from an existing API. This situation is another motivation for providing task-based administrative interfaces before introducing more detailed interfaces.
Such constraints must be considered in the rad API design. Consider migrating functionality from the CLI into the API to facilitate the creation of the new interface. Also consider presenting an interface which wraps the CLI and takes advantage of the existing functionality. Do not simply duplicate the functionality in the new rad interface, which would introduce redundancy and significantly increase maintenance complexity. One particular area where rad interface developers need to be careful is to avoid duplication around parameter checking and transformation. This duplication is likely to be a sign that existing CLI functionality should be migrated to an API.
rad modules must be written in C. Some subsystems, for instance, those written in other languages, have no mechanism for a C module to access API functionality. In these cases, rad module creators must access whatever functionality is available in the CLI or make a potentially significant engineering effort to access the existing functionality, for example, rewriting existing code in C, embedding a language interpreter in their C module, and the like.
Designing a rad interface is very similar to designing a library interface. The same general principles of design apply: be conservative, start small, consider evolutionary paths and carefully consider commitment levels.
Once an interface is established, the use of versioning and considered, incremental improvements will expand the functionality.
This section presents specific design advice on the most significant components of a rad module. Naming is addressed separately in Naming Guidelines
APIs are the primary deliverable of a rad module. They are a grouping of interfaces, events, methods and properties which enable a user to interact with a subsystem.
When exposing the elements of a subsystem consider carefully how existing functions can be grouped together to form an interface. Imperative languages, such as C, tend to pass structures as the first argument to functions, which provides a clear indicator as to how best to group functions into APIs.
Methods provide mechanisms for examining and modifying administrative state.
Consider grouping together existing native APIs into aggregated rad functions which enable higher order operations to be exposed.
Follow established good practice for RPC style development. rad is primarily for remote administration, and avoiding excessive network load is good practice.
Make sure to define an <error> element with properties which can be modified.
The module is responsible for providing a sequence number. Monotonically increasing sequence numbers are recommended for use, since these will be of most potential use to any clients.
Consider providing mechanisms for allowing a client to throttle event generation.
Carefully design event payloads to minimize network load.
Don't try to replicate the functionality of network monitoring protocols such as SNMP.
Judicious use of the is_proxy variable enables you to control where a module is loaded for execution: in the rad proxy or in a slave process.
A module should, by default, be loaded into a slave process unless the following conditions apply:
Module performs self-authentication
Module is very simple and cannot fail fatally
All method invocations in rad are synchronous. Asynchronous behavior can be obtained by adopting a design pattern that relies on the use of events to provide notifications. Refer to Synchronization for more details.
Do not duplicate code from existing CLIs. Instead, consider moving common code into a lower library layer that can be shared by rad and the CLI.
rad modules are designed to have a language agnostic interface. However, you might want to provide additional language support through the delivery of a language-specific extension. This type of deliverables should be restricted in use. The main reason for their existence is to help improve the fit of an interface into a language idiom.
When naming an API, interface, or object, module developers have broad leeway to choose names that make sense for their modules. However, some conventions can help avoid pitfalls that might arise when retrieving objects from the rad server.
The domain portion of rad object names follows a reverse-dotted naming convention that prevents collisions in rad's flat object namespace. This convention typically resembles a Java package naming scheme:
com.oracle.solaris.rad.zfs com.oracle.solaris.rad.zonesmgt com.oracle.solaris.rad.usermgt org.opensolaris.os.rad.ips ...
To distinguish a rad API from a native API designed and implemented for a specific language, include a "rad." component in the API name.
With the goal of storing objects with names consumers would expect, APIs, and the domains of the objects defined within them, should share the same name. This practice makes the mapping between the two easily identifiable by both the module consumer and module developer.
With the same goal of simplicity, identifying an interface object is made easier by adhering to a "type=interface" convention within the object name.
Applying both conventions, a typical API will look like the following example.
<api xmlns="http://xmlns.oracle.com/radadr" name="com.oracle.solaris.rad.zfs"> <interface name="ZPool"> <summary> zpool administration </summary> ...
Within the module, the API appears as follows:
int _rad_init(void *handle) { ... adr_name_t *name = adr_name_fromstr("com.oracle.solaris.rad.zfs:type=ZPool"); (void) cont_insert_singleton(rad_container, name, &interface_ZPool_svr);
On the consumer side (Python), the API appears as follows:
import rad.client import rad.util # Create a connection radconn = rad.util.connect_unix() # Retrieve a ZPool object zpool_name = rad.client.Name("com.oracle.solaris.rad.zfs", [("type", "ZPool")]) zpool = radconn.get_object(zpool_name)
Using this naming convention also precludes the need to specify a pragma to identify a package when generating Java interfaces because radadrgen(1) uses the API name as the Java package name by default.
In an effort to normalize the appearance of like items across development boundaries, and to minimize the awkwardness in generated language-specific interfaces, several case strategies have been informally adopted.
|
Though ADR is language-neutral, certain environments might have conventions that place additional constraints on interface design.
Currently known language-specific constraints:
JMX: Interface-defined method names that resemble derived method names
A JMX MXBean Proxy has a single namespace for both methods defined by the interface, and derived methods used for accessing attributes defined by the interface. If a method defined by the interface has a name and signature that is consistent with the JavaBeans-style naming of a derived attribute-access method, then the Proxy will assume calls to it are attempts to access a foo attribute on the JMX object and will fail. For example:
public void setFoo(String s); public int getFoo(); public boolean isFoo();
This constraint is not a limitation of Java or of JMX but of the Proxy implementation. The designer could choose not use the Proxy, or to use a Proxy implementation that does not have this limitation.