Ok, I hear it from many webdev now, Ruby is easy, Ruby is great, Ruby is … ect :)

I found myself bound to Ruby, because the only descent library (not over-complicated) I could find to connect to a VMWare vSphere server was RBVMomi.

This is just nice, now I want to learn Ruby but… It’s just too bad if you want a simple interface for another language/platform.

Since HTTP is a widely used protocol, I decided to implement a REST bridge between rbvmomi and any other applications I could use.

RBVMomi being in ruby, I decided to use the Sinatra framework.

So far, I just started learning it, and I’m really happy with ruby. That piece of code says it all. I got a POC done quickly and it’s working just awesome for my use :)

The code

rbvmomi-server.rb

# File rbvmomi-server.rb 
require 'sinatra'
require 'rbvmomi'
require 'json'
require 'iniparse'
require 'singleton'
require 'redis'
#
# Persistent API server. Lifetime is process lifetime
# Implementing redis as queue storage makes it multithread and restart safe
#
class APIServer
  include Singleton
   
  #internal : singletone init:
  def initialize 
    @config = IniParse.parse( File.read('config.ini') )
    @redis = Redis.new(@config['redis'])
    @vim = RbVmomi::VIM.connect(:host => @config['vsphere']['host'], :user => @config['vsphere']['user'],
      :password=> @config['vsphere']['password'], :insecure => @config['vsphere']['insecure'])
    @dc = @vim.serviceInstance.find_datacenter(@config['vsphere']['datacenter'])
    
  end
  #internal : jobqueue
  def getjob(jobid)
    return JSON.parse(@redis.get(jobid))
    
  end
  def setjob(jobid,data)
    return @redis.set(jobid, data.to_json)
  end
  
  #API functions :
  def _api_getdatastoreinfo(params)
    output = Array.new
    paths = %w(name info.url summary.accessible summary.capacity summary.freeSpace)
    propSet = [{ :type => 'Datastore', :pathSet => paths }]
    filterSpec = { :objectSet => @dc.datastore.map { |ds| { :obj => ds } }, :propSet => propSet }
    data = @vim.propertyCollector.RetrieveProperties(:specSet => [filterSpec])
    data.select { |d| d['summary.accessible'] }.sort_by { |d| d['name'] }.each do |d|
        size = d['summary.capacity']
        free = d['summary.freeSpace']
        used = size - free
        pct_used = used*100.0/size
        if d['name'].match('pcc-')
          output << {'url' => d['info.url'], 'size' => size,  'used' =>  used, 'free' => free, 'percent' => pct_used, 'name' => d['name']}
        end
    end
    return output
  end
  
  def _api_getjob(params)
    return getjob(params['jobid'])
  end
  
  def _api_setjob(params)
    return setjob(params['jobid'],params['data'])
  end
  
end

get '/api/*' do |method|
  APIServer.instance.method(['_api_' , method].join()).call(@params).to_json
end

config.ini

[vsphere]
hostname = xxxx
user = xxxx
password = xxxx
insecure = xxxx
datacenter = xxxx
[redis]
path = /var/run/redis.sock
---or----
host = xxxxx
port = xxxxx

Testing

We start the server :

$ ruby -rubygems rbvmomi-server.rb 
[2012-04-29 14:41:23] INFO  WEBrick 1.3.1
[2012-04-29 14:41:23] INFO  ruby 1.8.7 (2011-06-30) [x86_64-linux]
== Sinatra/1.3.2 has taken the stage on 4567 for development with backup from WEBrick
[2012-04-29 14:41:23] INFO  WEBrick::HTTPServer#start: pid=18829 port=4567

And we test ! :

$ curl "http://localhost:1234/api/getdatastoreinfo"
[{"percent":64.4684278916377,"used":188676997120,"free":103988736000,"size":292665733120,"name":"xxx","url":"netfs://3xxxxx/main/"},{"percent":60.7995903847724,"used":178150768640,"free":114862338048,"size":293013106688,"name":"xxx","url":"netfs://2xxx/main/"},{"percent":17.0715763111187,"used":49474035712,"free":240329523200,"size":289803558912,"name":"xxx","url":"netfs://1xxx/main/"}]
$

Yeaah

Running getjob/setjob

Sorry for my poor Ruby skills, I’m new at it.