OargridApi Tutorial
From Grid5000
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.
Prerequisites
- Oargrid knowledge: Oargrid tutorial, OAR2 use cases
- Ruby bases: Learning Ruby
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.
So, to access comfortably to the API from your local unix host, we can mount an ssh tunnel:
(replace <login> with your g5k login)
Then we can connect to the API and make our first query, with wget:
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:
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:
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:
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:
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:
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:
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):
Then, start the Ruby Restclient:
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:
--- - 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:
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:
And then, let's encode our job:
Now, we are ready to post it, on the Nancy site for example:
Note that we have requested the result in YAML, even if we posted the data in JSON :-)
--- 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:
---
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:
--- 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:
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):
Submission is made with a post on the /grid/jobs resource:
---
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:
---
- jobs:
216465:
name:
nodes:
- grelon-5.nancy.grid5000.fr
site: nancy
- jobs:
126937:
name:
nodes:
- genepi-34.grenoble.grid5000.fr
site: grenoble
=> nil
--- - 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:
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:
For details about only one node:
Finaly, deleting a grid job is as simple as a DELETE request on the job id:
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
