diff --git a/install.sh b/install.sh index b64f8643b7..70cb297a9e 100755 --- a/install.sh +++ b/install.sh @@ -128,12 +128,14 @@ ETC_DIRS="$ETC_LOCATION/im_kvm \ $ETC_LOCATION/tm_ssh \ $ETC_LOCATION/tm_dummy \ $ETC_LOCATION/hm \ - $ETC_LOCATION/ec2query_templates" + $ETC_LOCATION/ec2query_templates \ + $ETC_LOCATION/occi_templates" LIB_DIRS="$LIB_LOCATION/im_probes \ $LIB_LOCATION/ruby \ $LIB_LOCATION/ruby/OpenNebula \ $LIB_LOCATION/ruby/econe \ + $LIB_LOCATION/ruby/occi \ $LIB_LOCATION/tm_commands \ $LIB_LOCATION/tm_commands/nfs \ $LIB_LOCATION/tm_commands/ssh \ @@ -163,6 +165,8 @@ INSTALL_FILES[11]="TM_EXAMPLE_SHARE_FILES:$SHARE_LOCATION/examples/tm" INSTALL_FILES[12]="HOOK_SHARE_FILES:$SHARE_LOCATION/hooks" INSTALL_FILES[13]="ECO_LIB_FILES:$LIB_LOCATION/ruby/econe" INSTALL_FILES[14]="ECO_BIN_FILES:$BIN_LOCATION" +INSTALL_FILES[15]="OCCI_LIB_FILES:$LIB_LOCATION/ruby/occi" +INSTALL_FILES[16]="OCCI_BIN_FILES:$BIN_LOCATION" INSTALL_ETC_FILES[0]="ETC_FILES:$ETC_LOCATION" INSTALL_ETC_FILES[1]="VMM_XEN_ETC_FILES:$ETC_LOCATION/vmm_xen" @@ -179,6 +183,8 @@ INSTALL_ETC_FILES[11]="TM_DUMMY_ETC_FILES:$ETC_LOCATION/tm_dummy" INSTALL_ETC_FILES[12]="HM_ETC_FILES:$ETC_LOCATION/hm" INSTALL_ETC_FILES[13]="ECO_ETC_FILES:$ETC_LOCATION" INSTALL_ETC_FILES[14]="ECO_TEMPLATE_FILES:$ETC_LOCATION/ec2query_templates" +INSTALL_ETC_FILES[15]="OCCI_ETC_FILES:$ETC_LOCATION" +INSTALL_ETC_FILES[16]="OCCI_TEMPLATE_FILES:$ETC_LOCATION/occi_templates" #------------------------------------------------------------------------------- # Binary files, to be installed under $BIN_LOCATION @@ -405,6 +411,32 @@ ECO_ETC_FILES="src/cloud/ec2/econe.conf" ECO_TEMPLATE_FILES="src/cloud/ec2/templates/m1.small.erb" +#------------------------------------------------------------------------------- +# OCCI files +#------------------------------------------------------------------------------- + +OCCI_LIB_FILES="src/cloud/occi/OCCI.rb \ + src/cloud/occi/OCCIServer.rb \ + src/cloud/occi/lib/OCCIConfiguration.rb \ + src/cloud/occi/lib/ONEOCCIClient.rb \ + src/cloud/occi/lib/VirtualMachineOCCI.rb \ + src/cloud/occi/lib/VirtualMachinePoolOCCI.rb \ + src/cloud/occi/lib/VirtualNetworkOCCI.rb \ + src/cloud/occi/lib/VirtualNetworkPoolOCCI.rb" + + + +OCCI_BIN_FILES="src/cloud/occi/occi-server \ + src/cloud/occi/commands/occi-compute \ + src/cloud/occi/commands/occi-network \ + src/cloud/occi/commands/occi-storage" + +OCCI_ETC_FILES="src/cloud/occi/occi-server.conf" + +OCCI_TEMPLATE_FILES="src/cloud/occi/templates/small.erb \ + src/cloud/occi/templates/medium.erb \ + src/cloud/occi/templates/large.erb" + #------------------------------------------------------------------------------- #------------------------------------------------------------------------------- diff --git a/src/cloud/occi/OCCI.rb b/src/cloud/occi/OCCI.rb new file mode 100644 index 0000000000..f303615f74 --- /dev/null +++ b/src/cloud/occi/OCCI.rb @@ -0,0 +1,6 @@ +require 'lib/VirtualMachineOCCI' +require 'lib/VirtualMachinePoolOCCI' +require 'lib/VirtualNetworkOCCI' +require 'lib/VirtualNetworkPoolOCCI' + +require 'lib/OCCIConfiguration' diff --git a/src/cloud/occi/OCCIServer.rb b/src/cloud/occi/OCCIServer.rb new file mode 100644 index 0000000000..1fd615e4f5 --- /dev/null +++ b/src/cloud/occi/OCCIServer.rb @@ -0,0 +1,442 @@ +################################################ +# Find out where the needed ruby libraries are +################################################ +ONE_LOCATION=ENV["ONE_LOCATION"] + +if !ONE_LOCATION + RUBY_LIB_LOCATION="/usr/lib/one/ruby" +else + RUBY_LIB_LOCATION=ONE_LOCATION+"/lib/ruby" + TEMPLATES_LOCATION=ONE_LOCATION+"/etc/occi_templates" + CONF_LOCATION=ONE_LOCATION+"/etc" +end + +$: << RUBY_LIB_LOCATION +$: << RUBY_LIB_LOCATION+"/occiserver" +$: << RUBY_LIB_LOCATION+"/econe" # For the Repository Manager + +################################################ +# Required libraries +################################################ +require 'rubygems' +require 'sinatra' +require 'time' +require 'pp' + +require 'OpenNebula' +require 'OCCI' + + +include OpenNebula + +CONFIG=OCCIConfiguration.new(CONF_LOCATION+'/occi-server.conf') +AUTH="#{CONFIG[:user]}:#{CONFIG[:password]}" +ONE_RM_DATABASE=CONFIG[:database] + +# Load Repository Manager here to use ONE_RM_DATABASE from the configuration file +require 'repo_manager' +Image.image_dir=CONFIG[:image_dir] + +INSTANCE_TYPES=Hash.new + +puts "######################################" +puts " OCCI Server configuration " +puts "######################################" +puts "---------8<---------------------" +pp CONFIG +puts "------>8------------------------" + +if CONFIG[:vm_type].kind_of?(Array) + # Multiple instance types + CONFIG[:vm_type].each {|type| + INSTANCE_TYPES[type['NAME']]=type + } +else + # When only one instance type is defined + INSTANCE_TYPES[CONFIG[:vm_type]['NAME']]=CONFIG[:vm_type] +end + +puts "######################################" +puts " OCCI Available Instances Types " +puts "######################################" +puts "---------8<---------------------" +pp INSTANCE_TYPES +puts "------>8------------------------" + +set :host, CONFIG[:server] +set :port, CONFIG[:port] + +# Start repository manager +$repoman=RepoManager.new + +################################################ +# Client builders for ONE communication +################################################ + +def get_one_client + Client.new(AUTH) +end + +def get_one_client_user(user_name) + user=get_user(user_name) + + + auth="#{user[:name]}:#{user[:password]}" + + client=Client.new("dummy:dummy") + client.one_auth=auth + client +end + +def get_user(name) + user=nil + + user_pool=UserPool.new(get_one_client) + user_pool.info + user_pool.each{|u| + if u.name==name + user=Hash.new + user[:id]=u.id + user[:name]=u.name + user[:password]=u[:password] + end + } + + user +end + +################################################### +# Helpers to manage authentication & authorization +################################################### + + +helpers do + + def protected! + response['WWW-Authenticate'] = %(Basic realm="Testing HTTP Auth") and \ + throw(:halt, [401, "Not authorized\n"]) and \ + return unless authorized? + end + + def authorized? + + @auth ||= Rack::Auth::Basic::Request.new(request.env) + + if !(@auth.provided? && @auth.basic? && @auth.credentials) + return false + end + + user = get_user(@auth.credentials.first) + + if user + if user[:password] == @auth.credentials[1] + return true + end + else + return false + end + end + +end + +################################################### +# Helper functions +################################################### + + +def submit_vm(params) + + if params['occixml'] + @vm_info=Crack::XML.parse(params['occixml']) + else + halt 400, "OCCI XML representation of VM not present" + end + + @vm_info=@vm_info['COMPUTE'] + + if @vm_info['STORAGE'].class==Array + disks=@vm_info['STORAGE'] + else + disks=[@vm_info['STORAGE']] + end + + disks.each{|disk| + next if disk['DISK']==nil + image=$repoman.get(disk['DISK']['image']) + disk['DISK']['source']=image.path + } + + @vm_info['STORAGE']=disks[0] + + + if @vm_info['NETWORK']['NIC'].class==Array + nics=@vm_info['NETWORK']['NIC'] + else + nics=[@vm_info['NETWORK']['NIC']] + end + + nics.each{|nic| + vn=VirtualNetwork.new(VirtualNetwork.build_xml(nic['network']), get_one_client) + vn.info + vn_xml=Crack::XML.parse(vn.to_xml) + nic['network_id']=nic['network'] + nic['network']=vn_xml['VNET']['NAME'].strip + } + + @vm_info['NETWORK']['NIC']=nics + + instance_type_name=params['InstanceType'] + instance_type=INSTANCE_TYPES[instance_type_name] + + halt 400, "Bad instance type" if !instance_type + + @vm_info[:instance_type]=instance_type_name + + template=ERB.new(File.read( + TEMPLATES_LOCATION+"/#{instance_type['TEMPLATE']}")) + template_text=template.result(binding) + + vm=VirtualMachineOCCI.new( + VirtualMachine.build_xml, get_one_client_user(@auth.credentials[0])) + response=vm.allocate(template_text) + + if OpenNebula.is_error?(response) + status 400 + response.to_str + else + vm.info + vm.to_occi + end +end + +def change_state(params) + if params['occixml'] + vm_info=Crack::XML.parse(params['occixml']) + else + halt 400, "OCCI XML representation of VM not present" + end + + vm=VirtualMachineOCCI.new( + VirtualMachine.build_xml(params[:id]), get_one_client_user(@auth.credentials[0])) + + halt 400, "State not defined in the OCCI XML, cannot change state" if !vm_info['COMPUTE']['STATE'] + + case vm_info['COMPUTE']['STATE'] + when "stopped" + rc = vm.stop + when "suspended" + rc = vm.suspend + when "resume" + rc = vm.resume + when "cancel" + rc = vm.cancel + when "done" + rc = vm.finalize + else + halt 400, "Invalid state" + end + + if OpenNebula.is_error?(rc) + status 400 + response.to_str + else + status 202 + response_text = "Changing state of VM " + params[:id] + " to " + vm_info['COMPUTE']['STATE'] + end +end + +################################################### +# Pool Resources methods +################################################### + +post '/compute' do + # Auth check + protected! + + submit_vm(params) +end + +get '/compute' do + # Auth check + protected! + # Info retrieval + vmpool = VirtualMachinePoolOCCI.new(get_one_client) + vmpool.info + # OCCI conversion + begin + vmpool.to_occi(CONFIG[:server]) + rescue Exception => e + error = OpenNebula::Error.new(e.message) + return error + end +end + +post '/network' do + # Auth check + protected! + # Info retrieval from post params + if params['occixml'] + network_info=Crack::XML.parse(params['occixml']) + else + halt 400, "OCCI XML representation of Virtual Network not present in the request" + end + # Allocate the VirtualNetwork + network = VirtualNetworkOCCI.new( + VirtualNetwork.build_xml, + get_one_client_user(@auth.credentials[0])) + + vntemplate = network.to_one_template(network_info['NIC'],CONFIG[:bridge]) + rc = network.allocate(vntemplate) + + # Return status 201 XML if correct, status 500 otherwise + if rc + halt 500, "Error creating the Virtual Network: " + rc + else + network.info + status 201 + network.to_occi + end +end + +get '/network' do + # Auth check + protected! + # Info retrieval + network_pool = VirtualNetworkPoolOCCI.new(get_one_client) + network_pool.info + # OCCI conversion + begin + network_pool.to_occi(CONFIG[:server]) + rescue Exception => e + error = OpenNebula::Error.new(e.message) + return error + end +end + +post '/storage' do + # Auth check + protected! + # Info retrieval from post params + if params['occixml'] + image_info=Crack::XML.parse(params['occixml']) + else + halt 400, "OCCI XML representation of Image not present in the request" + end + + if params['file'] + file=params["file"] + else + halt 400, "File not present in the request" + end + + user = get_user(@auth.credentials[0]) + + # tmpfile where the file is stored + f_tmp=file[:tempfile] + img=$repoman.add(user[:id], f_tmp.path) + f_tmp.unlink + + img.get_image_info + img.change_metadata(:name=>image_info['DISK']['NAME']) + img.change_metadata(:description=>image_info['DISK']['URL']) + + xml_response = "" + img.uuid + "" + + "" + image_info['DISK']['NAME'] + "" + + "" + ((img.size/1024)/1024).to_s + "" + + "" + image_info['DISK']['URL'] + "" + + "" + + status 201 + xml_response +end + +get '/storage' do + # Auth check + protected! + # Retrieve images owned by this user + user = get_user(@auth.credentials[0]) + images=Image.filter(:owner => user[:id]) + + image_pool = "" + for image in images do + image_pool += "" + end + image_pool += "" + image_pool +end + +################################################### +# Entity Resources Methods +################################################### + +get '/compute/:id' do + protected! + vm = VirtualMachineOCCI.new(VirtualMachine.build_xml(params[:id]),get_one_client_user(@auth.credentials[0])) + vm.info + begin + vm.to_occi() + rescue Exception => e + error = OpenNebula::Error.new(e.message) + return error + end +end + +delete '/compute/:id' do + protected! + vm = VirtualMachineOCCI.new(VirtualMachine.build_xml(params[:id]),get_one_client_user(@auth.credentials[0])) + vm.finalize + "The Compute resource has been successfully deleted" +end + +post '/compute/:id' do + protected! + + change_state(params) +end + +get '/network/:id' do + protected! + vn = VirtualNetworkOCCI.new(VirtualNetwork.build_xml(params[:id]),get_one_client_user(@auth.credentials[0])) + vn.info + begin + vn.to_occi() + rescue Exception => e + error = OpenNebula::Error.new(e.message) + return error + end +end + +delete '/network/:id' do + protected! + vn = VirtualNetworkOCCI.new(VirtualNetwork.build_xml(params[:id]),get_one_client_user(@auth.credentials[0])) + vn.delete + "The Virtual Network has been successfully deleted" +end + +get '/storage/:id' do + protected! + image=$repoman.get(params[:id]) + + image.get_image_info + + if image + xml_response = "" + image.uuid + "" + + "" + image.name + "" + + "" + ((image.size/1024)/1024).to_s + "" + + "" + image.description + "" + + "" + else + status 404 + "Disk with id = \"" + params[:id] + "\" not found" + end +end + +delete '/storage/:id' do + protected! + "Not yet implemented" +end + + + + + diff --git a/src/cloud/occi/commands/occi-compute b/src/cloud/occi/commands/occi-compute new file mode 100755 index 0000000000..83e57be60e --- /dev/null +++ b/src/cloud/occi/commands/occi-compute @@ -0,0 +1,220 @@ +#!/usr/bin/env ruby + +# == Synopsis +# occi-compute +# +# Manages compute resources +# +# == Usage +# +# occi-compute [OPTIONS] [ARGUMENTS] +# +# COMMANDS +# +# create +# creates a new compute resource described by the provided +# +# +# list +# lists available compute resources +# +# show +# retrieves the OCCI XML representation of the compute resource +# identified by +# +# update +# updates the representation of the compute resource represented by the +# provided +# +# delete +# deletes the compute resource idenfitied by +# +# +# OPTIONS +# +# -h, --help: +# show help +# +# --username , -U : +# The username of the user +# +# --password , -P : +# The password of the user +# +# --url , -U : +# Set url as the web service url to use +# +# --debug, -D +# Enables verbosity +# +# --instance-type, -T +# Specifies the type of compute resource we want (compulsory for create) +# + +# -------------------------------------------------------------------------- # +# Copyright 2002-2009, Distributed Systems Architecture Group, Universidad # +# Complutense de Madrid (dsa-research.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. # +#--------------------------------------------------------------------------- # + +ONE_LOCATION=ENV["ONE_LOCATION"] + +if !ONE_LOCATION + RUBY_LIB_LOCATION="/usr/lib/one/ruby" +else + RUBY_LIB_LOCATION=ONE_LOCATION+"/lib/ruby" + TEMPLATES_LOCATION=ONE_LOCATION+"/etc/occi_templates" + CONF_LOCATION=ONE_LOCATION+"/etc" +end + +$: << RUBY_LIB_LOCATION +$: << RUBY_LIB_LOCATION+"/occi" + +require 'ONEOCCIClient' +require 'getoptlong' +require 'rdoc/usage' +require 'pp' + +opts = GetoptLong.new( + ['--help', '-h',GetoptLong::NO_ARGUMENT], + ['--username', '-U',GetoptLong::REQUIRED_ARGUMENT], + ['--password', '-P',GetoptLong::REQUIRED_ARGUMENT], + ['--url', '-R',GetoptLong::REQUIRED_ARGUMENT], + ['--instance-type','-T',GetoptLong::REQUIRED_ARGUMENT], + ['--debug', '-D',GetoptLong::NO_ARGUMENT] + ) + +url = nil +username = nil +password = nil +auth = nil +type = nil +debug = false + +begin + opts.each do |opt, arg| + case opt + when '--help' + RDoc::usage + when '--username' + username = arg + when '--password' + password = arg + when '--url' + url = arg + when '--instance-type' + type = arg + when '--debug' + debug = true + end + end +rescue Exception => e + exit -1 +end + +begin + occi_client = ONEOCCIClient::Client.new(url,username,password,debug) +rescue Exception => e + puts "#{$0}: #{e.message}" + exit -1 +end + +if !ARGV[0] + puts "#{$0}: [COMMAND] not present" + puts "#{$0}: Execute #{$0} -h for help." + exit -1 +end + +case ARGV[0].downcase +when 'list' + occi_client.get_vms + +when 'create' + if !type + puts "#{$0}: missing compulsory instance type" + exit -1 + end + + vm_xml = ARGV[1] + + if !vm_xml || !File.exists?(vm_xml) + puts "#{$0} create: missing OCCI-XML parameter or file not found" + exit -1 + end + + begin + occi_client = ONEOCCIClient::Client.new(url,username,password,debug) + rescue Exception => e + puts "#{$0} create: #{e.message}" + exit -1 + end + + occi_client.post_vms(type, vm_xml) + +when 'show' + vm_id = ARGV[1] + + if !vm_id + puts "#{$0} show: missing VM-ID parameter" + exit -1 + end + + begin + occi_client = ONEOCCIClient::Client.new(url,username,password,debug) + rescue Exception => e + puts "#{$0} show: #{e.message}" + exit -1 + end + + occi_client.get_vm(vm_id) + +when 'update' + vm_xml = ARGV[1] + + if !vm_xml || !File.exists?(vm_xml) + puts "#{$0} update: missing OCCI-XML parameter or file not found" + exit -1 + end + + begin + occi_client = ONEOCCIClient::Client.new(url,username,password,debug) + rescue Exception => e + puts "#{$0} update: #{e.message}" + exit -1 + end + + occi_client.put_vm(vm_xml) + +when 'delete' + vm_id = ARGV[1] + + if !vm_id + puts "#{$0} delete: missing VM-ID parameter" + exit -1 + end + + begin + occi_client = ONEOCCIClient::Client.new(url,username,password,debug) + rescue Exception => e + puts "#{$0} delete: #{e.message}" + exit -1 + end + + occi_client.delete_vm(vm_id) + +else + puts "Command #{ARGV[0]} not valid." + exit -1 + +end diff --git a/src/cloud/occi/commands/occi-network b/src/cloud/occi/commands/occi-network new file mode 100755 index 0000000000..c1b888c5e2 --- /dev/null +++ b/src/cloud/occi/commands/occi-network @@ -0,0 +1,173 @@ +#!/usr/bin/env ruby + +# == Synopsis +# occi-network +# +# Manages virtual networks +# +# == Usage +# +# occi-network [OPTIONS] [ARGUMENTS] +# +# COMMANDS +# +# create +# creates a new virtual network described by the provided +# +# +# list +# lists available virtual networks +# +# show +# retrieves the OCCI XML representation of the virtual network +# identified by +# +# delete +# deletes the virtual network idenfitied by +# +# +# OPTIONS +# +# -h, --help: +# show help +# +# --username , -U : +# The username of the user +# +# --password , -P : +# The password of the user +# +# --url , -U : +# Set url as the web service url to use +# +# --debug, -D +# Enables verbosity +# + +# -------------------------------------------------------------------------- # +# Copyright 2002-2009, Distributed Systems Architecture Group, Universidad # +# Complutense de Madrid (dsa-research.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. # +#--------------------------------------------------------------------------- # + +ONE_LOCATION=ENV["ONE_LOCATION"] + +if !ONE_LOCATION + RUBY_LIB_LOCATION="/usr/lib/one/ruby" +else + RUBY_LIB_LOCATION=ONE_LOCATION+"/lib/ruby" + TEMPLATES_LOCATION=ONE_LOCATION+"/etc/occi_templates" + CONF_LOCATION=ONE_LOCATION+"/etc" +end + +$: << RUBY_LIB_LOCATION +$: << RUBY_LIB_LOCATION+"/occi" + +require 'ONEOCCIClient' +require 'getoptlong' +require 'rdoc/usage' +require 'pp' + + +opts = GetoptLong.new( + ['--help', '-h',GetoptLong::NO_ARGUMENT], + ['--username', '-U',GetoptLong::REQUIRED_ARGUMENT], + ['--password', '-P',GetoptLong::REQUIRED_ARGUMENT], + ['--url', '-R',GetoptLong::REQUIRED_ARGUMENT], + ['--debug', '-D',GetoptLong::NO_ARGUMENT] + ) + +url = nil +username = nil +password = nil +auth = nil +debug = false + +begin + opts.each do |opt, arg| + case opt + when '--help' + RDoc::usage + when '--username' + username = arg + when '--password' + password = arg + when '--url' + url = arg + when '--debug' + debug = true + end + end +rescue Exception => e + exit -1 +end + + +begin + occi_client = ONEOCCIClient::Client.new(url,username,password,debug) +rescue Exception => e + puts "#{$0}: #{e.message}" + exit -1 +end + +if !ARGV[0] + puts "#{$0}: [COMMAND] not present" + puts "#{$0}: Execute #{$0} -h for help." + exit -1 +end + + +case ARGV[0].downcase +when 'create' + network_xml = ARGV[1] + + if !network_xml || !File.exists?(network_xml) + puts "#{$0} create: missing OCCI-XML parameter or file not found" + exit -1 + end + + occi_client.post_network(network_xml) + +when 'list' + occi_client.get_networks + +when 'show' + network_id = ARGV[1] + + if !network_id + puts "#{$0} show: missing NETWORK-ID parameter or file not found" + exit -1 + end + + occi_client.get_network(network_id) + +when 'delete' + network_id = ARGV[1] + + if !network_id + puts "#{$0} delete: missing NETWORK-ID parameter" + exit -1 + end + + occi_client.delete_network(network_id) + +else + puts "Command #{ARGV[0]} not valid." + exit -1 + +end + + + + diff --git a/src/cloud/occi/commands/occi-storage b/src/cloud/occi/commands/occi-storage new file mode 100755 index 0000000000..782c05a123 --- /dev/null +++ b/src/cloud/occi/commands/occi-storage @@ -0,0 +1,163 @@ +#!/usr/bin/env ruby + +# == Synopsis +# occi-storage +# +# Manages OCCI storage resource +# +# == Usage +# +# occi-storage [OPTIONS] [PARAMETERS] +# +# COMMANDS +# +# create +# creates a new storage resource described by the provided +# +# +# list +# lists available storage resources +# +# show +# retrieves the OCCI XML representation of the storage resource +# identified by +# +# update +# updates the representation of the storage resource represented by the +# provided +# +# delete +# deletes the storage resource idenfitied by +# +# +# OPTIONS +# -h, --help: +# show help +# +# --username , -U : +# The username of the user +# +# --password , -P : +# The password of the user +# +# --url , -U : +# Set url as the web service url to use +# +# --debug, -D +# Enables verbosity +# + +# -------------------------------------------------------------------------- # +# Copyright 2002-2009, Distributed Systems Architecture Group, Universidad # +# Complutense de Madrid (dsa-research.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. # +#--------------------------------------------------------------------------- # + +ONE_LOCATION=ENV["ONE_LOCATION"] + +if !ONE_LOCATION + RUBY_LIB_LOCATION="/usr/lib/one/ruby" +else + RUBY_LIB_LOCATION=ONE_LOCATION+"/lib/ruby" + TEMPLATES_LOCATION=ONE_LOCATION+"/etc/occi_templates" + CONF_LOCATION=ONE_LOCATION+"/etc" +end + +$: << RUBY_LIB_LOCATION +$: << RUBY_LIB_LOCATION+"/occi" + +require 'ONEOCCIClient' +require 'getoptlong' +require 'rdoc/usage' +require 'pp' + + +opts = GetoptLong.new( + ['--help', '-h',GetoptLong::NO_ARGUMENT], + ['--username', '-U',GetoptLong::REQUIRED_ARGUMENT], + ['--password', '-P',GetoptLong::REQUIRED_ARGUMENT], + ['--url', '-R',GetoptLong::REQUIRED_ARGUMENT], + ['--debug', '-D',GetoptLong::NO_ARGUMENT] + ) + +url = nil +username = nil +password = nil +auth = nil +debug = false + +begin + opts.each do |opt, arg| + case opt + when '--help' + RDoc::usage + when '--username' + username = arg + when '--password' + password = arg + when '--url' + url = arg + when '--debug' + debug = true + end + end +rescue Exception => e + exit -1 +end + + +begin + occi_client = ONEOCCIClient::Client.new(url,username,password,debug) +rescue Exception => e + puts "#{$0}: #{e.message}" + exit -1 +end + +case ARGV[0].downcase +when 'create' + image_xml = ARGV[1] + + if !image_xml || !File.exists?(image_xml) + puts "#{$0} create: missing occi xml parameter or file not found" + exit -1 + end + + occi_client.post_image(image_xml) + +when 'list' + occi_client.get_images + +when 'show' + image_id = ARGV[1] + + if !image_id + puts "#{$0} show: missing storage id parameter or file not found" + exit -1 + end + + occi_client.get_image(image_id) + +when 'delete' + puts 'Delete still not implemented' + exit -1 + +else + puts "Command #{ARGV[0]} not valid." + exit -1 + +end + + + + diff --git a/src/cloud/occi/lib/OCCIConfiguration.rb b/src/cloud/occi/lib/OCCIConfiguration.rb new file mode 100644 index 0000000000..453743ba72 --- /dev/null +++ b/src/cloud/occi/lib/OCCIConfiguration.rb @@ -0,0 +1,73 @@ + +class OCCIConfiguration + + NAME_REG=/[\w\d_-]+/ + VARIABLE_REG=/\s*(#{NAME_REG})\s*=\s*/ + SIMPLE_VARIABLE_REG=/#{VARIABLE_REG}([^\[]+?)(#.*)?/ + SINGLE_VARIABLE_REG=/^#{SIMPLE_VARIABLE_REG}$/ + ARRAY_VARIABLE_REG=/^#{VARIABLE_REG}\[(.*?)\]/m + + def initialize(file) + @conf=parse_conf(file) + end + + def add_value(conf, key, value) + if conf[key] + if !conf[key].kind_of?(Array) + conf[key]=[conf[key]] + end + conf[key]< e + error = OpenNebula::Error.new(e.message) + return error + end + + puts curl.body_str + end + + ####################################################################### + # Retieves the pool of Virtual Machines + ####################################################################### + def get_vms + curl=Curl::Easy.new(@endpoint+"/compute") + curl.userpwd=@occiauth + curl.verbose=true if @debug + + begin + curl.http_get + rescue Exception => e + error = OpenNebula::Error.new(e.message) + return error + end + puts curl.body_str + end + + ####################################################################### + # Post a new Network to the VN Pool + # :xmlfile xml description of the Virtual Network + ####################################################################### + def post_network(xmlfile) + curl=Curl::Easy.new(@endpoint+"/network") + curl.userpwd=@occiauth + curl.verbose=true if @debug + + xml=File.read(xmlfile) + + begin + curl.http_post( + Curl::PostField.content('occixml', xml) + ) + rescue Exception => e + error = OpenNebula::Error.new(e.message) + return error + end + + puts curl.body_str + end + + ####################################################################### + # Retieves the pool of Virtual Networks + ####################################################################### + def get_networks + curl=Curl::Easy.new(@endpoint+"/network") + curl.userpwd=@occiauth + curl.verbose=true if @debug + + begin + curl.http_get + rescue Exception => e + error = OpenNebula::Error.new(e.message) + return error + end + puts curl.body_str + end + + ####################################################################### + # Post a new Image to the Image Pool + # :xmlfile + ####################################################################### + def post_image(xmlfile) + xml=File.read(xmlfile) + image_info=Crack::XML.parse(xml) + + curl=Curl::Easy.new(@endpoint+"/storage") + curl.userpwd=@occiauth + curl.verbose=true if @debug + curl.multipart_form_post = true + + file_path = image_info['DISK']['URL'] + + m=file_path.match(/^\w+:\/\/(.*)$/) + + if m + file_path="/"+m[1] + end + + begin + curl.http_post( + Curl::PostField.content('occixml', xml), + Curl::PostField.file('file', file_path) + ) + rescue Exception => e + error = OpenNebula::Error.new(e.message) + return error + end + + puts curl.body_str + end + + ####################################################################### + # Retieves the pool of Images owned by the user + ####################################################################### + def get_images + curl=Curl::Easy.new(@endpoint+"/storage") + curl.userpwd=@occiauth + curl.verbose=true if @debug + + begin + curl.http_get + rescue Exception => e + error = OpenNebula::Error.new(e.message) + return error + end + puts curl.body_str + end + + ####################################################################### + # Entity Resource Request Methods + ####################################################################### + + ####################################################################### + # :id VM identifier + ####################################################################### + def get_vm(id) + curl=Curl::Easy.new(@endpoint+"/compute/" + id.to_s) + curl.userpwd=@occiauth + curl.verbose=true if @debug + + begin + curl.http_get + rescue Exception => e + error = OpenNebula::Error.new(e.message) + return error + end + puts curl.body_str + end + + ####################################################################### + # Puts a new Compute representation in order to change its state + # :xmlfile Compute OCCI xml representation + ####################################################################### + def put_vm(xmlfile) + xml=File.read(xmlfile) + vm_info=Crack::XML.parse(xml) + + curl=Curl::Easy.new(@endpoint+"/compute/"+vm_info['ID']) + curl.userpwd=@occiauth + curl.verbose=true if @debug + + begin + curl.http_post(Curl::PostField.content('occixml', xml)) + rescue Exception => e + error = OpenNebula::Error.new(e.message) + return error + end + + puts curl.body_str + end + + ####################################################################### + # :id Compute identifier + ####################################################################### + def delete_vm(id) + curl=Curl::Easy.new(@endpoint+"/compute/" + id.to_s) + curl.userpwd=@occiauth + curl.verbose=true if @debug + + begin + curl.http_delete + rescue Exception => e + error = OpenNebula::Error.new(e.message) + return error + end + puts curl.body_str + end + + ####################################################################### + # Retrieves a Virtual Network + # :id Virtual Network identifier + ####################################################################### + def get_network(id) + curl=Curl::Easy.new(@endpoint+"/network/" + id.to_s) + curl.userpwd=@occiauth + curl.verbose=true if @debug + + begin + curl.http_get + rescue Exception => e + error = OpenNebula::Error.new(e.message) + return error + end + puts curl.body_str + end + + ####################################################################### + # :id VM identifier + ####################################################################### + def delete_network(id) + curl=Curl::Easy.new(@endpoint+"/network/" + id.to_s) + curl.userpwd=@occiauth + curl.verbose=true if @debug + + begin + curl.http_delete + rescue Exception => e + error = OpenNebula::Error.new(e.message) + return error + end + puts curl.body_str + end + + ####################################################################### + # Retieves an Image + # :image_uuid Image identifier + ####################################################################### + def get_image(image_uuid) + curl=Curl::Easy.new(@endpoint+"/storage/"+image_uuid) + curl.userpwd=@occiauth + curl.verbose=true if @debug + + begin + curl.http_get + rescue Exception => e + error = OpenNebula::Error.new(e.message) + return error + end + puts curl.body_str + end + end +end + + + + + + diff --git a/src/cloud/occi/lib/VirtualMachineOCCI.rb b/src/cloud/occi/lib/VirtualMachineOCCI.rb new file mode 100644 index 0000000000..71ea297046 --- /dev/null +++ b/src/cloud/occi/lib/VirtualMachineOCCI.rb @@ -0,0 +1,69 @@ +require 'OpenNebula' + +include OpenNebula + +class VirtualMachineOCCI < VirtualMachine + # Creates the VMI representation of a Virtual Machine + def to_occi + occi_xml = "" + occi_xml += "" + id.to_s + "" + occi_xml += "" + self['NAME'] + "" + occi_xml += "" + self['OCCI_SIZE_TYPE'] + "" if self['OCCI_SIZE_TYPE'] + occi_xml += "" + state_str + "" + + # Now let's parse the template + template=self.to_hash("TEMPLATE") + + template['DISK']=[template['DISK']].flatten + + if template['DISK'] + + occi_xml += "" + + template['DISK'].each{|disk| + case disk['TYPE'] + when "disk" then + occi_xml += "" + when "swap" then + occi_xml += "" + when "fs" then + occi_xml += "" + end + } + + occi_xml += "" + end + + template['NIC']=[template['NIC']].flatten + + if template['NIC'] + occi_xml += "" + + template['NIC'].each{|nic| + + occi_xml += "' + } + occi_xml += "" + end +end + diff --git a/src/cloud/occi/lib/VirtualNetworkOCCI.rb b/src/cloud/occi/lib/VirtualNetworkOCCI.rb new file mode 100644 index 0000000000..f8170070df --- /dev/null +++ b/src/cloud/occi/lib/VirtualNetworkOCCI.rb @@ -0,0 +1,28 @@ +require 'OpenNebula' +require 'Crack' + +include OpenNebula + +class VirtualNetworkOCCI < VirtualNetwork + # Creates the OCCI representation of a Virtual Network + def to_occi() + vn_hash=Crack::XML.parse(to_xml) + occi_xml = "" + + occi_xml += "" + vn_hash['VNET']['ID'].strip + "" + occi_xml += "" + vn_hash['VNET']['NAME'].strip + "" + occi_xml += "
" + vn_hash['VNET']['TEMPLATE']['NETWORK_ADDRESS'].strip + "
" + occi_xml += "" + vn_hash['VNET']['TEMPLATE']['NETWORK_SIZE'].strip + "" + + occi_xml += "
" + end + + def to_one_template(network_hash, bridge) + one_template = "NAME=" + network_hash['NAME'] + "\n" + one_template += "TYPE=RANGED\n" + one_template += "BRIDGE=" + bridge + "\n" + one_template += "NETWORK_ADDRESS=" + network_hash['ADDRESS'] + "\n" + one_template += "NETWORK_SIZE=" + network_hash['SIZE'] + "\n" + end +end + diff --git a/src/cloud/occi/lib/VirtualNetworkPoolOCCI.rb b/src/cloud/occi/lib/VirtualNetworkPoolOCCI.rb new file mode 100644 index 0000000000..c1ba20b16d --- /dev/null +++ b/src/cloud/occi/lib/VirtualNetworkPoolOCCI.rb @@ -0,0 +1,19 @@ +require 'OpenNebula' +require 'Crack' + +include OpenNebula + +class VirtualNetworkPoolOCCI < VirtualNetworkPool + # Creates the VMI representation of a Virtual Network + def to_occi(base_url) + network_pool_hash=Crack::XML.parse(to_xml) + occi_xml = "" + + network_pool_hash['VNET_POOL']['VNET'].each{|network| + occi_xml+='' + } + + occi_xml += "" + end +end \ No newline at end of file diff --git a/src/cloud/occi/occi-server b/src/cloud/occi/occi-server new file mode 100755 index 0000000000..ce2922944d --- /dev/null +++ b/src/cloud/occi/occi-server @@ -0,0 +1,9 @@ +#!/bin/bash + +eval `grep ^IMAGE_DIR= $ONE_LOCATION/etc/occi-server.conf ` +export TMPDIR=$IMAGE_DIR/tmp +mkdir -p $TMPDIR + +nohup ruby $ONE_LOCATION/lib/ruby/occi/OCCIServer.rb >> $ONE_LOCATION/var/occi-server.log & + + diff --git a/src/cloud/occi/occi-server.conf b/src/cloud/occi/occi-server.conf new file mode 100644 index 0000000000..20dc160e7a --- /dev/null +++ b/src/cloud/occi/occi-server.conf @@ -0,0 +1,26 @@ +# OpenNebula administrator user +USER=oneadmin +PASSWORD= + +# OpenNebula server contact information +ONE_XMLRPC=http://localhost:2633/RPC2 + +# Host and port where the occi server will run +SERVER= +PORT=4567 + +# Configuration for the image repository +DATABASE=/var/occi.db +IMAGE_DIR= + +# Configuration for OpenNebula's Virtual Networks +BRIDGE= + +# Default format for FS +FS_FORMAT=ext3 + +# VM types allowed and its template file (inside templates directory) +VM_TYPE=[NAME=small, TEMPLATE=small.erb] +VM_TYPE=[NAME=medium, TEMPLATE=medium.erb] +VM_TYPE=[NAME=large, TEMPLATE=large.erb] + diff --git a/src/cloud/occi/templates/large.erb b/src/cloud/occi/templates/large.erb new file mode 100644 index 0000000000..44b60c6d9e --- /dev/null +++ b/src/cloud/occi/templates/large.erb @@ -0,0 +1,45 @@ +NAME = <%= @vm_info['NAME']%> + +CPU = 8 +MEMORY = 8192 + +OS = [ kernel = /vmlinuz, + initrd = /initrd.img, + root = sda1, + kernel_cmd = "ro xencons=tty console=tty1"] +<% @vm_info['STORAGE'].each do |key, image| + +case key + + when "SWAP" +%> +DISK = [ type = "swap", + size=<%= image['size']%>, + dev=<%= image['dev']%> ] +<% + when "DISK" +%> +DISK = [ type = "disk", + dev=<%= image['dev']%>, + source=<%= image['source']%>, + image_id=<%= image['image']%> ] +<% + when "FS" +%> +DISK = [ type = "fs", + dev=<%= image['dev']%>, + size=<%= image['size']%>, + format=<%= CONFIG[:fs_format]||"ext3"%> ] +<% end %> +<% end %> +<% @vm_info['NETWORK']['NIC'].each do |nic| %> +NIC = [ +<% if nic['ip'] %> + IP=<%= nic['ip'] %>, +<% end %> + NETWORK=<%= nic['network']%>, + NETWORK_ID=<%= nic['network_id'] %> +] +<% end %> +OCCI_SIZE_TYPE = <%= @vm_info[:instance_type ]%> + diff --git a/src/cloud/occi/templates/medium.erb b/src/cloud/occi/templates/medium.erb new file mode 100644 index 0000000000..eb8ba528e1 --- /dev/null +++ b/src/cloud/occi/templates/medium.erb @@ -0,0 +1,45 @@ +NAME = <%= @vm_info['NAME']%> + +CPU = 4 +MEMORY = 4096 + +OS = [ kernel = /vmlinuz, + initrd = /initrd.img, + root = sda1, + kernel_cmd = "ro xencons=tty console=tty1"] +<% @vm_info['STORAGE'].each do |key, image| + +case key + + when "SWAP" +%> +DISK = [ type = "swap", + size=<%= image['size']%>, + dev=<%= image['dev']%> ] +<% + when "DISK" +%> +DISK = [ type = "disk", + dev=<%= image['dev']%>, + source=<%= image['source']%>, + image_id=<%= image['image']%> ] +<% + when "FS" +%> +DISK = [ type = "fs", + dev=<%= image['dev']%>, + size=<%= image['size']%>, + format=<%= CONFIG[:fs_format]||"ext3"%> ] +<% end %> +<% end %> +<% @vm_info['NETWORK']['NIC'].each do |nic| %> +NIC = [ +<% if nic['ip'] %> + IP=<%= nic['ip'] %>, +<% end %> + NETWORK=<%= nic['network']%>, + NETWORK_ID=<%= nic['network_id'] %> +] +<% end %> +OCCI_SIZE_TYPE = <%= @vm_info[:instance_type ]%> + diff --git a/src/cloud/occi/templates/small.erb b/src/cloud/occi/templates/small.erb new file mode 100644 index 0000000000..c3157aefe0 --- /dev/null +++ b/src/cloud/occi/templates/small.erb @@ -0,0 +1,45 @@ +NAME = <%= @vm_info['NAME']%> + +CPU = 1 +MEMORY = 1024 + +OS = [ kernel = /vmlinuz, + initrd = /initrd.img, + root = sda1, + kernel_cmd = "ro xencons=tty console=tty1"] +<% @vm_info['STORAGE'].each do |key, image| + +case key + + when "SWAP" +%> +DISK = [ type = "swap", + size=<%= image['size']%>, + dev=<%= image['dev']%> ] +<% + when "DISK" +%> +DISK = [ type = "disk", + dev=<%= image['dev']%>, + source=<%= image['source']%>, + image_id=<%= image['image']%> ] +<% + when "FS" +%> +DISK = [ type = "fs", + dev=<%= image['dev']%>, + size=<%= image['size']%>, + format=<%= CONFIG[:fs_format]||"ext3"%> ] +<% end %> +<% end %> +<% @vm_info['NETWORK']['NIC'].each do |nic| %> +NIC = [ +<% if nic['ip'] %> + IP=<%= nic['ip'] %>, +<% end %> + NETWORK=<%= nic['network']%>, + NETWORK_ID=<%= nic['network_id'] %> +] +<% end %> +OCCI_SIZE_TYPE = <%= @vm_info[:instance_type ]%> +