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

feature #200: Better XML parsing

This commit is contained in:
Ruben S. Montero 2010-07-17 02:05:06 +02:00
parent bb4939fba7
commit e3b9eb63c0
4 changed files with 170 additions and 180 deletions

View File

@ -31,7 +31,7 @@ class CloudServer
attr_reader :one_client
# Initializes the Cloud server based on a config file
# config_file:: _String_ for the server. MUST include the following
# config_file:: _String_ for the server. MUST include the following
# variables:
# USER
# PASSWORD
@ -43,7 +43,7 @@ class CloudServer
# --- Load the Cloud Server configuration file ---
@config = Configuration.new(config_file)
@instance_types = Hash.new
if @config[:vm_type].kind_of?(Array)
@ -55,7 +55,7 @@ class CloudServer
end
# --- Start an OpenNebula Session ---
@one_client = Client.new()
@user_pool = UserPool.new(@one_client)
end
@ -72,7 +72,7 @@ class CloudServer
puts "--------------------------------------"
puts " Registered Instance Types "
puts "--------------------------------------"
pp @instance_types
pp @instance_types
end
###########################################################################
@ -80,19 +80,19 @@ class CloudServer
###########################################################################
# Generates an OpenNebula Session for the given user
# user:: _Hash_ the user information
# [return] an OpenNebula client session
# user:: _Hash_ the user information
# [return] an OpenNebula client session
def one_client_user(user)
client = Client.new("dummy:dummy")
client.one_auth = "#{user[:name]}:#{user[:password]}"
return client
end
# Authenticates a user
# name:: _String_ of the user
# password:: _String_ of the user
# [return] true if authenticated
# [return] true if authenticated
def authenticate?(name, password)
user = get_user(name)
@ -104,7 +104,7 @@ class CloudServer
# [return] _Hash_ with the user data
def get_user(name)
user = nil
@user_pool.info
@user_pool.each{ |u|
if u.name==name
@ -117,31 +117,17 @@ class CloudServer
}
return user
end
def xml_to_hash(xml)
begin
hash = Crack::XML.parse(xml)
rescue Exception => e
error = OpenNebula::Error.new(e.message)
return error
end
return hash
end
def get_template_path(instance_type_name)
if instance_type_name.nil?
instance_type=@instance_types.first
end
instance_type=@instance_types[instance_type_name]
if !instance_type
error = OpenNebula::Error.new("Bad instance type")
return error
return error
end
return @config[:template_location]+"/#{instance_type['TEMPLATE']}"
return @config[:template_location]+"/#{instance_type['TEMPLATE']}"
end
###########################################################################
@ -162,14 +148,14 @@ class CloudServer
error = OpenNebula::Error.new(error_msg)
return error
end
if !File.exists?(file_path)
error_msg = "Image file could not be found, aborting."
error = OpenNebula::Error.new(error_msg)
return error
end
end
template = image.to_one_template
rc = image.allocate(template)
@ -181,22 +167,22 @@ class CloudServer
image.info
template=image.to_hash
template=template['IMAGE']['TEMPLATE']
if file_path
rc = image.copy(file_path, image['SOURCE'])
file[:tempfile].unlink
elsif template['SIZE'] and template['FSTYPE']
rc = image.mk_datablock(
template['SIZE'],
template['FSTYPE'],
template['SIZE'],
template['FSTYPE'],
image['SOURCE'])
end
if OpenNebula.is_error?(rc)
image.delete
return rc
end
return nil
end

View File

@ -35,37 +35,37 @@ require 'pp'
##############################################################################
# The OCCI Server provides an OCCI implementation based on the
# The OCCI Server provides an OCCI implementation based on the
# OpenNebula Engine
##############################################################################
class OCCIServer < CloudServer
# Server initializer
# config_file:: _String_ path of the config file
# template:: _String_ path to the location of the templates
# template:: _String_ path to the location of the templates
def initialize(config_file,template)
super(config_file)
@config.add_configuration_value("TEMPLATE_LOCATION",template)
if @config[:ssl_server]
@base_url=@config[:ssl_server]
else
@base_url="http://#{@config[:server]}:#{@config[:port]}"
end
print_configuration
end
# Authorization function
# requestenv:: _Hash_ Hash containing the environment of the request
# [return] _Boolean_ Whether the user is authorized or not
# [return] _Boolean_ Whether the user is authorized or not
def authenticate?(requestenv)
auth ||= Rack::Auth::Basic::Request.new(requestenv)
if !(auth.provided? && auth.basic? && auth.credentials)
return false
end
end
user = get_user(requestenv, auth)
@ -85,7 +85,7 @@ class OCCIServer < CloudServer
auth = Rack::Auth::Basic::Request.new(requestenv) if !auth
super(auth.credentials.first)
end
# Retrieve a client with the user credentials
# requestenv:: _Hash_ Hash containing the environment of the request
# [return] _Client_ client with the user credentials
@ -93,20 +93,20 @@ class OCCIServer < CloudServer
user = get_user(requestenv)
return one_client_user(user)
end
# Prepare the OCCI XML Response
# resource:: _Pool_ or _PoolElement_ that represents a OCCI resource
# [return] _String_,_Integer_ Resource Representation or error, status code
def to_occi_xml(resource)
xml_response = resource.to_occi(@base_url)
return xml_response, 500 if OpenNebula.is_error?(xml_response)
return xml_response, 201
return xml_response, 201
end
def get_info_hash(body)
if body
info = xml_to_hash(body.read)
info = XMLUtilsElement::xml_to_hash(body.read)
return info
else
error_msg = "OCCI XML representation not present"
@ -114,18 +114,18 @@ class OCCIServer < CloudServer
return error
end
end
###################################################
# Pool Resources methods
###################################################
# Post a new compute to the COMPUTE pool
# request:: _Hash_ hash containing the data of the request
# [return] _String_,_Integer_ COMPUTE Representation or error, status code
# [return] _String_,_Integer_ COMPUTE Representation or error, status code
def post_compute(request)
# --- Get client with user credentials ---
client = get_client(request.env)
# --- Check OCCI XML from POST ---
vm_info = get_info_hash(request.body)
return vm_info, 400 if OpenNebula.is_error?(vm_info)
@ -134,16 +134,16 @@ class OCCIServer < CloudServer
if vm_info['COMPUTE']
path = get_template_path(vm_info['COMPUTE']['INSTANCE_TYPE'])
return path, 500 if OpenNebula.is_error?(path)
vm_info['TEMPLATE_PATH'] = path
end
# --- Create the new Instance ---
vm = VirtualMachineOCCI.new(
vm_info,
VirtualMachine.build_xml,
VirtualMachine.build_xml,
client)
# --- Generate the template and Allocate the new Instance ---
template = vm.to_one_template
return template, 500 if OpenNebula.is_error?(template)
@ -155,7 +155,7 @@ class OCCIServer < CloudServer
vm.info
return to_occi_xml(vm)
end
# Gets the pool representation of COMPUTES
# request:: _Hash_ hash containing the data of the request
# [return] _String_,_Integer_ Pool Representation or error, status code
@ -164,36 +164,29 @@ class OCCIServer < CloudServer
client = get_client(request.env)
# --- Get User's VMs ---
user_flag = -1
user_flag = -1
vmpool = VirtualMachinePoolOCCI.new(client, user_flag)
# --- Prepare XML Response ---
rc = vmpool.info
rc = vmpool.info
return rc, 404 if OpenNebula.is_error?(rc)
return to_occi_xml(vmpool)
end
# Post a new network to the NETWORK pool
# request:: _Hash_ hash containing the data of the request
# [return] _String_,_Integer_ Network Representation or error, status code
def post_network(request)
# --- Get client with user credentials ---
client = get_client(request.env)
# --- Check OCCI XML from POST ---
network_info = get_info_hash(request.body)
return network_info, 400 if OpenNebula.is_error?(network_info)
if network_info['NETWORK']
network_info['NETWORK']['BRIDGE'] = @config[:bridge]
end
# --- Create the new Instance ---
network = VirtualNetworkOCCI.new(
network_info,
VirtualNetwork.build_xml,
client)
client,
request.body,
@config[:bridge])
# --- Generate the template and Allocate the new Instance ---
template = network.to_one_template
@ -203,39 +196,39 @@ class OCCIServer < CloudServer
return rc, 500 if OpenNebula.is_error?(rc)
# --- Prepare XML Response ---
network.info
network.info
return to_occi_xml(network)
end
# Gets the pool representation of NETWORKS
# request:: _Hash_ hash containing the data of the request
# [return] _String_,_Integer_ Network pool representation or error,
# => status code
# [return] _String_,_Integer_ Network pool representation or error,
# => status code
def get_networks(request)
# --- Get client with user credentials ---
client = get_client(request.env)
# --- Get User's VNETs ---
user_flag = -1
user_flag = -1
network_pool = VirtualNetworkPoolOCCI.new(client, user_flag)
rc = network_pool.info
rc = network_pool.info
return rc, 404 if OpenNebula.is_error?(rc)
# --- Prepare XML Response ---
return to_occi_xml(network_pool)
end
# Post a new image to the STORAGE pool
# request:: _Hash_ hash containing the data of the request
# [return] _String_,_Integer_ Image representation or error, status code
# [return] _String_,_Integer_ Image representation or error, status code
def post_storage(request)
# Get client with user credentials
client = get_client(request.env)
# --- Check OCCI XML from POST ---
if request.params['occixml']
image_info = xml_to_hash(request.params['occixml'])
image_info = XMLUtilsElement::xml_to_hash(request.params['occixml'])
return image_info, 400 if OpenNebula.is_error?(image_info)
else
error_msg = "OCCI XML representation of Image" +
@ -243,53 +236,53 @@ class OCCIServer < CloudServer
error = OpenNebula::Error.new(error_msg)
return error, 400
end
# --- Create and Add the new Image ---
image = ImageOCCI.new(image_info, Image.build_xml, client)
rc = add_image(image, request.params['file'])
return rc, 500 if OpenNebula.is_error?(rc)
# --- Enable the new Image ---
rc = image.enable
return rc, 500 if OpenNebula.is_error?(rc)
# --- Prepare XML Response ---
return to_occi_xml(image)
end
# Gets the pool representation of STORAGES
# request:: _Hash_ hash containing the data of the request
# [return] _String_,_Integer_ Image pool representation or error,
# status code
# [return] _String_,_Integer_ Image pool representation or error,
# status code
def get_storages(request)
# --- Get client with user credentials ---
client = get_client(request.env)
# --- Get User's Images ---
user_flag = -1
user_flag = -1
image_pool = ImagePoolOCCI.new(client, user_flag)
result = image_pool.info
result = image_pool.info
return result, 404 if OpenNebula.is_error?(result)
# --- Prepare XML Response ---
return to_occi_xml(image_pool)
end
###################################################
# Entity Resources methods
###################################################
# Get the representation of a COMPUTE resource
# request:: _Hash_ hash containing the data of the request
# [return] _String_,_Integer_ COMPUTE representation or error,
# [return] _String_,_Integer_ COMPUTE representation or error,
# status code
def get_compute(request, params)
# --- Get client with user credentials ---
client = get_client(request.env)
# --- Get the VM ---
# --- Get the VM ---
vm = VirtualMachineOCCI.new(
nil,
VirtualMachine.build_xml(params[:id]),
@ -301,31 +294,31 @@ class OCCIServer < CloudServer
# --- Prepare XML Response ---
return to_occi_xml(vm)
end
# Deletes a COMPUTE resource
# request:: _Hash_ hash containing the data of the request
# [return] _String_,_Integer_ Delete confirmation msg or error,
# [return] _String_,_Integer_ Delete confirmation msg or error,
# status code
def delete_compute(request, params)
# --- Get client with user credentials ---
client = get_client(request.env)
vm = VirtualMachineOCCI.new(
nil,
VirtualMachine.build_xml(params[:id]),
client)
# --- Finalize the VM ---
result = vm.finalize
return result, 500 if OpenNebula::is_error?(result)
return "", 204
end
# Updates a COMPUTE resource
# request:: _Hash_ hash containing the data of the request
# [return] _String_,_Integer_ Update confirmation msg or error,
# status code
# [return] _String_,_Integer_ Update confirmation msg or error,
# status code
def put_compute(request, params)
# --- Get client with user credentials ---
client = get_client(request.env)
@ -338,33 +331,32 @@ class OCCIServer < CloudServer
if vm_info['COMPUTE'] && vm_info['COMPUTE']['STATE']
vm = VirtualMachineOCCI.new(
vm_info,
VirtualMachine.build_xml(params[:id]),
VirtualMachine.build_xml(params[:id]),
client)
rc = vm.mk_action(vm_info['COMPUTE']['STATE'])
return rc, 400 if OpenNebula.is_error?(rc)
else
error_msg = "State not defined in the OCCI XML"
error_msg = "State not defined in the OCCI XML"
error = OpenNebula::Error.new(error_msg)
return error, 400
return error, 400
end
# --- Prepare XML Response ---
vm.info
vm.info
return to_occi_xml(vm)
end
# Retrieves a NETWORK resource
# request:: _Hash_ hash containing the data of the request
# [return] _String_,_Integer_ NETWORK occi representation or error,
# status code
# [return] _String_,_Integer_ NETWORK occi representation or error,
# status code
def get_network(request, params)
# --- Get client with user credentials ---
client = get_client(request.env)
# --- Get the VM ---
# --- Get the VM ---
network = VirtualNetworkOCCI.new(
nil,
VirtualNetwork.build_xml(params[:id]),
client)
@ -374,36 +366,35 @@ class OCCIServer < CloudServer
# --- Prepare XML Response ---
return to_occi_xml(network)
end
# Deletes a NETWORK resource
# request:: _Hash_ hash containing the data of the request
# [return] _String_,_Integer_ Delete confirmation msg or error,
# status code
# [return] _String_,_Integer_ Delete confirmation msg or error,
# status code
def delete_network(request, params)
# --- Get client with user credentials ---
client = get_client(request.env)
vn = VirtualNetworkOCCI.new(
nil,
VirtualNetwork.build_xml(params[:id]),
client)
# --- Delete the VNET ---
result = vn.delete
return result, 500 if OpenNebula::is_error?(result)
return "", 204
end
# Get a STORAGE resource
# request:: _Hash_ hash containing the data of the request
# [return] _String_,_Integer_ STORAGE occi representation or error,
# status code
# [return] _String_,_Integer_ STORAGE occi representation or error,
# status code
def get_storage(request, params)
# --- Get client with user credentials ---
client = get_client(request.env)
# --- Get the Image ---
# --- Get the Image ---
image = ImageOCCI.new(nil, Image.build_xml(params[:id]), client)
result = image.info
@ -412,17 +403,17 @@ class OCCIServer < CloudServer
# --- Prepare XML Response ---
return to_occi_xml(image)
end
# Deletes a STORAGE resource (Not yet implemented)
# request:: _Hash_ hash containing the data of the request
# [return] _String_,_Integer_ Delete confirmation msg or error,
# status code
# [return] _String_,_Integer_ Delete confirmation msg or error,
# status code
def delete_storage(request, params)
# --- Get client with user credentials ---
client = get_client(request.env)
image = ImageOCCI.new(nil, Image.build_xml(params[:id]), client)
# --- Delete the Image ---
result = image.delete
return result, 500 if OpenNebula::is_error?(result)

View File

@ -23,55 +23,57 @@ class VirtualNetworkOCCI < VirtualNetwork
<NETWORK href="<%= base_url %>/network/<%= self.id.to_s %>">
<ID><%= self.id.to_s %></ID>
<NAME><%= self.name %></NAME>
<ADDRESS><%= template['NETWORK_ADDRESS'] %></ADDRESS>
<% if template['NETWORK_SIZE'] %>
<SIZE><%= template['NETWORK_SIZE'] %></SIZE>
<ADDRESS><%= self['TEMPLATE/NETWORK_ADDRESS'] %></ADDRESS>
<% if self['TEMPLATE/NETWORK_SIZE'] %>
<SIZE><%= self['TEMPLATE/NETWORK_SIZE'] %></SIZE>
<% end %>
</NETWORK>
}
ONE_NETWORK = %q{
NAME = <%= vnet_info['NAME'] %>
NAME = <%= @vnet_info.elements['NAME'].text %>
TYPE = RANGED
BRIDGE = <%= vnet_info['BRIDGE'] %>
NETWORK_ADDRESS = <%= vnet_info['ADDRESS'] %>
NETWORK_SIZE = <%= vnet_info['SIZE'] %>
BRIDGE = <%= @vnet_info.elements['BRIDGE'].text %>
NETWORK_ADDRESS = <%= @vnet_info.elements['ADDRESS'].text %>
NETWORK_SIZE = <%= @vnet_info.elements['SIZE'].text %>
}.gsub(/^ /, '')
# Class constructor
def initialize(vnet_info, xml, client)
def initialize(xml, client, xml_info=nil, bridge=nil)
super(xml, client)
@vnet_info = vnet_info
end
# Creates the OCCI representation of a Virtual Network
def to_occi(base_url)
vn_hash = self.to_hash
return vn_hash, 500 if OpenNebula.is_error?(vn_hash)
template = vn_hash['VNET']['TEMPLATE']
if xml_info != nil and bridge != nil
@vnet_info = REXML::Document.new(xml_info).root
bridge_element = REXML::Element.new("BRIDGE")
bridge_element.add_text(bridge)
@vnet_info.add(bridge_element)
else
@vnet_info = nil
end
end
# Creates the OCCI representation of a Virtual Network
def to_occi(base_url)
begin
occi = ERB.new(OCCI_NETWORK)
occi_text = occi.result(binding)
occi_text = occi.result(binding)
rescue Exception => e
error = OpenNebula::Error.new(e.message)
return error
end
end
return occi_text.gsub(/\n\s*/,'')
end
def to_one_template()
if @vnet_info['NETWORK']
vnet_info = @vnet_info['NETWORK']
else
error_msg = "Missing STORAGE section in the XML body"
if @vnet_info.name != 'NETWORK'
error_msg = "Missing NETWORK section in the XML body"
error = OpenNebula::Error.new(error_msg)
return error
end
one = ERB.new(ONE_NETWORK)
return one.result(binding)
end

View File

@ -7,7 +7,7 @@ module OpenNebula
rescue LoadError
NOKOGIRI=false
end
# Require crack library if present, otherwise don't bother
# This is just for OCCI use
begin
@ -17,7 +17,7 @@ module OpenNebula
###########################################################################
# The XMLUtilsElement module provides an abstraction of the underlying
# XML parser engine. It provides XML-related methods for the Pool Elements
# XML parser engine. It provides XML-related methods for the Pool Elements
###########################################################################
module XMLUtilsElement
# Initialize a XML document for the element
@ -34,7 +34,7 @@ module OpenNebula
# Extract an element from the XML description of the PoolElement.
# key::_String_ The name of the element
# [return] _String_ the value of the element
# [return] _String_ the value of the element
# Examples:
# ['VID'] # gets VM id
# ['HISTORY/HOSTNAME'] # get the hostname from the history
@ -47,16 +47,16 @@ module OpenNebula
else
element=@xml.elements[key.to_s.upcase]
end
if element
if element
element.text
end
end
def template_str(indent=true)
template_like_str('TEMPLATE', indent)
end
def template_like_str(root_element, indent=true)
if NOKOGIRI
xml_template=@xml.xpath(root_element).to_s
@ -64,7 +64,7 @@ module OpenNebula
else
rexml=@xml.elements[root_element]
end
if indent
ind_enter="\n"
ind_tab=' '
@ -72,7 +72,7 @@ module OpenNebula
ind_enter=''
ind_tab=' '
end
str=rexml.collect {|n|
if n.class==REXML::Element
str_line=""
@ -93,11 +93,11 @@ module OpenNebula
str_line
end
}.compact.join("\n")
str
end
def to_hash
def to_hash
if !@hash && @xml
begin
@hash = Crack::XML.parse(to_xml)
@ -116,18 +116,29 @@ module OpenNebula
str = ""
if pretty
REXML::Formatters::Pretty.new(1).write(@xml,str)
else
else
REXML::Formatters::Default.new.write(@xml,str)
end
str
end
end
def XMLUtilsElement.xml_to_hash(xml)
begin
hash = Crack::XML.parse(xml)
rescue Exception => e
error = OpenNebula::Error.new(e.message)
return error
end
return hash
end
end
###########################################################################
# The XMLUtilsPool module provides an abstraction of the underlying
# XML parser engine. It provides XML-related methods for the Pools
# XML parser engine. It provides XML-related methods for the Pools
###########################################################################
module XMLUtilsPool
@ -141,9 +152,9 @@ module OpenNebula
xml=REXML::Document.new(xml).root
end
end
#Executes the given block for each element of the Pool
#block:: _Block_
#block:: _Block_
def each_element(block)
if NOKOGIRI
@xml.xpath(
@ -165,14 +176,14 @@ module OpenNebula
str = ""
if pretty
REXML::Formatters::Pretty.new(1).write(@xml,str)
else
else
REXML::Formatters::Default.new.write(@xml,str)
end
str
end
end
def to_hash
def to_hash
if !@hash && @xml
@hash=Crack::XML.parse(to_xml)
end