ManageONTAP™ SDK v2.0P2

          

 

Using ONTAPI™ programming interfaces

This document talks about the ONTAPI API mechanism and shows you how to use it from C/C++, Perl and Java.
 

Introduction


 
The ONTAPI API mechanism satisfies a number of requirements for Network Appliance. First, it allows for remote management of Netapp appliances when port 80 is the only available port (this is sometimes the case for NetCache machines). Second, it is transport-independent; either DCE/RPC or HTTP may be used as the transport for any call. Third, it is simple. It consists of a small set of primitives that can be ported relatively easily from one language to another. 


The ONTAPI core APIs
 


The core APIs are those necessary to actually make ONTAPI API calls to a filer. All of them deal with packaging up or unpackaging messages in XML format, or setting up connections, determining state and so on.

This document is a getting started guide. For complete documentation of the core routines, including discussion of memory management and so on, please see the language-specific documentation by following these links:

ONTAPI core routines in C
ONTAPI core routines in Perl
ONTAPI core routines in Java

The table in this companion document displays the primitives' names in the various languages side by side. In the event that you need to port code from one language to another, the ONTAPI calls in the code should port pretty easily using this reference.


API concepts and conventions


  Both the client and server side use similar API functions and concepts to manipulate elements, the containers used for both requests and results. Elements are so named because a subset of XML--which calls its containers elements--is used to package up the query and return results. An element has a name, child elements, and contents (which can be a string, integer, or array of elements). API functions that manipulate these things come in several categories, identified by the prefix of their name. The main ones are: 
 
na_elem_* These functions manage elements, get and set contents, free memory associated with them and so on.
na_child_* These functions manipulate element children, which are just other elements nested within an element. Elements can be arbitrarily nested, but in practice two levels of them is enough of a headache to keep one's wilder ambitions in check.
na_server_*
These functions manage server connections, including the transport style and the user associated with them.
na_results_*
These functions manipulate results elements (which are children of an element returned by a ONTAPI call) to get error numbers and messages.

Several opaque types are manipulated by these functions
 

na_elem_t*  The representation of an RPC invocation or response. The na_elem_* routines add input parameters and extract output parameters from an instance of an na_elem_t.
na_server_t*  Server context for a NetApp filer or NetCache.
na_elem_iter_t  A structure managing iteration over possibly unbounded data.


First, let's look at some sample code to see what programmers have to do to get rolling with this stuff.


Client Setup


 

The first thing a client program must do is initialize the server context. In Perl and Java, this is done automatically--apart from declaring the libraries--as part of creating a server object (next section). In Perl, declaring the use of NaElement is optional because NaServer already does that as part of its own initialization.

C/C++
Perl
Java

#include <netapp_api.h>
...
char    err[256];

if (!na_startup(err, sizeof(err))) {
    fprintf(stderr, "Error: %s\n", err);
    exit(-1);
}

use lib '../../../lib/perl/NetApp';
use NaServer;
use NaElement;    #optional

import java.util.*;
import netapp.manage.*;


Next you identify which server (filer or netcache) you want to talk to, and the version of the API you expect. This setup gives you a pointer to a na_server_t* value (C/C++) or an object (Perl/Java) that is a "server context" used for subsequent API invocations.

 
C/C++
Perl
Java

na_server_t*    s;
 
/*
 * server to talk to, and
 * API version, in this case 1.3 or higher
 */
 
s = na_server_open("jetfighter",1,3);  


 
 
 
 
 
my $s = NaServer->new("jetfighter", 1, 3);

NaServer     s;
 
 
 
 

s = new NaServer("jetfighter",1,3);

  


Next you set the transport parameters. If you're writing a Windows application in C, you'll normally want to use Windows DCE/RPC, which furnishes native Windows authentication and authorization. On the other hand, if you need to use HTTP (which you must always do with Perl and Java), you must furnish a username and password for use by the server context, or have the server context authenticate against the /etc/hosts.equiv file on the filer. This file consists of either hostnames, in which case everyone coming from that host is allowed in as root, or hostname/username pairs, in which case the named users are allowed in as root when they connect from the named host. The filer System Admin Guide (SAG) has more details on the /etc/hosts.equiv file. When using HOSTSEQUIV login style, you need to set option httpd.admin.hostsequiv.enable on the filer to on.

C/C++
Perl
Java

bool          use_rpc = 1;
bool          use_hosts_equiv = 0;

if (use_rpc) {
    na_server_set_style(s, NA_STYLE_RPC);
}
else if (use_hosts_equiv) {
    na_server_set_style(s,
                   NA_STYLE_HOSTSEQUIV);

}
else {
    na_server_set_style(s,
                   NA_STYLE_LOGIN_PASSWORD);
    na_server_set_admin_user(s,
                   "root", "F18fan");
}


my $use_hosts_equiv = 0;




if ($use_hosts_equiv) {
   $s->set_style(HOSTSEQUIV);
}

else {
   $s->set_style(LOGIN_PASSWORD);


   $s->set_admin_user("root", "F18fan");
}


boolean useHostsEquiv = false;




if (useHostsEquiv) {
   s.setStyle(NaServer.HOSTSEQUIV);
}

else {
   s.setStyle(NaServer.LOGIN_PASSWORD);

   s.setAdminUser("root", "F18fan");
}



A good practice is to check for ONTAPI version before invoking APIs. Use system-get-ontapi-version to obtain ONTAPI version of a filer.
C/C++
Perl
Java

na_elem_t*      out;

/*
 * Make sure version is available
 */

out = na_server_invoke(s,
          "system-get-version", NULL);
if (na_results_status(out) != NA_OK) {
    printf("Version 1.3 is unsupported: %s\n",
           na_results_reason(out));
}
 
na_free(out);






my $out = $s->invoke(
             "system-get-version");
if ($out->results_status() eq "failed") {
    print($out->results_reason() . "\n");

}

NaElement    xi;
NaElement    xo;



xi = new NaElement("system-get-version");
try {
    xo = s.invokeElem(xi);
}
catch (NaAPIFailedException e) {
    System.err.println("API failed: " +
        e.getReason() +" (errno=" +
        e.getErrno() + ")");
}
catch (Exception e) {
    e.printStackTrace();
}

 


Finally, you need to close the server context to free memory and resources (the Perl bindings take care of this automatically when the context goes out of scope).

C/C++
Perl
Java

na_server_close(s);  


s.close();

 



Client Example #1 - The ONTAPI version of Hello World


 

In the ONTAPI version of HelloWorld, we set up a session and transport, as above, then get the ONTAP version number and print it out.

When you make an API call in C you get back a pointer to an instance of na_elem_t , and retrieve status and values from it with na_child_* primitives.  When you are done with the result, a single call to na_free() frees everything associated with the returned element. Perl and Java work similarly, but there's no need to free anything in these garbage-collected languages.

C/C++
Perl
Java

#include <stdio.h>
#include <netapp_api.h>


na_server_t*    s;
char            err[256];

na_elem_t*      out;

int main(int argc, char* argv[]) {
  if (argc < 4) {
    fprintf(stderr,
      "Usage: hello filer user passwd\n");
    exit(-1);
  }
  if (!na_startup(err, sizeof(err))){

    fprintf(stderr, "Error: %s\n",err);
    exit(-2);
  }
 
  s = na_server_open(argv[1],1,3);
  na_server_set_style(s,
        NA_STYLE_LOGIN_PASSWORD);
  na_server_set_admin_user(s,
        argv[2], argv[3]);

  out = na_server_invoke(s,
          "system-get-version", NULL);
  if (na_results_status(out) != NA_OK) {
    printf("Version 1.3 is unsupported: %s\n",
           na_results_reason(out));
  }
  else {
    printf("Hello!  Filer version is %s\n",
           na_child_get_string
(out, "version")
          );
  }
 
  na_free(out);
  na_server_close(s);

  return 0;
}

use lib '../../../lib/perl/NetApp';
use NaServer;
use NaElement;    #optional

my $filer = shift;
my $user = shift;
my $pw = shift;

if ($user eq "") {
  print("Usage: hello
filer user passwd");
  exit(-1);
}

my $s = NaServer->new($filer, 1, 3);

$s->set_style(LOGIN_PASSWORD);
$s->set_admin_user($user, $pw);

my $out = $s->invoke(
                 "system-get-version");
if ($out->results_status() eq "failed") {
    print($out->results_reason() . "\n");

}
else {
  print("Hello!  Filer version is " .
        $out->child_get_string("version")
        . "\n");
}

0;

import java.util.*;
import netapp.manage.*;

public class hello {
  public static void main(String[] args) {
    NaElement xi;
    NaElement xo;
    NaServer  s;

    if ( args.length < 3 ) {
      System.err.println(
        "Usage: hello filer user passwd");
      System.exit(1);
    }

    s = new NaServer(args[0],1,3);
    s.setStyle(NaServer.LOGIN_PASSWORD);
    s.setAdminUser(args[1], args[2]);

    xi = new NaElement(
             "system-get-version");
    try {
      xo = s.invokeElem(xi);
      System.out.print(
         "Hello!  Filer version is ");
      System.out.println(
         xo.childGetString("version"));
    }
    catch (Exception e) {
      System.out.println(e.toString());
    }
    finally {
      s.close();
    }
  }
}

Note: values pulled out of the result (as by na_child_get_string() in this example) point to memory that is freed when you call na_free() to free the result. You need to copy the value elsewhere if you need to make use of it later. The same is true in Perl and Java, as the memory will be released the next time the garbage collector runs after the variable goes out of scope.

API versioning : A client program needs to specify the version of ONTAPI that it needs (1.3 in the example above). A client program should specify the lowest-possible ONTAPI version that has the APIs that the client program actually uses. This will ensure that the client program is usable on as many filers as possible. If you specify the latest version of ONTAPI in your client, your program will only be usable on the latest version of ONTAPI, even if it uses older APIs

Compilation environment

C/C++

For the sample code in this document to compile and run, you have to have the following things in place in your environment:

$ONTAPI_ROOT/include
In your include path. Visual Studio users should add this in the C/C++/Preprocessor pane. Solaris and cl users can specify it with a -I directive, or copy the include directory tree from here to a suitable location on your system.
$ONTAPI_ROOT/lib/<architecture>
In your library path. Visual Studio users should add this in the Link/Input pane. Solaris and cl users can specify it with a -L directive, or copy the libraries from here into a suitable location on your system.
libnetapp.lib, libxml.lib, libadt.lib, libpthread.lib, ntapadmin.lib (and in Windows only, ws2_32.lib) Linked into your application. Visual Studio users should add them in the Link/General options pane. Solaris, AIX users should specifiy -lnetapp -lxml ... on the command line
The version of these libraries for Multithreaded applications and 64 bit platforms are also available in SDK. For more details see the notes for windows users.
ntapadmin.dll, pthread.dll (Windows users only, and only if you are linking )
installed into the %systemroot%/system32 directory on the machine that will be running your application
libcrypto.so, libssl.so (Solaris)
You need these libraries when using HTTPS transport in your application. Ensure the path for these libraries is defined in LD_LIBRARY_PATH environment variable.
libcrypto.a, libssl.a (AIX)
Link it to your application when using HTTPS transport.
libeay32.dll, ssleay32.dll (Windows)
You need these libraries when using HTTPS transport in your application. Ensure they are placed in a directory somewhere in your PATH environment.
 
Perl

For the example code in this document to compile and run, you have to have the following things in place in your environment:

use lib '<dir>'
Make sure <dir> is on your @INC path. The perl libraries for the SDK are located at %sdk-root%/lib/perl/NetApp, so that's the directory you should use unless you've installed them somewhere else.
 

 

Java

For the example code in this document to compile and run, you have to have the following things in place in your environment:

manageontap.jar
The full path for this library in the SDK is %sdk-root%/lib/java/classes/manageontap.jar. You need to include this jar file in the javac command, e.g.

  % javac -classpath <path>/manageontap.jar hello.java

 



Client example #2  Taking a named snapshot


  This example takes filer, volume and snapshot parameters from the command line, uses them to create a named snapshot on the given volume, and does error checking to report the success or failure of the command. Secure transport is arranged by calling na_server_set_transport_type().


 
C/C++
Perl
Java

#include <stdio.h>
#include <netapp_api.h>

int main(int argc, char* argv[])
{
na_server_t*  s;
  na_elem_t*  out;
  char        err[256];
  int         use_rpc = 0;
  int         rtn = 0;
  char*       filername = argv[1];
  char*       user = argv[2]; 
  char*       passwd = argv[3]; 
  char*       vol = argv[4];
  char*       ssname = argv[5];

  if (argc < 6) {
    fprintf(stderr, "Usage: %s <filername> "
            "<user> <password> "
            "<volname> <newsnapshotname>\n",
            argv[0]);
    return -1;
  }

  if (!na_startup(err, sizeof(err))) {
    fprintf(stderr, "Error in na_startup: "
            "%s\n", err);
    return -2;
  }
  s = na_server_open(filername, 1, 1);

  if ( use_rpc) {
    na_server_set_style(s, NA_STYLE_RPC);
  }
  else {
    na_server_set_style(s,
          
NA_STYLE_LOGIN_PASSWORD);
    na_server_adminuser(s, user, passwd);
  }
  rtn = na_server_set_transport_type(s,
          
NA_SERVER_TRANSPORT_HTTPS,0);
       
  if (rtn == 0) {
    printf("Error setting transport              type");
    return -3;      
  }
       
  out = na_server_invoke(s,
          "snapshot-create",
          "snapshot", ssname,
          "volume", vol,
          NULL);

  if (na_results_status(out) != NA_OK) {
    printf("Error %d: %s\n",                                 na_results_errno(out),                 na_results_reason(out));
    return -4;      
  }
  else {
    na_elem_free(out);
  }
  return 0;
}

use lib '../../../lib/perl/NetApp';
use NaServer;
use NaElement;    #optional

my $filer = shift;
my $user = shift;
my $pw = shift;
my $vol = shift;
my $ssname = shift;


if ($ssname eq "") {
  print("Usage: snap
filer user passwd");
  print(" volname newsnapshotname\n");
  exit(-1);
}

my $s = NaServer->new($filer, 1, 3);

$s->set_style(LOGIN_PASSWORD);
$s->set_admin_user($user, $pw);
$s->set_transport_type(
   NA_SERVER_TRANSPORT_HTTPS);

my $out = $s->invoke(
          
"snapshot-create",
          "snapshot", $ssname,
          "volume", $vol);

if ($out->results_status() eq "failed") {
  print($out->results_reason() . "\n");
  exit(-2);

}

0;

import java.util.*;
import netapp.manage.*;

public class hello {
  public static void main(String[] args) {
    NaElement xi;
    NaElement xo;
    NaServer  s;
    String filer = args[0];
    String user = args[1];
    String passwd = args[2];
    String vol = args[3];
    String ssname = args[4];

    if ( args.length < 5 ) {
      System.err.print(
        "Usage: snap filer user passwd");
      System.err.println(
        " volume snapshotname");
      System.exit(-1);
    }

    s = new NaServer(filer,1,3);
    s.setStyle(NaServer.LOGIN_PASSWORD);
    s.setAdminUser(user, passwd);
    s.setTransportType(
      NaServer.NA_SERVER_TRANSPORT_HTTPS);

    xi = new NaElement(
             "snapshot-create");
    xi.addNewChild("volume", vol);
    xi.addNewChild("snapshot", ssname);

    try {
      xo = s.invokeElem(xi);
    }
    catch (Exception e) {
      e.printStackTrace();
      System.exit(-3);
    }
    finally {
      s.close();
    }
  }
}