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

Merge remote-tracking branch 'upstream/feature-924' into feature-1112

This commit is contained in:
Hector Sanjuan 2012-03-07 12:55:19 +01:00
commit b525bf7ff2
51 changed files with 1026 additions and 255 deletions

View File

@ -39,11 +39,12 @@ class CloudAuth
# Tokens will be generated if time > EXPIRE_TIME - EXPIRE_MARGIN
EXPIRE_MARGIN = 300
attr_reader :client, :token
attr_reader :client, :token, :logger
# conf a hash with the configuration attributes as symbols
def initialize(conf)
def initialize(conf, logger=nil)
@conf = conf
@logger = logger
@token_expiration_time = Time.now.to_i + EXPIRE_DELTA

View File

@ -37,7 +37,7 @@ class CloudServer
##########################################################################
# Public attributes
##########################################################################
attr_reader :config
attr_reader :config, :logger
# Initializes the Cloud server based on a config file
# config_file:: _String_ for the server. MUST include the following
@ -45,9 +45,10 @@ class CloudServer
# AUTH
# VM_TYPE
# XMLRPC
def initialize(config)
def initialize(config, logger=nil)
# --- Load the Cloud Server configuration file ---
@config = config
@logger = logger
end
#
# Prints the configuration of the server
@ -82,3 +83,54 @@ class CloudServer
return false
end
end
module CloudLogger
require 'logger'
DEBUG_LEVEL = [
Logger::ERROR, # 0
Logger::WARN, # 1
Logger::INFO, # 2
Logger::DEBUG # 3
]
# Mon Feb 27 06:02:30 2012 [Clo] [E]: Error message example
MSG_FORMAT = %{%s [%s]: %s\n}
# Mon Feb 27 06:02:30 2012
DATE_FORMAT = "%a %b %d %H:%M:%S %Y"
# Patch logger class to be compatible with Rack::CommonLogger
class ::Logger
def write(msg)
info msg.chop
end
end
def enable_logging(path=nil, debug_level=3)
path ||= $stdout
logger = ::Logger.new(path)
logger.level = DEBUG_LEVEL[debug_level]
logger.formatter = proc do |severity, datetime, progname, msg|
MSG_FORMAT % [
datetime.strftime(DATE_FORMAT),
severity[0..0],
msg ]
end
# Add the logger instance to the Sinatra settings
set :logger, logger
# The logging will be configured in Rack, not in Sinatra
disable :logging
# Use the logger instance in the Rack methods
use Rack::CommonLogger, logger
helpers do
def logger
settings.logger
end
end
end
end

View File

@ -34,6 +34,9 @@
# x509, for x509 certificate encryption of tokens
:core_auth: cipher
# 0 = ERROR, 1 = WARNING, 2 = INFO, 3 = DEBUG
:debug_level: 3
# VM types allowed and its template file (inside templates directory)
:instance_types:
:m1.small:

View File

@ -61,8 +61,8 @@ class EC2QueryServer < CloudServer
###########################################################################
def initialize(client, config)
super(config)
def initialize(client, config, logger)
super(config, logger)
@client = client
end

View File

@ -20,19 +20,23 @@
ONE_LOCATION=ENV["ONE_LOCATION"]
if !ONE_LOCATION
RUBY_LIB_LOCATION = "/usr/lib/one/ruby"
LOG_LOCATION = "/var/log/one"
VAR_LOCATION = "/var/lib/one"
CONFIGURATION_FILE = "/etc/one/econe.conf"
TEMPLATE_LOCATION = "/etc/one/ec2query_templates"
ETC_LOCATION = "/etc/one"
RUBY_LIB_LOCATION = "/usr/lib/one/ruby"
else
RUBY_LIB_LOCATION = ONE_LOCATION+"/lib/ruby"
VAR_LOCATION = ONE_LOCATION+"/var"
CONFIGURATION_FILE = ONE_LOCATION+"/etc/econe.conf"
TEMPLATE_LOCATION = ONE_LOCATION+"/etc/ec2query_templates"
VAR_LOCATION = ONE_LOCATION + "/var"
LOG_LOCATION = ONE_LOCATION + "/var"
ETC_LOCATION = ONE_LOCATION + "/etc"
RUBY_LIB_LOCATION = ONE_LOCATION+"/lib/ruby"
end
VIEWS_LOCATION = RUBY_LIB_LOCATION + "/cloud/econe/views"
EC2_AUTH = VAR_LOCATION + "/.one/ec2_auth"
EC2_AUTH = VAR_LOCATION + "/.one/ec2_auth"
EC2_LOG = LOG_LOCATION + "/econe-server.log"
CONFIGURATION_FILE = ETC_LOCATION + "/occi-server.conf"
TEMPLATE_LOCATION = ETC_LOCATION + "/occi_templates"
VIEWS_LOCATION = RUBY_LIB_LOCATION + "/cloud/econe/views"
$: << RUBY_LIB_LOCATION
$: << RUBY_LIB_LOCATION+"/cloud"
@ -57,12 +61,13 @@ include OpenNebula
begin
conf = YAML.load_file(CONFIGURATION_FILE)
rescue Exception => e
puts "Error parsing config file #{CONFIGURATION_FILE}: #{e.message}"
STDERR.puts "Error parsing config file #{CONFIGURATION_FILE}: #{e.message}"
exit 1
end
conf[:template_location] = TEMPLATE_LOCATION
conf[:views] = VIEWS_LOCATION
conf[:debug_level] ||= 3
CloudServer.print_configuration(conf)
@ -70,21 +75,28 @@ CloudServer.print_configuration(conf)
# Sinatra Configuration
##############################################################################
set :config, conf
set :bind, settings.config[:server]
set :port, settings.config[:port]
include CloudLogger
enable_logging EC2_LOG, settings.config[:debug_level].to_i
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
settings.logger.error {
"Port #{settings.config[:port]} busy, please shutdown " <<
"the service or move occi server port."
}
exit -1
end
set :bind, settings.config[:server]
set :port, settings.config[:port]
begin
ENV["ONE_CIPHER_AUTH"] = EC2_AUTH
cloud_auth = CloudAuth.new(settings.config)
cloud_auth = CloudAuth.new(settings.config, settings.logger)
rescue => e
puts "Error initializing authentication system"
puts e.message
settings.logger.error {"Error initializing authentication system"}
settings.logger.error {e.message}
exit -1
end
@ -116,6 +128,7 @@ before do
params['econe_path'] = settings.econe_path
username = settings.cloud_auth.auth(request.env, params)
rescue Exception => e
logger.error {e.message}
error 500, error_xml("AuthFailure", 0)
end
@ -123,7 +136,7 @@ before do
error 401, error_xml("AuthFailure", 0)
else
client = settings.cloud_auth.client(username)
@econe_server = EC2QueryServer.new(client, settings.config)
@econe_server = EC2QueryServer.new(client, settings.config, settings.logger)
end
end
@ -179,6 +192,7 @@ def do_http_request(params)
end
if OpenNebula::is_error?(result)
logger.error(result.message)
error rc, error_xml(result.message, 0)
end

View File

@ -34,8 +34,8 @@
# x509, for x509 certificate encryption of tokens
:core_auth: cipher
# Life-time in seconds for token renewal (that used to handle OpenNebula auths)
:token_expiration_delta: 1800
# 0 = ERROR, 1 = WARNING, 2 = INFO, 3 = DEBUG
:debug_level: 3
# VM types allowed and its template file (inside templates directory)
:instance_types:

View File

@ -52,8 +52,8 @@ class OCCIServer < CloudServer
# Server initializer
# config_file:: _String_ path of the config file
# template:: _String_ path to the location of the templates
def initialize(client, config)
super(config)
def initialize(client, config, logger)
super(config, logger)
if config[:ssl_server]
@base_url=config[:ssl_server]
@ -81,10 +81,10 @@ class OCCIServer < CloudServer
############################################################################
def get_collections(request)
xml_resp = "<COLLECTIONS>\n"
xml_resp = "<COLLECTIONS>"
COLLECTIONS.each { |c|
xml_resp << "\t<#{c.upcase}_COLLECTION href=\"#{@base_url}/#{c}\">\n"
COLLECTIONS.sort.each { |c|
xml_resp << "<#{c.upcase}_COLLECTION href=\"#{@base_url}/#{c}\"/>"
}
xml_resp << "</COLLECTIONS>"
@ -97,7 +97,7 @@ class OCCIServer < CloudServer
<INSTANCE_TYPE href="<%= @base_url %>/instance_type/<%=name%>" name="<%= name %>">
<ID><%= name.to_s %></ID>
<NAME><%= name.to_s %></NAME>
<% opts.each { |elem, value|
<% opts.sort{|k1,k2| k1[0].to_s<=>k2[0].to_s}.each { |elem, value|
next if elem==:template
str = elem.to_s.upcase %>
<<%= str %>><%= value %></<%= str %>>
@ -106,9 +106,11 @@ class OCCIServer < CloudServer
}
def get_instance_types(request)
xml_resp = "<INSTANCE_TYPE_COLLECTION>\n"
xml_resp = "<INSTANCE_TYPE_COLLECTION>"
@config[:instance_types].each { |name, opts|
@config[:instance_types].sort { |k1,k2|
k1[0].to_s<=>k2[0].to_s
}.each { |name, opts|
if request.params['verbose']
begin
occi_it = ERB.new(INSTANCE_TYPE)
@ -120,7 +122,7 @@ class OCCIServer < CloudServer
xml_resp << occi_it.gsub(/\n\s*/,'')
else
xml_resp << "\t<INSTANCE_TYPE href=\"#{@base_url}/instance_type/#{name.to_s}\" name=\"#{name}\">\n"
xml_resp << "<INSTANCE_TYPE href=\"#{@base_url}/instance_type/#{name.to_s}\" name=\"#{name}\"/>"
end
}
@ -163,7 +165,7 @@ class OCCIServer < CloudServer
return rc, CloudServer::HTTP_ERROR_CODE[rc.errno]
end
return to_occi_xml(vmpool, :status=>200, :verbose=>request.params['verbose'])
return to_occi_xml(vmpool, :code=>200, :verbose=>request.params['verbose'])
end
@ -183,7 +185,7 @@ class OCCIServer < CloudServer
return rc, CloudServer::HTTP_ERROR_CODE[rc.errno]
end
return to_occi_xml(network_pool, :status=>200, :verbose=>request.params['verbose'])
return to_occi_xml(network_pool, :code=>200, :verbose=>request.params['verbose'])
end
# Gets the pool representation of STORAGES
@ -202,7 +204,7 @@ class OCCIServer < CloudServer
return rc, CloudServer::HTTP_ERROR_CODE[rc.errno]
end
return to_occi_xml(image_pool, :status=>200, :verbose=>request.params['verbose'])
return to_occi_xml(image_pool, :code=>200, :verbose=>request.params['verbose'])
end
# Gets the pool representation of USERs
@ -219,7 +221,7 @@ class OCCIServer < CloudServer
return rc, CloudServer::HTTP_ERROR_CODE[rc.errno]
end
return to_occi_xml(user_pool, :status=>200, :verbose=>request.params['verbose'])
return to_occi_xml(user_pool, :code=>200, :verbose=>request.params['verbose'])
end
############################################################################
@ -255,7 +257,7 @@ class OCCIServer < CloudServer
# --- Prepare XML Response ---
vm.info
return to_occi_xml(vm, :status=>201)
return to_occi_xml(vm, :code=>201)
end
# Get the representation of a COMPUTE resource
@ -274,7 +276,7 @@ class OCCIServer < CloudServer
return rc, CloudServer::HTTP_ERROR_CODE[rc.errno]
end
return to_occi_xml(vm, :status=>200)
return to_occi_xml(vm, :code=>200)
end
# Deletes a COMPUTE resource
@ -317,7 +319,7 @@ class OCCIServer < CloudServer
return result, code
else
vm.info
return to_occi_xml(vm, :status=>code)
return to_occi_xml(vm, :code=>code)
end
end
@ -347,7 +349,7 @@ class OCCIServer < CloudServer
# --- Prepare XML Response ---
network.info
return to_occi_xml(network, :status=>201)
return to_occi_xml(network, :code=>201)
end
# Retrieves a NETWORK resource
@ -365,7 +367,7 @@ class OCCIServer < CloudServer
return rc, CloudServer::HTTP_ERROR_CODE[rc.errno]
end
return to_occi_xml(network, :status=>200)
return to_occi_xml(network, :code=>200)
end
# Deletes a NETWORK resource
@ -411,7 +413,7 @@ class OCCIServer < CloudServer
# --- Prepare XML Response ---
vnet.info
return to_occi_xml(vnet, :status=>202)
return to_occi_xml(vnet, :code=>202)
end
############################################################################
@ -457,7 +459,7 @@ class OCCIServer < CloudServer
end
# --- Prepare XML Response ---
return to_occi_xml(image, :status=>201)
return to_occi_xml(image, :code=>201)
end
# Get a STORAGE resource
@ -476,7 +478,7 @@ class OCCIServer < CloudServer
end
# --- Prepare XML Response ---
return to_occi_xml(image, :status=>200)
return to_occi_xml(image, :code=>200)
end
# Deletes a STORAGE resource (Not yet implemented)
@ -530,7 +532,7 @@ class OCCIServer < CloudServer
# --- Prepare XML Response ---
image.info
return to_occi_xml(image, :status=>202)
return to_occi_xml(image, :code=>202)
end
# Get the representation of a USER
@ -549,7 +551,7 @@ class OCCIServer < CloudServer
return rc, CloudServer::HTTP_ERROR_CODE[rc.errno]
end
return to_occi_xml(user, :status=>200)
return to_occi_xml(user, :code=>200)
end
############################################################################
@ -566,7 +568,7 @@ class OCCIServer < CloudServer
return [404, error]
end
vnc_proxy = OpenNebulaVNC.new(config,{:json_errors => false})
vnc_proxy = OpenNebulaVNC.new(config, logger, {:json_errors => false})
return vnc_proxy.start(vm)
end
@ -574,7 +576,8 @@ class OCCIServer < CloudServer
begin
OpenNebulaVNC.stop(pipe)
rescue Exception => e
return [500, e.message]
logger.error {e.message}
return [500, "Error stopping VNC. Please check server logs."]
end
return [200,nil]

View File

@ -25,18 +25,22 @@
ONE_LOCATION=ENV["ONE_LOCATION"]
if !ONE_LOCATION
RUBY_LIB_LOCATION="/usr/lib/one/ruby"
LOG_LOCATION = "/var/log/one"
VAR_LOCATION = "/var/lib/one"
TEMPLATE_LOCATION="/etc/one/occi_templates"
CONFIGURATION_FILE = "/etc/one/occi-server.conf"
ETC_LOCATION = "/etc/one"
RUBY_LIB_LOCATION = "/usr/lib/one/ruby"
else
RUBY_LIB_LOCATION=ONE_LOCATION+"/lib/ruby"
VAR_LOCATION = ONE_LOCATION+"/var"
TEMPLATE_LOCATION=ONE_LOCATION+"/etc/occi_templates"
CONFIGURATION_FILE = ONE_LOCATION+"/etc/occi-server.conf"
VAR_LOCATION = ONE_LOCATION + "/var"
LOG_LOCATION = ONE_LOCATION + "/var"
ETC_LOCATION = ONE_LOCATION + "/etc"
RUBY_LIB_LOCATION = ONE_LOCATION+"/lib/ruby"
end
OCCI_AUTH = VAR_LOCATION + "/.one/occi_auth"
OCCI_AUTH = VAR_LOCATION + "/.one/occi_auth"
OCCI_LOG = LOG_LOCATION + "/occi-server.log"
CONFIGURATION_FILE = ETC_LOCATION + "/occi-server.conf"
TEMPLATE_LOCATION = ETC_LOCATION + "/occi_templates"
$: << RUBY_LIB_LOCATION
$: << RUBY_LIB_LOCATION+"/cloud/occi"
@ -59,42 +63,55 @@ require 'CloudAuth'
include OpenNebula
##############################################################################
# Parse Configuration file
# Configuration
##############################################################################
# Set Configuration settings
begin
conf = YAML.load_file(CONFIGURATION_FILE)
rescue Exception => e
puts "Error parsing config file #{CONFIGURATION_FILE}: #{e.message}"
STDERR.puts "Error parsing config file #{CONFIGURATION_FILE}: #{e.message}"
exit 1
end
conf[:template_location] = TEMPLATE_LOCATION
conf[:debug_level] ||= 3
CloudServer.print_configuration(conf)
##############################################################################
# Sinatra Configuration
##############################################################################
set :config, conf
# Enable Logger
include CloudLogger
enable_logging OCCI_LOG, settings.config[:debug_level].to_i
# Set Sinatra configuration
use Rack::Session::Pool, :key => 'occi'
set :public, Proc.new { File.join(root, "ui/public") }
set :views, settings.root + '/ui/views'
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
settings.logger.error {
"Port #{settings.config[:port]} busy, please shutdown " <<
"the service or move occi server port."
}
exit -1
end
set :bind, settings.config[:server]
set :port, settings.config[:port]
# Create CloudAuth
begin
ENV["ONE_CIPHER_AUTH"] = OCCI_AUTH
cloud_auth = CloudAuth.new(settings.config)
cloud_auth = CloudAuth.new(settings.config, settings.logger)
rescue => e
puts "Error initializing authentication system"
puts e.message
settings.logger.error {"Error initializing authentication system"}
settings.logger.error {e.message}
exit -1
end
@ -110,17 +127,21 @@ before do
begin
username = settings.cloud_auth.auth(request.env, params)
rescue Exception => e
error 500, e.message
logger.error {e.message}
error 500, ""
end
else
username = session[:user]
end
if username.nil? #unable to authenticate
logger.error {"User not authorized"}
error 401, ""
else
client = settings.cloud_auth.client(username)
@occi_server = OCCIServer.new(client, settings.config)
@occi_server = OCCIServer.new(client,
settings.config,
settings.logger)
end
end
end
@ -147,20 +168,24 @@ helpers do
begin
username = settings.cloud_auth.auth(request.env, params)
rescue Exception => e
error 500, e.message
logger.error {e.message}
error 500, ""
end
if username.nil?
logger.error {"User not authorized"}
error 401, ""
else
client = settings.cloud_auth.client(username)
@occi_server = OCCIServer.new(client, settings.config)
@occi_server = OCCIServer.new(client,
settings.config,
settings.logger)
user_id = OpenNebula::User::SELF
user = OpenNebula::User.new_with_id(user_id, client)
rc = user.info
if OpenNebula.is_error?(rc)
# Add a log message
logger.error {rc.message}
return [500, ""]
end
@ -190,6 +215,7 @@ helpers do
def treat_response(result,rc)
if OpenNebula::is_error?(result)
logger.error {result.message}
halt rc, result.message
end

View File

@ -132,12 +132,19 @@ div#login input#login_btn:hover {
}
.error_message {
width: 400px;
margin-left: auto;
margin-right: auto;
display: none;
position: relative;
top: 80px;
font-family: Arial, Helvetica, sans-serif;
color:red;
font-size:1.6em;
font-size:1.0em;
}
#login_spinner {
left: 44px;
position: relative;
top: 2px;
}
#label_remember {

View File

@ -24,14 +24,20 @@ function auth_error(req, error){
switch (status){
case 401:
$("#one_error").hide();
$("#auth_error").fadeIn("slow");
$("#error_box").text("Invalid username or password");
break;
case 500:
$("#auth_error").hide();
$("#one_error").fadeIn("slow");
$("#error_box").text("OpenNebula is not running or there was a server exception. Please check the server logs.");
break;
case 0:
$("#error_box").text("No answer from server. Is it running?");
break;
default:
$("#error_box").text("Unexpected error. Status "+status+". Check the server logs.");
};
$("#error_box").fadeIn("slow");
$("#login_spinner").hide();
}
function authenticate(){
@ -40,6 +46,9 @@ function authenticate(){
password = Crypto.SHA1(password);
var remember = $("#check_remember").is(":checked");
$("#error_box").fadeOut("slow");
$("#login_spinner").show();
var obj = { data: {username: username,
password: password},
remember: remember,
@ -70,4 +79,5 @@ $(document).ready(function(){
};
$("input#username.box").focus();
$("#login_spinner").hide();
});

View File

@ -189,7 +189,7 @@ var OCCI = {
$.ajax({
url: resource.toLowerCase(),
type: "GET",
data: {timeout: timeout},
data: {timeout: timeout, verbose: true},
dataType: "xml ONEjson",
success: function(response){
var res = {};

View File

@ -153,13 +153,6 @@ var vm_actions = {
error: onError
},
"VM.showstate" : {
type: "single",
call: OCCI.VM.show,
callback: updateVMStateCB,
error: onError
},
"VM.refresh" : {
type: "custom",
call : function (){
@ -178,7 +171,7 @@ var vm_actions = {
"VM.suspend" : {
type: "multiple",
call: OCCI.VM.suspend,
callback: updateVMStateCB,
callback: updateVMachineElement,
elements: vmElements,
error: onError,
notify: true
@ -187,7 +180,7 @@ var vm_actions = {
"VM.resume" : {
type: "multiple",
call: OCCI.VM.resume,
callback: updateVMStateCB,
callback: updateVMachineElement,
elements: vmElements,
error: onError,
notify: true
@ -196,7 +189,7 @@ var vm_actions = {
"VM.stop" : {
type: "multiple",
call: OCCI.VM.stop,
callback: updateVMStateCB,
callback: updateVMachineElement,
elements: vmElements,
error: onError,
notify: true
@ -214,7 +207,7 @@ var vm_actions = {
"VM.shutdown" : {
type: "multiple",
call: OCCI.VM.shutdown,
callback: updateVMStateCB,
callback: updateVMachineElement,
elements: vmElements,
error: onError,
notify: true
@ -223,7 +216,7 @@ var vm_actions = {
"VM.cancel" : {
type: "multiple",
call: OCCI.VM.cancel,
callback: updateVMStateCB,
callback: updateVMachineElement,
elements: vmElements,
error: onError,
notify: true
@ -240,7 +233,7 @@ var vm_actions = {
"VM.saveas" : {
type: "single",
call: OCCI.VM.saveas,
callback: updateVMStateCB,
callback: updateVMachineElement,
error:onError
},
@ -259,7 +252,7 @@ var vm_actions = {
};
var options = "";
for (var i = 0; i<response.length; i++){
var type = response[i].INSTANCE_TYPE.NAME;
var type = response[i].INSTANCE_TYPE.name;
options += '<option value="'+type+'">'+type+'</option>';
};
$('#dialog select#instance_type').html(options);
@ -423,7 +416,7 @@ function vMachineElementArray(vm_json){
return [
'<input class="check_item" type="checkbox" id="vm_'+id+'" name="selected_items" value="'+id+'"/>',
id,
name
VMStateBulletStr(vm_json) + name
];
}
@ -473,16 +466,15 @@ function updateVMachinesView(request, vmachine_list){
$.each(vmachine_list,function(){
el_array = vMachineElementArray(this);
vmachine_list_array.push(el_array);
Sunstone.runAction("VM.showstate",el_array[1]);
});
updateView(vmachine_list_array,dataTable_vMachines);
updateDashboard("vms",vmachine_list);
};
function updateVMStateCB(request,vm){
function VMStateBulletStr(vm){
var vm_state = vm.COMPUTE.STATE;
var state_html = vm_state;
var state_html = "";
switch (vm_state) {
case "INIT":
case "PENDING":
@ -499,13 +491,8 @@ function updateVMStateCB(request,vm){
state_html = '<img style="display:inline-block;margin-right:5px;" src="images/red_bullet.png" alt="'+vm_state+'" title="'+vm_state+'"/>';
break;
};
var tag = 'input#vm_'+vm.COMPUTE.ID;
var array = vMachineElementArray(vm);
array[2] = state_html + array[2];
updateSingleElement(array,dataTable_vMachines,tag);
};
return state_html;
}
// Refreshes the information panel for a VM
function updateVMInfo(request,vm){

View File

@ -3,7 +3,7 @@
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>OpenNebula Self-Service Login</title>
<link rel="stylesheet" type="text/css" href="css/login.css" />
<link rel="stylesheet" type="text/css" href="vendor/jQueryUI/jquery-ui-1.8.16.custom.css" />
<!-- Vendor Libraries -->
<script type="text/javascript" src="vendor/jQuery/jquery-1.7.1.min.js"></script>
<script type="text/javascript" src="vendor/crypto-js/2.3.0-crypto-sha1.js"></script>
@ -28,11 +28,7 @@
<div id="logo_selfservice">
</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 id="error_box" class="error_message ui-state-error ui-corner-all">
</div>
<form id="login_form">
@ -46,7 +42,7 @@
<input type="checkbox" id="check_remember" />
<label id="label_remember" for="check_remember">Remember me</label>
<input type="submit" id="login_btn" value="" />
<img src="images/ajax-loader.gif" alt="retrieving" id="login_spinner" />
</div>
</div>
</form>

View File

@ -0,0 +1 @@
<COMPUTE_COLLECTION></COMPUTE_COLLECTION>

View File

@ -0,0 +1,20 @@
<COMPUTE href="http://localhost:4567/compute/0">
<ID>0</ID>
<USER href="http://localhost:4567/user/2" name="my_first_occi_user"/>
<GROUP>oneadmin</GROUP>
<CPU>1</CPU>
<MEMORY>1024</MEMORY>
<NAME>Compute</NAME>
<INSTANCE_TYPE href="http://localhost:4567/instance_type/small">small</INSTANCE_TYPE>
<STATE>PENDING</STATE>
<DISK id="0">
<STORAGE href="http://localhost:4567/storage/0" name="Storage"/>
<TYPE>DISK</TYPE>
<TARGET>hde</TARGET>
</DISK>
<NIC>
<NETWORK href="http://localhost:4567/network/0" name="Network"/>
<IP>192.168.1.1</IP>
<MAC>02:00:c0:a8:01:01</MAC>
</NIC>
</COMPUTE>

View File

@ -0,0 +1,20 @@
<COMPUTE href="http://localhost:4567/compute/0">
<ID>0</ID>
<USER href="http://localhost:4567/user/2" name="my_first_occi_user"/>
<GROUP>oneadmin</GROUP>
<CPU>1</CPU>
<MEMORY>1024</MEMORY>
<NAME>Compute</NAME>
<INSTANCE_TYPE href="http://localhost:4567/instance_type/small">small</INSTANCE_TYPE>
<STATE>DONE</STATE>
<DISK id="0">
<STORAGE href="http://localhost:4567/storage/0" name="Storage"/>
<TYPE>DISK</TYPE>
<TARGET>hde</TARGET>
</DISK>
<NIC>
<NETWORK href="http://localhost:4567/network/0" name="Network"/>
<IP>192.168.1.1</IP>
<MAC>02:00:c0:a8:01:01</MAC>
</NIC>
</COMPUTE>

View File

@ -0,0 +1,20 @@
<COMPUTE href="http://localhost:4567/compute/1">
<ID>1</ID>
<USER href="http://localhost:4567/user/3" name="my_second_occi_user"/>
<GROUP>users</GROUP>
<CPU>1</CPU>
<MEMORY>1024</MEMORY>
<NAME>Compute2</NAME>
<INSTANCE_TYPE href="http://localhost:4567/instance_type/small">small</INSTANCE_TYPE>
<STATE>PENDING</STATE>
<DISK id="0">
<STORAGE href="http://localhost:4567/storage/1" name="Storage2"/>
<TYPE>DISK</TYPE>
<TARGET>hde</TARGET>
</DISK>
<NIC>
<NETWORK href="http://localhost:4567/network/1" name="Network2"/>
<IP>192.168.2.1</IP>
<MAC>02:00:c0:a8:02:01</MAC>
</NIC>
</COMPUTE>

View File

@ -0,0 +1,20 @@
<COMPUTE href="http://localhost:4567/compute/1">
<ID>1</ID>
<USER href="http://localhost:4567/user/3" name="my_second_occi_user"/>
<GROUP>users</GROUP>
<CPU>1</CPU>
<MEMORY>1024</MEMORY>
<NAME>Compute2</NAME>
<INSTANCE_TYPE href="http://localhost:4567/instance_type/small">small</INSTANCE_TYPE>
<STATE>DONE</STATE>
<DISK id="0">
<STORAGE href="http://localhost:4567/storage/1" name="Storage2"/>
<TYPE>DISK</TYPE>
<TARGET>hde</TARGET>
</DISK>
<NIC>
<NETWORK href="http://localhost:4567/network/1" name="Network2"/>
<IP>192.168.2.1</IP>
<MAC>02:00:c0:a8:02:01</MAC>
</NIC>
</COMPUTE>

View File

@ -0,0 +1,20 @@
<INSTANCE_TYPE_COLLECTION>
<INSTANCE_TYPE href="http://localhost:4567/instance_type/large" name="large">
<ID>large</ID>
<NAME>large</NAME>
<CPU>8</CPU>
<MEMORY>8192</MEMORY>
</INSTANCE_TYPE>
<INSTANCE_TYPE href="http://localhost:4567/instance_type/medium" name="medium">
<ID>medium</ID>
<NAME>medium</NAME>
<CPU>4</CPU>
<MEMORY>4096</MEMORY>
</INSTANCE_TYPE>
<INSTANCE_TYPE href="http://localhost:4567/instance_type/small" name="small">
<ID>small</ID>
<NAME>small</NAME>
<CPU>1</CPU>
<MEMORY>1024</MEMORY>
</INSTANCE_TYPE>
</INSTANCE_TYPE_COLLECTION>

View File

@ -0,0 +1,5 @@
<INSTANCE_TYPE_COLLECTION>
<INSTANCE_TYPE href="http://localhost:4567/instance_type/large" name="large"/>
<INSTANCE_TYPE href="http://localhost:4567/instance_type/medium" name="medium"/>
<INSTANCE_TYPE href="http://localhost:4567/instance_type/small" name="small"/>
</INSTANCE_TYPE_COLLECTION>

View File

@ -0,0 +1 @@
<NETWORK_COLLECTION></NETWORK_COLLECTION>

View File

@ -0,0 +1,11 @@
<NETWORK href="http://localhost:4567/network/0">
<ID>0</ID>
<NAME>Network</NAME>
<USER href="http://localhost:4567/user/2" name="my_first_occi_user"/>
<GROUP>oneadmin</GROUP>
<DESCRIPTION>Network of the user my_first_occi_user</DESCRIPTION>
<ADDRESS>192.168.1.1</ADDRESS>
<SIZE>125</SIZE>
<USED_LEASES>0</USED_LEASES>
<PUBLIC>NO</PUBLIC>
</NETWORK>

View File

@ -0,0 +1,11 @@
<NETWORK href="http://localhost:4567/network/1">
<ID>1</ID>
<NAME>Network2</NAME>
<USER href="http://localhost:4567/user/3" name="my_second_occi_user"/>
<GROUP>users</GROUP>
<DESCRIPTION>Network of the user my_second_occi_user</DESCRIPTION>
<ADDRESS>192.168.2.1</ADDRESS>
<SIZE>125</SIZE>
<USED_LEASES>0</USED_LEASES>
<PUBLIC>NO</PUBLIC>
</NETWORK>

7
src/cloud/occi/test/fixtures/root.xml vendored Normal file
View File

@ -0,0 +1,7 @@
<COLLECTIONS>
<COMPUTE_COLLECTION href="http://localhost:4567/compute"/>
<INSTANCE_TYPE_COLLECTION href="http://localhost:4567/instance_type"/>
<NETWORK_COLLECTION href="http://localhost:4567/network"/>
<STORAGE_COLLECTION href="http://localhost:4567/storage"/>
<USER_COLLECTION href="http://localhost:4567/user"/>
</COLLECTIONS>

View File

@ -0,0 +1 @@
<STORAGE_COLLECTION></STORAGE_COLLECTION>

View File

@ -0,0 +1,13 @@
<STORAGE href="http://localhost:4567/storage/0">
<ID>0</ID>
<NAME>Storage</NAME>
<USER href="http://localhost:4567/user/2" name="my_first_occi_user"/>
<GROUP>oneadmin</GROUP>
<STATE>READY</STATE>
<TYPE>DATABLOCK</TYPE>
<DESCRIPTION>Storage of the user my_first_occi_user</DESCRIPTION>
<SIZE>100</SIZE>
<FSTYPE>ext3</FSTYPE>
<PUBLIC>NO</PUBLIC>
<PERSISTENT>NO</PERSISTENT>
</STORAGE>

View File

@ -0,0 +1,13 @@
<STORAGE href="http://localhost:4567/storage/1">
<ID>1</ID>
<NAME>Storage2</NAME>
<USER href="http://localhost:4567/user/3" name="my_second_occi_user"/>
<GROUP>users</GROUP>
<STATE>READY</STATE>
<TYPE>DATABLOCK</TYPE>
<DESCRIPTION>Storage of the user my_second_occi_user</DESCRIPTION>
<SIZE>100</SIZE>
<FSTYPE>ext3</FSTYPE>
<PUBLIC>NO</PUBLIC>
<PERSISTENT>NO</PERSISTENT>
</STORAGE>

View File

@ -0,0 +1,283 @@
# -------------------------------------------------------------------------- #
# Copyright 2002-2012, OpenNebula Project Leads (OpenNebula.org) #
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may #
# not use this file except in compliance with the License. You may obtain #
# a copy of the License at #
# #
# http://www.apache.org/licenses/LICENSE-2.0 #
# #
# Unless required by applicable law or agreed to in writing, software #
# distributed under the License is distributed on an "AS IS" BASIS, #
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
# See the License for the specific language governing permissions and #
# limitations under the License. #
#--------------------------------------------------------------------------- #
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
# The following methods are helpers defined in spec_helper
# - compare_xml
# - get_fixture
# - network_template
# - storage_template
# - compute_template
describe 'OCCI tests' do
before(:all) do
@user_oneadmin = "my_first_occi_user"
`oneuser create #{@user_oneadmin} my_pass`.scan(/^ID: (\d+)/) { |uid|
`oneuser show #{uid.first}`.scan(/PASSWORD\s*:\s*(\w*)/) { |password|
@user_pass = password.first.strip
}
`oneuser chgrp #{uid.first} 0`
}
@user_users = "my_second_occi_user"
`oneuser create #{@user_users} my_pass2`.scan(/^ID: (\d+)/) { |uid|
`oneuser show #{uid.first}`.scan(/PASSWORD\s*:\s*(\w*)/) { |password|
@user_pass2 = password.first.strip
}
}
# Define BRIDGE attirbute in network.erb, otherwise the NETWORK creation will
`sed -i.bck "s%^#\\(BRIDGE = \\).*$%\\1 br0%" $ONE_LOCATION/etc/occi_templates/network.erb`
end
describe "with a user of the oneadmin group" do
before(:each) do
basic_authorize(@user_oneadmin, @user_pass)
end
it "should retrieve the list of collections" do
get '/'
compare_xml(last_response.body, get_fixture('/root.xml'))
last_response.status.should == 200
end
it "should retrieve the list of INSTANCE_TYPEs" do
get '/instance_type'
compare_xml(last_response.body, get_fixture('/instance_type/list.xml'))
last_response.status.should == 200
end
it "should retrieve the extended list of INSTANCE_TYPEs" do
get '/instance_type', {'verbose'=>true}
compare_xml(last_response.body, get_fixture('/instance_type/extended.xml'))
last_response.status.should == 200
end
context "for NETWORK" do
it "should retrieve the empty list" do
get '/network'
compare_xml(last_response.body, get_fixture('/network/empty.xml'))
last_response.status.should == 200
end
it "should create a new NETWORK" do
network = {
:name => "Network",
:description => "Network of the user #{@user_oneadmin}",
:address => "192.168.1.0",
:size => "100",
:pubic => "YES"
}
post '/network', network_template(network)
compare_xml(last_response.body, get_fixture('/network/first_net.xml'))
last_response.status.should == 201
end
it "should retrieve the NETWORK with ID 0" do
get '/network/0'
compare_xml(last_response.body, get_fixture('/network/first_net.xml'))
last_response.status.should == 200
end
end
context "for STORAGE" do
it "should retrieve the empty list" do
get '/storage'
compare_xml(last_response.body, get_fixture('/storage/empty.xml'))
last_response.status.should == 200
end
it "should create a new STORAGE, type DATABLOCK. This request waits until the IMAGE is ready in OpenNebula" do
storage = {
:name => "Storage",
:description => "Storage of the user #{@user_oneadmin}",
:type => "DATABLOCK",
:size => "100",
:fstype => "ext3"
}
post '/storage', {'occixml' => storage_template(storage)}
compare_xml(last_response.body, get_fixture('/storage/first_storage.xml'))
last_response.status.should == 201
end
it "should retrieve the STORAGE with ID 0" do
get '/storage/0'
compare_xml(last_response.body, get_fixture('/storage/first_storage.xml'))
last_response.status.should == 200
end
end
context "for COMPUTE" do
it "should retrieve the empty list" do
get '/compute'
compare_xml(last_response.body, get_fixture('/compute/empty.xml'))
last_response.status.should == 200
end
it "should create a new COMPUTE using the previous NETWORK (ID=0) and STORAGE(ID=0)" do
compute = {
:name => "Compute",
:instance_type => "small",
:disk => [ {:storage => '0'} ],
:nic => [ {:network => '0'} ]
}
post '/compute', compute_template(compute)
compare_xml(last_response.body, get_fixture('/compute/first_compute.xml'))
last_response.status.should == 201
end
it "should retrieve the COMPUTE with ID 0" do
get '/compute/0'
compare_xml(last_response.body, get_fixture('/compute/first_compute.xml'))
last_response.status.should == 200
end
it "should terminate (DONE) the COMPUTE with ID 0" do
compute = {
:id => "0",
:state => "DONE"
}
put '/compute/0', compute_action(compute)
compare_xml(last_response.body, get_fixture('/compute/first_compute_done.xml'))
last_response.status.should == 202
end
end
end
describe "with a user of the users group" do
before(:each) do
basic_authorize(@user_users, @user_pass2)
end
it "should retrieve the list of collections" do
get '/'
compare_xml(last_response.body, get_fixture('/root.xml'))
last_response.status.should == 200
end
it "should retrieve the list of INSTANCE_TYPEs" do
get '/instance_type'
compare_xml(last_response.body, get_fixture('/instance_type/list.xml'))
last_response.status.should == 200
end
it "should retrieve the extended list of INSTANCE_TYPEs" do
get '/instance_type', {'verbose'=>true}
compare_xml(last_response.body, get_fixture('/instance_type/extended.xml'))
last_response.status.should == 200
end
context "for NETWORK" do
it "should retrieve the empty list" do
get '/network'
compare_xml(last_response.body, get_fixture('/network/empty.xml'))
last_response.status.should == 200
end
it "should create a new NETWORK" do
network = {
:name => "Network2",
:description => "Network of the user #{@user_users}",
:address => "192.168.2.0",
:size => "100",
:pubic => "YES"
}
post '/network', network_template(network)
compare_xml(last_response.body, get_fixture('/network/second_net.xml'))
last_response.status.should == 201
end
it "should retrieve the NETWORK with ID 1" do
get '/network/1'
compare_xml(last_response.body, get_fixture('/network/second_net.xml'))
last_response.status.should == 200
end
end
context "for STORAGE" do
it "should retrieve the empty list" do
get '/storage'
compare_xml(last_response.body, get_fixture('/storage/empty.xml'))
last_response.status.should == 200
end
it "should create a new STORAGE, type DATABLOCK. This request waits until the IMAGE is ready in OpenNebula" do
storage = {
:name => "Storage2",
:description => "Storage of the user #{@user_users}",
:type => "DATABLOCK",
:size => "100",
:fstype => "ext3"
}
post '/storage', {'occixml' => storage_template(storage)}
compare_xml(last_response.body, get_fixture('/storage/second_storage.xml'))
last_response.status.should == 201
end
it "should retrieve the STORAGE with ID 1" do
get '/storage/1'
compare_xml(last_response.body, get_fixture('/storage/second_storage.xml'))
last_response.status.should == 200
end
end
context "for COMPUTE" do
it "should retrieve the empty list" do
get '/compute'
compare_xml(last_response.body, get_fixture('/compute/empty.xml'))
last_response.status.should == 200
end
it "should create a new COMPUTE using the previous NETWORK (ID=1) and STORAGE(ID=1)" do
compute = {
:name => "Compute2",
:instance_type => "small",
:disk => [ {:storage => '1'} ],
:nic => [ {:network => '1'} ]
}
post '/compute', compute_template(compute)
compare_xml(last_response.body, get_fixture('/compute/second_compute.xml'))
last_response.status.should == 201
end
it "should retrieve the COMPUTE with ID 1" do
get '/compute/1'
compare_xml(last_response.body, get_fixture('/compute/second_compute.xml'))
last_response.status.should == 200
end
it "should terminate (DONE) the COMPUTE with ID 1" do
compute = {
:id => "1",
:state => "DONE"
}
put '/compute/1', compute_action(compute)
compare_xml(last_response.body, get_fixture('/compute/second_compute_done.xml'))
last_response.status.should == 202
end
end
end
end

View File

@ -0,0 +1,4 @@
--colour
--format progress
--loadby mtime
--reverse

View File

@ -24,6 +24,8 @@ require 'rubygems'
require 'rspec'
require 'rack/test'
require 'rexml/document'
# Load the Sinatra app
require 'occi-server'
@ -39,3 +41,153 @@ set :environment, :test
def app
Sinatra::Application
end
def get_fixture(path)
File.read(FIXTURES_PATH + path).strip
end
def compare_xml(a, b)
a = REXML::Document.new(a.to_s)
b = REXML::Document.new(b.to_s)
normalized = Class.new(REXML::Formatters::Pretty) do
def write_text(node, output)
super(node.to_s.strip, output)
end
end
normalized.new(indentation=0,ie_hack=false).write(node=a, a_normalized='')
normalized.new(indentation=0,ie_hack=false).write(node=b, b_normalized='')
a_normalized.should == b_normalized
end
OCCI_NETWORK = %q{
<NETWORK>
<% if hash[:name] %>
<NAME><%= hash[:name] %></NAME>
<% end %>
<% if hash[:description] %>
<DESCRIPTION><%= hash[:description] %></DESCRIPTION>
<% end %>
<% if hash[:address] %>
<ADDRESS><%= hash[:address] %></ADDRESS>
<% end %>
<% if hash[:size] %>
<SIZE><%= hash[:size] %></SIZE>
<% end %>
<% if hash[:public] %>
<PUBLIC><%= hash[:public] %></PUBLIC>
<% end %>
</NETWORK>
}
def network_template(hash)
ERB.new(OCCI_NETWORK).result(binding)
end
OCCI_IMAGE = %q{
<STORAGE>
<% if hash[:name] %>
<NAME><%= hash[:name] %></NAME>
<% end %>
<% if hash[:type] %>
<TYPE><%= hash[:type] %></TYPE>
<% end %>
<% if hash[:description] %>
<DESCRIPTION><%= hash[:description] %></DESCRIPTION>
<% end %>
<% if hash[:size] %>
<SIZE><%= hash[:size] %></SIZE>
<% end %>
<% if hash[:fstype] %>
<FSTYPE><%= hash[:fstype] %></FSTYPE>
<% end %>
<% if hash[:public] %>
<PUBLIC><%= hash[:public] %></PUBLIC>
<% end %>
<% if hash[:persistent] %>
<PERSISTENT><%= hash[:persistent] %></PERSISTENT>
<% end %>
</STORAGE>
}
def storage_template(hash)
ERB.new(OCCI_IMAGE).result(binding)
end
OCCI_VM = %q{
<COMPUTE>
<% if hash[:name] %>
<NAME><%= hash[:name] %></NAME>
<% end %>
<% if hash[:instance_type] %>
<INSTANCE_TYPE href="http://localhost:4567/instance_type/<%= hash[:instance_type] %>"/>
<% end %>
<% if hash[:disk] %>
<% hash[:disk].each { |disk| %>
<DISK>
<% if disk[:storage] %>
<STORAGE href="http://localhost:4567/storage/<%= disk[:storage] %>"/>
<% end %>
</DISK>
<% } %>
<% end %>
<% if hash[:nic] %>
<% hash[:nic].each { |nic| %>
<NIC>
<% if nic[:network] %>
<NETWORK href="http://localhost:4567/network/<%= nic[:network] %>"/>
<% end %>
<% if nic[:ip] %>
<IP><%= nic[:ip] %></IP>
<% end %>
</NIC>
<% } %>
<% end %>
<% if hash[:context] %>
<CONTEXT>
<% hash[:context].each { |key, value| %>
<<%= key.to_s.upcase %>><%= value %></<%= key.to_s.upcase %>>
<% } %>
</CONTEXT>
<% end %>
</COMPUTE>
}
OCCI_VM_ACTION = %q{
<COMPUTE>
<% if hash[:id] %>
<ID><%= hash[:id] %></ID>
<% end %>
<% if hash[:state] %>
<STATE><%= hash[:state] %></STATE>
<% end %>
</COMPUTE>
}
def compute_template(hash)
ERB.new(OCCI_VM).result(binding)
end
def compute_action(hash)
ERB.new(OCCI_VM_ACTION).result(binding)
end

View File

@ -106,7 +106,7 @@ conf = YAML::load(File.read(CONF_FILE))
rc, data = do_action("virsh -c #{@uri} --readonly nodeinfo")
if rc == false
exit info
exit data
end
data.split(/\n/).each{|line|

View File

@ -19,14 +19,14 @@
if [ -z "$ONE_LOCATION" ]; then
OZONES_PID=/var/run/one/ozones.pid
OZONES_LOCATION=/usr/lib/one/ozones
OZONES_SERVER=$OZONES_LOCATION/config.ru
OZONES_SERVER=$OZONES_LOCATION/ozones-server.rb
OZONES_LOCK_FILE=/var/lock/one/.ozones.lock
OZONES_LOG=/var/log/one/ozones-server.log
OZONES_CONF=/etc/one/ozones-server.conf
else
OZONES_PID=$ONE_LOCATION/var/ozones.pid
OZONES_LOCATION=$ONE_LOCATION/lib/ozones
OZONES_SERVER=$OZONES_LOCATION/config.ru
OZONES_SERVER=$OZONES_LOCATION/ozones-server.rb
OZONES_LOCK_FILE=$ONE_LOCATION/var/.ozones.lock
OZONES_LOG=$ONE_LOCATION/var/ozones-server.log
OZONES_CONF=$ONE_LOCATION/etc/ozones-server.conf
@ -58,29 +58,26 @@ start()
exit 1
fi
HOST=`cat $OZONES_CONF|grep ^\:host\:|cut -d' ' -f 2`
PORT=`cat $OZONES_CONF|grep ^\:port\:|cut -d' ' -f 2`
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 ozones daemon
touch $OZONES_LOCK_FILE
rackup $OZONES_SERVER -s thin -p $PORT -o $HOST \
-P $OZONES_PID &> $OZONES_LOG &
ruby $OZONES_SERVER > $OZONES_LOG 2>&1 &
LASTPID=$!
if [ $? -ne 0 ]; then
echo "Error executing $OZONES_SERVER, please check the log $OZONES_LOG"
exit 1
else
echo $LASTPID > $OZONES_PID
fi
sleep 2
ps -p $(cat $OZONES_PID 2>/dev/null) > /dev/null 2>&1
ps $LASTPID &> /dev/null
if [ $? -ne 0 ]; then
echo "Error executing $OZONES_SERVER, please check the log $OZONES_LOG"
exit 1
fi
echo "ozones-server listening on $HOST:$PORT"
echo "ozones-server started"
}
#

View File

@ -1,23 +0,0 @@
#!/usr/bin/ruby
# -------------------------------------------------------------------------- #
# Copyright 2002-2012, OpenNebula Project Leads (OpenNebula.org) #
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may #
# not use this file except in compliance with the License. You may obtain #
# a copy of the License at #
# #
# http://www.apache.org/licenses/LICENSE-2.0 #
# #
# Unless required by applicable law or agreed to in writing, software #
# distributed under the License is distributed on an "AS IS" BASIS, #
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
# See the License for the specific language governing permissions and #
# limitations under the License. #
#--------------------------------------------------------------------------- #
$: << File.dirname(__FILE__)
require 'ozones-server.rb'
run Sinatra::Application

View File

@ -19,13 +19,17 @@
##############################################
######################
# DB Options
# DB Options
######################
:databasetype: sqlite
#:databaseserver: dbuser:dbpassword@localhost
#:htaccess: /var/www/.htaccess
:dbdebug: no
# 0 = ERROR, 1 = WARNING, 2 = INFO, 3 = DEBUG
:debug_level: 3
#####################
# Server Configuration
#####################

View File

@ -28,12 +28,20 @@ module OZones
zone.vdcs.all.each{|vdc|
htaccess << "RewriteRule ^#{vdc.NAME} " +
"#{zone.ENDPOINT} [P]\n"
if zone.SUNSENDPOINT != nil
htaccess << "RewriteRule ^sunstone_#{vdc.NAME}/(.+) " +
"#{zone.SUNSENDPOINT}/$1 [P]\n"
htaccess << "RewriteRule ^sunstone_#{vdc.NAME} " +
"#{zone.SUNSENDPOINT}/ [P]\n"
end
if zone.SELFENDPOINT != nil
htaccess << "RewriteRule ^self_#{vdc.NAME}/(.+) " +
"#{zone.SELFENDPOINT}/$1 [P]\n"
htaccess << "RewriteRule ^self_#{vdc.NAME} " +
"#{zone.SELFENDPOINT}/ [P]\n"
end
}
}

View File

@ -30,6 +30,7 @@ module OZones
property :ONEPASS, String, :required => true
property :ENDPOINT, String, :required => true
property :SUNSENDPOINT, String
property :SELFENDPOINT, String
has n, :vdcs

View File

@ -14,13 +14,16 @@
# limitations under the License. #
#--------------------------------------------------------------------------- #
require 'CloudServer'
require 'JSONUtils'
class OzonesServer
class OzonesServer < CloudServer
include OpenNebulaJSON::JSONUtils
def initialize(cipher)
def initialize(cipher, config, logger)
super(config, logger)
#Set cipher for Zone classes
OZones::Zones.cipher = cipher
end

View File

@ -19,19 +19,25 @@
ONE_LOCATION=ENV["ONE_LOCATION"]
if !ONE_LOCATION
ETC_LOCATION="/etc/one"
LIB_LOCATION="/usr/lib/one"
RUBY_LIB_LOCATION="/usr/lib/one/ruby"
VAR_LOCATION="/var/lib/one"
LOG_LOCATION = "/var/log/one"
VAR_LOCATION = "/var/lib/one"
ETC_LOCATION = "/etc/one"
LIB_LOCATION = "/usr/lib/one"
RUBY_LIB_LOCATION = "/usr/lib/one/ruby"
else
ETC_LOCATION=ONE_LOCATION+"/etc"
LIB_LOCATION=ONE_LOCATION+"/lib"
RUBY_LIB_LOCATION=ONE_LOCATION+"/lib/ruby"
VAR_LOCATION=ONE_LOCATION+"/var"
VAR_LOCATION = ONE_LOCATION + "/var"
LOG_LOCATION = ONE_LOCATION + "/var"
ETC_LOCATION = ONE_LOCATION + "/etc"
LIB_LOCATION = ONE_LOCATION+"/lib"
RUBY_LIB_LOCATION = ONE_LOCATION+"/lib/ruby"
end
OZONES_LOG = LOG_LOCATION + "/ozones-server.log"
CONFIGURATION_FILE = ETC_LOCATION + "/ozones-server.conf"
$: << LIB_LOCATION + "/sunstone/models"
$: << RUBY_LIB_LOCATION
$: << RUBY_LIB_LOCATION+'/cloud'
$: << LIB_LOCATION+'/ozones/models'
$: << LIB_LOCATION+'/ozones/lib'
$: << RUBY_LIB_LOCATION+"/cli"
@ -52,12 +58,46 @@ require 'OzonesServer'
##############################################################################
# Read configuration
##############################################################################
config_data=File.read(ETC_LOCATION+'/ozones-server.conf')
config=YAML::load(config_data)
begin
config=YAML::load_file(CONFIGURATION_FILE)
rescue Exception => e
warn "Error parsing config file #{CONFIGURATION_FILE}: #{e.message}"
exit 1
end
config[:debug_level] ||= 3
CloudServer.print_configuration(config)
db_type = config[:databasetype]
db_url = db_type + "://" + VAR_LOCATION + "/ozones.db"
case db_type
when "sqlite" then
db_url = db_type + "://" + VAR_LOCATION + "/ozones.db"
when "mysql","postgres" then
if config[:databaseserver].nil?
warn "DB server needed for this type of DB backend"
exit -1
end
db_url = db_type + "://" + config[:databaseserver] + "/ozones"
else
warn "DB type #{db_type} not recognized"
exit -1
end
##############################################################################
# Sinatra Configuration
##############################################################################
set :config, config
set :bind, config[:host]
set :port, config[:port]
use Rack::Session::Pool, :key => 'ozones'
#Enable logger
include CloudLogger
enable_logging OZONES_LOG, settings.config[:debug_level].to_i
##############################################################################
# DB bootstrapping
@ -79,7 +119,7 @@ if Auth.all.size == 0
credentials = IO.read(ENV['OZONES_AUTH']).strip.split(':')
if credentials.length < 2
warn "Authorization data malformed"
settings.logger.error {"Authorization data malformed"}
exit -1
end
credentials[1] = Digest::SHA1.hexdigest(credentials[1])
@ -87,7 +127,8 @@ if Auth.all.size == 0
:password => credentials[1]})
@auth.save
else
warn "oZones admin credentials not set, missing OZONES_AUTH file."
error_m = "oZones admin credentials not set, missing OZONES_AUTH file."
settings.logger.error { error_m }
exit -1
end
else
@ -100,19 +141,10 @@ ADMIN_PASS = @auth.password
begin
OZones::ProxyRules.new("apache",config[:htaccess])
rescue Exception => e
warn e.message
settings.logger {e.message}
exit -1
end
##############################################################################
# Sinatra Configuration
##############################################################################
use Rack::Session::Pool, :key => 'ozones'
set :bind, config[:host]
set :port, config[:port]
set :show_exceptions, false
##############################################################################
# Helpers
##############################################################################
@ -144,10 +176,11 @@ helpers do
return [204, ""]
else
logger.info {"User not authorized login attempt"}
return [401, ""]
end
end
logger.error {"Authentication settings wrong or not provided"}
return [401, ""]
end
@ -168,7 +201,9 @@ before do
end
end
@OzonesServer = OzonesServer.new(session[:key])
@OzonesServer = OzonesServer.new(session[:key],
settings.config,
settings.logger)
@pr = OZones::ProxyRules.new("apache",config[:htaccess])
end
end

View File

@ -133,12 +133,19 @@ div#login input#login_btn:hover {
}
.error_message {
width: 400px;
margin-left: auto;
margin-right: auto;
display: none;
position: relative;
top: 80px;
font-family: Arial, Helvetica, sans-serif;
color:red;
font-size:1.6em;
font-size:1.0em;
}
#login_spinner {
left: 44px;
position: relative;
top: 2px;
}
#label_remember {

View File

@ -23,15 +23,21 @@ function auth_error(req, error){
var status = error.error.http_status;
switch (status){
case 401:
$("#one_error").hide();
$("#auth_error").fadeIn("slow");
break;
case 500:
$("#auth_error").hide();
$("#one_error").fadeIn("slow");
break;
}
case 401:
$("#error_box").text("Invalid username or password");
break;
case 500:
$("#error_box").text("OpenNebula is not running or there was a server exception. Please check the server logs.");
break;
case 0:
$("#error_box").text("No answer from server. Is it running?");
break;
default:
$("#error_box").text("Unexpected error. Status "+status+". Check the server logs.");
};
$("#error_box").fadeIn("slow");
$("#login_spinner").hide();
}
function authenticate(){
@ -39,6 +45,9 @@ function authenticate(){
var password = $("#password").val();
var remember = $("#check_remember").is(":checked");
$("#error_box").fadeOut("slow");
$("#login_spinner").show();
oZones.Auth.login({ data: {username: username
, password: password}
, remember: remember
@ -61,4 +70,5 @@ $(document).ready(function(){
};
$("input#username.box").focus();
$("#login_spinner").hide();
});

View File

@ -516,6 +516,10 @@ function setupCreateVDCDialog(){
function openCreateVDCDialog(){
var dialog = $('div#create_vdc_dialog')
if (!zones_select){
notifyError(tr("No zones defined: You need to create at least 1 zone before creating an VDC"));
return false;
};
$('select#zoneid',dialog).html(zones_select);
$('select#zoneid',dialog).trigger("change");
$('#vdc_available_hosts_list',dialog).empty();

View File

@ -3,7 +3,7 @@
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>OpenNebula oZones Login</title>
<link rel="stylesheet" type="text/css" href="css/login.css" />
<link rel="stylesheet" type="text/css" href="vendor/jQueryUI/jquery-ui-1.8.16.custom.css" />
<!-- Vendor Libraries -->
<script type="text/javascript" src="vendor/jQuery/jquery-1.7.1.min.js"></script>
<!-- End Vendor Libraries -->
@ -24,11 +24,7 @@
<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 id="error_box" class="error_message ui-state-error ui-corner-all">
</div>
<form id="login_form">
@ -42,6 +38,7 @@
<input type="checkbox" id="check_remember">
<label id="label_remember" for="check_remember">Remember me</label>
<input type="submit" id="login_btn" value="" />
<img src="images/ajax-loader.gif" alt="retrieving" id="login_spinner" />
</div>
</div>
</form>

View File

@ -21,7 +21,7 @@ require 'OpenNebula'
# This class provides support for launching and stopping a websockify proxy
#
class OpenNebulaVNC
def initialize(config, opt={:json_errors => true})
def initialize(config, logger, opt={:json_errors => true})
@proxy_path = config[:vnc_proxy_path]
@proxy_base_port = config[:vnc_proxy_base_port].to_i
@ -36,6 +36,7 @@ class OpenNebulaVNC
end
@options = opt
@logger = logger
end
# Start a VNC proxy
@ -71,7 +72,7 @@ class OpenNebulaVNC
cmd ="#{@proxy_path} #{proxy_options} #{proxy_port} #{host}:#{vnc_port}"
begin
$stderr.puts("Starting vnc proxy: #{cmd}")
@logger.info { "Starting vnc proxy: #{cmd}" }
pipe = IO.popen(cmd)
rescue Exception => e
return [500, OpenNebula::Error.new(e.message).to_json]

View File

@ -1,23 +0,0 @@
#!/usr/bin/ruby
# -------------------------------------------------------------------------- #
# Copyright 2002-2012, OpenNebula Project Leads (OpenNebula.org) #
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may #
# not use this file except in compliance with the License. You may obtain #
# a copy of the License at #
# #
# http://www.apache.org/licenses/LICENSE-2.0 #
# #
# Unless required by applicable law or agreed to in writing, software #
# distributed under the License is distributed on an "AS IS" BASIS, #
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
# See the License for the specific language governing permissions and #
# limitations under the License. #
#--------------------------------------------------------------------------- #
$: << File.dirname(__FILE__)
require 'sunstone-server.rb'
run Sinatra::Application

View File

@ -1,6 +1,9 @@
# OpenNebula sever contact information
:one_xmlrpc: http://localhost:2633/RPC2
# 0 = ERROR, 1 = WARNING, 2 = INFO, 3 = DEBUG
:debug_level: 3
# Server Configuration
:host: 127.0.0.1
:port: 9869

View File

@ -14,6 +14,8 @@
# limitations under the License. #
#--------------------------------------------------------------------------- #
require 'CloudServer'
require 'OpenNebulaJSON'
include OpenNebulaJSON
@ -22,14 +24,15 @@ require 'OpenNebulaVNC'
require 'OpenNebulaJSON/JSONUtils'
include JSONUtils
class SunstoneServer
class SunstoneServer < CloudServer
# FLAG that will filter the elements retrieved from the Pools
POOL_FILTER = Pool::INFO_ALL
# Secs to sleep between checks to see if image upload to repo is finished
IMAGE_POLL_SLEEP_TIME = 5
def initialize(client)
def initialize(client, config, logger)
super(config, logger)
@client = client
end
@ -194,7 +197,8 @@ class SunstoneServer
begin
log = File.read(vm_log_file)
rescue Exception => e
return [200, "Log for VM #{id} not available"]
msg = "Log for VM #{id} not available"
return [200, {:vm_log => msg}.to_json]
end
return [200, {:vm_log => log}.to_json]
@ -210,7 +214,7 @@ class SunstoneServer
return [404, resource.to_json]
end
vnc_proxy = OpenNebulaVNC.new(config)
vnc_proxy = OpenNebulaVNC.new(config, logger)
return vnc_proxy.start(resource)
end
@ -221,7 +225,8 @@ class SunstoneServer
begin
OpenNebulaVNC.stop(pipe)
rescue Exception => e
error = Error.new(e.message)
logger.error {e.message}
error = Error.new("Error stopping VNC. Please check server logs.")
return [500, error.to_json]
end

View File

@ -134,10 +134,17 @@ div#login input#login_btn:hover {
.error_message {
display: none;
position: relative;
width:400px;
margin-left: auto;
margin-right: auto;
top: 80px;
font-family: Arial, Helvetica, sans-serif;
color:red;
font-size:1.6em;
font-size:1.0em;
}
#login_spinner {
left: 44px;
position: relative;
top: 2px;
}
#label_remember {

View File

@ -24,14 +24,19 @@ function auth_error(req, error){
switch (status){
case 401:
$("#one_error").hide();
$("#auth_error").fadeIn("slow");
$("#error_box").text("Invalid username or password");
break;
case 500:
$("#auth_error").hide();
$("#one_error").fadeIn("slow");
$("#error_box").text("OpenNebula is not running or there was a server exception. Please check the server logs.");
break;
case 0:
$("#error_box").text("No answer from server. Is it running?");
break;
default:
$("#error_box").text("Unexpected error. Status "+status+". Check the server logs.");
};
$("#error_box").fadeIn("slow");
$("#login_spinner").hide();
}
function authenticate(){
@ -39,6 +44,9 @@ function authenticate(){
var password = $("#password").val();
var remember = $("#check_remember").is(":checked");
$("#error_box").fadeOut("slow");
$("#login_spinner").show();
OpenNebula.Auth.login({ data: {username: username
, password: password}
, remember: remember
@ -61,4 +69,5 @@ $(document).ready(function(){
};
$("input#username.box").focus();
$("#login_spinner").hide();
});

View File

@ -131,9 +131,11 @@ var user_actions = {
"User.passwd" : {
type: "multiple",
call: OpenNebula.User.passwd,
//nocallback
callback: function(req,res){
notifyMessage(tr("Change password successful"));
},
elements: userElements,
error: onError
error: onError,
},
"User.chgrp" : {
type: "multiple",
@ -492,6 +494,7 @@ function popUpCreateUserDialog(){
function popUpUpdatePasswordDialog(){
$('#new_password',$update_pw_dialog).val("");
$update_pw_dialog.dialog('open');
}

View File

@ -32,7 +32,9 @@ else
end
SUNSTONE_AUTH = VAR_LOCATION + "/.one/sunstone_auth"
SUNSTONE_LOG = LOG_LOCATION + "/sunstone.log"
CONFIGURATION_FILE = ETC_LOCATION + "/sunstone-server.conf"
PLUGIN_CONFIGURATION_FILE = ETC_LOCATION + "/sunstone-plugins.yaml"
SUNSTONE_ROOT_DIR = File.dirname(__FILE__)
@ -54,27 +56,42 @@ require 'CloudAuth'
require 'SunstoneServer'
require 'SunstonePlugins'
##############################################################################
# Configuration
##############################################################################
begin
conf = YAML.load_file(CONFIGURATION_FILE)
rescue Exception => e
puts "Error parsing config file #{CONFIGURATION_FILE}: #{e.message}"
STDERR.puts "Error parsing config file #{CONFIGURATION_FILE}: #{e.message}"
exit 1
end
##############################################################################
# Sinatra Configuration
##############################################################################
use Rack::Session::Pool, :key => 'sunstone'
conf[:debug_level] ||= 3
CloudServer.print_configuration(conf)
#Sinatra configuration
set :config, conf
set :bind, settings.config[:host]
set :port, settings.config[:port]
use Rack::Session::Pool, :key => 'sunstone'
# Enable logger
include CloudLogger
enable_logging SUNSTONE_LOG, settings.config[:debug_level].to_i
begin
ENV["ONE_CIPHER_AUTH"] = SUNSTONE_AUTH
cloud_auth = CloudAuth.new(settings.config)
rescue => e
puts "Error initializing authentication system"
puts e.message
settings.logger.error {
"Error initializing authentication system" }
settings.logger.error { e.message }
exit -1
end
@ -93,10 +110,12 @@ helpers do
settings.cloud_auth.update_userpool_cache
result = settings.cloud_auth.auth(request.env, params)
rescue Exception => e
error 500, e.message
error 500, ""
logger.error { e.message }
end
if result.nil?
logger.info { "Unauthorized login attempt" }
return [401, ""]
else
client = settings.cloud_auth.client(result)
@ -105,7 +124,7 @@ helpers do
user = OpenNebula::User.new_with_id(user_id, client)
rc = user.info
if OpenNebula.is_error?(rc)
# Add a log message
logger.error { rc.message }
return [500, ""]
end
@ -157,7 +176,9 @@ before do
halt 401 unless authorized?
@SunstoneServer = SunstoneServer.new(
settings.cloud_auth.client(session[:user]))
settings.cloud_auth.client(session[:user]),
settings.config,
settings.logger)
end
end
@ -244,7 +265,10 @@ end
post '/config' do
begin
body = JSON.parse(request.body.read)
rescue
rescue Exception => e
msg = "Error parsing configuration JSON"
logger.error { msg }
logger.error { e.message }
[500, OpenNebula::Error.new(msg).to_json]
end

View File

@ -3,6 +3,7 @@
<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" />
<link rel="stylesheet" type="text/css" href="vendor/jQueryUI/jquery-ui-1.8.16.custom.css" />
<!-- Vendor Libraries -->
<script type="text/javascript" src="vendor/jQuery/jquery-1.7.1.min.js"></script>
@ -25,11 +26,7 @@
<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 id="error_box" class="error_message ui-state-error ui-corner-all">
</div>
<form id="login_form">
@ -43,6 +40,7 @@
<input type="checkbox" id="check_remember" />
<label id="label_remember" for="check_remember">Remember me</label>
<input type="submit" id="login_btn" value="" />
<img src="images/ajax-loader.gif" alt="retrieving" id="login_spinner" />
</div>
</div>