#!/usr/bin/env ruby
# coding: utf-8

# -------------------------------------------------------------------------- #
# Copyright 2016, Équipe EOLE <eole@ac-dijon.fr>                             #
# Author: Daniel Dehennin <daniel.dehennin@ac-dijon.fr>                      #
#                                                                            #
# 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.                                             #
#--------------------------------------------------------------------------- #

##############################################################################
# Script to implement initial snapshot for DISKs
##############################################################################

##############################################################################
ONE_LOCATION=ENV["ONE_LOCATION"]

if !ONE_LOCATION
    RUBY_LIB_LOCATION="/usr/lib/one/ruby"
    VMDIR="/var/lib/one"
    CONFIG_FILE="/var/lib/one/config"
else
    RUBY_LIB_LOCATION=ONE_LOCATION+"/lib/ruby"
    VMDIR=ONE_LOCATION+"/var"
    CONFIG_FILE=ONE_LOCATION+"/var/config"
end

$: << RUBY_LIB_LOCATION

require 'opennebula'
include OpenNebula

# VM
RUNNING_STATE = OpenNebula::VirtualMachine::LCM_STATE.index('RUNNING')

# Images
OS = OpenNebula::Image::IMAGE_TYPES.index("OS")
DATABLOCK = OpenNebula::Image::IMAGE_TYPES.index("DATABLOCK")
SNAPSHOTABLE_IMAGE_TYPES = [OS, DATABLOCK]

if !(vm_id = ARGV[0].to_i)
    puts "Missing VM ID"
    exit(-1)
end

if !(prev_lcm_state = ARGV[1])
    puts "Missing previous LCM_STATE"
end

if prev_lcm_state != "BOOT"
    puts "VM not just created: skip initial disk snapshot creation"
    exit(0)
end

# Check if an image can be
# Params:
# +image+:: an OpenNebula::Image object
def is_snapshotable?(image)
    return !image.has_elements?("TYPE") \
           || SNAPSHOTABLE_IMAGE_TYPES.include?(image.retrieve_elements("TYPE").first.to_i)
    
end

# Check if an image is non-persistent
# Params:
# +image+:: an OpenNebula::Image object
def is_persistent?(image)
    return !image.has_elements?("PERSISTENT") \
           || image.retrieve_elements("PERSISTENT").first == "1"
end

# Check if a disk of a VM already has a snapshot
# Params:
# +disk_id+:: ID of the disk
# +vm+:: an OpenNebula::VirtualMachine
def has_snapshots?(disk_id, vm)
    return vm.element_xml("/VM/SNAPSHOTS[child::DISK_ID='#{disk_id}']") != ''
end

begin
    client = Client.new()
rescue Exception => error
    puts "Error: #{error}"
    exit(-1)
end

begin
    vm = OpenNebula::VirtualMachine.new_with_id(vm_id, client)
    vm.info
    if vm.lcm_state() != RUNNING_STATE
        raise Exception, "VM is not running: #{vm.lcm_state_str()}"
    end
rescue Exception => error
    puts "VM #{vm_id} error: #{error}"
    exit(-1)
end

vm.each '/VM/TEMPLATE/DISK' do |disk|
    disk_id = disk.retrieve_elements("DISK_ID").first.to_i

    image_id = disk.retrieve_elements("IMAGE_ID")
    if image_id.nil?
        puts "VM #{vm_id}: disk #{disk_id} is volatile => skip"
        next
    end
    image_id = image_id.first.to_i
    image = OpenNebula::Image.new_with_id(image_id, client)
    image.info

    if !is_snapshotable?(image)
        puts "VM #{vm_id}: disk #{disk_id} type does not support snapshot => skip"
        next
    elsif is_persistent?(image)
        puts "VM #{vm_id}: disk #{disk_id} is persistent => skip "
        next
    elsif has_snapshots?(disk_id, vm)
        puts "VM #{vm_id}: disk #{disk_id} already has snapshots => skip"
        next
    end

    puts "VM #{vm_id}: create initial snapshot for disk #{disk_id}"
    vm.disk_snapshot_create(disk_id, "initial snapshot")
    try = 0
    max_try = 60
    begin
        if try > max_try
            puts "VM #{vm_id} error: timeout waiting for VM status change"
            exit(-1)
        end

        try += 1
        sleep(1)
        vm.info
    end until vm.lcm_state() == RUNNING_STATE
end
