From 1c3045fd657846e02d7cba6971bef1371a55a6ae Mon Sep 17 00:00:00 2001 From: Scott Schneider Date: Tue, 14 Jul 2015 09:57:47 -0700 Subject: [PATCH] Host snapshot functionality --- lib/vmpooler/api/reroute.rb | 8 +++ lib/vmpooler/api/v1.rb | 46 ++++++++++++++ lib/vmpooler/pool_manager.rb | 109 +++++++++++++++++++++++++++++++-- lib/vmpooler/vsphere_helper.rb | 22 +++++++ 4 files changed, 180 insertions(+), 5 deletions(-) diff --git a/lib/vmpooler/api/reroute.rb b/lib/vmpooler/api/reroute.rb index 81fe0ab..e318a19 100644 --- a/lib/vmpooler/api/reroute.rb +++ b/lib/vmpooler/api/reroute.rb @@ -50,6 +50,14 @@ module Vmpooler put '/vm/:hostname/?' do call env.merge("PATH_INFO" => "/api/v#{api_version}/vm/#{params[:hostname]}") end + + post '/vm/:hostname/snapshot/?' do + call env.merge("PATH_INFO" => "/api/v#{api_version}/vm/#{params[:hostname]}/snapshot") + end + + post '/vm/:hostname/snapshot/:snapshot/?' do + call env.merge("PATH_INFO" => "/api/v#{api_version}/vm/#{params[:hostname]}/snapshot/#{params[:snapshot]}") + end end end end diff --git a/lib/vmpooler/api/v1.rb b/lib/vmpooler/api/v1.rb index d27c474..e30a16e 100644 --- a/lib/vmpooler/api/v1.rb +++ b/lib/vmpooler/api/v1.rb @@ -413,6 +413,11 @@ module Vmpooler result[params[:hostname]]['tags'] ||= {} result[params[:hostname]]['tags'][$1] = rdata[key] end + + if key.match('^snapshot\:(.+?)$') + result[params[:hostname]]['snapshots'] ||= [] + result[params[:hostname]]['snapshots'].push($1) + end end if config['domain'] @@ -509,6 +514,47 @@ module Vmpooler JSON.pretty_generate(result) end + + post "#{api_prefix}/vm/:hostname/snapshot/?" do + content_type :json + + status 404 + result = { 'ok' => false } + + params[:hostname] = hostname_shorten(params[:hostname], config['domain']) + + if backend.exists('vmpooler__vm__' + params[:hostname]) + result[params[:hostname]] = {} + + o = [('a'..'z'), ('0'..'9')].map(&:to_a).flatten + result[params[:hostname]]['snapshot'] = o[rand(25)] + (0...31).map { o[rand(o.length)] }.join + + backend.sadd('vmpooler__tasks__snapshot', params[:hostname] + ':' + result[params[:hostname]]['snapshot']) + + status 202 + result['ok'] = true + end + + JSON.pretty_generate(result) + end + + post "#{api_prefix}/vm/:hostname/snapshot/:snapshot/?" do + content_type :json + + status 404 + result = { 'ok' => false } + + params[:hostname] = hostname_shorten(params[:hostname], config['domain']) + + if backend.exists('vmpooler__vm__' + params[:hostname]) and backend.hget('vmpooler__vm__' + params[:hostname], 'snapshot:' + params[:snapshot]) + backend.sadd('vmpooler__tasks__snapshot-revert', params[:hostname] + ':' + params[:snapshot]) + + status 202 + result['ok'] = true + end + + JSON.pretty_generate(result) + end end end end diff --git a/lib/vmpooler/pool_manager.rb b/lib/vmpooler/pool_manager.rb index 7cb702b..162e870 100644 --- a/lib/vmpooler/pool_manager.rb +++ b/lib/vmpooler/pool_manager.rb @@ -286,6 +286,100 @@ module Vmpooler end end + def create_vm_snapshot(vm, snapshot_name) + Thread.new do + _create_vm_snapshot(vm, snapshot_name) + end + end + + def _create_vm_snapshot(vm, snapshot_name) + host = $vsphere['snapshot_manager'].find_vm(vm) || + $vsphere['snapshot_manager'].find_vm_heavy(vm)[vm] + + if (host) && ((! snapshot_name.nil?) || (! snapshot_name.empty?)) + $logger.log('s', "[ ] [snapshot_manager] '#{vm}' is being snapshotted") + + start = Time.now + + host.CreateSnapshot_Task( + name: snapshot_name, + description: 'vmpooler', + memory: true, + quiesce: true + ).wait_for_completion + + finish = '%.2f' % (Time.now - start) + + $redis.hset('vmpooler__vm__' + vm, 'snapshot:' + snapshot_name, Time.now.to_s) + + $logger.log('s', "[+] [snapshot_manager] '#{vm}' snapshot created in #{finish} seconds") + end + end + + def revert_vm_snapshot(vm, snapshot_name) + Thread.new do + _revert_vm_snapshot(vm, snapshot_name) + end + end + + def _revert_vm_snapshot(vm, snapshot_name) + host = $vsphere['snapshot_manager'].find_vm(vm) || + $vsphere['snapshot_manager'].find_vm_heavy(vm)[vm] + + if host + snapshot = $vsphere['snapshot_manager'].find_snapshot(host, snapshot_name) + + if snapshot + $logger.log('s', "[ ] [snapshot_manager] '#{vm}' is being reverted to snapshot '#{snapshot_name}'") + + start = Time.now + + snapshot.RevertToSnapshot_Task.wait_for_completion + + finish = '%.2f' % (Time.now - start) + + $logger.log('s', "[<] [snapshot_manager] '#{vm}' reverted to snapshot in #{finish} seconds") + end + end + end + + def check_snapshot_queue + $logger.log('d', "[*] [snapshot_manager] starting worker thread") + + $vsphere['snapshot_manager'] ||= Vmpooler::VsphereHelper.new + + $threads['snapshot_manager'] = Thread.new do + loop do + _check_snapshot_queue + sleep(5) + end + end + end + + def _check_snapshot_queue + vm = $redis.spop('vmpooler__tasks__snapshot') + + unless vm.nil? + begin + vm_name, snapshot_name = vm.split(':') + create_vm_snapshot(vm_name, snapshot_name) + rescue + $logger.log('s', "[!] [snapshot_manager] snapshot appears to have failed") + end + end + + vm = $redis.spop('vmpooler__tasks__snapshot-revert') + + unless vm.nil? + begin + vm_name, snapshot_name = vm.split(':') + revert_vm_snapshot(vm_name, snapshot_name) + rescue + $logger.log('s', "[!] [snapshot_manager] snapshot revert appears to have failed") + end + end + end + def check_pool(pool) $logger.log('d', "[*] [#{pool['name']}] starting worker thread") @@ -455,14 +549,19 @@ module Vmpooler $redis.set('vmpooler__tasks__clone', 0) loop do + if ! $threads['snapshot_manager'] + check_snapshot_queue + elsif ! $threads['snapshot_manager'].alive? + $logger.log('d', "[!] [snapshot_manager] worker thread died, restarting") + check_snapshot_queue + end + $config[:pools].each do |pool| if ! $threads[pool['name']] check_pool(pool) - else - unless $threads[pool['name']].alive? - $logger.log('d', "[!] [#{pool['name']}] worker thread died, restarting") - check_pool(pool) - end + elsif ! $threads[pool['name']].alive? + $logger.log('d', "[!] [#{pool['name']}] worker thread died, restarting") + check_pool(pool) end end diff --git a/lib/vmpooler/vsphere_helper.rb b/lib/vmpooler/vsphere_helper.rb index 68509b3..e571e57 100644 --- a/lib/vmpooler/vsphere_helper.rb +++ b/lib/vmpooler/vsphere_helper.rb @@ -99,6 +99,14 @@ module Vmpooler base end + def find_snapshot(vm, snapshotname) + if vm.snapshot + get_snapshot_list(vm.snapshot.rootSnapshotList, snapshotname) + else + return [] + end + end + def find_vm(vmname) begin @connection.serviceInstance.CurrentTime @@ -178,6 +186,20 @@ module Vmpooler ) end + def get_snapshot_list(tree, snapshotname) + snapshot = [] + + tree.each do |child| + if child.name == snapshotname + snapshot ||= child.snapshot + else + snapshot ||= get_snapshot_list(child.childSnapshotList, snapshotname) + end + end + + snapshot + end + def close @connection.close end