Skip to content

Commit

Permalink
Implement update operation
Browse files Browse the repository at this point in the history
  • Loading branch information
dann1 committed Dec 5, 2023
1 parent f3105b2 commit ef626e3
Show file tree
Hide file tree
Showing 7 changed files with 248 additions and 80 deletions.
67 changes: 45 additions & 22 deletions src/server/function.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,14 @@ module ProvisionEngine
#
class Function < OpenNebula::VirtualMachine

STATES = {
:pending => 'PENDING',
:running => 'RUNNING',
:error => 'ERROR',
:updating => 'UPDATING'
}.freeze
STATE_MAP = {
'PENDING' => [
STATES[:pending] => [
'LCM_INIT',
'BOOT',
'PROLOG',
Expand All @@ -18,8 +24,8 @@ class Function < OpenNebula::VirtualMachine
'PROLOG_UNDEPLOY',
'CLEANUP_RESUBMIT'
],
'RUNNING' => ['RUNNING'],
'ERROR' => [
STATES[:running] => ['RUNNING'],
STATES[:error] => [
'FAILURE',
'UNKNOWN',
'BOOT_FAILURE',
Expand All @@ -36,10 +42,10 @@ class Function < OpenNebula::VirtualMachine
'PROLOG_MIGRATE_UNKNOWN_FAILURE'
]
}.freeze
FUNCTIONS = ['FAAS', 'DAAS']
FUNCTIONS = ['FAAS', 'DAAS'].freeze

T = '//TEMPLATE/'
SRF = 'Serverless Runtime Function VM'
T = '//TEMPLATE/'.freeze
SRF = 'Serverless Runtime Function VM'.freeze

def id
@pe_id.to_i
Expand Down Expand Up @@ -179,7 +185,7 @@ def to_function
function = {}

function['VM_ID'] = @pe_id
function['STATE'] = map_state
function['STATE'] = state_function

if function['STATE'] == 'ERROR'
if error
Expand All @@ -204,7 +210,7 @@ def to_function
#
# @return [Array] [Response Code, ''/Error]
#
def resize_capacity?(specification)
def resize_capacity?(specification, logger)
capacity_template = []

if specification['MEMORY'] != memory[:memory]
Expand All @@ -221,6 +227,11 @@ def resize_capacity?(specification)

return [200, ''] if capacity_template.empty?

capacity_template = capacity_template.join("\n")

logger.info("Updating #{SRF} #{@id} capacity")
logger.debug(capacity_template)

response = resize(capacity_template, true)

if OpenNebula.is_error?(response)
Expand All @@ -239,10 +250,13 @@ def resize_capacity?(specification)
#
# @return [Array] [Response Code, ''/Error]
#
def resize_disk?(specification)
def resize_disk?(specification, logger)
return [200, ''] unless specification['DISK_SIZE'] != size

response = disk_resize(0, new_size)
logger.info("Resizing #{SRF} #{@id} disk")
logger.debug("From: #{size} To: #{specification['DISK_SIZE']}")

response = disk_resize(0, specification['DISK_SIZE'])

if OpenNebula.is_error?(response)
rc = ProvisionEngine::Error.map_error_oned(response.errno)
Expand All @@ -253,24 +267,33 @@ def resize_disk?(specification)
[200, '']
end

def pending?
state_function == STATES[:pending]
end

def running?
state_function == STATES[:running]
end

def error?
state_function == STATES[:error]
end

def updating?
state_function == STATES[:updating]
end

#
# Maps an OpenNebula VM state to the accepted Function VM states
#
# @return [String] Serverless Runtime Function state
#
def map_state
case state_str
when 'INIT', 'PENDING', 'HOLD'
STATE_MAP.keys[0]
when 'ACTIVE'
STATE_MAP.each do |function_state, lcm_states|
return function_state if lcm_states.include?(lcm_state_str)
end
when 'STOPPED', 'SUSPENDED', 'POWEROFF', 'UNDEPLOYED', 'CLONING'
STATE_MAP.keys[2]
else
STATE_MAP.keys[3]
def state_function
STATE_MAP.each do |function_state, lcm_states|
return function_state if lcm_states.include?(lcm_state_str)
end

return STATES[:updating]
end

end
Expand Down
126 changes: 92 additions & 34 deletions src/server/runtime.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ class ServerlessRuntime < OpenNebula::DocumentJSON
SCHEMA = JSON.load_file('/etc/provision-engine/schemas/serverless_runtime.json').freeze

SR = 'Serverless Runtime'.freeze
SRR = 'SERVERLESS_RUNTIME'.freeze
SRD = "#{SR} Document".freeze
SRF = 'Serverless Runtime Function VM'.freeze
SRS = "#{SR} Service".freeze
SRS_NOT_FOUND = "#{SRS} not found".freeze
SRS_NO_READ = "Failed to read #{SRS}".freeze
Expand All @@ -29,7 +31,7 @@ def self.create(client, specification)
response = ServerlessRuntime.validate(specification)
return response unless response[0] == 200

specification = specification['SERVERLESS_RUNTIME']
specification = specification[SRR]

response = ServerlessRuntime.to_service(client, specification)
return response unless response[0] == 200
Expand Down Expand Up @@ -98,20 +100,34 @@ def self.get(client, id)
return ProvisionEngine::Error.new(rc, error, message)
end

response = ServerlessRuntime.sync(client, document.body)
document.cclient = client
document.update
end

#
# Syncronizes the Serverless Runtime backing components with the document
#
# @return [Array] [Response Code, ServerlessRuntime/error]
#
def update
cclient?
initial_state = to_hash

response = ProvisionEngine::ServerlessRuntime.sync(@cclient, @body)
return response unless response[0] == 200

response = document.update
return [200, self] if to_hash == initial_state

@cclient.logger.info("Updating #{SRD} #{@id}")
response = super()

if OpenNebula.is_error?(response)
rc = ProvisionEngine::Error.map_error_oned(response.errno)
error = "Failed to update #{SR}"
error = "Failed to update #{SR} #{@id}"
return ProvisionEngine::Error.new(rc, error, response.error)
end

document.cclient = client

[200, document]
[200, self]
end

#
Expand All @@ -127,45 +143,83 @@ def update_sr(specification)
response = ServerlessRuntime.validate(specification)
return response unless response[0] == 200

specification = specification['SERVERLESS_RUNTIME']

rename?(specification)
specification = specification[SRR]

ProvisionEngine::Function::FUNCTIONS.each do |function|
next if specification[function].nil?

vm_id = specification[function]['VM_ID']
next if vm_id.nil?

response = ProvisionEngine::Function.new_with_id(vm_id, @cclient.client_oned)
rc = response[0]
vm = ProvisionEngine::Function.new_with_id(vm_id, @cclient.client_oned)
response = vm.info

if rc != 200
if OpenNebula.is_error?(response)
rc = ProvisionEngine::Error.map_error_oned(response.errno)
error = "Failed to read #{SRF} #{function}"
return ProvisionEngine::Error.new(rc, error, response[1])
return ProvisionEngine::Error.new(rc, error, response.message)
end

vm = response[1]
# Resize VM hardware
case vm.state_function
when ProvisionEngine::Function::STATES[:updating], ProvisionEngine::Function::STATES[:pending]
rc = 423
error = "Cannot update #{SRF} #{function} on a transient state"
return ProvisionEngine::Error.new(rc, error, vm.state_function)
when ProvisionEngine::Function::STATES[:error]
vm.recover(2) # retry
error = "Cannot update #{SRF} #{function} on an error state. A recovery was attempted"
return ProvisionEngine::Error.new(500, error,
ProvisionEngine::Function::STATES[:error])
when ProvisionEngine::Function::STATES[:running]
['capacity', 'disk'].each do |resource|
response = vm.public_send("resize_#{resource}?", specification[function],
@cclient.logger)
return response unless response[0] == 200

['capactiy', 'disk'].each do |resource|
@cclient.logger.info("Updating #{SRF} #{function} #{resource}")
1.upto(@cclient.conf[:timeout]) do |t|
vm.info

if t == @cclient.conf[:timeout]
rc = 504
error = "#{SRF} #{function} stuck while updating capabilities"
return ProvisionEngine::Error.new(rc, error, vm.state_function)
end

case vm.state_function
when ProvisionEngine::Function::STATES[:running]
break
when ProvisionEngine::Function::STATES[:updating]
sleep 1
next
else
rc = 500
error = "#{SRF} #{function} entered unexpected state"
return ProvisionEngine::Error.new(rc, error, vm.state_function)
end
end
end

response = vm.public_send("resize_#{resource}?", specification[function])
return response unless response[0] == 200
end
end

@cclient.logger.info("Updating #{SRD} #{@id}")
# Update document body and VMs USER_TEMPLATE
['SCHEDULING', 'DEVICE_INFO'].each do |schevice|
next if specification[function][schevice].nil?
next if specification[function][schevice] == @body[SRR][function][schevice]

response = ProvisionEngine::ServerlessRuntime.sync(@cclient, @body)
return response unless response[0] == 200
@body[SRR][function][schevice] = specification[function][schevice]
vm.update(specification[function][schevice], true)

response = update
next unless OpenNebula.is_error?(response)

if OpenNebula.is_error?(response)
rc = ProvisionEngine::Error.map_error_oned(response.errno)
error = "Failed to update #{SR}"
return ProvisionEngine::Error.new(rc, error, response.error)
rc = ProvisionEngine::Error.map_error_oned(response.errno)
error = "Failed to update #{SRF} #{schevice}"
return ProvisionEngine::Error.new(rc, error, response.message)
end
end

[200, self]
update
end

#
Expand Down Expand Up @@ -430,11 +484,9 @@ def self.to_service(client, specification)
#
# Perform recovery operations on the Serverless Runtime backing components
#
# @param [Int] service_id flow service backing the Serverless Runtime
#
# @return [Array] [Response Code, Service Document Body/Error]
#
def recover(service_id)
def recover
response = @cclient.service_recover(service_id)
rc = response[0]

Expand Down Expand Up @@ -471,11 +523,12 @@ def recover(service_id)
# @return [Array] [Response Code, ''/Error]
#
def rename?(specification)
new_name = specification['NAME']
new_name = specification[SRR]['NAME']

return [200, ''] unless new_name && new_name != name

@cclient.logger.info("Renaming #{SRD} #{@id}")
@cclient.logger.debug("From: #{name} To: #{new_name}")

response = rename(new_name)

Expand Down Expand Up @@ -524,19 +577,24 @@ def to_sr
load?

runtime = {
:SERVERLESS_RUNTIME => {
SRR => {
:NAME => name,
:ID => id
}
}
rsr = runtime[:SERVERLESS_RUNTIME]
rsr = runtime[SRR]

rsr.merge!(@body)
rsr.delete('registration_time')

runtime
end

def service_id
load?
@body[SRR]['SERVICE_ID']
end

#
# Generates the flow template name for the service instantiation
#
Expand Down
4 changes: 2 additions & 2 deletions tests/init.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,11 @@

include_context('crud', sr_template)
end

tests.delete('crud')
end

tests.each do |examples, enabled|
next if examples == 'crud'

if enabled
require examples
include_context(examples)
Expand Down
2 changes: 1 addition & 1 deletion tests/lib/auth.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
@conf[:auth][:create] = true

runtime = JSON.parse(response.body)
@conf[:auth][:id] = runtime['SERVERLESS_RUNTIME']['ID'].to_i
@conf[:auth][:id] = runtime[SRR]['ID'].to_i
end

it 'missing auth on Create' do
Expand Down
Loading

0 comments on commit ef626e3

Please sign in to comment.