Getting Started with the Remote Administration Daemon on Oracle Solaris 11

Version 14

    by Glynn Foster

     

    Learn how to use the administrative interfaces provided by the Oracle Solaris Remote Administration Daemon to perform local and remote configuration of a mix of technologies, including Oracle Solaris Zones, the ZFS file system, and the Service Management Facility.

     


    Introduction

     

    One of the exciting technologies that has been included in Oracle Solaris 11 is the Remote Administration Daemon (also known as RAD). RAD provides a set of programmatic interfaces to allow administrators to manage Oracle Solaris 11 subsystems such as Oracle Solaris Zones, the ZFS file system, the Service Management Facility (SMF), and more. RAD is also intended for developers as a complete development framework for creating custom interfaces that manage subsystems.

     

    Why RAD?

     

    There’s no doubt that the rise of virtualization in the data center has led to increased agility and efficiency, allowing administrators to consolidate the physical real estate they need to manage and improve time to deployment for applications. At the same time, this has caused an explosion in easy-to-create virtual environments with little perceived cost. Unfortunately this increased use of virtualization has also led to increased management and administration costs.

     

    As the next generation data center moves towards the cloud, it’s important to be able to curb these costs and manage environments effectively at scale through automation. And for this, tooling is hugely important. This is where RAD fits in—a programmatic interface to Oracle Solaris technologies that can be consumed directly by administrators and, crucially, third-party management applications.

     

    What Is RAD?

     

    RAD is an integrated technology included in Oracle Solaris 11 that exposes a set of programmatic interfaces through a set of modules in a unified way to Oracle Solaris technologies. The client-side language bindings currently supported are C, Java, Python, and REST APIs over HTTP. Administrators and developers can use these client bindings to connect locally or remotely to systems, to view, and to manage system configuration much like the existing Oracle Solaris command-line interfaces. SMF starts the RAD daemon running as the root user, with three service instances being responsible for managing connections to RAD.

     

    • The svc:/system/rad:local service instance manages local connections through UNIX sockets.
    • The svc:/system/rad:remote service instance manages secure remote connections using TLS. The ability to accept remote connections is disabled by default.
    • Finally, the svc:/system/rad:local-http service instance provides access for local connections over HTTP.

     

    When a request comes in and authentication has been established, the primary RAD daemon will spawn a slave daemon running at the same privilege level as the authenticated user. It then proxies the requests through to this slave daemon, executing the appropriate APIs with the authenticated user’s privileges and audit context. RAD supports authentication through PAM, getpeerucred(3C), and GSSAPI for environments that have been configured to use Kerberos.

     

    All RAD APIs are versioned allowing clients to define which version they would like to interact with (and, thus, being able to simultaneously support newer interfaces without breaking older ones). An interface provides a definition of how a client can interact with a system through a set of exposed methods, attributes, and events using a well-defined namespace. The various Oracle Solaris subsystems implement these interfaces and usually map to common administrative tasks for that particular subsystem.

     

    An interface may be shared across several technology areas where it makes sense. As we will see when we start to go through the examples below, it’s simply a case of connecting to RAD, obtaining references to interface instances (either by creating new references or looking up existing ones), and interacting with these references via their properties, methods, and events.

     

    Some of the modules provided now in Oracle Solaris 11 are for SMF, Oracle Solaris Zones, Elastic Virtual Switch, Image Packaging System (IPS), user management, KStats, and ZFS. Further modules are expected in future Oracle Solaris releases.

     

    image001.png

    Figure 1: A high-level architecture of RAD

     

    Getting Started with RAD

     

    In this article, we will explore how to consume RAD and the available RAD modules for Oracle Solaris technologies using a very simple set of examples. To start, we will examine a single example using each of the different available language bindings—Python, C, Java, and the REST API. Once completed, we will then continue to explore examples for other RAD modules using Python for convenience.

     

    Example 1: Querying Oracle Solaris Zones

     

    In our first example, we will use the Oracle Solaris Zones RAD module to query a system locally for zones. We will use the module to retrieve some basic information about the zone including its name, state, whether it’s set to automatically boot on system startup, whether it has any memory capping and, finally, what brand it is (either an Oracle Solaris non-global zone or an Oracle Solaris Kernel Zone).

     

    Python Bindings

     

    We will start with Python. The current Oracle Solaris RAD modules are using Python 2.6, so we need to make sure any scripts that we write include this version in the shell interpreter.

     

    The first thing that we will need to do is to import two Python libraries: rad.connect to be able to set up the connection with the RAD daemon and com.oracle.solaris.rad.zonemgr_1 to provide access to a variety of classes that provide a set of interfaces to configure and administer Oracle Solaris Zones. All RAD modules currently are at version 1. We achieve this using the following code:

     

    import rad.connect as radc

    import rad.bindings.com.oracle.solaris.rad.zonemgr_1 as zonemgr

     

    The RadConnection class offers a number of different ways to communicate with the RAD daemon. In our case, we will simply set up a connection over UNIX sockets. To do this, we will create a string that will hold our RAD URI, and then call the connect() method on it and store the returned RadConnection object. We will see in future examples how different URIs can be used to connect remotely to systems.

     

    uri = radc.RadURI(“unix:///”)
    rc = uri.connect()

     

    Once we have successfully created a connection, we can use it to query the system. We will use our connection handle to list all objects that implement the Zone() interface as follows:

     

    zones = rc.list_objects(zonemgr.Zone())

     

    The list_objects() method will return us a list of object names of type ADRName (this corresponds to how RAD internally maps between Python and the interfaces that use the Abstract Data Representation [ADR] definition language). We can now loop through these names to get the underlying objects that are associated to those names:

     

    for name in zones:

            zone = rc.get_object(name)

     

    Finally, for each zone that we find, let’s query the configuration to see whether the zone is set to boot automatically, and whether it has any resource management associated with it. We will use the getResourceProperties() method to pull the autoboot property within the global resource scope using the Resource class. Within the zone configuration, resource scoping allows properties to be logically grouped together—you can see this when using zonecfg(1M) to add an automatic VNIC (anet), for example.

     

    We will also use the getResources() method to determine whether the capped-memory resource has been declared on a given zone and set an appropriate variable; all Oracle Solaris Kernel Zones will have this resource by default.

     

    autoboot_prop = zone.getResourceProperties(zonemgr.Resource('global'),

                                                      ['autoboot'])

            if len(zone.getResources(zonemgr.Resource('capped-memory'))) == 0:

                    capping_prop = 'no'

            else:

                    capping_prop = 'yes'

     

    Let’s quickly summarize our full Python script:

     

    #!/usr/bin/python2.6

     

    import rad.connect as radc

    import rad.bindings.com.oracle.solaris.rad.zonemgr_1 as zonemgr

     

    # Connect to RAD through local UNIX socket

    uri = radc.RadURI("unix:///")

    rc = uri.connect()

     

    # List the Zone objects

    zones = rc.list_objects(zonemgr.Zone())

     

    # Print out our headings

    print "%-16s %-11s %-11s %-10s %-6s" % ("NAME", "STATUS", "AUTOBOOT",

                                            "CAPPED", "BRAND")

     

    # Iterate over the Zone objects. Retrieve the autoboot property and
    # check to see whether a capped-memory resource is set.

    for name in zones:

        zone = rc.get_object(name)

        autoboot_prop = zone.getResourceProperties(zonemgr.Resource('global'),

                                                  ['autoboot'])

        if len(zone.getResources(zonemgr.Resource('capped-memory'))) == 0:

            capping_prop = 'no'

        else:

            capping_prop = 'yes'

        # Print out the results
        print "%-16s %-11s %-11s %-10s %-6s" % (zone.name, zone.state,

                                                autoboot_prop[0].value,

                                                capping_prop, zone.brand)

     

    # Close the RAD connection

    rc.close()

     

    As you’ll see, we’ve purposely ignored basic error handling for simplicity, but it’s important to add that for completeness. Once we have saved the script, we can go ahead and run it on the command line:

     

    # ./zones.py

    NAME        STATUS       AUTOBOOT     CAPPED    BRAND

    zone1       running      false        no        solaris

    zone2       installed    true         no        solaris

    zone3       configured   false        yes       solaris-kz

     

    One handy tip is that the Python RAD bindings provide very helpful inline documentation if you prototype using IPython. For example, to view the docs for RAD connections, you might do the following:

     

    # ipython-2.6

    Python 2.6.8 (unknown, May 26 2015, 00:32:22) [C]

    Type "copyright", "credits" or "license" for more information.

     

    IPython 0.10 -- An enhanced Interactive Python.

    ?        -> Introduction and overview of IPython's features.

    %quickref -> Quick reference.

    help      -> Python's own help system.

    object?  -> Details about 'object'. ?object also works, ?? prints more.

     

    In [1]: import rad.connect as radc

     

    In [2]: help radc

    Help on module rad.connect in rad:

     

    NAME

        rad.connect - connect - RAD connection oriented utilities

     

    FILE

        /usr/lib/python2.6/vendor-packages/rad/connect.py

     

    MODULE DOCS

    http://docs.python.org/library/rad.connect

     

    DESCRIPTION

        This module facilitates communication with RAD instances via a number of

        alternative transports. The RadConnection class acts as the primary interface

        and offers a connection to a RAD instance which may be used to:

     

            - locate objects

            - invoke object methods

            - read/write object properties

            - subscribe/unsubscribe to object events

     

    CLASSES

        __builtin__.object

            ProcessPseudoSocket

            RadConnection

            RadEvent

            RadURI

        rad.client.RADStruct(rad.client.RADObject)

            RadUpdateParameter

    ...

     

    C Bindings

     

    Unsurprisingly, writing a RAD client using the C bindings is a little more complex, but the same basics apply—connect to RAD, list the zone objects that RAD knows about, and iterate over the array that we get back. We have a little more work to do, particularly in terms of memory management, but essentially we get a similar result at the end.

     

    #include <stdio.h>

    #include <rad/adr.h>

    #include <rad/radclient.h>

    #include <rad/client/1/zonemgr.h>

     

    int main(int argc, char *argv[])

    {

        rc_uri_t *uri;

        rc_err_t status;

        int name_count, i;

        adr_name_t **name_list;

     

        /* Connect to RAD through local UNIX socket */

        uri = rc_alloc_uri("unix:///", RCS_UNIX);

        rc_conn_t *conn = rc_connect_uri(uri, RCC_NONE);

        rc_free_uri(uri);

     

        /* List the list of Zone ADR names and return result into an array */

        zonemgr_Zone__rad_list(conn, B_TRUE, NS_GLOB,

                              &name_list, &name_count, 0);

     

        /* Print out our headings */

        printf("%-16s %-11s %-11s %-10s %-6s\n", "NAME", "STATUS",

                                                "AUTOBOOT", "CAPPED",

                                                "BRAND");

     

        /* Iterate over the Zone objects. Retrieve the autoboot property and

        * check to see whether a capped-memory resource is set.

        */

     

        for (i = 0; i < name_count; i++) {

            /* Define Resource structures that can be used to look up

            * the configuration for the global and capped-memory

            * scope.

            */

            zonemgr_Resource_t global = { .zr_type = "global"};

            zonemgr_Resource_t capped = { .zr_type = "capped-memory"};

            zonemgr_Property_t *result;

            zonemgr_Result_t *error;

     

            rc_instance_t *zone_inst;

     

            char *zone_name, *zone_brand, *zone_state;

            const char *autoboot_prop, *capped_prop;

            int property_count, j;

     

            /* Lookup the instance from the ADR name */

            rc_lookup(conn, name_list[i], NULL, B_TRUE, &zone_inst);

     

            zonemgr_Zone_get_name(zone_inst, &zone_name);

            zonemgr_Zone_get_brand(zone_inst, &zone_brand);

            zonemgr_Zone_get_state(zone_inst, &zone_state);

     

            zonemgr_Zone_getResourceProperties(zone_inst, &global, NULL,

                                              0, &result, &property_count,

                                              &error);

            /* We need to iterate over all the properties to find autoboot */

            for (j = 0; j < property_count; j++)

                if (strncmp(result[j].zp_name, "autoboot", strlen("autoboot")) == 0)

                    autoboot_prop = strdup(result[j].zp_value);

     

            zonemgr_Result_free(error);

            zonemgr_Property_array_free(result, property_count);

     

            zonemgr_Zone_getResourceProperties(zone_inst, &capped, NULL,

                                              0, &result, &property_count, &error);

     

            /* Check if there is a capped-memory resource */

            if (error && (int) error->zr_code == ZEC_RESOURCE_NOT_FOUND)

                capped_prop = "no";

            else

                capped_prop = "yes";

     

            zonemgr_Result_free(error);

            zonemgr_Property_array_free(result, property_count);

            printf("%-16s %-11s %-11s %-10s %-6s\n", zone_name, zone_state,

                                                    autoboot_prop, capped_prop,

                                                    zone_brand);

            free(zone_name);

            free(zone_state);

            free(zone_brand);

        }

     

        rc_disconnect(conn);

    }

     

    As in the Python example, there’s a lot more error checking that we should be doing; for example, checking to see if we made a valid RAD connection. However, we can now quickly compile this code and run it to verify that it’s returning the same information as before:

     

    # cc -o zones zones.c -lradclient -ladr -lzonemgr_client \
    -L /usr/lib/rad/client/c/ -R /usr/lib/rad/client/c/

    # export LD_LIBRARY_PATH=/usr/lib/rad/client/c/
    # ./zones

    NAME       STATUS      AUTOBOOT    CAPPED    BRAND

    zone1      running     false       no        solaris

    zone2      installed   true        no        solaris

    zone3      configured  false       yes       solaris-kz

     

    To use other RAD C client bindings, the best place to look is in the header files included in /usr/include/rad/client/1/. It also probably helps to look at the Python documentation to see the typical interaction that you should expect with these interfaces.

     

    Java Bindings

     

    Writing a Java client ends up being a little less cumbersome than using C, but again, it follows similar principles.

     

    import java.util.*;

    import java.io.IOException;

    import com.oracle.solaris.rad.client.*;

    import com.oracle.solaris.rad.connect.*;

    import com.oracle.solaris.rad.zonemgr.*;

     

    class RadClient

    {

        public static void main(String[] args) {

            Connection conn;

            URIConnection uri;

            ZoneInfo zi;

            try {
                // First we open a RAD connection on a UNIX socket 

                uri = new URIConnection("unix:///");

                conn = uri.connect(null);

     

                // Print out our header

                System.out.format("%-16s %-11s %-11s %-10s %-6s\n", "NAME", "STATUS",

                                                                    "AUTOBOOT", "CAPPED", "BRAND");                               


                // Iterate over all the Zone objects we can find

                for (ADRName name: conn.listObjects(new Zone())) {
                    Zone zone =  conn.getObject(name);

                    String capped = "no";

                    String autoboot = "false";
                    // Create resource filters for the global and capped-memory scopes

                    Resource global_filter = new Resource("global", null, null);

                    Resource capped_filter = new Resource("capped-memory", null, null);

     

                    List<Property> props = zone.getResourceProperties(global_filter, null);

                    try {         

                        List<Property> capped_props = zone.getResourceProperties(capped_filter, null);

                        if (!capped_props.isEmpty())

                            capped = "yes";       

                    } catch (RadException e) {}

                    for (Property prop: props) {

                            if (prop.getName().equals("autoboot"))

                            autoboot = prop.getValue();

                        }                 

                    System.out.format("%-16s %-11s %-11s %-10s %-6s\n", zone.getname(),
                                                                        zone.getstate(),

                                                                        autoboot, capped,

                                                                        zone.getbrand());                               

                      }

     

            } catch (IOException e) {}

        } 

    }

     

    As soon as we’ve written the Java code, we can compile it through to a Java class file using the javac compiler, as follows:


    # export CLASSPATH=/usr/lib/rad/java/rad.jar:/usr/lib/rad/java/zonemgr.jar:/root
    # javac RadClient.java
    # java RadClient
    NAME         STATUS      AUTOBOOT    CAPPED  BRAND

    zone1        running     false       no      solaris

    zone2        installed   true        no      solaris

    zone3        configured  false       yes     solaris-kz

     

    To use other RAD Java client bindings, one way of looking to see what is available is by copying one of the JAR files locally, unpacking it, and checking the classes with javap, for example:

     

    # cp /usr/lib/rad/java/rad.jar .

    # /usr/jdk/instances/jdk1.7.0/bin/jar xf rad.jar

    # cd com/oracle/solaris/rad/connect/

    # /usr/jdk/instances/jdk1.7.0/bin/javap URIConnection.class

    Compiled from "URIConnection.java"

    public class com.oracle.solaris.rad.connect.URIConnection {

      public static final java.lang.String SCHEME_UNIX;

      public static final java.lang.String SCHEME_RAD;

      public static final java.lang.String SCHEME_RADS;

      public static final java.lang.String SCHEME_SSH;

      public static final java.util.Set<java.lang.String> DEFAULT_SCHEMES;

      public static final java.util.Set<java.lang.String> CREDENTIAL_CLASSES;

      public com.oracle.solaris.rad.connect.URIConnection(java.lang.String) throws

    java.io.IOException;

      public com.oracle.solaris.rad.connect.URIConnection(java.lang.String,

    java.util.Set<java.lang.String>) throws java.io.IOException;

      public com.oracle.solaris.rad.connect.URIConnection(java.lang.String,

    java.util.Set<java.lang.String>, java.util.Set<java.lang.String>) throws

    java.io.IOException;

      public void addCertFile(java.lang.String);

      public void rmCertFile(java.lang.String);

      public com.oracle.solaris.rad.connect.Connection

    connect(com.oracle.solaris.rad.connect.Credentials) throws java.io.IOException;

      public void processPAMAuth(com.oracle.solaris.rad.connect.PAMCredentials,

    com.oracle.solaris.rad.connect.Connection) throws java.io.IOException;

      public java.lang.String getAuth();

      public java.lang.String getCredClass();

      public void setCredClass(java.lang.String) throws java.io.IOException;

      public java.lang.String getHost();

      public java.lang.String getPath();

      public int getPort();

      public java.lang.String getSrc();

      public java.lang.String getScheme();

      public java.util.Set<java.lang.String> getSchemes();

      public java.lang.String getUser();

      public java.lang.String toString();

      static {};

    }

     

    This details all the methods associated for a particular class.

     

    REST Bindings

     

    From Oracle Solaris 11.3 onwards, a new REST interface to RAD has been added. REST APIs are an increasingly popular way of interacting with system services across the network over both HTTP and HTTPS using an encoding payload such as JSON or XML. Oracle Solaris 11.3 includes a new SMF service instance, svc:/system/rad:local-http, that is responsible for facilitating RAD communication from HTTP clients. Given that HTTP connections are not encrypted, the default configuration allows only for accepting connections from a local host on a UNIX socket. Administrators can choose to accept connections over a public port if desired, and secure transport is expected to be provided in a future release.

     

    A RESTful architecture uses resources, identified by a URI, as the primary interface through which to manipulate data, or it uses call methods to operate on that data. Resources can be accessed individually or as a collection of member resources. The RAD REST functionality supports HTTP GET, POST, PUT, and DELETE requests.

     

    As before, we’ll first need to set up the RAD connection. For this, we’ll send a POST request to the /api/com.oracle.solaris.rad.authentication/1.0/Session API and we’ll also provide some credentials included in a JSON file, as follows:


    {

          "username": "root",

          "password": "solaris11",

          "scheme": "pam",

          "preserve": true,

          "timeout": -1

    }

     

    From the code above, you can see that we’ve provided both a username and a password, that we want to authenticate using PAM over a UNIX socket located at /system/volatile/rad/radsocket-http, and that we’d like to preserve this connection and reconnect to it later (using a default timeout of 60 minutes, which is indicated by the -1 argument). Once we have saved this code in a file called body.json, we can make the POST request using curl, saving the authentication token to a file called cookie.txt:

     

    # curl -H "Content-type: application/json" -X POST --data-binary @body.json \

    localhost/api/com.oracle.solaris.rad.authentication/1.0/Session \
    --unix-socket /system/volatile/rad/radsocket-http -v -c cookie.txt -b cookie.txt

    *  Trying /system/volatile/rad/radsocket-http...

    * Connected to localhost (/system/volatile/rad/radsocket-http) port 80 (#0)

    > POST /api/com.oracle.solaris.rad.authentication/1.0/Session HTTP/1.1

    > User-Agent: curl/7.40.0

    > Host: localhost

    > Accept: */*

    > Cookie: _rad_instance=1792; _rad_token=0f96e2ed-af68-47a7-91aa-9fe80239c661

    > Content-type: application/json

    > Content-Length: 103

    >

    * upload completely sent off: 103 out of 103 bytes

    < HTTP/1.1 201 Created

    < Connection: Keep-Alive

    < Content-Length: 164

    < Expires: 0

    < Pragma: no-cache

    < Cache-Control: no-cache, no-store, must-revalidate

    < Location: /api/com.oracle.solaris.rad.authentication/1.0/Session/_rad_reference/2048

    * Replaced cookie _rad_instance="2048" for domain localhost, path /api, expire 1435280743

    < Set-Cookie: _rad_instance=2048; Path=/api; Max-Age=3600; HttpOnly

    * Replaced cookie _rad_token="d5009d3f-35d5-45d1-919a-bcacc468da84" for domain localhost, path /api, expire 1435280743

    < Set-Cookie: _rad_token=d5009d3f-35d5-45d1-919a-bcacc468da84; Path=/api; Max-Age=3600; HttpOnly

    < Date: Fri, 26 Jun 2015 00:05:43 GMT

    <

    {

            "status": "success",

            "payload": {

                    "href":

    "/api/com.oracle.solaris.rad.authentication/1.0/Session/_rad_reference/2048"

            }

    * Connection #0 to host localhost left intact

    # cat cookie.txt

    # Netscape HTTP Cookie File

    # http://curl.haxx.se/docs/http-cookies.html

    # This file was generated by libcurl! Edit at your own risk.

     

    #HttpOnly_localhost    FALSE /api    FALSE  1435280743      _rad_instance   2048

    #HttpOnly_localhost    FALSE /api    FALSE  1435280743      _rad_token      d5009d3f-35d5-45d1-919a-bcacc468da84

    Now that we have successfully obtained the token, we can go ahead and start using the zone RAD module. For this we’ll use the /api/com.oracle.solaris.rad.zonemgr/1.0/Zone API. Let’s first do a GET request, using our cookie, and see what we get back:


    # curl -H "Content-type: application/json" -X GET \
    localhost/api/com.oracle.solaris.rad.zonemgr/1.0/Zone \
    --unix-socket /system/volatile/rad/radsocket-http -b cookie.txt

    {

            "status": "success",

            "payload": [

                    {

                            "href": "api/com.oracle.solaris.rad.zonemgr/1.2/Zone/zone1"

                    },

                    {

                            "href": "api/com.oracle.solaris.rad.zonemgr/1.2/Zone/zone2"

                    },

                    {

                            "href": "api/com.oracle.solaris.rad.zonemgr/1.2/Zone/zone3"

                    }

            ]

    }

     

    The resulting payload returned is very similar to the previous examples. However, instead of returning a list of ADR names for the zone instances it has found, we get back a URI instead. We can now use this URIs to reference individual zones. Let’s look at the properties of zone1, as follows:


    # curl -H "Content-type: application/json" -X GET \
    localhost/api/com.oracle.solaris.rad.zonemgr/1.2/Zone/zone1?_rad_detail=true \
    --unix-socket /system/volatile/rad/radsocket-http -b cookie.txt

    {

            "status": "success",

            "payload": {

                    "href": "api/com.oracle.solaris.rad.zonemgr/1.2/Zone/zone1",

                    "Zone": {

                            "auxstate": [],

                            "brand": "solaris",

                            "id": 2,

                            "uuid": "321b2c4f-ca4f-4b93-a0e0-e183b86f982f",

                            "name": "zone1",

                            "state": "running"

                    }

            }

    }

     

    From the code above, you can see we’ve taken the URI provided and also added a query parameter, _rad_detail, to provide more information about this zone. We can see we’ve identified the zone name, brand, and state.

     

    To look at more properties of the zone, we will need to call a method on this interface. This is not considered a traditional RESTful operation, but many REST-based APIs use method invocation. To use method calls, we will need to include _rad_method within the URI to distinguish between the RAD instance and the method name. We will also provide empty data using the following JSON file, meaning that we would like all properties of the zone to be returned:

     

    {

     

    }

     

    The following lists all the properties of the zone:

     

    # curl -H "Content-type: application/json" -X PUT --data @zones.json \

    localhost/api/com.oracle.solaris.rad.zonemgr/1.2/Zone/zone1/_rad_method/getResources \

    --unix-socket /system/volatile/rad/radsocket-http -b cookie.txt

    {

            "status": "success",

            "payload": [

                    {

                            "type": "global",

                            "properties": [

                                    {

                                            "name": "zonename",

                                            "value": "zone1",

                                            "type": "PROP_SIMPLE",

                                            "listvalue": null,

                                            "complexvalue": null

                                    },

                                    {

                                            "name": "zonepath",

                                            "value": "/system/zones/%{zonename}",

                                            "type": "PROP_SIMPLE",

                                            "listvalue": null,

                                            "complexvalue": null

                                    },

                                    {

                                            "name": "brand",

                                            "value": "solaris",

                                            "type": "PROP_SIMPLE",

                                            "listvalue": null,

                                            "complexvalue": null

                                    },

     

    [output trunctated]

     

                                    {

                                            "name": "vport",

                                            "value": "",

                                            "type": "PROP_SIMPLE",

                                            "listvalue": null,

                                            "complexvalue": null

                                    },

                                    {

                                            "name": "tmp-id",

                                            "value": "0",

                                            "type": "PROP_SIMPLE",

                                            "listvalue": null,

                                            "complexvalue": null

                                    }

                            ],

                            "parent": null

                    }

            ]

    }

     

    As you can see, the REST API provides a very powerful API, especially for those interested in integrating Oracle Solaris functionality into a web-based administration portal.

     

    For the remainder of this article, we will focus on examples of using RAD with Python.

     

    Example 2: Creating, Installing, and Booting Oracle Solaris Zones

     

    We saw in the previous example how to query a system with RAD, list out the zones that have been configured, and search for specific properties associated with that zone configuration. We did this using the Zone() interface in the zonemgr RAD module.

     

    In this next example, we will actually make some changes to the system by creating a new zone using the ZoneManager() interface.

     

    #!/usr/bin/python2.6

     

    import rad.connect as radc

    import rad.client as radclient

    import rad.bindings.com.oracle.solaris.rad.zonemgr_1 as zonemgr

     

    uri = radc.RadURI("unix:///")

    with uri.connect() as rc

     

        # Get a reference to the ZonesManager object

        zonemanager = rc.get_object(zonemgr.ZoneManager())

     

        # Create a zone using the SYSsolaris template (a non-global zone)

        zonemanager.create("zone4", None, "SYSsolaris")

     

    We’ve also used a different way of connecting to RAD using the Python with statement—this has the benefit of protecting the entire execution if the connection to the RAD daemon dies.

     

    When we run this code, we can immediately see that new zone has been created (using the zones.py script that we created in “Example 1: Querying Oracle Solaris Zones”).

     

    # ./zones.py

    NAME     STATUS      AUTOBOOT    CAPPED    BRAND

    zone1    running     false       no        solaris

    zone2    installed   true        no        solaris

    zone3    configured  false       yes       solaris-kz

    zone4    configured  false       no        solaris

     

    As we can see from the example above, much of the output is the same. The key part of the output is getting a reference to the ZoneManager() object. Once we have this, we can call the create() method and provide both a zone name and an appropriate template to use. If we wanted to make some property changes to the zone, say, to set the autoboot property, we can use the setResourceProperties() method, as follows:

     

    # Find a list of zones that matches the zone name zone2
    zonelist = rc.list_objects(zonemgr.Zone(),

                              radclient.ADRGlobPattern({"name" : "zone2"}))
    z = rc.get_object(zonelist[0])


    # Initiate zone configuration editing

    z.editConfig()

     

    # Create a Resource and Property for the autoboot property
    resource = zonemgr.Resource(‘global’)

    prop = zonemgr.Property(‘autoboot’,’true’)

     

    # Set the sources
    z.setResourceProperties(resource,[prop])

     

    # Commit the new zone configuration
    z.commitConfig()

     

    In this snippet, we used the ADRGlobPattern() method, which provides us with the simple ability to filter our results using a search pattern. More powerful search capabilities are available using the ADRRegexPattern().

     

    Finally, we can also install the zone. Let’s quickly clone it from the existing zone2 zone, as follows:

     

    # Find the list of zones that match the zone name of zone2 and
    # get the object reference

    zonelist = rc.list_objects(zonemgr.Zone()

                              radclient.ADRGlobPattern({"name" : "zone2"}))
    zone2 = rc.get_object(zonelist[0])

     

    # Use zone2 to clone zone4
    z.clone([z1.name])

     

    Let’s again check to see if our new zone4 was installed:

     

    # ./zones.py

    NAME       STATUS       AUTOBOOT    CAPPED    BRAND

    zone1      running      false       no        solaris

    zone2      installed    true        no        solaris

    zone3      configured   false       yes       solaris-kz

    zone4      installed    true        no        solaris

     

    We can see that the zone is now installed and the autoboot property is set to true. There are many more things you can do with the zone RAD module such as boot, halt, attach, and detach zones. All the interfaces can be viewed by looking at the zonemgr(3rad) man page.

     

    Example 3: Querying ZFS Data Sets and Pools

     

    The ZFS RAD module provides a set of interfaces to manage ZFS storage pools and data sets. Now that we’ve gotten familiar with much of the basics of using the Python bindings, querying ZFS is relatively straightforward. In this example, we will use the Zpool() and ZfsDataset() interfaces primarily.

     

    Let’s look at a simple example of listing out ZFS pools on a system:

     

    #!/usr/bin/python2.6

     

    import rad.connect as radc

    import rad.bindings.com.oracle.solaris.rad.zfsmgr_1 as zfsmgr

     

    # Connect to RAD through local UNIX socket

    uri = radc.RadURI("unix:///")

     

    with uri.connect() as rc:

        pools = rc.list_objects(zfsmgr.Zpool())

        print pools

     

    This time, we will import the zfsmgr module. Like before, we are searching the RAD namespace for objects that implement the Zpool() interface. Running this simple example results in the following:


    # ./zfs.py

    [Name: com.oracle.solaris.rad.zfsmgr:type=Zpool,name=rpool Version: (1.0)]

     

    We can also print a string representation of all objects that implement the ZfsDataset() interface by using the following snippet of code:


    datasets = rc.list_objects(zfsmgr.ZfsDataset())

        for name in datasets:

            print name

     

    Finally, let’s extend our script to create a new ZFS data set called rpool/repository within rpool and set the mount point to be /repository:


    #!/usr/bin/python2.6

     

    import rad.connect as radc

    import rad.client as radclient

    import rad.bindings.com.oracle.solaris.rad.zfsmgr_1 as zfsmgr

     

    # Connect to RAD through local UNIX socket

    uri = radc.RadURI("unix:///")

     

    with uri.connect() as rc:

        pattern = radclient.ADRGlobPattern({"name" : "rpool"})

        rpool = rc.get_object(zfsmgr.ZfsDataset(), pattern)

     

        dataset = rpool.create_filesystem(name="rpool/repository")

     

        prop = zfsmgr.ZfsProp('mountpoint', '/repository')

        dataset.set_props([prop])

     

    As we can see from the code above, we have used a ADRGlobPattern to find the appropriate ZfsDataset() object that is connected with the ZFS pool rpool—since we are creating ZFS data sets in the context of an overall ZFS storage pool.

     

    Running the script gives the expected result:

     

    # ./zfs.py

    # zfs list rpool/repository

    NAME              USED  AVAIL  REFER  MOUNTPOINT

    rpool/repository  31K  160G    31K    /repository

     

    The ZFS RAD module, documented in zfsmgr(3rad), does not support all the functions currently available with zpool(1M) and zfs(1M). Additional functionality is expected in future.

     

    Example 4: Querying SMF Services and Properties

     

    The SMF RAD module exposes a vast array of different interfaces for managing SMF services and service instances, property groups, and properties. As a simple example, let’s show the list of SMF services installed on a system, by using the Service() interface:

     

    #!/usr/bin/python2.6

     

    import rad.connect as radc

    import rad.client as radclient

    import rad.bindings.com.oracle.solaris.rad.smf_1 as smf

     

    # Connect to RAD through local UNIX socket

    uri = radc.RadURI("unix:///")

     

    with uri.connect() as rc:

        services = rc.list_objects(smf.Service())

        for name services:

            print name

     

    Predictably, when we run this script, we get the following output:


    # ./smf.py

    com.oracle.solaris.rad.smf:type=Service,service=application/texinfo-update

    com.oracle.solaris.rad.smf:type=Service,service=smf/manifest

    com.oracle.solaris.rad.smf:type=Service,service=application/stosreg

    com.oracle.solaris.rad.smf:type=Service,service=application/management/net-snmp

    com.oracle.solaris.rad.smf:type=Service,service=application/cups/scheduler

    com.oracle.solaris.rad.smf:type=Service,service=application/cups/in-lpd

    com.oracle.solaris.rad.smf:type=Service,service=application/man-index

    com.oracle.solaris.rad.smf:type=Service,service=application/security/tcsd

    com.oracle.solaris.rad.smf:type=Service,service=application/font/fc-cache

    com.oracle.solaris.rad.smf:type=Service,service=application/pkg/dynamic-mirror
    [output truncated]

    com.oracle.solaris.rad.smf:type=Service,service=application/pkg/depot

    com.oracle.solaris.rad.smf:type=Service,service=network/tcp/tcpkey

    com.oracle.solaris.rad.smf:type=Service,service=network/evs-controller

    com.oracle.solaris.rad.smf:type=Service,service=network/ib/ib-management

    com.oracle.solaris.rad.smf:type=Service,service=system/svc/periodic-restarter

    We can also manipulate service instances, choosing to enable, disable, or restart them. Let’s modify the script above to look at the svc:/network/ftp:default service instance and start it. We will use an ADRGlobPattern() to achieve this, and then call the enable() method on the service instance that we find:

     

    #!/usr/bin/python2.6

    import rad.connect as radc
    import rad.client as radclient
    import rad.bindings.com.oracle.solaris.rad.smf_1 as smf

    # Connect to RAD through local UNIX socket
    uri = radc.RadURI("unix:///")
    with uri.connect() as rc:

        instance = rc.get_object(smf.Instance(),

                                  radclient.ADRGlobPattern({"service" : "network/ftp",

                                                            "instance" : "default"}))

        instance.enable ("")


    Running this script gets what we expect, an online svc:/network/ftp:default service instance:

     

    # svcs ftp:default

    STATE     STIME    FMRI

    disabled  5:58:59  svc:/network/ftp:default

    # ./smf.py

    # svcs ftp:default

    STATE     STIME    FMRI

    online    5:59:33  svc:/network/ftp:default

     

    We’ve just touched on the very basics of using the SMF RAD module. Further information, including examples, is available in the smf(3rad) man page.

     

    Managing Remote Systems

     

    As we’ve seen in the previous examples, we’re just connecting to a local UNIX socket. Managing remote systems is also possible using RAD.

     

    One way provide secure management of a remote system is over SSH, where a chain of trust has already been developed through the transfer of SSH public keys. This provides password-less authentication, allowing easy connection to a remote RAD instance. Let’s start by enabling the remote RAD service instance:

     

    # svcadm enable rad:remote

     

    Next, simply copy the SSH public key to the remote host you are trying to manage and store it as an authorized key. For example, let’s create an SSH key with an empty passphrase, and then copy it to the remote host:

     

    # ssh-keygen -t rsa

    Generating public/private rsa key pair.

    Enter file in which to save the key (/root/.ssh/id_rsa): [enter]

    Created directory '/root/.ssh'.

    Enter passphrase (empty for no passphrase): [enter]

    Enter same passphrase again: [enter]

    Your identification has been saved in /root/.ssh/id_rsa.

    Your public key has been saved in /root/.ssh/id_rsa.pub.

    The key fingerprint is:

     

    Once we have generated the SSH key, we can copy it to our remote host:

     

    # scp .ssh/id_rsa.pub root@remotehost:/root/.ssh/authorized_keys

    The authenticity of host 'remotehost (10.134.79.160)' can't be established.

    RSA key fingerprint is 03:05:30:c7:c9:d8:52:b2:69:50:50:da:70:b9:1d:39.

    Are you sure you want to continue connecting (yes/no)? yes

    Warning: Permanently added 'dcsw-t52-1,10.134.79.160' (RSA) to the list of known

    hosts.

    Password:

    id_rsa.pub  |***********************************************|  398 00:00

     

    On the remote side, depending on the permissions, we might need to correct the permissions using the following commands:

     

    # chmod 700 .ssh
    # cd .ssh

    # chmod 640 authorized_keys

     

    We can then proceed to validate this chain of trust, as follows:

     

    # ssh root@remotehost.us.oracle.com

    The authenticity of host remotehost.us.oracle.com (10.134.79.160)' can't be

    established.

    RSA key fingerprint is 03:05:30:c7:c9:d8:52:b2:69:50:50:da:70:b9:1d:39.

    Are you sure you want to continue connecting (yes/no)? yes

    Warning: Permanently added ‘remotehost.us.oracle.com' (RSA) to the list of known hosts.

    Last login: Fri Jun 26 06:26:54 2015 from remotehost.us.

    Oracle Corporation SunOS 5.12      s12_74  May 2015

    # logout

    Connection to remotehost.us.oracle.com closed.

    # ssh root@remotehost.us.oracle.com

    Last login: Fri Jun 26 06:27:29 2015 from remotehost.us.

    Oracle Corporation SunOS 5.12      s12_74  May 2015

     

    We can now proceed by using a RAD connection over SSH by using the following URIConnection() string:

     

    uri = radc.RadURI("ssh://remotehost.us.oracle.com")

     

    Assuming we’ve set up an SSH password-less connection, we should now be able to run the same RAD queries that we’ve been doing in the preceding examples.

     

    Summary

     

    The Oracle Solaris Remote Administration Daemon (RAD) provides a powerful set of administrative interfaces to allow local and remote configuration of a mix of technologies, including Oracle Solaris Zones, ZFS, SMF, and others. These programmatic interfaces provide a central point for exposing the capabilities of Oracle Solaris to consumers using common development runtimes, including C, Java, Python, and REST APIs. This capability allows secure remote administration at scale, which is critical for a typical cloud environment.

     

    See Also

     

     

    Also see these additional resources:

     

     

     

    About the Author

     

    Glynn Foster is a principal product manager for Oracle Solaris. He is responsible for a number of technology areas including OpenStack, the Oracle Solaris Image Packaging System, installation, and configuration management.

     

    Follow us:
    Blog | Facebook | Twitter | YouTube