mirror of
https://github.com/OpenNebula/one.git
synced 2026-11-15 01:03:14 +09:00
566 lines
17 KiB
Ruby
566 lines
17 KiB
Ruby
# -------------------------------------------------------------------------- #
|
|
# Copyright 2002-2018, OpenNebula Project, OpenNebula Systems #
|
|
# #
|
|
# 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 'rubygems'
|
|
require 'sinatra'
|
|
require 'yaml'
|
|
|
|
ONE_LOCATION = ENV["ONE_LOCATION"]
|
|
|
|
if !ONE_LOCATION
|
|
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
|
|
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
|
|
|
|
ONEFLOW_AUTH = VAR_LOCATION + "/.one/oneflow_auth"
|
|
|
|
ONEFLOW_LOG = LOG_LOCATION + "/oneflow.log"
|
|
CONFIGURATION_FILE = ETC_LOCATION + "/oneflow-server.conf"
|
|
|
|
$: << RUBY_LIB_LOCATION
|
|
$: << RUBY_LIB_LOCATION+'/cloud'
|
|
$: << LIB_LOCATION+'/oneflow/lib'
|
|
|
|
require 'CloudAuth'
|
|
require 'CloudServer'
|
|
|
|
require 'models'
|
|
require 'log'
|
|
|
|
DEFAULT_VM_NAME_TEMPLATE = '$ROLE_NAME_$VM_NUMBER_(service_$SERVICE_ID)'
|
|
|
|
##############################################################################
|
|
# Configuration
|
|
##############################################################################
|
|
|
|
begin
|
|
conf = YAML.load_file(CONFIGURATION_FILE)
|
|
rescue Exception => e
|
|
STDERR.puts "Error parsing config file #{CONFIGURATION_FILE}: #{e.message}"
|
|
exit 1
|
|
end
|
|
|
|
conf[:debug_level] ||= 2
|
|
conf[:lcm_interval] ||= 30
|
|
conf[:default_cooldown] ||= 300
|
|
conf[:shutdown_action] ||= 'terminate'
|
|
conf[:action_number] ||= 1
|
|
conf[:action_period] ||= 60
|
|
|
|
conf[:auth] = 'opennebula'
|
|
|
|
set :bind, conf[:host]
|
|
set :port, conf[:port]
|
|
|
|
set :config, conf
|
|
|
|
include CloudLogger
|
|
logger = enable_logging ONEFLOW_LOG, conf[:debug_level].to_i
|
|
|
|
use Rack::Session::Pool, :key => 'oneflow'
|
|
|
|
Log.logger = logger
|
|
Log.level = conf[:debug_level].to_i
|
|
|
|
|
|
LOG_COMP = "ONEFLOW"
|
|
|
|
Log.info LOG_COMP, "Starting server"
|
|
|
|
begin
|
|
ENV["ONE_CIPHER_AUTH"] = ONEFLOW_AUTH
|
|
cloud_auth = CloudAuth.new(conf)
|
|
rescue => e
|
|
message = "Error initializing authentication system : #{e.message}"
|
|
Log.error LOG_COMP, message
|
|
STDERR.puts message
|
|
exit -1
|
|
end
|
|
|
|
set :cloud_auth, cloud_auth
|
|
|
|
##############################################################################
|
|
# Helpers
|
|
##############################################################################
|
|
|
|
|
|
before do
|
|
auth = Rack::Auth::Basic::Request.new(request.env)
|
|
|
|
if auth.provided? && auth.basic?
|
|
username, password = auth.credentials
|
|
|
|
@client = OpenNebula::Client.new("#{username}:#{password}", conf[:one_xmlrpc])
|
|
else
|
|
error 401, "A username and password must be provided"
|
|
end
|
|
end
|
|
|
|
##############################################################################
|
|
# Defaults
|
|
##############################################################################
|
|
|
|
Role.init_default_cooldown(conf[:default_cooldown])
|
|
Role.init_default_shutdown(conf[:shutdown_action])
|
|
Role.init_force_deletion(conf[:force_deletion])
|
|
|
|
conf[:vm_name_template] ||= DEFAULT_VM_NAME_TEMPLATE
|
|
Role.init_default_vm_name_template(conf[:vm_name_template])
|
|
|
|
##############################################################################
|
|
# LCM thread
|
|
##############################################################################
|
|
|
|
t = Thread.new {
|
|
require 'LifeCycleManager'
|
|
|
|
ServiceLCM.new(conf[:lcm_interval], cloud_auth).loop
|
|
}
|
|
t.abort_on_exception = true
|
|
|
|
|
|
##############################################################################
|
|
# Service
|
|
##############################################################################
|
|
|
|
get '/service' do
|
|
service_pool = OpenNebula::ServicePool.new(@client, OpenNebula::Pool::INFO_ALL)
|
|
|
|
rc = service_pool.info
|
|
if OpenNebula.is_error?(rc)
|
|
error CloudServer::HTTP_ERROR_CODE[rc.errno], rc.message
|
|
end
|
|
|
|
status 200
|
|
|
|
body service_pool.to_json
|
|
end
|
|
|
|
get '/service/:id' do
|
|
service_pool = OpenNebula::ServicePool.new(@client)
|
|
|
|
service = service_pool.get(params[:id])
|
|
|
|
if OpenNebula.is_error?(service)
|
|
error CloudServer::HTTP_ERROR_CODE[service.errno], service.message
|
|
end
|
|
|
|
status 200
|
|
|
|
body service.to_json
|
|
end
|
|
|
|
delete '/service/:id' do
|
|
service_pool = OpenNebula::ServicePool.new(@client)
|
|
|
|
rc = nil
|
|
service = service_pool.get(params[:id]) { |service|
|
|
rc = service.delete
|
|
}
|
|
|
|
if OpenNebula.is_error?(service)
|
|
error CloudServer::HTTP_ERROR_CODE[service.errno], service.message
|
|
end
|
|
|
|
if OpenNebula.is_error?(rc)
|
|
error CloudServer::HTTP_ERROR_CODE[rc.errno], rc.message
|
|
end
|
|
|
|
status 204
|
|
end
|
|
|
|
post '/service/:id/action' do
|
|
service_pool = OpenNebula::ServicePool.new(@client)
|
|
action = JSON.parse(request.body.read)['action']
|
|
opts = action['params']
|
|
|
|
rc = nil
|
|
service = service_pool.get(params[:id]) { |service|
|
|
rc = case action['perform']
|
|
when 'shutdown'
|
|
service.shutdown
|
|
when 'recover', 'deploy'
|
|
service.recover
|
|
when 'chown'
|
|
if opts && opts['owner_id']
|
|
args = Array.new
|
|
args << opts['owner_id'].to_i
|
|
args << (opts['group_id'] || -1).to_i
|
|
|
|
ret = service.chown(*args)
|
|
|
|
if !OpenNebula.is_error?(ret)
|
|
Log.info(LOG_COMP, "Service owner changed to #{args[0]}:#{args[1]}", params[:id])
|
|
end
|
|
|
|
ret
|
|
else
|
|
OpenNebula::Error.new("Action #{action['perform']}: " <<
|
|
"You have to specify a UID")
|
|
end
|
|
when 'chgrp'
|
|
if opts && opts['group_id']
|
|
ret = service.chown(-1, opts['group_id'].to_i)
|
|
|
|
if !OpenNebula.is_error?(ret)
|
|
Log.info(LOG_COMP, "Service group changed to #{opts['group_id']}", params[:id])
|
|
end
|
|
|
|
ret
|
|
else
|
|
OpenNebula::Error.new("Action #{action['perform']}: " <<
|
|
"You have to specify a GID")
|
|
end
|
|
when 'chmod'
|
|
if opts && opts['octet']
|
|
ret = service.chmod_octet(opts['octet'])
|
|
|
|
if !OpenNebula.is_error?(ret)
|
|
Log.info(LOG_COMP, "Service permissions changed to #{opts['octet']}", params[:id])
|
|
end
|
|
|
|
ret
|
|
else
|
|
OpenNebula::Error.new("Action #{action['perform']}: " <<
|
|
"You have to specify an OCTET")
|
|
end
|
|
when 'rename'
|
|
service.rename(opts['name'])
|
|
when 'update'
|
|
if opts && opts['append']
|
|
if opts['template_json']
|
|
begin
|
|
rc = service.update(opts['template_json'], true)
|
|
status 204
|
|
rescue Validator::ParseException, JSON::ParserError
|
|
OpenNebula::Error.new($!.message)
|
|
end
|
|
elsif opts['template_raw']
|
|
rc = service.update_raw(opts['template_raw'], true)
|
|
status 204
|
|
else
|
|
OpenNebula::Error.new("Action #{action['perform']}: " <<
|
|
"You have to provide a template")
|
|
end
|
|
else
|
|
OpenNebula::Error.new("Action #{action['perform']}: " <<
|
|
"Only supported for append")
|
|
end
|
|
else
|
|
OpenNebula::Error.new("Action #{action['perform']} not supported")
|
|
end
|
|
}
|
|
|
|
if OpenNebula.is_error?(service)
|
|
error CloudServer::HTTP_ERROR_CODE[service.errno], service.message
|
|
end
|
|
|
|
if OpenNebula.is_error?(rc)
|
|
error CloudServer::HTTP_ERROR_CODE[rc.errno], rc.message
|
|
end
|
|
|
|
status 204
|
|
end
|
|
|
|
put '/service/:id/role/:name' do
|
|
|
|
service_pool = OpenNebula::ServicePool.new(@client)
|
|
|
|
rc = nil
|
|
service = service_pool.get(params[:id]) do |service|
|
|
begin
|
|
rc = service.update_role(params[:name], request.body.read)
|
|
rescue Validator::ParseException, JSON::ParserError
|
|
return error 400, $!.message
|
|
end
|
|
end
|
|
|
|
if OpenNebula.is_error?(service)
|
|
error CloudServer::HTTP_ERROR_CODE[service.errno], service.message
|
|
end
|
|
|
|
if OpenNebula.is_error?(rc)
|
|
error CloudServer::HTTP_ERROR_CODE[rc.errno], rc.message
|
|
end
|
|
|
|
status 204
|
|
end
|
|
|
|
post '/service/:id/role/:role_name/action' do
|
|
service_pool = OpenNebula::ServicePool.new(@client)
|
|
action = JSON.parse(request.body.read)['action']
|
|
opts = action['params']
|
|
|
|
rc = nil
|
|
service = service_pool.get(params[:id]) { |service|
|
|
roles = service.get_roles
|
|
|
|
role = roles[params[:role_name]]
|
|
if role.nil?
|
|
rc = OpenNebula::Error.new("Role '#{params[:role_name]}' not found")
|
|
else
|
|
# Use defaults only if one of the options is supplied
|
|
if opts['period'].nil? ^ opts['number'].nil?
|
|
opts['period'] = conf[:action_period] if opts['period'].nil?
|
|
opts['number'] = conf[:action_number] if opts['number'].nil?
|
|
end
|
|
|
|
rc = role.batch_action(action['perform'], opts['period'], opts['number'])
|
|
end
|
|
}
|
|
|
|
if OpenNebula.is_error?(service)
|
|
error CloudServer::HTTP_ERROR_CODE[service.errno], service.message
|
|
end
|
|
|
|
if OpenNebula.is_error?(rc)
|
|
error CloudServer::HTTP_ERROR_CODE[rc.errno], rc.message
|
|
end
|
|
|
|
status 201
|
|
body rc.to_json
|
|
end
|
|
|
|
##############################################################################
|
|
# Service Template
|
|
##############################################################################
|
|
|
|
get '/service_template' do
|
|
s_template_pool = OpenNebula::ServiceTemplatePool.new(@client, OpenNebula::Pool::INFO_ALL)
|
|
|
|
rc = s_template_pool.info
|
|
if OpenNebula.is_error?(rc)
|
|
error CloudServer::HTTP_ERROR_CODE[rc.errno], rc.message
|
|
end
|
|
|
|
status 200
|
|
|
|
body s_template_pool.to_json
|
|
end
|
|
|
|
get '/service_template/:id' do
|
|
service_template = OpenNebula::ServiceTemplate.new_with_id(params[:id], @client)
|
|
|
|
rc = service_template.info
|
|
if OpenNebula.is_error?(rc)
|
|
error CloudServer::HTTP_ERROR_CODE[rc.errno], rc.message
|
|
end
|
|
|
|
status 200
|
|
|
|
body service_template.to_json
|
|
end
|
|
|
|
delete '/service_template/:id' do
|
|
service_template = OpenNebula::ServiceTemplate.new_with_id(params[:id], @client)
|
|
|
|
rc = service_template.delete
|
|
if OpenNebula.is_error?(rc)
|
|
error CloudServer::HTTP_ERROR_CODE[rc.errno], rc.message
|
|
end
|
|
|
|
status 204
|
|
end
|
|
|
|
put '/service_template/:id' do
|
|
service_template = OpenNebula::ServiceTemplate.new_with_id(params[:id], @client)
|
|
|
|
begin
|
|
rc = service_template.update(request.body.read)
|
|
rescue Validator::ParseException, JSON::ParserError
|
|
error 400, $!.message
|
|
end
|
|
|
|
if OpenNebula.is_error?(rc)
|
|
error CloudServer::HTTP_ERROR_CODE[rc.errno], rc.message
|
|
end
|
|
|
|
service_template.info
|
|
|
|
status 200
|
|
body service_template.to_json
|
|
end
|
|
|
|
post '/service_template' do
|
|
s_template = OpenNebula::ServiceTemplate.new(
|
|
OpenNebula::ServiceTemplate.build_xml,
|
|
@client)
|
|
|
|
begin
|
|
rc = s_template.allocate(request.body.read)
|
|
rescue Validator::ParseException, JSON::ParserError
|
|
error 400, $!.message
|
|
end
|
|
|
|
if OpenNebula.is_error?(rc)
|
|
error CloudServer::HTTP_ERROR_CODE[rc.errno], rc.message
|
|
end
|
|
|
|
s_template.info
|
|
|
|
status 201
|
|
#body Parser.render(rc)
|
|
body s_template.to_json
|
|
end
|
|
|
|
post '/service_template/:id/action' do
|
|
service_template = OpenNebula::ServiceTemplate.new_with_id(params[:id], @client)
|
|
|
|
action = JSON.parse(request.body.read)['action']
|
|
|
|
opts = action['params']
|
|
opts = {} if opts.nil?
|
|
|
|
rc = case action['perform']
|
|
when 'instantiate'
|
|
rc = service_template.info
|
|
if OpenNebula.is_error?(rc)
|
|
error CloudServer::HTTP_ERROR_CODE[rc.errno], rc.message
|
|
end
|
|
|
|
merge_template = opts['merge_template']
|
|
|
|
if !merge_template.nil?
|
|
begin
|
|
orig_template = JSON.parse(service_template.template)
|
|
|
|
instantiate_template = orig_template.merge(merge_template)
|
|
|
|
ServiceTemplate.validate(instantiate_template)
|
|
|
|
instantiate_template["roles"].each { |role|
|
|
if role["vm_template_contents"]
|
|
# $CUSTOM1_VAR Any word character (letter, number, underscore)
|
|
role["vm_template_contents"].scan(/\$(\w+)/).each { |key|
|
|
if instantiate_template["custom_attrs_values"].has_key?(key[0])
|
|
role["vm_template_contents"].gsub!(
|
|
"$"+key[0],
|
|
instantiate_template["custom_attrs_values"][key[0]])
|
|
end
|
|
}
|
|
end
|
|
|
|
if role["user_inputs_values"]
|
|
role["vm_template_contents"] ||= ""
|
|
role["user_inputs_values"].each{ |key, value|
|
|
role["vm_template_contents"] += "\n#{key}=\"#{value}\""
|
|
}
|
|
end
|
|
}
|
|
|
|
instantiate_template_json = instantiate_template.to_json
|
|
|
|
rescue Validator::ParseException, JSON::ParserError
|
|
error 400, $!.message
|
|
end
|
|
else
|
|
instantiate_template_json = service_template.template
|
|
end
|
|
|
|
service = OpenNebula::Service.new(OpenNebula::Service.build_xml, @client)
|
|
rc = service.allocate(instantiate_template_json)
|
|
if OpenNebula.is_error?(rc)
|
|
error CloudServer::HTTP_ERROR_CODE[rc.errno], rc.message
|
|
end
|
|
|
|
service.info
|
|
|
|
status 201
|
|
body service.to_json
|
|
when 'chown'
|
|
if opts && opts['owner_id']
|
|
args = Array.new
|
|
args << opts['owner_id'].to_i
|
|
args << (opts['group_id'].to_i || -1)
|
|
|
|
status 204
|
|
service_template.chown(*args)
|
|
else
|
|
OpenNebula::Error.new("Action #{action['perform']}: " <<
|
|
"You have to specify a UID")
|
|
end
|
|
when 'chgrp'
|
|
if opts && opts['group_id']
|
|
status 204
|
|
service_template.chown(-1, opts['group_id'].to_i)
|
|
else
|
|
OpenNebula::Error.new("Action #{action['perform']}: " <<
|
|
"You have to specify a GID")
|
|
end
|
|
when 'chmod'
|
|
if opts && opts['octet']
|
|
status 204
|
|
service_template.chmod_octet(opts['octet'])
|
|
else
|
|
OpenNebula::Error.new("Action #{action['perform']}: " <<
|
|
"You have to specify an OCTET")
|
|
end
|
|
when 'update'
|
|
if opts && opts['template_json']
|
|
begin
|
|
rc = service_template.update(
|
|
opts['template_json'],
|
|
(opts['append'] == true))
|
|
|
|
status 204
|
|
rescue Validator::ParseException, JSON::ParserError
|
|
OpenNebula::Error.new($!.message)
|
|
end
|
|
elsif opts && opts['template_raw']
|
|
rc = service_template.update_raw(
|
|
opts['template_raw'],
|
|
(opts['append'] == true))
|
|
|
|
status 204
|
|
else
|
|
OpenNebula::Error.new("Action #{action['perform']}: " <<
|
|
"You have to provide a template")
|
|
end
|
|
when 'rename'
|
|
status 204
|
|
service_template.rename(opts['name'])
|
|
when 'clone'
|
|
rc = service_template.clone(opts['name'])
|
|
if OpenNebula.is_error?(rc)
|
|
error CloudServer::HTTP_ERROR_CODE[rc.errno], rc.message
|
|
end
|
|
|
|
new_stemplate = OpenNebula::ServiceTemplate.new_with_id(rc, @client)
|
|
new_stemplate.info
|
|
if OpenNebula.is_error?(new_stemplate)
|
|
error CloudServer::HTTP_ERROR_CODE[new_stemplate.errno], new_stemplate.message
|
|
end
|
|
|
|
status 201
|
|
body new_stemplate.to_json
|
|
else
|
|
OpenNebula::Error.new("Action #{action['perform']} not supported")
|
|
end
|
|
|
|
if OpenNebula.is_error?(rc)
|
|
error CloudServer::HTTP_ERROR_CODE[rc.errno], rc.message
|
|
end
|
|
end
|