OargridApi Tutorial

From Grid5000
Jump to: navigation, search


Contents

Introduction

In this tutorial, we'll learn how to connect and use the oargrid RESTful API. We'll make a script taking advantage of this API to show you how it can be useful for your Grid5000 experiments.

You may also refer to the documentation of the API on the OAR wiki site.

Warning.png Warning

At the moment of writing, the OAR and OARGRID APIs developpement is a work still in progress. The OARGRID API is currently only installed on the Grenoble's web server. Efforts are made to provide to you an API that is working on the current version of oar and oargrid even if a full rewriting is already planned for the next oar and oargrid releases, to improve performances and functionalities.

Warning.png Warning

The OAR and OARGRID API are currently usable, but unstable: it means that there's no warranty that the output data structures will not change in a near future, until a stable version (1.x) is released

Prerequisites

Connection

The API uses a protocol (ident) that automatically recognizes your login to authenticate your requests. But, as the API needs to fully trust in the security of the hosts implementing this protocol, only a few hosts are allowed to connect to the API using this authentication method.

The hosts allowed to connect to the oargrid api are the access hosts and frontends of Grid5000.

Warning.png Warning

At the moment of writing, the only hosts trusted are grenoble's frontend and access host

So, to access comfortably to the API from your local unix host, we can mount an ssh tunnel:

Terminal.png outside:
ssh -NL 3443:api.grenoble.grid5000.fr:443 <login>@access.grenoble.grid5000.fr

(replace <login> with your g5k login)

Then we can connect to the API and make our first query, with wget:

Terminal.png outside:
wget -O - -nv --no-check-certificate https://localhost:3443/oargridapi/version.yaml

Html browsing

A very simple HTML version of the API makes it look like a little sober web interface. You can point your web browser to https://localhost:3443/oargridapi/index.html and access to most of the OargridAPI functions, even grid-job submission! This is not intended to be a powerful web interface and some functions may be missing, but it may help you to quickly test something.

First REST queries

Well, we already did some REST queries, especially if you browsed the HTML version :-) Let's go back to our wget command line client and try some other things:

Terminal.png outside:
wget -nv -O - --no-check-certificate https://localhost:3443/oargridapi/sites/grenoble --header='Accept: application/json'
{
  "resources" : "/sites/grenoble/resources",
  "site" : "grenoble",
  "frontend" : "genepi.grenoble.grid5000.fr",
  "clusters" : [
     "genepi"
  ],
  "uri" : "/sites/grenoble"
}

Here, we asked for the /sites/grenoble resource and we obtained informations about the Grenoble's site. REST is a resource based model. Don't mistake the REST resources (objects of the queries) with the Grid5000 resources (nodes or cpus). In this answer, you can see that there's an URI "resources" : "/sites/grenoble/resources" that you can get to obtain more details or other linked informations. It is common with REST to give URIs in the answers that point to other informations.

This query should give you the same thing:

Terminal.png outside:
wget -nv -O - --no-check-certificate https://localhost:3443/oargridapi/sites/grenoble.json

So, you now know the 2 ways to ask for a format: by setting the HTTP ACCEPT header variable or by adding an extension to the resource queried.

Then, try this:

Terminal.png outside:
wget -nv -O - --no-check-certificate https://localhost:3443/oargridapi/sites/grenoble.yaml

You get the same data formated in another standard. The currently supported formats are YAML and JSON. There's also support for HTML format, but it is for the browser support and it is not intended to be usable by your scripts (it adds an HTML header and some tags for the links)

Let's see what happens if we do not specify the format:

Terminal.png outside:
wget -O - --no-check-certificate https://localhost:3443/oargridapi/sites/grenoble
HTTP request sent, awaiting response... 406 Not Acceptable
2009-04-08 09:07:00 ERROR 406: Not Acceptable.

Here, you get a 406 http error code, meaning that the format requested is "Not Acceptable". This is one of the principles of REST. We try to use, as far as we can, the standard HTTP error codes to inform you about a problem. Likewise, we use standard GET, POST, PUT and DELETE http methods in the queries. For the moment, we only dit GET requests. You'll see later that the submission of a new grid job is nothing else than a POST to the /grid/jobs resource.

But regarding this 406 error, couldn't we have a more precise information? Let's try this:

Terminal.png outside:
wget -nv -O - --no-check-certificate "https://localhost:3443/oargridapi/sites/grenoble?debug=1"
{
  "title" : "ERROR 406 - Invalid content type required */*",
  "message" : "Valid types are text/yaml, application/json or text/html",
  "code" : "200"
}

In fact, in the 406 error given by the API, there's also a body. But wget did not print it. With the debug=1 variable, I ask to the API to always return a 200 OK status, even if there's an error so that I can see the body with a simple browser or a rest client without having to manage the errors.

Another way to use this debug feature is to connect to oargridapi-debug instead of oargridapi:

Terminal.png outside:
wget -nv -O - --no-check-certificate "https://localhost:3443/oargridapi-debug/sites/grenoble"

Job posting

Using the Ruby REST client

Before starting to do more interesting things like submitting jobs, let's give up our wget tool and use a real REST client. We are going to use a Ruby library called restclient that may be interactively used through the ruby interactive shell (irb). If you have ruby and restclient installed on your local host, you can go on using the tunnel you opened in the previous section. But for the purpose of this tutorial, restclient is installed on the Grenoble's frontend, and you can connect to it (replace <login> with your login):

Terminal.png outside:
ssh <login>@access.grenoble.grid5000.fr
Terminal.png access:
ssh frontend

Then, start the Ruby Restclient:

Terminal.png frontend:
export PATH=$PATH:/var/lib/gems/1.8/gems/rest-client-0.8/bin
Terminal.png frontend:
restclient http://www.grenoble.grid5000.fr/oargridapi-debug
irb(main):001:0>

Here we are: inside an IRB (the interactive ruby shell) with a REST library pre-loaded and ready to use. Let's make a simple query to test it:

Terminal.png irb:
puts get('/sites',:accept=>'text/yaml')
---
- site: nancy
  uri: /sites/nancy
- site: grenoble-obs
  uri: /sites/grenoble-obs
- site: sophia
  uri: /sites/sophia
- site: orsay
  uri: /sites/orsay
- site: grenoble-exp
  uri: /sites/grenoble-exp
- site: toulouse
  uri: /sites/toulouse
- site: bordeaux
  uri: /sites/bordeaux
- site: lille
  uri: /sites/lille
- site: lyon
  uri: /sites/lyon
- site: luxembourg
  uri: /sites/luxembourg
- site: grenoble-ext
  uri: /sites/grenoble-ext
- site: grenoble 
  uri: /sites/grenoble
- site: portoalegre
  uri: /sites/portoalegre
- site: rennes
  uri: /sites/rennes
=> nil

Job submission on a site

The OARGRID API allows you to submit simple OAR jobs (as opposed to grid jobs) on a given site. It is equivalent to do a oarsub on the frontend of ths site. To submit a job, we must firstly prepare a hash describing this job. Here is a very simple job:

Terminal.png irb:
j={ 'resource' => '/nodes=2/cpu=1', 'script_path' => '/usr/bin/id' }

But before posting this job to the API, we must encode it into a supported format (YAML or JSON). Let's use JSON; so we must load the ruby JSON library:

Terminal.png irb:
require 'json'

And then, let's encode our job:

Terminal.png irb:
j=j.to_json

Now, we are ready to post it, on the Nancy site for example:

Terminal.png irb:
job=post('/sites/nancy/jobs.yaml' , j , :content_type => 'application/json')

Note that we have requested the result in YAML, even if we posted the data in JSON :-)

Terminal.png irb:
puts job
---
id: 216456
state: submitted
uri: /sites/nancy/jobs/216456
=> nil

If all is ok, we got a submited state. Then, we can ask some details about our job:

Terminal.png irb:
puts get('/sites/nancy/jobs/216456.yaml')
---                                                                                          
Job_Id: 216456                                                                               
assigned_network_address:                                                                    
  - grelon-3.nancy.grid5000.fr                                                               
  - grelon-4.nancy.grid5000.fr
assigned_resources:
  - 167
  - 168
  - 571
  - 572
command: /usr/bin/id
cpuset_name: bbzeznik_216456
dependencies: []
events:
  - date: 1238005424
    description: '[bipbip 216456] Ask to change the job state'
    event_id: 557856
    job_id: 216456
    to_check: NO
    type: SWITCH_INTO_TERMINATE_STATE
exit_code: 0
jobType: PASSIVE
launchingDirectory: /home/grenoble/bbzeznik
message: FIFO scheduling OK
name: ~
owner: bbzeznik
project: default
properties: 
queue: default
reservation: None
resubmit_job_id: 0
scheduledStart: ~
startTime: 1238005421
state: Terminated
submissionTime: 1238005419
types: []
walltime: 3600
wanted_resources: "-l \"{type = 'default'}/network_address=2/cpu=1,walltime=1:0:0\" "
=> nil

Now, let's delete this job, but this time, we are going to decode the YAML data to extract the id of our submited job:

Terminal.png irb:
require 'yaml'
Terminal.png irb:
job=YAML::load(job)
Terminal.png irb:
puts delete("/sites/nancy/jobs/#{job['id']}.yaml")
---
id: 216463
oardel_output: |
  Deleting the job = 216463 ...REGISTERED.
  The job(s) [ 216463 ] will be deleted in a near future.
status: Delete request registered
=> nil

More complicated jobs may be submitted: each oarsub option may be used as an hash key. For example:

Terminal.png irb:
j={ 'resource' => '/nodes=2/cpu=1', 'script_path' => '/usr/bin/id', 'type' => 'besteffort', 'property' => '"virtual"=\"ivt\"' }

Grid job submission

A grid job looks like that (the mandatory resources key has the same syntax of the oargridsub DESC syntax):

Terminal.png irb:
j={ 'resources' => 'grenoble:rdef=/core=1,nancy:rdef=/core=1' }

Submission is made with a post on the /grid/jobs resource:

Terminal.png irb:
puts job=post('/grid/jobs.yaml' , j.to_json , :content_type => 'application/json')
---
cluster_jobs:
  - cluster: nancy
    id: 217029
    uri: /sites/nancy/jobs/217029
  - cluster: grenoble
    id: 171159
    uri: /sites/grenoble/jobs/171159
command: 'oargridsub  grenoble:rdef=/core=1,nancy:rdef=/core=1'
id: 19307
nodes_uri: /grid/jobs/19307/resources/nodes
resources_uri: /grid/jobs/19307/resources
ssh_key_path: /tmp/oargrid//oargrid_ssh_key_bbzeznik_19307
ssh_private_key_uri: /grid/jobs/19307/keys/private
ssh_public_key_uri: /grid/jobs/19307/keys/public
state: submitted
uri: /grid/jobs/19307
=> nil

If the job is successful, you get a submited state and URIs to details about your job. The resources uri gives you details about the resources obtained. The nodes uri gives you a simple array containing the names of the nodes obtained (each node may be listed several times if you have several resources per node), for example:

Terminal.png irb:
puts get('/grid/jobs/19023/resources.yaml')
---
- jobs:
    216465:
      name: 
      nodes:
        - grelon-5.nancy.grid5000.fr
  site: nancy
- jobs:
    126937:
      name: 
      nodes:
        - genepi-34.grenoble.grid5000.fr
  site: grenoble
=> nil
Terminal.png irb:
puts get('/grid/jobs/19023/resources/nodes.yaml')
---
- grelon-5.nancy.grid5000.fr
- genepi-34.grenoble.grid5000.fr
=> nil

If you need details about the nodes, you can get the resources properties of the sites, for example:

Terminal.png irb:
puts get('/sites/grenoble/resources.yaml')

The previous request gives you only the list and uris to details about each resource. If you want all the details about the resources at once (properties, jobs running on the resources,...) you can use the following request:

Terminal.png irb:
puts get('/sites/grenoble/resources/all.yaml')

For details about only one node:

Terminal.png irb:
puts get('/sites/grenoble/resources/nodes/genepi-34.grenoble.grid5000.fr.yaml')

Finaly, deleting a grid job is as simple as a DELETE request on the job id:

Terminal.png irb:
puts delete('/grid/jobs/19023.yaml')

Let's make a nice ruby script

Putting all this together, let's try to make a script that will do:

  • Discover 3 free resources (cpu core) from 3 different sites
  • Automatically submit a grid job on this 3 resources
  • Wait for all the resources to be effectively allocated
  • Print the properties of the resources
  • Delete the grid job

The ruby rest client may be used into a script. Simply start your script with:

#!/usr/bin/ruby
require 'rubygems'                                                                                                  
require 'rest_client' 

We can make a function to automatically convert GET queries into ruby objects:

# Function to get objects from the api                                                                              
# We use the JSON format                                                         
def get(api,uri)                                                                                                    
  begin                                                                                                             
    return JSON.parse(api[uri].get(:accept => 'application/json'))                              
  rescue => e                                                                                                       
    if e.respond_to?('http_code')                                                                                   
      puts "ERROR #{e.http_code}:\n #{e.response.body}"                                                             
    else                                                                                                            
      puts "Parse error:"                                                                                           
      puts e.inspect                                                                                                
    end                                                                                                             
    exit 1                                                                                                          
  end                                                                                                               
end                                                                                                                 

Note the error management with the begin/rescue blocks.

Such a function may be used like that:

# Instanciate an api connection                                                                                     
api = RestClient::Resource.new http://www.grenoble.grid5000.fr/oargridapi
# Get the sites list                                           
sites = get(api,'/sites')
# Parse the sites
sites.each |site| do
...

Now, your turn :-)

Solution is on the OAR wiki site

Personal tools
Namespaces

Variants
Actions
Public Portal
Users Portal
Admin portal
Wiki special pages
Toolbox