1
0
mirror of https://github.com/OpenNebula/one.git synced 2025-03-21 14:50:08 +03:00

Merge branch 'feature-873'

This commit is contained in:
Ruben S. Montero 2011-10-19 23:51:39 +02:00
commit cee35c0750
24 changed files with 648 additions and 127 deletions

View File

@ -99,7 +99,7 @@ cmd=CommandParser::CmdParser.new(ARGV) do
arg_list.map! {|a| a.to_i }
[0, arg_list]
end
set :format, :userid, OpenNebulaHelper.rname_to_id_desc("USER") do |arg|
OpenNebulaHelper.rname_to_id(arg, "USER")
end
@ -130,6 +130,26 @@ cmd=CommandParser::CmdParser.new(ARGV) do
exit_with_code 0
end
########################################################################
unset_desc = <<-EOT.unindent
Unset a quota for a given user.
Examples:
onequota unset 3 cpu
onequota unset 4 cpu,memory,storage
EOT
command :unset, unset_desc, :userid, :quota_list do
user_id, keys = args
values_hash = Hash.new
keys.each_with_index { |k,i|
values_hash[k.to_sym] = 0
}
quota.set_quota(user_id, values_hash)
exit_with_code 0
end
########################################################################
delete_desc = "Delete the defined quotas for the given user"
@ -140,7 +160,7 @@ cmd=CommandParser::CmdParser.new(ARGV) do
########################################################################
show_desc = "Show the user's quota and usage. (usage/quota)"
FORCE={
:name => "force",
:short => "-f",

View File

@ -154,6 +154,19 @@ class Quota
set(QUOTA_TABLE, uid, quota)
end
# Retrieves quota information for a given user
#
# @param [Integer, nil] uid the user id from which get the quota
# information, if nil will retrieve the quotas for all users.
# @return [Hash] Hash containing the quota information and the user id
#
# {
# :uid => 4,
# :cpu => 8,
# :memory => 8064,
# :num_vms => 4,
# :storage => 1240019
# }
def get_quota(uid=nil)
limit = get(QUOTA_TABLE, uid)
limit ? limit : @conf[:defaults].merge!(:uid => uid)
@ -192,14 +205,16 @@ class Quota
msg = ""
separator = ""
info.each { |qname, quota_requested|
unless quota[qname]
unless quota[qname] || quota[qname]==0
next
end
used = send(DB_QUOTA_SCHEMA[qname].name.to_sym, total[qname])
request = send(DB_QUOTA_SCHEMA[qname].name.to_sym, quota_requested)
limit = send(DB_QUOTA_SCHEMA[qname].name.to_sym, quota[qname])
spent = used + request
type = DB_QUOTA_SCHEMA[qname].name.to_sym
used = send(type, total[qname])
request = send(type, quota_requested)
limit = send(type, quota[qname])
spent = used + request
if spent > limit
msg << separator
@ -228,6 +243,22 @@ class Quota
###########################################################################
# Usage
###########################################################################
# Retrieves usage information for a given user
#
# @param [Integer] uid the user id from which get the usage information.
# @param ["VM", "IMAGE"] resource kind of resource. If nil will return
# the usage for all kinds of resources
# @param [true, false] force If true will force the usage calculation
# instead of retrieving it from the cache
# @return [Hash] Hash containing the usage information and the user id
#
# {
# :uid => 4,
# :cpu => 8,
# :memory => 8064,
# :num_vms => 4,
# :storage => 1240019
# }
def get_usage(user_id, resource=nil, force=false)
if force
if RESOURCES.include?(resource)
@ -242,9 +273,9 @@ class Quota
pool = get_pool(res, user_id)
base_xpath = "/#{res}_POOL/#{resource}"
Quota.const_get("#{res}_USAGE".to_sym).each { |key, params|
usage[key] ||= 0
pool.each_xpath("#{base_xpath}/#{params[:xpath]}") { |elem|
if elem
usage[key] ||= 0
if params[:count]
usage[key] += 1
else

View File

@ -31,7 +31,7 @@ describe "Quota testing" do
<TEMPLATE>
<CPU>2</CPU>
<MEMORY>128</MEMORY>
<TEMPLATE>
</TEMPLATE>
EOT
vm_base64 = Base64::encode64(vm_template)
@ -41,7 +41,7 @@ describe "Quota testing" do
image_template = <<-EOT
<TEMPLATE>
<PATH>/etc/hosts</PATH>
<TEMPLATE>
</TEMPLATE>
EOT
image_base64 = Base64::encode64(image_template)
@ -52,7 +52,7 @@ describe "Quota testing" do
<TEMPLATE>
<CPU>2</CPU>
<MEMORY>128</MEMORY>
<TEMPLATE>
</TEMPLATE>
EOT
temp_base64 = Base64::encode64(temp_template)
@ -80,10 +80,10 @@ describe "Quota testing" do
it "should check default cache (force)" do
usage1force = @quota.get_usage(@uid1, nil, true)
usage1force[:uid].should eql(0)
usage1force[:num_vms].should eql(nil)
usage1force[:cpu].should eql(nil)
usage1force[:memory].should eql(nil)
usage1force[:storage].should eql(nil)
usage1force[:num_vms].should eql(0)
usage1force[:cpu].should eql(0)
usage1force[:memory].should eql(0)
usage1force[:storage].should eql(0)
end
it "should authorize the user because there is no quota defined" do
@ -106,10 +106,10 @@ describe "Quota testing" do
it "should check the usage cache is not updated" do
usage1cache = @quota.get_usage(@uid1)
usage1cache[:uid].should eql(0)
usage1cache[:num_vms].should eql(nil)
usage1cache[:cpu].should eql(nil)
usage1cache[:memory].should eql(nil)
usage1cache[:storage].should eql(nil)
usage1cache[:num_vms].should eql(0)
usage1cache[:cpu].should eql(0.0)
usage1cache[:memory].should eql(0)
usage1cache[:storage].should eql(0)
end
it "should check the cache (force)" do
@ -118,7 +118,7 @@ describe "Quota testing" do
usage1force[:num_vms].should eql(1)
usage1force[:cpu].should eql(2.0)
usage1force[:memory].should eql(128)
usage1force[:storage].should eql(nil)
usage1force[:storage].should eql(0)
end
it "should check the usage cache is updated and contains the last usage" do
@ -127,7 +127,7 @@ describe "Quota testing" do
usage1cache[:num_vms].should eql(1)
usage1cache[:cpu].should eql(2.0)
usage1cache[:memory].should eql(128)
usage1cache[:storage].should eql(nil)
usage1cache[:storage].should eql(0)
end
it "should add a new Image" do
@ -146,7 +146,7 @@ describe "Quota testing" do
usage1cache[:num_vms].should eql(1)
usage1cache[:cpu].should eql(2.0)
usage1cache[:memory].should eql(128)
usage1cache[:storage].should eql(nil)
usage1cache[:storage].should eql(0)
end
it "should check the cache (force)" do
@ -252,7 +252,7 @@ describe "Quota testing" do
end
it "should not authorize the user because the vm quota is spent" do
err_msg = "CPU quota exceeded (Quota: 2.4, Used: 4.0, Asked: 2.0)."
err_msg = "CPU quota exceeded (Quota: 2.4, Used: 4.0, Requested: 2.0)"
@quota.authorize(@uid1, @acl_vm_create).should eql(err_msg)
@quota.authorize(@uid1, @acl_template_instantiate).should eql(err_msg)
@ -320,7 +320,7 @@ describe "Quota testing" do
@quota.authorize(@uid1, @acl_vm_create).should eql(false)
@quota.authorize(@uid1, @acl_template_instantiate).should eql(false)
err_msg = "STORAGE quota exceeded (Quota: 0, Used: 2000, Asked: 1474)."
err_msg = "STORAGE quota exceeded (Quota: 0, Used: 2000, Requested: 271)"
@quota.authorize(@uid1, @acl_image_create).should eql(err_msg)
end

View File

@ -22,6 +22,18 @@ require 'CloudAuth'
# API (OCA). Any cloud implementation should derive from this class
##############################################################################
class CloudServer
##########################################################################
# Class Constants. Define the OpenNebula Error and HTTP codes mapping
##########################################################################
HTTP_ERROR_CODE = {
OpenNebula::Error::EAUTHENTICATION => 401,
OpenNebula::Error::EAUTHORIZATION => 403,
OpenNebula::Error::ENO_EXISTS => 404,
OpenNebula::Error::EACTION => 500,
OpenNebula::Error::EXML_RPC_API => 500,
OpenNebula::Error::EINTERNAL => 500,
OpenNebula::Error::ENOTDEFINED => 500
}
##########################################################################
# Public attributes

View File

@ -27,6 +27,8 @@ require 'VirtualNetworkOCCI'
require 'VirtualNetworkPoolOCCI'
require 'ImageOCCI'
require 'ImagePoolOCCI'
require 'UserOCCI'
require 'UserPoolOCCI'
require 'pp'
@ -36,7 +38,6 @@ require 'pp'
# OpenNebula Engine
##############################################################################
class OCCIServer < CloudServer
# Server initializer
# config_file:: _String_ path of the config file
# template:: _String_ path to the location of the templates
@ -72,7 +73,7 @@ class OCCIServer < CloudServer
def get_computes(request)
# --- Get User's VMs ---
user_flag = -1
vmpool = VirtualMachinePoolOCCI.new(
self.client,
user_flag)
@ -99,14 +100,14 @@ class OCCIServer < CloudServer
def get_networks(request)
# --- Get User's VNETs ---
user_flag = -1
network_pool = VirtualNetworkPoolOCCI.new(
self.client,
user_flag)
# --- Prepare XML Response ---
rc = network_pool.info
if OpenNebula.is_error?(rc)
if rc.message.match("Error getting")
return rc, 404
@ -125,14 +126,14 @@ class OCCIServer < CloudServer
def get_storages(request)
# --- Get User's Images ---
user_flag = -1
image_pool = ImagePoolOCCI.new(
self.client,
user_flag)
# --- Prepare XML Response ---
rc = image_pool.info
if OpenNebula.is_error?(rc)
if rc.message.match("Error getting")
return rc, 404
@ -144,6 +145,24 @@ class OCCIServer < CloudServer
return to_occi_xml(image_pool, 200)
end
# Gets the pool representation of USERs
# request:: _Hash_ hash containing the data of the request
# [return] _String_,_Integer_ User pool representation or error,
# status code
def get_users(request)
# --- Get Users Pool ---
user_pool = UserPoolOCCI.new(self.client)
# --- Prepare XML Response ---
rc = user_pool.info
if OpenNebula.is_error?(rc)
return rc, CloudServer::HTTP_ERROR_CODE[rc.errno]
end
return to_occi_xml(user_pool, 200)
end
############################################################################
############################################################################
# ENTITY RESOURCE METHODS
@ -190,7 +209,7 @@ class OCCIServer < CloudServer
# --- Prepare XML Response ---
rc = vm.info
if OpenNebula.is_error?(rc)
if rc.message.match("Error getting")
return rc, 404
@ -232,58 +251,20 @@ class OCCIServer < CloudServer
vm = VirtualMachineOCCI.new(
VirtualMachine.build_xml(params[:id]),
self.client)
rc = vm.info
return rc, 400 if OpenNebula.is_error?(rc)
xmldoc = XMLElement.build_xml(request.body, 'COMPUTE')
vm_info = XMLElement.new(xmldoc) if xmldoc != nil
# Check the number of changes in the request
image_name = nil
image_type = nil
vm_info.each('DISK/SAVE_AS') { |disk|
if image_name
error_msg = "It is only allowed one save_as per request"
return OpenNebula::Error.new(error_msg), 400
end
image_name = disk.attr('.', 'name')
image_type = disk.attr('.', 'type')
}
state = vm_info['STATE']
if image_name && state
error_msg = "It is not allowed to change the state and save_as" <<
" a disk in the same request"
return OpenNebula::Error.new(error_msg), 400
elsif image_name
# Get the disk id
disk_id = vm_info.attr('DISK/SAVE_AS/..', 'id')
if disk_id.nil?
error_msg = "DISK id attribute not specified"
return OpenNebula::Error.new(error_msg), 400
end
disk_id = disk_id.to_i
if vm["TEMPLATE/DISK[DISK_ID=\"#{disk_id}\"]/SAVE_AS"]
error_msg = "The disk #{disk_id} is already" <<
" suppossed to be saved"
return OpenNebula::Error.new(error_msg), 400
end
rc = vm.save_as(disk_id, image_name)
if OpenNebula.is_error?(rc)
image.delete
return rc, 400
end
elsif state
rc = vm.mk_action(state)
return rc, 400 if OpenNebula.is_error?(rc)
rc = vm.info
if OpenNebula.is_error?(rc)
return rc, CloudServer::HTTP_ERROR_CODE[rc.errno]
end
# --- Prepare XML Response ---
vm.info
return to_occi_xml(vm, 202)
result, code = vm.update_from_xml(request.body)
if OpenNebula.is_error?(result)
return result, code
else
vm.info
return to_occi_xml(vm, code)
end
end
############################################################################
@ -324,7 +305,7 @@ class OCCIServer < CloudServer
# --- Prepare XML Response ---
rc = network.info
if OpenNebula.is_error?(rc)
if rc.message.match("Error getting")
return rc, 404
@ -354,7 +335,7 @@ class OCCIServer < CloudServer
return "", 204
end
# Updates a NETWORK resource
# request:: _Hash_ hash containing the data of the request
# [return] _String_,_Integer_ Update confirmation msg or error,
@ -366,10 +347,10 @@ class OCCIServer < CloudServer
vnet = VirtualNetworkOCCI.new(
VirtualNetwork.build_xml(params[:id]),
self.client)
rc = vnet.info
return rc, 400 if OpenNebula.is_error?(rc)
if vnet_info['PUBLIC'] == 'YES'
rc = vnet.publish
return rc, 400 if OpenNebula.is_error?(rc)
@ -432,7 +413,7 @@ class OCCIServer < CloudServer
self.client)
rc = image.info
if OpenNebula.is_error?(rc)
if rc.message.match("Error getting")
return rc, 404
@ -464,7 +445,7 @@ class OCCIServer < CloudServer
return "", 204
end
# Updates a STORAGE resource
# request:: _Hash_ hash containing the data of the request
# [return] _String_,_Integer_ Update confirmation msg or error,
@ -476,10 +457,10 @@ class OCCIServer < CloudServer
image = ImageOCCI.new(
Image.build_xml(params[:id]),
self.client)
rc = image.info
return rc, 400 if OpenNebula.is_error?(rc)
if image_info['PERSISTENT'] && image_info['PUBLIC']
error_msg = "It is not allowed more than one change per request"
return OpenNebula::Error.new(error_msg), 400
@ -501,4 +482,24 @@ class OCCIServer < CloudServer
image.info
return to_occi_xml(image, 202)
end
# Get the representation of a USER
# request:: _Hash_ hash containing the data of the request
# [return] _String_,_Integer_ USER representation or error,
# status code
def get_user(request, params)
# --- Get the USER ---
user = UserOCCI.new(
User.build_xml(params[:id]),
self.client)
# --- Prepare XML Response ---
rc = user.info
if OpenNebula.is_error?(rc)
return rc, CloudServer::HTTP_ERROR_CODE[rc.errno]
end
return to_occi_xml(user, 200)
end
end

View File

@ -0,0 +1,64 @@
# -------------------------------------------------------------------------- #
# Copyright 2002-2011, 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 'OpenNebula'
include OpenNebula
require 'quota'
class UserOCCI < User
FORCE_USAGE = true
OCCI_USER = %q{
<USER href="<%= base_url %>/user/<%= self.id.to_s %>">
<ID><%= self.id.to_s %></ID>
<NAME><%= self.name %></NAME>
<QUOTA>
<% user_quota.each { |key,value|
key_s = key.to_s.upcase
value_i = value.to_i %>
<<%= key_s %>><%= value_i %></<%= key_s %>>
<% } %>
</QUOTA>
<USAGE>
<% user_usage.each { |key,value|
key_s = key.to_s.upcase
value_i = value.to_i %>
<<%= key_s %>><%= value_i %></<%= key_s %>>
<% } %>
</USAGE>
</USER>
}
# Class constructor
def initialize(xml, client)
super(xml, client)
end
# Creates the OCCI representation of a User
def to_occi(base_url)
quota = Quota.new
user_usage = quota.get_usage(self.id, nil, FORCE_USAGE)
user_usage.delete(:uid)
user_quota = quota.get_quota(self.id)
user_quota.delete(:uid)
occi = ERB.new(OCCI_USER)
return occi.result(binding).gsub(/\n\s*/,'')
end
end

View File

@ -0,0 +1,43 @@
# -------------------------------------------------------------------------- #
# Copyright 2002-2011, 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 'OpenNebula'
include OpenNebula
class UserPoolOCCI < UserPool
OCCI_USER_POOL = %q{
<USER_COLLECTION>
<% self.each{ |user| %>
<USER href="<%= base_url %>/user/<%= user.id.to_s %>" name="<%= user.name %>"/>
<% } %>
</USER_COLLECTION>
}
# Creates the OCCI representation of a User Pool
def to_occi(base_url)
begin
occi = ERB.new(OCCI_USER_POOL)
occi_text = occi.result(binding)
rescue Exception => e
error = OpenNebula::Error.new(e.message)
return error
end
return occi_text.gsub(/\n\s*/,'')
end
end

View File

@ -61,62 +61,48 @@ class VirtualMachineOCCI < VirtualMachine
<% end %>
</COMPUTE>
}
OCCI_ACTION = {
"STOPPED" => { :from => ["ACTIVE"], :action => :stop},
"SUSPENDED" => { :from => ["ACTIVE"], :action => :suspend},
"RESUME" => { :from => ["STOPPED", "SUSPENDED"], :action => :resume},
"CANCEL" => { :from => ["ACTIVE"], :action => :cancel},
"SHUTDOWN" => { :from => ["ACTIVE"], :action => :shutdown},
"DONE" => { :from => VM_STATE, :action => :finalize},
}
# Class constructor
def initialize(xml, client, xml_info=nil, types=nil, base=nil)
super(xml, client)
@vm_info = nil
@template = nil
@common_template = base + '/common.erb' if base
if xml_info != nil
xmldoc = XMLElement.build_xml(xml_info, 'COMPUTE')
@vm_info = XMLElement.new(xmldoc) if xmldoc != nil
end
if @vm_info != nil
itype = @vm_info['INSTANCE_TYPE']
if itype != nil and types[itype.to_sym] != nil
@template = base + "/#{types[itype.to_sym][:template]}"
end
end
end
def mk_action(action_str)
case action_str.downcase
when "stopped"
rc = self.stop
when "suspended"
rc = self.suspend
when "resume"
rc = self.resume
when "cancel"
rc = self.cancel
when "shutdown"
rc = self.shutdown
when "done"
rc = self.finalize
else
error_msg = "Invalid state"
error = OpenNebula::Error.new(error_msg)
return error
end
return rc
end
def to_one_template()
if @vm_info == nil
error_msg = "Missing COMPUTE section in the XML body"
return OpenNebula::Error.new(error_msg), 400
end
if @template == nil
return OpenNebula::Error.new("Bad instance type"), 500
end
begin
template = ERB.new(File.read(@common_template)).result(binding)
template << ERB.new(File.read(@template)).result(binding)
@ -124,10 +110,10 @@ class VirtualMachineOCCI < VirtualMachine
error = OpenNebula::Error.new(e.message)
return error
end
return template
end
# Creates the VMI representation of a Virtual Machine
def to_occi(base_url)
begin
@ -138,9 +124,90 @@ class VirtualMachineOCCI < VirtualMachine
return error
end
return occi_vm_text.gsub(/\n\s*/,'')
end
# Update de resource from an XML representation of the COMPUTE
# @param [String] xml_compute XML representation of the COMPUTE
# @return [[nil, OpenNebula::Error], HTTP_CODE] If there is no error
#  the first component is nil.
def update_from_xml(xml_compute)
xmldoc = XMLElement.build_xml(xml_compute, 'COMPUTE')
vm_info = XMLElement.new(xmldoc) if xmldoc != nil
action = nil
args = []
# Check if a state change is required
occi_state = vm_info['STATE']
if occi_state
# If a state is provided
occi_state.upcase!
if OCCI_ACTION.keys.include?(occi_state)
# If the requested state is one the OCCI action states
shash = OCCI_ACTION[occi_state]
if shash[:from].include?(state_str)
# Action to be performed
action = shash[:action]
elsif occi_state != state_str
# If the requested state is different from the resource
# state but it does not belong to the "from" state array
error_msg = "The state of the resource cannot be changed" \
" from #{state_str} to #{occi_state}."
error = OpenNebula::Error.new(error_msg)
return error, 403
end
elsif !VM_STATE.include?(occi_state)
# The requested state is not one of the OCCI action states nor
# a resource state
error_msg = "Invalid state: \"#{occi_state}\""
error = OpenNebula::Error.new(error_msg)
return error, 400
end
end
# Check if a disk image save as is required
image_name = nil
vm_info.each('DISK/SAVE_AS') { |save_as|
image_name = save_as.attr('.', 'name')
if image_name
if action
# Return erro if an action has been defined before
if action == :save_as
error_msg = "Only one disk can be saved per request"
else
error_msg = "Changig the state of the resource and" \
" saving a disk is not allowed in the same request"
end
error = OpenNebula::Error.new(error_msg)
return error, 403
else
# if no action is defined yet and a save_as is requested
action = :save_as
disk_id = save_as.attr('..', 'id')
# Params for the save_as action:
# save_as(disk_id, image_name)
args << disk_id.to_i
args << image_name
end
end
}
# Perform the requested action
if action
rc = self.send(action, *args)
if OpenNebula.is_error?(rc)
return rc, CloudServer::HTTP_ERROR_CODE[rc.errno]
else
return nil, 202
end
else
# There is no change requested
return nil, 200
end
end
end

View File

@ -144,6 +144,11 @@ get '/storage' do
treat_response(result,rc)
end
get '/user' do
result,rc = @occi_server.get_users(request)
treat_response(result,rc)
end
###################################################
# Entity Resources Methods
###################################################
@ -192,3 +197,8 @@ put '/storage/:id' do
result,rc = @occi_server.put_storage(request, params)
treat_response(result,rc)
end
get '/user/:id' do
result,rc = @occi_server.get_user(request, params)
treat_response(result,rc)
end

View File

@ -0,0 +1 @@
<USER href="http://localhost:4567/user/0"><ID>0</ID><NAME>oneadmin</NAME><QUOTA><CPU>0</CPU><MEMORY>0</MEMORY><NUM_VMS>0</NUM_VMS><STORAGE>0</STORAGE></QUOTA><USAGE><CPU>0</CPU><MEMORY>0</MEMORY><NUM_VMS>0</NUM_VMS><STORAGE>0</STORAGE></USAGE></USER>

View File

@ -0,0 +1 @@
<USER_COLLECTION><USER href="http://localhost:4567/user/0" name="oneadmin"/><USER href="http://localhost:4567/user/1" name="my_first_occi_user"/></USER_COLLECTION>

View File

@ -0,0 +1 @@
<COMPUTE href="http://localhost:4567/compute/0"><ID>0</ID><CPU>1</CPU><MEMORY>512</MEMORY><NAME>one-0</NAME><STATE>ACTIVE</STATE><DISK id="0"><STORAGE href="http://localhost:4567/storage/0" name="first_image"/><TYPE>DISK</TYPE><TARGET>hda</TARGET></DISK><DISK id="1"><STORAGE href="http://localhost:4567/storage/1" name="second_image"/><TYPE>DISK</TYPE><TARGET>hde</TARGET></DISK></COMPUTE>

View File

@ -0,0 +1 @@
<COMPUTE href="http://localhost:4567/compute/0"><ID>0</ID><CPU>1</CPU><MEMORY>512</MEMORY><NAME>one-0</NAME><STATE>SUSPENDED</STATE><DISK id="0"><STORAGE href="http://localhost:4567/storage/0" name="first_image"/><SAVE_AS name="save_as1"/><TYPE>DISK</TYPE><TARGET>hda</TARGET></DISK><DISK id="1"><STORAGE href="http://localhost:4567/storage/1" name="second_image"/><TYPE>DISK</TYPE><TARGET>hde</TARGET></DISK></COMPUTE>

View File

@ -0,0 +1 @@
<COMPUTE href="http://localhost:4567/compute/0"><ID>0</ID><CPU>1</CPU><MEMORY>512</MEMORY><NAME>one-0</NAME><STATE>ACTIVE</STATE><DISK id="0"><STORAGE href="http://localhost:4567/storage/0" name="first_image"/><SAVE_AS name="save_as1"/><TYPE>DISK</TYPE><TARGET>hda</TARGET></DISK><DISK id="1"><STORAGE href="http://localhost:4567/storage/1" name="second_image"/><TYPE>DISK</TYPE><TARGET>hde</TARGET></DISK></COMPUTE>

View File

@ -0,0 +1 @@
<COMPUTE href="http://localhost:4567/compute/0"><ID>0</ID><CPU>1</CPU><MEMORY>512</MEMORY><NAME>one-0</NAME><STATE>ACTIVE</STATE><DISK id="0"><STORAGE href="http://localhost:4567/storage/0" name="first_image"/><TYPE>DISK</TYPE><TARGET>hda</TARGET></DISK><DISK id="1"><SAVE_AS name="save_as2"/><STORAGE href="http://localhost:4567/storage/1" name="second_image"/><TYPE>DISK</TYPE><TARGET>hde</TARGET></DISK></COMPUTE>

View File

@ -0,0 +1 @@
<COMPUTE href="http://localhost:4567/compute/0"><ID>0</ID><CPU>1</CPU><MEMORY>512</MEMORY><NAME>one-0</NAME><STATE>ACTIVE</STATE><DISK id="0"><STORAGE href="http://localhost:4567/storage/0" name="first_image"/><SAVE_AS name="save_as1"/><TYPE>DISK</TYPE><TARGET>hda</TARGET></DISK><DISK id="1"><STORAGE href="http://localhost:4567/storage/1" name="second_image"/><SAVE_AS name="save_as2"/><TYPE>DISK</TYPE><TARGET>hde</TARGET></DISK></COMPUTE>

View File

@ -0,0 +1,41 @@
# -------------------------------------------------------------------------- #
# Copyright 2002-2011, 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. #
#--------------------------------------------------------------------------- #
FIXTURES_PATH = File.join(File.dirname(__FILE__),'../fixtures')
TEMPLATES_PATH = File.join(File.dirname(__FILE__),'../templates')
$: << File.join(File.dirname(__FILE__), '..', '..', 'lib')
# Load the testing libraries
require 'rubygems'
require 'rspec'
require 'rack/test'
# Load the Sinatra app
require 'occi-server'
# Make Rack::Test available to all spec contexts
RSpec.configure do |conf|
conf.include Rack::Test::Methods
end
# Set the Sinatra environment
set :environment, :test
# Add an app method for RSpec
def app
Sinatra::Application
end

View File

@ -0,0 +1,73 @@
# -------------------------------------------------------------------------- #
# Copyright 2002-2011, 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')
describe 'OCCI User tests' do
before(:all) do
@username_1 = "my_first_occi_user"
@userpass_1 = "my_first_occi_pass"
`oneuser create #{@username_1} #{@userpass_1}`
end
it "should list the user collection" do
basic_authorize('oneadmin','4478db59d30855454ece114e8ccfa5563d21c9bd')
get '/user'
last_response.status.should eql(200)
xml_body = last_response.body
user_collection = File.read(FIXTURES_PATH + '/user/user_collection.xml')
xml_body.strip.should eql(user_collection.strip)
end
it "should check the error if the user collection is retrieved by a non oneadmin user" do
basic_authorize('my_first_occi_user','c08c5a6c535b6060b7b2af34e0d2f0ffb7e63b28')
get '/user'
last_response.status.should eql(403)
end
it "should show the user information, no quotas and no usage" do
basic_authorize('oneadmin','4478db59d30855454ece114e8ccfa5563d21c9bd')
get '/user/0'
last_response.status.should eql(200)
xml_body = last_response.body
user = File.read(FIXTURES_PATH + '/user/user.xml')
xml_body.strip.should eql(user.strip)
end
it "should get a 404 error when trying to get a non existing user" do
basic_authorize('oneadmin','4478db59d30855454ece114e8ccfa5563d21c9bd')
get '/user/99'
last_response.status.should eql(404)
end
it "should get a 403 error when trying to get a different user" do
basic_authorize('my_first_occi_user','c08c5a6c535b6060b7b2af34e0d2f0ffb7e63b28')
get '/user/0'
last_response.status.should eql(403)
end
end

View File

@ -0,0 +1,77 @@
# -------------------------------------------------------------------------- #
# Copyright 2002-2011, 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')
describe 'OCCI User tests' do
# PREREQUISITES
# OpenNebula installed and running using dummy drivers
before(:all) do
# Starting the drivers
sleep 2
system("onehost create myhost im_dummy vmm_dummy tm_dummy").should == true
system("oneimage create #{TEMPLATES_PATH+"/image1.template"}").should == true
system("oneimage create #{TEMPLATES_PATH+"/image2.template"}").should == true
# Copying the images
sleep 1
system("onevm create #{TEMPLATES_PATH+"/vm.template"}").should == true
system("onevm deploy 0 0").should == true
end
it "should show the new compute" do
basic_authorize('oneadmin','4478db59d30855454ece114e8ccfa5563d21c9bd')
get '/compute/0'
last_response.body.should == File.read(FIXTURES_PATH+"/vm_save_as/newcompute.xml")
end
it "should get an error when trying to change the resource state and save a disk " do
basic_authorize('oneadmin','4478db59d30855454ece114e8ccfa5563d21c9bd')
body = File.read(FIXTURES_PATH+"/vm_save_as/save_a_disk_and_change_state.xml")
put '/compute/0', body
last_response.status.should == 403
end
it "should get an error when trying to save two disks" do
basic_authorize('oneadmin','4478db59d30855454ece114e8ccfa5563d21c9bd')
body = File.read(FIXTURES_PATH+"/vm_save_as/save_two_disks.xml")
put '/compute/0', body
last_response.status.should == 403
end
it "should save the first disk" do
basic_authorize('oneadmin','4478db59d30855454ece114e8ccfa5563d21c9bd')
body = File.read(FIXTURES_PATH+"/vm_save_as/save_first_disk.xml")
put '/compute/0', body
last_response.status.should == 202
end
it "should save the second disk" do
basic_authorize('oneadmin','4478db59d30855454ece114e8ccfa5563d21c9bd')
body = File.read(FIXTURES_PATH+"/vm_save_as/save_second_disk.xml")
put '/compute/0', body
last_response.status.should == 202
end
end

View File

@ -0,0 +1,2 @@
NAME = first_image
PATH = /etc/hosts

View File

@ -0,0 +1,3 @@
NAME = second_image
PATH = /etc/hosts
TYPE = DATABLOCK

View File

@ -0,0 +1,10 @@
CPU = 1
MEMORY = 512
DISK = [
IMAGE_ID = 0
]
DISK = [
IMAGE_ID = 1
]

49
src/cloud/occi/test/test.sh Executable file
View File

@ -0,0 +1,49 @@
#!/bin/bash
# -------------------------------------------------------------------------- #
# Copyright 2002-2011, 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. #
#--------------------------------------------------------------------------- #
if [ -z $ONE_LOCATION ]; then
echo "ONE_LOCATION not defined."
exit -1
fi
VAR_LOCATION="$ONE_LOCATION/var"
if [ "$(ls -A $VAR_LOCATION)" ]; then
echo "$VAR_LOCATION is not empty."
exit -1
fi
for j in `ls ./spec/*_spec.rb` ; do
find $VAR_LOCATION -mindepth 1 ! \( -path "$VAR_LOCATION/remotes*" -o -path "$VAR_LOCATION/images" \) -delete
PID=$$
oned -f &
sleep 2s;
rspec $j -f s
CODE=$?
pkill -P $PID oned
sleep 2s;
pkill -9 -P $PID oned
if [ $CODE != 0 ] ; then
exit 1
fi
done

View File

@ -50,11 +50,22 @@ module OpenNebula
# Any function in the OpenNebula module will return an Error
# object in case of error.
class Error
attr_reader :message
ESUCCESS = 0x0000
EAUTHENTICATION = 0x0100
EAUTHORIZATION = 0x0200
ENO_EXISTS = 0x0400
EACTION = 0x0800
EXML_RPC_API = 0x1000
EINTERNAL = 0x2000
ENOTDEFINED = 0x1111
# +message+ a description of the error
def initialize(message=nil)
@message=message
attr_reader :message, :errno
# +message+ Description of the error
# +errno+ OpenNebula code error
def initialize(message=nil, errno=0x1111)
@message = message
@errno = errno
end
def to_str()
@ -127,7 +138,7 @@ module OpenNebula
response = @server.call_async("one."+action, @one_auth, *args)
if response[0] == false
Error.new(response[1])
Error.new(response[1], response[2])
else
response[1] #response[1..-1]
end