1
0
mirror of https://github.com/OpenNebula/one.git synced 2025-01-10 01:17:40 +03:00

Merge branch 'master' of git.opennebula.org:one

This commit is contained in:
Tino Vazquez 2011-09-23 16:29:58 +02:00
commit dcc181b5f1
21 changed files with 615 additions and 456 deletions

View File

@ -202,6 +202,7 @@ LIB_DIRS="$LIB_LOCATION/ruby \
$LIB_LOCATION/ruby/cloud/econe \
$LIB_LOCATION/ruby/cloud/econe/views \
$LIB_LOCATION/ruby/cloud/occi \
$LIB_LOCATION/ruby/cloud/CloudAuth \
$LIB_LOCATION/ruby/onedb \
$LIB_LOCATION/tm_commands \
$LIB_LOCATION/tm_commands/shared \
@ -345,6 +346,7 @@ INSTALL_FILES=(
HOOK_FT_FILES:$VAR_LOCATION/remotes/hooks/ft
HOOK_NETWORK_FILES:$VAR_LOCATION/remotes/hooks/vnm
COMMON_CLOUD_LIB_FILES:$LIB_LOCATION/ruby/cloud
CLOUD_AUTH_LIB_FILES:$LIB_LOCATION/ruby/cloud/CloudAuth
ECO_LIB_FILES:$LIB_LOCATION/ruby/cloud/econe
ECO_LIB_VIEW_FILES:$LIB_LOCATION/ruby/cloud/econe/views
ECO_BIN_FILES:$BIN_LOCATION
@ -789,10 +791,15 @@ RUBY_OPENNEBULA_LIB_FILES="src/oca/ruby/OpenNebula/Host.rb \
COMMON_CLOUD_LIB_FILES="src/cloud/common/CloudServer.rb \
src/cloud/common/CloudClient.rb \
src/cloud/common/CloudAuth.rb
src/cloud/common/Configuration.rb"
COMMON_CLOUD_CLIENT_LIB_FILES="src/cloud/common/CloudClient.rb"
CLOUD_AUTH_LIB_FILES="src/cloud/common/CloudAuth/BasicCloudAuth.rb \
src/cloud/common/CloudAuth/EC2CloudAuth.rb \
src/cloud/common/CloudAuth/X509CloudAuth.rb"
#-------------------------------------------------------------------------------
# EC2 Query for OpenNebula
#-------------------------------------------------------------------------------
@ -926,7 +933,8 @@ SUNSTONE_MODELS_JSON_FILES="src/sunstone/models/OpenNebulaJSON/HostJSON.rb \
src/sunstone/models/OpenNebulaJSON/AclJSON.rb \
src/sunstone/models/OpenNebulaJSON/VirtualNetworkJSON.rb"
SUNSTONE_TEMPLATE_FILES="src/sunstone/templates/login.html"
SUNSTONE_TEMPLATE_FILES="src/sunstone/templates/login.html \
src/sunstone/templates/login_x509.html"
SUNSTONE_VIEWS_FILES="src/sunstone/views/index.erb"
@ -1007,6 +1015,7 @@ SUNSTONE_PUBLIC_IMAGES_FILES="src/sunstone/public/images/ajax-loader.gif \
src/sunstone/public/images/opennebula-sunstone-big.png \
src/sunstone/public/images/opennebula-sunstone-small.png \
src/sunstone/public/images/panel.png \
src/sunstone/public/images/panel_short.png \
src/sunstone/public/images/pbar.gif \
src/sunstone/public/images/Refresh-icon.png \
src/sunstone/public/images/vnc_off.png \

View File

@ -0,0 +1,53 @@
# -------------------------------------------------------------------------- #
# 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. #
#--------------------------------------------------------------------------- #
class CloudAuth
AUTH_MODULES = {
"basic" => 'BasicCloudAuth',
"ec2" => 'EC2CloudAuth',
"x509" => 'X509CloudAuth'
}
attr_reader :client, :token
def initialize(conf)
@conf = conf
if AUTH_MODULES.include?(@conf[:auth])
require 'CloudAuth/' + AUTH_MODULES[@conf[:auth]]
extend Kernel.const_get(AUTH_MODULES[@conf[:auth]])
else
raise "Auth module not specified"
end
end
protected
def get_password(username)
@oneadmin_client ||= OpenNebula::Client.new(nil, @conf[:one_xmlrpc])
if @user_pool.nil?
@user_pool ||= OpenNebula::UserPool.new(@oneadmin_client)
rc = @user_pool.info
if OpenNebula.is_error?(rc)
raise rc.message
end
end
return @user_pool["USER[NAME=\"#{username}\"]/PASSWORD"]
end
end

View File

@ -0,0 +1,40 @@
# -------------------------------------------------------------------------- #
# 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. #
#--------------------------------------------------------------------------- #
module BasicCloudAuth
def auth(env, params={})
auth = Rack::Auth::Basic::Request.new(env)
if auth.provided? && auth.basic?
username, password = auth.credentials
if @conf[:hash_passwords]
password = Digest::SHA1.hexdigest(password)
end
one_pass = get_password(username)
if one_pass && one_pass == password
@token = "#{username}:#{password}"
@client = Client.new(@token, @conf[:one_xmlrpc], false)
return nil
else
return "Authentication failure"
end
else
return "Basic auth not provided"
end
end
end

View File

@ -0,0 +1,92 @@
# -------------------------------------------------------------------------- #
# 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. #
#--------------------------------------------------------------------------- #
module EC2CloudAuth
def auth(env, params={})
username = params['AWSAccessKeyId']
one_pass = get_password(username)
return "Invalid credentials" unless one_pass
signature = case params['SignatureVersion']
when "1" then signature_v1(params.clone,one_pass)
when "2" then signature_v2(params.clone,one_pass,env,true,false)
end
if params['Signature'] != signature
if params['SignatureVersion']=="2"
signature = signature_v2(params.clone,one_pass,env,false,false)
if params['Signature'] != signature
return "Invalid Credentials"
end
else
return "Invalid Credentials"
end
end
@token = "#{username}:#{one_pass}"
@client = Client.new(@token, @conf[:one_xmlrpc], false)
return nil
end
private
# Calculates signature version 1
def signature_v1(params, secret_key, digest='sha1')
params.delete('Signature')
req_desc = params.sort {|x,y| x[0].downcase <=> y[0].downcase}.to_s
digest_generator = OpenSSL::Digest::Digest.new(digest)
digest = OpenSSL::HMAC.digest(digest_generator, secret_key, req_desc)
b64sig = Base64.b64encode(digest)
return b64sig.strip
end
# Calculates signature version 2
def signature_v2(params, secret_key, env, include_port=true, urlencode=true)
params.delete('Signature')
params.delete('file')
server_host = params.delete(:econe_host)
server_port = params.delete(:econe_port)
if include_port
server_str = "#{server_host}:#{server_port}"
else
server_str = server_host
end
canonical_str = AWS.canonical_string(
params,
server_str,
env['REQUEST_METHOD'])
# Use the correct signature strength
sha_strength = case params['SignatureMethod']
when "HmacSHA1" then 'sha1'
when "HmacSHA256" then 'sha256'
else 'sha1'
end
digest = OpenSSL::Digest::Digest.new(sha_strength)
hmac = OpenSSL::HMAC.digest(digest, secret_key, canonical_str)
b64hmac = Base64.encode64(hmac).gsub("\n","")
if urlencode
return CGI::escape(b64hmac)
else
return b64hmac
end
end
end

View File

@ -0,0 +1,108 @@
# -------------------------------------------------------------------------- #
# 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. #
#--------------------------------------------------------------------------- #
module X509CloudAuth
# Gets the username associated with a password
# password:: _String_ the password
# [return] _Hash_ with the username
def get_username(password)
@oneadmin_client ||= OpenNebula::Client.new(nil, @conf[:one_xmlrpc])
if @user_pool.nil?
@user_pool ||= OpenNebula::UserPool.new(@oneadmin_client)
rc = @user_pool.info
if OpenNebula.is_error?(rc)
raise rc.message
end
end
username = @user_pool["USER[PASSWORD=\"#{password}\"]/NAME"]
return username if (username != nil)
# Check if the DN is part of a |-separted multi-DN password
user_elts = Array.new
@user_pool.each {|e| user_elts << e['PASSWORD']}
multiple_users = user_elts.select {|e| e=~ /\|/ }
matched = nil
multiple_users.each do |e|
e.to_s.split('|').each do |w|
if (w == password)
matched=e
break
end
end
break if matched
end
if matched
password = matched.to_s
end
return @user_pool["USER[PASSWORD=\"#{password}\"]/NAME"]
end
def auth(env, params={})
failed = 'Authentication failed. '
# For https, the web service should be set to include the user cert in the environment.
cert_line = env['HTTP_SSL_CLIENT_CERT']
cert_line = nil if cert_line == '(null)' # For Apache mod_ssl
# Use the https credentials for authentication
require 'server_auth'
while cert_line
begin
cert_array=cert_line.scan(/([^\s]*)\s/)
cert_array = cert_array[2..-2]
cert_array.unshift('-----BEGIN CERTIFICATE-----')
cert_array.push('-----END CERTIFICATE-----')
cert_pem = cert_array.join("\n")
cert = OpenSSL::X509::Certificate.new(cert_pem)
rescue
raise failed + "Could not create X509 certificate from " + cert_line
end
# Password should be DN with whitespace removed.
subjectname = cert.subject.to_s.delete("\s")
begin
username = get_username(subjectname)
rescue
username = nil
end
break if username
chain_dn = (!chain_dn ? "" : chain_dn) + "\n" + subjectname
chain_index = !chain_index ? 0 : chain_index + 1
cert_line = env["HTTP_SSL_CLIENT_CERT_CHAIN_#{chain_index}"]
cert_line = nil if cert_line == '(null)' # For Apache mod_ssl
end
if !cert_line
msg = ""
msg << failed
msg << "Username not found in certificate chain "
msg << chain_dn if chain_dn
raise msg
end
auth = ServerAuth.new
@token = auth.login_token(username, subjectname, 300)
@client = Client.new(@token, @conf[:one_xmlrpc], false)
return nil
end
end

View File

@ -14,9 +14,8 @@
# limitations under the License. #
#--------------------------------------------------------------------------- #
require 'Configuration'
require 'OpenNebula'
require 'pp'
require 'CloudAuth'
##############################################################################
# This class represents a generic Cloud Server using the OpenNebula Cloud
@ -28,80 +27,39 @@ class CloudServer
# Public attributes
##########################################################################
attr_reader :config
attr_reader :one_client
# Initializes the Cloud server based on a config file
# config_file:: _String_ for the server. MUST include the following
# variables:
# USER
# PASSWORD
# AUTH
# VM_TYPE
# IMAGE_DIR
# DATABASE
def initialize(config_file)
# XMLRPC
def initialize(config)
# --- Load the Cloud Server configuration file ---
@config = config
@cloud_auth = CloudAuth.new(@config)
end
@config = Configuration.new(config_file)
def authenticate(env, params={})
@cloud_auth.auth(env, params)
end
if @config[:vm_type] == nil
raise "No VM_TYPE defined."
end
@instance_types = Hash.new
if @config[:vm_type].kind_of?(Array)
@config[:vm_type].each {|type|
@instance_types[type['NAME']]=type
}
else
@instance_types[@config[:vm_type]['NAME']]=@config[:vm_type]
end
# --- Start an OpenNebula Session ---
@one_client = Client.new(nil,@config[:one_xmlrpc])
@user_pool = UserPool.new(@one_client)
def client
@cloud_auth.client
end
#
# Prints the configuration of the server
#
def print_configuration
def self.print_configuration(config)
puts "--------------------------------------"
puts " Server configuration "
puts "--------------------------------------"
pp @config
pp config
puts "--------------------------------------"
puts " Registered Instance Types "
puts "--------------------------------------"
pp @instance_types
STDOUT.flush
end
###########################################################################
# USER and OpenNebula Session Methods
###########################################################################
# Generates an OpenNebula Session for the given user
# user:: _Hash_ the user information
# [return] an OpenNebula client session
def one_client_user(name, password)
client = Client.new("dummy:dummy")
client.one_auth = "#{name}:#{password}"
return client
end
# Gets the data associated with a user
# name:: _String_ the name of the user
# [return] _Hash_ with the user data
def get_user_password(name)
@user_pool.info
return @user_pool["USER[NAME=\"#{name}\"]/PASSWORD"]
end
# Finds out if a port is available on ip
# ip:: _String_ IP address where the port to check is
# port:: _String_ port to find out whether is open
@ -123,4 +81,3 @@ class CloudServer
return false
end
end

View File

@ -15,14 +15,21 @@
#--------------------------------------------------------------------------- #
# OpenNebula sever contact information
ONE_XMLRPC=http://localhost:2633/RPC2
:one_xmlrpc: http://localhost:2633/RPC2
# Host and port where econe server will run
SERVER=<PUT HERE FQDN OF SERVER>
PORT=4567
:server: localhost
:port: 4567
# SSL proxy that serves the API (set if is being used)
#SSL_SERVER=fqdm.of.the.server
#:ssl_server: fqdm.of.the.server
# Authentication protocol for the econe server:
# ec2, default Acess key and Secret key scheme
# x509, for x509 certificates based authentication
:auth: ec2
# VM types allowed and its template file (inside templates directory)
VM_TYPE=[NAME=m1.small, TEMPLATE=m1.small.erb]
:instance_types:
:m1.small:
:template: m1.small.erb

View File

@ -15,7 +15,6 @@
#--------------------------------------------------------------------------- #
require 'rubygems'
require 'sinatra'
require 'erb'
require 'time'
require 'AWS'
@ -62,67 +61,26 @@ class EC2QueryServer < CloudServer
###########################################################################
def initialize(config_file,template,views)
super(config_file)
@config.add_configuration_value("TEMPLATE_LOCATION",template)
@config.add_configuration_value("VIEWS",views)
if @config[:ssl_server]
@server_host=@config[:ssl_server]
else
@server_host=@config[:server]
end
@server_port=@config[:port]
print_configuration
def initialize(config)
super(config)
end
def authenticate(env, params)
econe_host = @config[:ssl_server]
econe_host ||= @config[:server]
###########################################################################
# Authentication functions
###########################################################################
econe_port = @config[:port]
# EC2 protocol authentication function
# params:: of the request
# [return] true if authenticated
def authenticate(params,env)
password = get_user_password(params['AWSAccessKeyId'])
return nil if !password
signature = case params['SignatureVersion']
when "1" then signature_version_1(params.clone, password)
when "2" then signature_version_2(params,
password,
env,
true,
false)
end
if params['Signature']==signature
return one_client_user(params['AWSAccessKeyId'], password)
else
if params['SignatureVersion']=="2"
signature = signature_version_2(params,
password,
env,
false,
false)
if params['Signature']==signature
return one_client_user(params['AWSAccessKeyId'], password)
end
end
end
return nil
params.merge!({:econe_host => econe_host, :econe_port => econe_port})
super(env, params)
end
###########################################################################
# Repository Interface
###########################################################################
def upload_image(params, one_client)
image = ImageEC2.new(Image.build_xml, one_client, params['file'])
def upload_image(params)
image = ImageEC2.new(Image.build_xml, self.client, params['file'])
template = image.to_one_template
if OpenNebula.is_error?(template)
@ -140,11 +98,11 @@ class EC2QueryServer < CloudServer
return response.result(binding), 200
end
def register_image(params, one_client)
def register_image(params)
# Get the Image ID
tmp, img=params['ImageLocation'].split('-')
image = Image.new(Image.build_xml(img.to_i), one_client)
image = Image.new(Image.build_xml(img.to_i), self.client)
# Enable the new Image
rc = image.info
@ -160,9 +118,9 @@ class EC2QueryServer < CloudServer
return response.result(binding), 200
end
def describe_images(params, one_client)
def describe_images(params)
user_flag = OpenNebula::Pool::INFO_GROUP
impool = ImagePool.new(one_client, user_flag)
impool = ImagePool.new(self.client, user_flag)
impool.info
erb_version = params['Version']
@ -175,14 +133,14 @@ class EC2QueryServer < CloudServer
# Instance Interface
###########################################################################
def run_instances(params, one_client)
def run_instances(params)
# Get the instance type and path
if params['InstanceType'] != nil
instance_type_name = params['InstanceType']
instance_type = @instance_types[instance_type_name]
instance_type = @config[:instance_types][instance_type_name.to_sym]
if instance_type != nil
path = @config[:template_location] + "/#{instance_type['TEMPLATE']}"
path = @config[:template_location] + "/#{instance_type[:template]}"
end
end
@ -201,7 +159,7 @@ class EC2QueryServer < CloudServer
template_text = template.result(binding)
# Start the VM.
vm = VirtualMachine.new(VirtualMachine.build_xml, one_client)
vm = VirtualMachine.new(VirtualMachine.build_xml, self.client)
rc = vm.allocate(template_text)
if OpenNebula::is_error?(rc)
@ -219,26 +177,26 @@ class EC2QueryServer < CloudServer
return response.result(binding), 200
end
def describe_instances(params, one_client)
def describe_instances(params)
user_flag = OpenNebula::Pool::INFO_MINE
vmpool = VirtualMachinePool.new(one_client, user_flag)
vmpool = VirtualMachinePool.new(self.client, user_flag)
vmpool.info
erb_version = params['Version']
erb_user_name = params['AWSAccessKeyId']
response = ERB.new(File.read(@config[:views]+"/describe_instances.erb"))
return response.result(binding), 200
end
def terminate_instances(params, one_client)
def terminate_instances(params)
# Get the VM ID
vmid=params['InstanceId.1']
vmid=params['InstanceId.01'] if !vmid
tmp, vmid=vmid.split('-') if vmid[0]==?i
vm = VirtualMachine.new(VirtualMachine.build_xml(vmid),one_client)
vm = VirtualMachine.new(VirtualMachine.build_xml(vmid),self.client)
rc = vm.info
return OpenNebula::Error.new('Unsupported'),400 if OpenNebula::is_error?(rc)
@ -257,55 +215,6 @@ class EC2QueryServer < CloudServer
return response.result(binding), 200
end
private
# Calculates signature version 1
def signature_version_1(params, secret_key, digest='sha1')
params.delete('Signature')
req_desc = params.sort {|x,y| x[0].downcase <=> y[0].downcase}.to_s
digest_generator = OpenSSL::Digest::Digest.new(digest)
digest = OpenSSL::HMAC.digest(digest_generator,
secret_key,
req_desc)
b64sig = Base64.b64encode(digest)
return b64sig.strip
end
# Calculates signature version 2
def signature_version_2(params, secret_key, env, includeport=true, urlencode=true)
signature_params = params.reject { |key,value|
key=='Signature' or key=='file' }
if includeport
server_str = @server_host + ':' + @server_port
else
server_str = @server_host
end
canonical_str = AWS.canonical_string(signature_params,
server_str,
env['REQUEST_METHOD'])
# Use the correct signature strength
sha_strength = case params['SignatureMethod']
when "HmacSHA1" then 'sha1'
when "HmacSHA256" then 'sha256'
else 'sha1'
end
digest = OpenSSL::Digest::Digest.new(sha_strength)
b64hmac =
Base64.encode64(
OpenSSL::HMAC.digest(digest, secret_key, canonical_str)).gsub("\n","")
if urlencode
return CGI::escape(b64hmac)
else
return b64hmac
end
end
###########################################################################
# Helper functions
###########################################################################

View File

@ -42,37 +42,54 @@ require 'rubygems'
require 'sinatra'
require 'EC2QueryServer'
require 'Configuration'
include OpenNebula
##############################################################################
# Parse Configuration file
##############################################################################
begin
$econe_server = EC2QueryServer.new(CONFIGURATION_FILE,
TEMPLATE_LOCATION, VIEWS_LOCATION)
conf = YAML.load_file(CONFIGURATION_FILE)
rescue Exception => e
puts "Error starting server: #{e}"
exit(-1)
puts "Error parsing config file #{CONFIGURATION_FILE}: #{e.message}"
exit 1
end
if CloudServer.is_port_open?($econe_server.config[:server],
$econe_server.config[:port])
puts "Port busy, please shutdown the service or move econe server port."
exit
end
conf[:template_location] = TEMPLATE_LOCATION
conf[:views] = VIEWS_LOCATION
CloudServer.print_configuration(conf)
##############################################################################
# Sinatra Configuration
##############################################################################
set :host, $econe_server.config[:server]
set :port, $econe_server.config[:port]
set :config, conf
set :host, settings.config[:server]
set :port, settings.config[:port]
if CloudServer.is_port_open?(settings.config[:server],
settings.config[:port])
puts "Port busy, please shutdown the service or move econe server port."
exit 1
end
##############################################################################
# Actions
##############################################################################
before do
@client = $econe_server.authenticate(params,env)
if @client.nil?
@econe_server = EC2QueryServer.new(settings.config)
begin
result = @econe_server.authenticate(request.env, params)
rescue Exception => e
# Add a log message
error 500, error_xml("AuthFailure", 0)
end
if result
# Add a log message
error 400, error_xml("AuthFailure", 0)
end
end
@ -80,7 +97,7 @@ end
helpers do
def error_xml(code,id)
message = ''
case code
when 'AuthFailure'
message = 'User not authorized'
@ -88,45 +105,44 @@ helpers do
message = 'Specified AMI ID does not exist'
when 'Unsupported'
message = 'The instance type or feature is not supported in your requested Availability Zone.'
else
else
message = code
end
xml = "<Response><Errors><Error><Code>"+
code +
"</Code><Message>" +
message +
"</Message></Error></Errors><RequestID>" +
id.to_s +
code +
"</Code><Message>" +
message +
"</Message></Error></Errors><RequestID>" +
id.to_s +
"</RequestID></Response>"
return xml
end
return xml
end
end
post '/' do
do_http_request(params, @client)
do_http_request(params)
end
get '/' do
do_http_request(params, @client)
do_http_request(params)
end
def do_http_request(params, client)
def do_http_request(params)
case params['Action']
when 'UploadImage'
result,rc = $econe_server.upload_image(params, client)
result,rc = @econe_server.upload_image(params)
when 'RegisterImage'
result,rc = $econe_server.register_image(params, client)
result,rc = @econe_server.register_image(params)
when 'DescribeImages'
result,rc = $econe_server.describe_images(params, client)
result,rc = @econe_server.describe_images(params)
when 'RunInstances'
result,rc = $econe_server.run_instances(params, client)
result,rc = @econe_server.run_instances(params)
when 'DescribeInstances'
result,rc = $econe_server.describe_instances(params, client)
result,rc = @econe_server.describe_instances(params)
when 'TerminateInstances'
result,rc = $econe_server.terminate_instances(params, client)
result,rc = @econe_server.terminate_instances(params)
end
if OpenNebula::is_error?(result)
@ -135,5 +151,5 @@ def do_http_request(params, client)
headers['Content-Type'] = 'application/xml'
result
result
end

View File

@ -14,21 +14,31 @@
# limitations under the License. #
#--------------------------------------------------------------------------- #
# OpenNebula server contact information
ONE_XMLRPC=http://localhost:2633/RPC2
# OpenNebula sever contact information
:one_xmlrpc: http://localhost:2633/RPC2
# Host and port where the occi server will run
SERVER=<FQDN OF OCCI SERVER>
PORT=4567
# Host and port where OCCI server will run
:server: localhost
:port: 4567
# SSL proxy that serves the API (set if is being used)
#SSL_SERVER=https://localhost:443
#:ssl_server: fqdm.of.the.server
# Configuration for OpenNebula's Virtual Networks
BRIDGE=<NAME OF DEFAULT BRIDGE>
#:bridge: NAME_OF_DEFAULT_BRIDGE
# Authentication protocol for the OCCI server:
# basic, for OpenNebula's user-password scheme
# x509, for x509 certificates based authentication
:auth: basic
# VM types allowed and its template file (inside templates directory)
VM_TYPE=[NAME=custom, TEMPLATE=custom.erb]
VM_TYPE=[NAME=small, TEMPLATE=small.erb]
VM_TYPE=[NAME=medium, TEMPLATE=medium.erb]
VM_TYPE=[NAME=large, TEMPLATE=large.erb]
:instance_types:
:custom:
:template: custom.erb
:small:
:template: small.erb
:medium:
:template: medium.erb
:large:
:template: large.erb

View File

@ -15,12 +15,9 @@
#--------------------------------------------------------------------------- #
# Common cloud libs
require 'rubygems'
require 'sinatra'
require 'CloudServer'
# OCA
require 'OpenNebula'
include OpenNebula
# OCCI libs
@ -43,29 +40,13 @@ class OCCIServer < CloudServer
# Server initializer
# config_file:: _String_ path of the config file
# template:: _String_ path to the location of the templates
def initialize(config_file,template)
super(config_file)
def initialize(config)
super(config)
@config.add_configuration_value("TEMPLATE_LOCATION",template)
if @config[:ssl_server]
@base_url=@config[:ssl_server]
if config[:ssl_server]
@base_url=config[:ssl_server]
else
@base_url="http://#{@config[:server]}:#{@config[:port]}"
end
print_configuration
end
# Retrieve a client with the user credentials
# requestenv:: _Hash_ Hash containing the environment of the request
# [return] _Client_ client with the user credentials
def get_client(requestenv)
auth = Rack::Auth::Basic::Request.new(requestenv)
if auth
return one_client_user(auth.credentials[0], auth.credentials[1])
else
return nil
@base_url="http://#{config[:server]}:#{config[:port]}"
end
end
@ -92,13 +73,8 @@ class OCCIServer < CloudServer
# --- Get User's VMs ---
user_flag = -1
one_client = get_client(request.env)
if !one_client
return "No authorization data present", 401
end
vmpool = VirtualMachinePoolOCCI.new(
one_client,
self.client,
user_flag)
# --- Prepare XML Response ---
@ -124,13 +100,8 @@ class OCCIServer < CloudServer
# --- Get User's VNETs ---
user_flag = -1
one_client = get_client(request.env)
if !one_client
return "No authorization data present", 401
end
network_pool = VirtualNetworkPoolOCCI.new(
one_client,
self.client,
user_flag)
# --- Prepare XML Response ---
@ -155,13 +126,8 @@ class OCCIServer < CloudServer
# --- Get User's Images ---
user_flag = -1
one_client = get_client(request.env)
if !one_client
return "No authorization data present", 401
end
image_pool = ImagePoolOCCI.new(
one_client,
self.client,
user_flag)
# --- Prepare XML Response ---
@ -193,16 +159,11 @@ class OCCIServer < CloudServer
# [return] _String_,_Integer_ COMPUTE Representation or error, status code
def post_compute(request)
# --- Create the new Instance ---
one_client = get_client(request.env)
if !one_client
return "No authorization data present", 401
end
vm = VirtualMachineOCCI.new(
VirtualMachine.build_xml,
one_client,
self.client,
request.body.read,
@instance_types,
@config[:instance_types],
@config[:template_location])
# --- Generate the template and Allocate the new Instance ---
@ -223,14 +184,9 @@ class OCCIServer < CloudServer
# status code
def get_compute(request, params)
# --- Get the VM ---
one_client = get_client(request.env)
if !one_client
return "No authorization data present", 401
end
vm = VirtualMachineOCCI.new(
VirtualMachine.build_xml(params[:id]),
one_client)
self.client)
# --- Prepare XML Response ---
rc = vm.info
@ -253,14 +209,9 @@ class OCCIServer < CloudServer
# status code
def delete_compute(request, params)
# --- Get the VM ---
one_client = get_client(request.env)
if !one_client
return "No authorization data present", 401
end
vm = VirtualMachineOCCI.new(
VirtualMachine.build_xml(params[:id]),
one_client)
self.client)
rc = vm.info
return rc, 404 if OpenNebula::is_error?(rc)
@ -278,14 +229,9 @@ class OCCIServer < CloudServer
# status code
def put_compute(request, params)
# --- Get the VM ---
one_client = get_client(request.env)
if !one_client
return "No authorization data present", 401
end
vm = VirtualMachineOCCI.new(
VirtualMachine.build_xml(params[:id]),
one_client)
self.client)
rc = vm.info
return rc, 400 if OpenNebula.is_error?(rc)
@ -349,14 +295,9 @@ class OCCIServer < CloudServer
# [return] _String_,_Integer_ Network Representation or error, status code
def post_network(request)
# --- Create the new Instance ---
one_client = get_client(request.env)
if !one_client
return "No authorization data present", 401
end
network = VirtualNetworkOCCI.new(
VirtualNetwork.build_xml,
one_client,
self.client,
request.body,
@config[:bridge])
@ -377,15 +318,9 @@ class OCCIServer < CloudServer
# [return] _String_,_Integer_ NETWORK occi representation or error,
# status code
def get_network(request, params)
# --- Get the VNET ---
one_client = get_client(request.env)
if !one_client
return "No authorization data present", 401
end
network = VirtualNetworkOCCI.new(
VirtualNetwork.build_xml(params[:id]),
one_client)
self.client)
# --- Prepare XML Response ---
rc = network.info
@ -406,15 +341,9 @@ class OCCIServer < CloudServer
# [return] _String_,_Integer_ Delete confirmation msg or error,
# status code
def delete_network(request, params)
# --- Get the VNET ---
one_client = get_client(request.env)
if !one_client
return "No authorization data present", 401
end
network = VirtualNetworkOCCI.new(
VirtualNetwork.build_xml(params[:id]),
one_client)
self.client)
rc = network.info
return rc, 404 if OpenNebula::is_error?(rc)
@ -433,15 +362,10 @@ class OCCIServer < CloudServer
def put_network(request, params)
xmldoc = XMLElement.build_xml(request.body, 'NETWORK')
vnet_info = XMLElement.new(xmldoc) if xmldoc != nil
one_client = get_client(request.env)
if !one_client
return "No authorization data present", 401
end
vnet = VirtualNetworkOCCI.new(
VirtualNetwork.build_xml(params[:id]),
one_client)
self.client)
rc = vnet.info
return rc, 400 if OpenNebula.is_error?(rc)
@ -474,11 +398,6 @@ class OCCIServer < CloudServer
error = OpenNebula::Error.new(error_msg)
return error, 400
end
one_client = get_client(request.env)
if !one_client
return "No authorization data present", 401
end
# --- Create and Add the new Image ---
occixml = request.params['occixml']
@ -486,7 +405,7 @@ class OCCIServer < CloudServer
image = ImageOCCI.new(
Image.build_xml,
one_client,
self.client,
occixml,
request.params['file'])
@ -508,14 +427,9 @@ class OCCIServer < CloudServer
# status code
def get_storage(request, params)
# --- Get the Image ---
one_client = get_client(request.env)
if !one_client
return "No authorization data present", 401
end
image = ImageOCCI.new(
Image.build_xml(params[:id]),
one_client)
self.client)
rc = image.info
@ -537,14 +451,9 @@ class OCCIServer < CloudServer
# status code
def delete_storage(request, params)
# --- Get the Image ---
one_client = get_client(request.env)
if !one_client
return "No authorization data present", 401
end
image = ImageOCCI.new(
Image.build_xml(params[:id]),
one_client)
self.client)
rc = image.info
return rc, 404 if OpenNebula::is_error?(rc)
@ -563,15 +472,10 @@ class OCCIServer < CloudServer
def put_storage(request, params)
xmldoc = XMLElement.build_xml(request.body, 'STORAGE')
image_info = XMLElement.new(xmldoc) if xmldoc != nil
one_client = get_client(request.env)
if !one_client
return "No authorization data present", 401
end
image = ImageOCCI.new(
Image.build_xml(params[:id]),
one_client)
self.client)
rc = image.info
return rc, 400 if OpenNebula.is_error?(rc)

View File

@ -77,8 +77,8 @@ class VirtualMachineOCCI < VirtualMachine
if @vm_info != nil
itype = @vm_info['INSTANCE_TYPE']
if itype != nil and types[itype] != nil
@template = base + "/#{types[itype]['TEMPLATE']}"
if itype != nil and types[itype.to_sym] != nil
@template = base + "/#{types[itype.to_sym][:template]}"
end
end

View File

@ -43,35 +43,56 @@ $: << RUBY_LIB_LOCATION+"/cloud" # For the Repository Manager
################################################
require 'rubygems'
require 'sinatra'
require 'OCCIServer'
require 'OpenNebula'
require 'OCCIServer'
include OpenNebula
##############################################################################
# Parse Configuration file
##############################################################################
begin
$occi_server = OCCIServer.new(CONFIGURATION_FILE, TEMPLATE_LOCATION)
conf = YAML.load_file(CONFIGURATION_FILE)
rescue Exception => e
puts "Error starting server: #{e}"
exit(-1)
puts "Error parsing config file #{CONFIGURATION_FILE}: #{e.message}"
exit 1
end
if CloudServer.is_port_open?($occi_server.config[:server],
$occi_server.config[:port])
puts "Port busy, please shutdown the service or move occi server port."
exit
end
conf[:template_location] = TEMPLATE_LOCATION
CloudServer.print_configuration(conf)
##############################################################################
# Sinatra Configuration
##############################################################################
set :host, $occi_server.config[:server]
set :port, $occi_server.config[:port]
set :config, conf
if CloudServer.is_port_open?(settings.config[:server],
settings.config[:port])
puts "Port busy, please shutdown the service or move occi server port."
exit
end
set :host, settings.config[:server]
set :port, settings.config[:port]
##############################################################################
# Helpers
##############################################################################
before do
@occi_server = OCCIServer.new(settings.config)
begin
result = @occi_server.authenticate(request.env)
rescue Exception => e
error 500, e.message
end
if result
error 401, result
end
end
# Response treatment
helpers do
def treat_response(result,rc)
@ -93,32 +114,32 @@ end
###################################################
post '/compute' do
result,rc = $occi_server.post_compute(request)
result,rc = @occi_server.post_compute(request)
treat_response(result,rc)
end
get '/compute' do
result,rc = $occi_server.get_computes(request)
result,rc = @occi_server.get_computes(request)
treat_response(result,rc)
end
post '/network' do
result,rc = $occi_server.post_network(request)
result,rc = @occi_server.post_network(request)
treat_response(result,rc)
end
get '/network' do
result,rc = $occi_server.get_networks(request)
result,rc = @occi_server.get_networks(request)
treat_response(result,rc)
end
post '/storage' do
result,rc = $occi_server.post_storage(request)
result,rc = @occi_server.post_storage(request)
treat_response(result,rc)
end
get '/storage' do
result,rc = $occi_server.get_storages(request)
result,rc = @occi_server.get_storages(request)
treat_response(result,rc)
end
@ -127,46 +148,46 @@ end
###################################################
get '/compute/:id' do
result,rc = $occi_server.get_compute(request, params)
result,rc = @occi_server.get_compute(request, params)
treat_response(result,rc)
end
delete '/compute/:id' do
result,rc = $occi_server.delete_compute(request, params)
result,rc = @occi_server.delete_compute(request, params)
treat_response(result,rc)
end
put '/compute/:id' do
result,rc = $occi_server.put_compute(request, params)
result,rc = @occi_server.put_compute(request, params)
treat_response(result,rc)
end
get '/network/:id' do
result,rc = $occi_server.get_network(request, params)
result,rc = @occi_server.get_network(request, params)
treat_response(result,rc)
end
delete '/network/:id' do
result,rc = $occi_server.delete_network(request, params)
result,rc = @occi_server.delete_network(request, params)
treat_response(result,rc)
end
put '/network/:id' do
result,rc = $occi_server.put_network(request, params)
result,rc = @occi_server.put_network(request, params)
treat_response(result,rc)
end
get '/storage/:id' do
result,rc = $occi_server.get_storage(request, params)
result,rc = @occi_server.get_storage(request, params)
treat_response(result,rc)
end
delete '/storage/:id' do
result,rc = $occi_server.delete_storage(request, params)
result,rc = @occi_server.delete_storage(request, params)
treat_response(result,rc)
end
put '/storage/:id' do
result,rc = $occi_server.put_storage(request, params)
result,rc = @occi_server.put_storage(request, params)
treat_response(result,rc)
end

View File

@ -19,11 +19,6 @@ module OZones
class ApacheWritter
def initialize(file_path)
@file_path = file_path
File.open(@file_path, 'w') {|f|
f.flock(File::LOCK_EX)
f.write(htaccess)
}
end
def update

View File

@ -226,7 +226,7 @@ class OzonesServer
mandatory_params = [:onename, :onepass, :endpoint, :name]
mandatory_params.each { |param|
if !vdc_data[param]
if !zone_data[param]
return [400, OZones::Error.new(
"Error: Couldn't create resource #{kind}. " +
"Mandatory attribute '#{param}' is missing.").to_json]

View File

@ -19,13 +19,13 @@
if [ -z "$ONE_LOCATION" ]; then
SUNSTONE_PID=/var/run/one/sunstone.pid
SUNSTONE_SERVER=/usr/lib/one/sunstone/config.ru
SUNSTONE_SERVER=/usr/lib/one/sunstone/sunstone-server.rb
SUNSTONE_LOCK_FILE=/var/lock/one/.sunstone.lock
SUNSTONE_LOG=/var/log/one/sunstone.log
SUNSTONE_CONF=/etc/one/sunstone-server.conf
else
SUNSTONE_PID=$ONE_LOCATION/var/sunstone.pid
SUNSTONE_SERVER=$ONE_LOCATION/lib/sunstone/config.ru
SUNSTONE_SERVER=$ONE_LOCATION/lib/sunstone/sunstone-server.rb
SUNSTONE_LOCK_FILE=$ONE_LOCATION/var/.sunstone.lock
SUNSTONE_LOG=$ONE_LOCATION/var/sunstone.log
SUNSTONE_CONF=$ONE_LOCATION/etc/sunstone-server.conf
@ -56,23 +56,16 @@ start()
echo "Can not find $SUNSTONE_SERVER."
exit 1
fi
source $SUNSTONE_CONF
lsof -i:$PORT &> /dev/null
if [ $? -eq 0 ]; then
echo "The port $PORT is being used. Please specify a different one."
exit 1
fi
# Start the sunstone daemon
touch $SUNSTONE_LOCK_FILE
rackup $SUNSTONE_SERVER -s thin -p $PORT -o $HOST \
-P $SUNSTONE_PID &> $SUNSTONE_LOG &
ruby $SUNSTONE_SERVER > $SUNSTONE_LOG 2>&1 &
LASTPID=$!
if [ $? -ne 0 ]; then
echo "Error executing $SUNSTONE_SERVER, please check the log $SUNSTONE_LOG"
exit 1
else
echo $LASTPID > $SUNSTONE_PID
fi
sleep 1
@ -83,7 +76,7 @@ start()
exit 1
fi
echo "sunstone-server listening on $HOST:$PORT"
echo "sunstone-server started"
}
#

View File

@ -1,7 +1,12 @@
# OpenNebula sever contact information
:one_xmlrpc: http://localhost:2633/RPC2
# Server Configuration
HOST=127.0.0.1
PORT=9869
:host: 127.0.0.1
:port: 9869
:auth: basic
# VNC Configuration
VNC_PROXY_BASE_PORT=29876
NOVNC_PATH=
:vnc_proxy_base_port: 29876
:novnc_path:

View File

@ -23,37 +23,8 @@ class SunstoneServer
# FLAG that will filter the elements retrieved from the Pools
POOL_FILTER = Pool::INFO_GROUP
def initialize(username, password)
# TBD one_client_user(name) from CloudServer
@client = Client.new("dummy:dummy")
@client.one_auth = "#{username}:#{password}"
end
############################################################################
#
############################################################################
def self.authorize(user="", sha1_pass="")
if user.empty? || sha1_pass.empty?
return [401, false]
end
# TBD get_user_password(name) from CloudServer
user_pool = UserPool.new(Client.new)
rc = user_pool.info
if OpenNebula.is_error?(rc)
return [500, false]
end
user_pass = user_pool["USER[NAME=\"#{user}\"]/PASSWORD"]
user_id = user_pool["USER[NAME=\"#{user}\"]/ID"]
user_gid = user_pool["USER[NAME=\"#{user}\"]/GID"]
user_gname = user_pool["USER[NAME=\"#{user}\"]/GNAME"]
if user_pass == sha1_pass
return [204, [user_id, user_gid, user_gname]]
else
return [401, nil]
end
def initialize(token, xmlrpc)
@client = Client.new(token, xmlrpc, false)
end
############################################################################

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -36,6 +36,7 @@ end
SUNSTONE_ROOT_DIR = File.dirname(__FILE__)
$: << RUBY_LIB_LOCATION
$: << RUBY_LIB_LOCATION+'/cloud'
$: << SUNSTONE_ROOT_DIR+'/models'
##############################################################################
@ -45,16 +46,23 @@ require 'rubygems'
require 'sinatra'
require 'erb'
require 'cloud/Configuration'
require 'CloudAuth'
require 'SunstoneServer'
require 'SunstonePlugins'
set :config, Configuration.new(CONFIGURATION_FILE)
begin
conf = YAML.load_file(CONFIGURATION_FILE)
conf[:hash_passwords] = true
rescue Exception => e
puts "Error parsing config file #{CONFIGURATION_FILE}: #{e.message}"
exit 1
end
##############################################################################
# Sinatra Configuration
##############################################################################
use Rack::Session::Pool, :key => 'sunstone'
set :config, conf
set :host, settings.config[:host]
set :port, settings.config[:port]
@ -67,32 +75,40 @@ helpers do
end
def build_session
auth = Rack::Auth::Basic::Request.new(request.env)
if auth.provided? && auth.basic? && auth.credentials
user = auth.credentials[0]
sha1_pass = Digest::SHA1.hexdigest(auth.credentials[1])
cloud_auth = CloudAuth.new(settings.config)
rc = SunstoneServer.authorize(user, sha1_pass)
if rc[1]
session[:user] = user
session[:user_id] = rc[1][0]
session[:user_gid] = rc[1][1]
session[:user_gname] = rc[1][2]
session[:password] = sha1_pass
session[:ip] = request.ip
session[:remember] = params[:remember]
if params[:remember]
env['rack.session.options'][:expire_after] = 30*60*60*24
end
return [204, ""]
else
return [rc.first, ""]
end
begin
result = cloud_auth.auth(request.env, params)
rescue Exception => e
error 500, e.message
end
return [401, ""]
if result
return [401, ""]
else
user_id = OpenNebula::User::SELF
user = OpenNebula::User.new_with_id(user_id, cloud_auth.client)
rc = user.info
if OpenNebula.is_error?(rc)
# Add a log message
return [500, ""]
end
session[:user] = user['NAME']
session[:user_id] = user['ID']
session[:user_gid] = user['GID']
session[:user_gname] = user['GNAME']
session[:token] = cloud_auth.token
session[:ip] = request.ip
session[:remember] = params[:remember]
if params[:remember]
env['rack.session.options'][:expire_after] = 30*60*60*24
end
return [204, ""]
end
end
def destroy_session
@ -105,7 +121,9 @@ before do
unless request.path=='/login' || request.path=='/'
halt 401 unless authorized?
@SunstoneServer = SunstoneServer.new(session[:user], session[:password])
@SunstoneServer = SunstoneServer.new(
session[:token],
settings.config[:one_xmlrpc])
end
end
@ -125,9 +143,10 @@ end
# HTML Requests
##############################################################################
get '/' do
return File.read(File.dirname(__FILE__)+
'/templates/login.html') unless authorized?
if !authorized?
templ = settings.config[:auth]=="basic"? "login.html" : "login_x509.html"
return File.read(File.dirname(__FILE__)+'/templates/'+templ)
end
time = Time.now + 60
response.set_cookie("one-user",
:value=>"#{session[:user]}",
@ -146,7 +165,10 @@ get '/' do
end
get '/login' do
File.read(SUNSTONE_ROOT_DIR+'/templates/login.html')
if !authorized?
templ = settings.confing[:auth]=="basic"? "login.html" : "login_x509.html"
return File.read(File.dirname(__FILE__)+'/templates/'+templ)
end
end
##############################################################################

View File

@ -0,0 +1,47 @@
<!DOCTYPE html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>OpenNebula Sunstone Login</title>
<link rel="stylesheet" type="text/css" href="css/login.css" />
<!-- Vendor Libraries -->
<script type="text/javascript" src="vendor/jQuery/jquery-1.4.4.min.js"></script>
<!-- End Vendor Libraries -->
<script type="text/javascript" src="js/opennebula.js"></script>
<script type="text/javascript" src="js/login.js"></script>
</head>
<body>
<div id="header">
<div id="logo">
The OpenNebula Cloud Operations Center
</div>
</div>
<div id="wrapper">
<div id="logo_sunstone">
</div>
<div id="auth_error" class="error_message">
Invalid username or password
</div>
<div id="one_error" class="error_message">
OpenNebula is not running
</div>
<form id="login_form">
<div class="border" id="login" style='height:169px;background:url("../images/panel_short.png") no-repeat scroll center transparent'>
<div class="content">
<input style="float:left;" type="submit" id="login_btn" value="" />
<input type="checkbox" id="check_remember" />
<label id="label_remember" for="check_remember">Remember me</label>
</div>
</div>
</form>
</div>
</body>
</html>