diff --git a/src/cloud/occi/test/fixtures/compute/empty.xml b/src/cloud/occi/test/fixtures/compute/empty.xml
new file mode 100644
index 0000000000..17ef393d05
--- /dev/null
+++ b/src/cloud/occi/test/fixtures/compute/empty.xml
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/cloud/occi/test/fixtures/compute/first_compute.xml b/src/cloud/occi/test/fixtures/compute/first_compute.xml
new file mode 100644
index 0000000000..4824a2b00e
--- /dev/null
+++ b/src/cloud/occi/test/fixtures/compute/first_compute.xml
@@ -0,0 +1,20 @@
+
+ 0
+
+ oneadmin
+ 1
+ 1024
+ Compute
+ small
+ PENDING
+
+
+ DISK
+ hde
+
+
+
+ 192.168.1.1
+ 02:00:c0:a8:01:01
+
+
diff --git a/src/cloud/occi/test/fixtures/compute/first_compute_done.xml b/src/cloud/occi/test/fixtures/compute/first_compute_done.xml
new file mode 100644
index 0000000000..46bd2c8352
--- /dev/null
+++ b/src/cloud/occi/test/fixtures/compute/first_compute_done.xml
@@ -0,0 +1,20 @@
+
+ 0
+
+ oneadmin
+ 1
+ 1024
+ Compute
+ small
+ DONE
+
+
+ DISK
+ hde
+
+
+
+ 192.168.1.1
+ 02:00:c0:a8:01:01
+
+
diff --git a/src/cloud/occi/test/fixtures/compute/second_compute.xml b/src/cloud/occi/test/fixtures/compute/second_compute.xml
new file mode 100644
index 0000000000..ee057ed43e
--- /dev/null
+++ b/src/cloud/occi/test/fixtures/compute/second_compute.xml
@@ -0,0 +1,20 @@
+
+ 1
+
+ users
+ 1
+ 1024
+ Compute2
+ small
+ PENDING
+
+
+ DISK
+ hde
+
+
+
+ 192.168.2.1
+ 02:00:c0:a8:02:01
+
+
diff --git a/src/cloud/occi/test/fixtures/compute/second_compute_done.xml b/src/cloud/occi/test/fixtures/compute/second_compute_done.xml
new file mode 100644
index 0000000000..e73c28ba3b
--- /dev/null
+++ b/src/cloud/occi/test/fixtures/compute/second_compute_done.xml
@@ -0,0 +1,20 @@
+
+ 1
+
+ users
+ 1
+ 1024
+ Compute2
+ small
+ DONE
+
+
+ DISK
+ hde
+
+
+
+ 192.168.2.1
+ 02:00:c0:a8:02:01
+
+
diff --git a/src/cloud/occi/test/fixtures/instance_type/extended.xml b/src/cloud/occi/test/fixtures/instance_type/extended.xml
new file mode 100644
index 0000000000..f774763320
--- /dev/null
+++ b/src/cloud/occi/test/fixtures/instance_type/extended.xml
@@ -0,0 +1,20 @@
+
+
+ large
+ large
+ 8
+ 8192
+
+
+ medium
+ medium
+ 4
+ 4096
+
+
+ small
+ small
+ 1
+ 1024
+
+
diff --git a/src/cloud/occi/test/fixtures/instance_type/list.xml b/src/cloud/occi/test/fixtures/instance_type/list.xml
new file mode 100644
index 0000000000..135ae70491
--- /dev/null
+++ b/src/cloud/occi/test/fixtures/instance_type/list.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/src/cloud/occi/test/fixtures/network/empty.xml b/src/cloud/occi/test/fixtures/network/empty.xml
new file mode 100644
index 0000000000..c96d82e6ed
--- /dev/null
+++ b/src/cloud/occi/test/fixtures/network/empty.xml
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/cloud/occi/test/fixtures/network/first_net.xml b/src/cloud/occi/test/fixtures/network/first_net.xml
new file mode 100644
index 0000000000..24b388783d
--- /dev/null
+++ b/src/cloud/occi/test/fixtures/network/first_net.xml
@@ -0,0 +1,11 @@
+
+ 0
+ Network
+
+ oneadmin
+ Network of the user my_first_occi_user
+ 192.168.1.1
+ 125
+ 0
+ NO
+
diff --git a/src/cloud/occi/test/fixtures/network/second_net.xml b/src/cloud/occi/test/fixtures/network/second_net.xml
new file mode 100644
index 0000000000..9cad2d9594
--- /dev/null
+++ b/src/cloud/occi/test/fixtures/network/second_net.xml
@@ -0,0 +1,11 @@
+
+ 1
+ Network2
+
+ users
+ Network of the user my_second_occi_user
+ 192.168.2.1
+ 125
+ 0
+ NO
+
diff --git a/src/cloud/occi/test/fixtures/root.xml b/src/cloud/occi/test/fixtures/root.xml
new file mode 100644
index 0000000000..38843f0418
--- /dev/null
+++ b/src/cloud/occi/test/fixtures/root.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/src/cloud/occi/test/fixtures/storage/empty.xml b/src/cloud/occi/test/fixtures/storage/empty.xml
new file mode 100644
index 0000000000..622d314777
--- /dev/null
+++ b/src/cloud/occi/test/fixtures/storage/empty.xml
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/cloud/occi/test/fixtures/storage/first_storage.xml b/src/cloud/occi/test/fixtures/storage/first_storage.xml
new file mode 100644
index 0000000000..c24bea57ae
--- /dev/null
+++ b/src/cloud/occi/test/fixtures/storage/first_storage.xml
@@ -0,0 +1,13 @@
+
+ 0
+ Storage
+
+ oneadmin
+ READY
+ DATABLOCK
+ Storage of the user my_first_occi_user
+ 100
+ ext3
+ NO
+ NO
+
diff --git a/src/cloud/occi/test/fixtures/storage/second_storage.xml b/src/cloud/occi/test/fixtures/storage/second_storage.xml
new file mode 100644
index 0000000000..defd5b1a56
--- /dev/null
+++ b/src/cloud/occi/test/fixtures/storage/second_storage.xml
@@ -0,0 +1,13 @@
+
+ 1
+ Storage2
+
+ users
+ READY
+ DATABLOCK
+ Storage of the user my_second_occi_user
+ 100
+ ext3
+ NO
+ NO
+
diff --git a/src/cloud/occi/test/spec/occi_spec.rb b/src/cloud/occi/test/spec/occi_spec.rb
new file mode 100644
index 0000000000..ba35ae94dd
--- /dev/null
+++ b/src/cloud/occi/test/spec/occi_spec.rb
@@ -0,0 +1,283 @@
+# -------------------------------------------------------------------------- #
+# Copyright 2002-2012, OpenNebula Project Leads (OpenNebula.org) #
+# #
+# Licensed under the Apache License, Version 2.0 (the "License"); you may #
+# not use this file except in compliance with the License. You may obtain #
+# a copy of the License at #
+# #
+# http://www.apache.org/licenses/LICENSE-2.0 #
+# #
+# Unless required by applicable law or agreed to in writing, software #
+# distributed under the License is distributed on an "AS IS" BASIS, #
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
+# See the License for the specific language governing permissions and #
+# limitations under the License. #
+#--------------------------------------------------------------------------- #
+
+require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
+
+# The following methods are helpers defined in spec_helper
+# - compare_xml
+# - get_fixture
+# - network_template
+# - storage_template
+# - compute_template
+
+describe 'OCCI tests' do
+ before(:all) do
+ @user_oneadmin = "my_first_occi_user"
+ `oneuser create #{@user_oneadmin} my_pass`.scan(/^ID: (\d+)/) { |uid|
+ `oneuser show #{uid.first}`.scan(/PASSWORD\s*:\s*(\w*)/) { |password|
+ @user_pass = password.first.strip
+ }
+
+ `oneuser chgrp #{uid.first} 0`
+ }
+
+ @user_users = "my_second_occi_user"
+ `oneuser create #{@user_users} my_pass2`.scan(/^ID: (\d+)/) { |uid|
+ `oneuser show #{uid.first}`.scan(/PASSWORD\s*:\s*(\w*)/) { |password|
+ @user_pass2 = password.first.strip
+ }
+ }
+
+ # Define BRIDGE attirbute in network.erb, otherwise the NETWORK creation will
+ `sed -i.bck "s%^#\\(BRIDGE = \\).*$%\\1 br0%" $ONE_LOCATION/etc/occi_templates/network.erb`
+ end
+
+ describe "with a user of the oneadmin group" do
+ before(:each) do
+ basic_authorize(@user_oneadmin, @user_pass)
+ end
+
+ it "should retrieve the list of collections" do
+ get '/'
+ compare_xml(last_response.body, get_fixture('/root.xml'))
+ last_response.status.should == 200
+ end
+
+ it "should retrieve the list of INSTANCE_TYPEs" do
+ get '/instance_type'
+ compare_xml(last_response.body, get_fixture('/instance_type/list.xml'))
+ last_response.status.should == 200
+ end
+
+ it "should retrieve the extended list of INSTANCE_TYPEs" do
+ get '/instance_type', {'verbose'=>true}
+ compare_xml(last_response.body, get_fixture('/instance_type/extended.xml'))
+ last_response.status.should == 200
+ end
+
+ context "for NETWORK" do
+ it "should retrieve the empty list" do
+ get '/network'
+ compare_xml(last_response.body, get_fixture('/network/empty.xml'))
+ last_response.status.should == 200
+ end
+
+ it "should create a new NETWORK" do
+ network = {
+ :name => "Network",
+ :description => "Network of the user #{@user_oneadmin}",
+ :address => "192.168.1.0",
+ :size => "100",
+ :pubic => "YES"
+ }
+
+ post '/network', network_template(network)
+ compare_xml(last_response.body, get_fixture('/network/first_net.xml'))
+ last_response.status.should == 201
+ end
+
+ it "should retrieve the NETWORK with ID 0" do
+ get '/network/0'
+ compare_xml(last_response.body, get_fixture('/network/first_net.xml'))
+ last_response.status.should == 200
+ end
+ end
+
+ context "for STORAGE" do
+ it "should retrieve the empty list" do
+ get '/storage'
+ compare_xml(last_response.body, get_fixture('/storage/empty.xml'))
+ last_response.status.should == 200
+ end
+
+ it "should create a new STORAGE, type DATABLOCK. This request waits until the IMAGE is ready in OpenNebula" do
+ storage = {
+ :name => "Storage",
+ :description => "Storage of the user #{@user_oneadmin}",
+ :type => "DATABLOCK",
+ :size => "100",
+ :fstype => "ext3"
+ }
+
+ post '/storage', {'occixml' => storage_template(storage)}
+ compare_xml(last_response.body, get_fixture('/storage/first_storage.xml'))
+ last_response.status.should == 201
+ end
+
+ it "should retrieve the STORAGE with ID 0" do
+ get '/storage/0'
+ compare_xml(last_response.body, get_fixture('/storage/first_storage.xml'))
+ last_response.status.should == 200
+ end
+ end
+
+ context "for COMPUTE" do
+ it "should retrieve the empty list" do
+ get '/compute'
+ compare_xml(last_response.body, get_fixture('/compute/empty.xml'))
+ last_response.status.should == 200
+ end
+
+ it "should create a new COMPUTE using the previous NETWORK (ID=0) and STORAGE(ID=0)" do
+ compute = {
+ :name => "Compute",
+ :instance_type => "small",
+ :disk => [ {:storage => '0'} ],
+ :nic => [ {:network => '0'} ]
+ }
+
+ post '/compute', compute_template(compute)
+ compare_xml(last_response.body, get_fixture('/compute/first_compute.xml'))
+ last_response.status.should == 201
+ end
+
+ it "should retrieve the COMPUTE with ID 0" do
+ get '/compute/0'
+ compare_xml(last_response.body, get_fixture('/compute/first_compute.xml'))
+ last_response.status.should == 200
+ end
+
+ it "should terminate (DONE) the COMPUTE with ID 0" do
+ compute = {
+ :id => "0",
+ :state => "DONE"
+ }
+
+ put '/compute/0', compute_action(compute)
+ compare_xml(last_response.body, get_fixture('/compute/first_compute_done.xml'))
+ last_response.status.should == 202
+ end
+ end
+ end
+
+ describe "with a user of the users group" do
+ before(:each) do
+ basic_authorize(@user_users, @user_pass2)
+ end
+
+ it "should retrieve the list of collections" do
+ get '/'
+ compare_xml(last_response.body, get_fixture('/root.xml'))
+ last_response.status.should == 200
+ end
+
+ it "should retrieve the list of INSTANCE_TYPEs" do
+ get '/instance_type'
+ compare_xml(last_response.body, get_fixture('/instance_type/list.xml'))
+ last_response.status.should == 200
+ end
+
+ it "should retrieve the extended list of INSTANCE_TYPEs" do
+ get '/instance_type', {'verbose'=>true}
+ compare_xml(last_response.body, get_fixture('/instance_type/extended.xml'))
+ last_response.status.should == 200
+ end
+
+ context "for NETWORK" do
+ it "should retrieve the empty list" do
+ get '/network'
+ compare_xml(last_response.body, get_fixture('/network/empty.xml'))
+ last_response.status.should == 200
+ end
+
+ it "should create a new NETWORK" do
+ network = {
+ :name => "Network2",
+ :description => "Network of the user #{@user_users}",
+ :address => "192.168.2.0",
+ :size => "100",
+ :pubic => "YES"
+ }
+
+ post '/network', network_template(network)
+ compare_xml(last_response.body, get_fixture('/network/second_net.xml'))
+ last_response.status.should == 201
+ end
+
+ it "should retrieve the NETWORK with ID 1" do
+ get '/network/1'
+ compare_xml(last_response.body, get_fixture('/network/second_net.xml'))
+ last_response.status.should == 200
+ end
+ end
+
+ context "for STORAGE" do
+ it "should retrieve the empty list" do
+ get '/storage'
+ compare_xml(last_response.body, get_fixture('/storage/empty.xml'))
+ last_response.status.should == 200
+ end
+
+ it "should create a new STORAGE, type DATABLOCK. This request waits until the IMAGE is ready in OpenNebula" do
+ storage = {
+ :name => "Storage2",
+ :description => "Storage of the user #{@user_users}",
+ :type => "DATABLOCK",
+ :size => "100",
+ :fstype => "ext3"
+ }
+
+ post '/storage', {'occixml' => storage_template(storage)}
+ compare_xml(last_response.body, get_fixture('/storage/second_storage.xml'))
+ last_response.status.should == 201
+ end
+
+ it "should retrieve the STORAGE with ID 1" do
+ get '/storage/1'
+ compare_xml(last_response.body, get_fixture('/storage/second_storage.xml'))
+ last_response.status.should == 200
+ end
+ end
+
+ context "for COMPUTE" do
+ it "should retrieve the empty list" do
+ get '/compute'
+ compare_xml(last_response.body, get_fixture('/compute/empty.xml'))
+ last_response.status.should == 200
+ end
+
+ it "should create a new COMPUTE using the previous NETWORK (ID=1) and STORAGE(ID=1)" do
+ compute = {
+ :name => "Compute2",
+ :instance_type => "small",
+ :disk => [ {:storage => '1'} ],
+ :nic => [ {:network => '1'} ]
+ }
+
+ post '/compute', compute_template(compute)
+ compare_xml(last_response.body, get_fixture('/compute/second_compute.xml'))
+ last_response.status.should == 201
+ end
+
+ it "should retrieve the COMPUTE with ID 1" do
+ get '/compute/1'
+ compare_xml(last_response.body, get_fixture('/compute/second_compute.xml'))
+ last_response.status.should == 200
+ end
+
+ it "should terminate (DONE) the COMPUTE with ID 1" do
+ compute = {
+ :id => "1",
+ :state => "DONE"
+ }
+
+ put '/compute/1', compute_action(compute)
+ compare_xml(last_response.body, get_fixture('/compute/second_compute_done.xml'))
+ last_response.status.should == 202
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/src/cloud/occi/test/spec/spec.opts b/src/cloud/occi/test/spec/spec.opts
new file mode 100644
index 0000000000..ad561bd258
--- /dev/null
+++ b/src/cloud/occi/test/spec/spec.opts
@@ -0,0 +1,4 @@
+--colour
+--format progress
+--loadby mtime
+--reverse
\ No newline at end of file
diff --git a/src/cloud/occi/test/spec/spec_helper.rb b/src/cloud/occi/test/spec/spec_helper.rb
index b2a2917a97..0d7423a2fc 100644
--- a/src/cloud/occi/test/spec/spec_helper.rb
+++ b/src/cloud/occi/test/spec/spec_helper.rb
@@ -24,6 +24,8 @@ require 'rubygems'
require 'rspec'
require 'rack/test'
+require 'rexml/document'
+
# Load the Sinatra app
require 'occi-server'
@@ -39,3 +41,153 @@ set :environment, :test
def app
Sinatra::Application
end
+
+
+
+def get_fixture(path)
+ File.read(FIXTURES_PATH + path).strip
+end
+
+
+def compare_xml(a, b)
+ a = REXML::Document.new(a.to_s)
+ b = REXML::Document.new(b.to_s)
+
+ normalized = Class.new(REXML::Formatters::Pretty) do
+ def write_text(node, output)
+ super(node.to_s.strip, output)
+ end
+ end
+
+ normalized.new(indentation=0,ie_hack=false).write(node=a, a_normalized='')
+ normalized.new(indentation=0,ie_hack=false).write(node=b, b_normalized='')
+
+ a_normalized.should == b_normalized
+end
+
+
+OCCI_NETWORK = %q{
+
+ <% if hash[:name] %>
+ <%= hash[:name] %>
+ <% end %>
+
+ <% if hash[:description] %>
+ <%= hash[:description] %>
+ <% end %>
+
+ <% if hash[:address] %>
+ <%= hash[:address] %>
+ <% end %>
+
+ <% if hash[:size] %>
+ <%= hash[:size] %>
+ <% end %>
+
+ <% if hash[:public] %>
+ <%= hash[:public] %>
+ <% end %>
+
+}
+
+def network_template(hash)
+ ERB.new(OCCI_NETWORK).result(binding)
+end
+
+OCCI_IMAGE = %q{
+
+ <% if hash[:name] %>
+ <%= hash[:name] %>
+ <% end %>
+
+ <% if hash[:type] %>
+ <%= hash[:type] %>
+ <% end %>
+
+ <% if hash[:description] %>
+ <%= hash[:description] %>
+ <% end %>
+
+ <% if hash[:size] %>
+ <%= hash[:size] %>
+ <% end %>
+
+ <% if hash[:fstype] %>
+ <%= hash[:fstype] %>
+ <% end %>
+
+ <% if hash[:public] %>
+ <%= hash[:public] %>
+ <% end %>
+
+ <% if hash[:persistent] %>
+ <%= hash[:persistent] %>
+ <% end %>
+
+}
+
+def storage_template(hash)
+ ERB.new(OCCI_IMAGE).result(binding)
+end
+
+OCCI_VM = %q{
+
+ <% if hash[:name] %>
+ <%= hash[:name] %>
+ <% end %>
+
+ <% if hash[:instance_type] %>
+
+ <% end %>
+
+ <% if hash[:disk] %>
+ <% hash[:disk].each { |disk| %>
+
+ <% if disk[:storage] %>
+
+ <% end %>
+
+ <% } %>
+ <% end %>
+
+ <% if hash[:nic] %>
+ <% hash[:nic].each { |nic| %>
+
+ <% if nic[:network] %>
+
+ <% end %>
+ <% if nic[:ip] %>
+ <%= nic[:ip] %>
+ <% end %>
+
+ <% } %>
+ <% end %>
+
+ <% if hash[:context] %>
+
+ <% hash[:context].each { |key, value| %>
+ <<%= key.to_s.upcase %>><%= value %><%= key.to_s.upcase %>>
+ <% } %>
+
+ <% end %>
+
+}
+
+OCCI_VM_ACTION = %q{
+
+ <% if hash[:id] %>
+ <%= hash[:id] %>
+ <% end %>
+ <% if hash[:state] %>
+ <%= hash[:state] %>
+ <% end %>
+
+}
+
+def compute_template(hash)
+ ERB.new(OCCI_VM).result(binding)
+end
+
+def compute_action(hash)
+ ERB.new(OCCI_VM_ACTION).result(binding)
+end