#!/usr/bin/ruby

# -------------------------------------------------------------------------- #
# Copyright 2002-2019, 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.                                             #
# -------------------------------------------------------------------------- #

ONE_LOCATION = ENV['ONE_LOCATION']

if !ONE_LOCATION
    RUBY_LIB_LOCATION = '/usr/lib/one/ruby'
    GEMS_LOCATION     = '/usr/share/one/gems'
else
    RUBY_LIB_LOCATION = ONE_LOCATION + '/lib/ruby'
    GEMS_LOCATION     = ONE_LOCATION + '/share/gems'
end

if File.directory?(GEMS_LOCATION)
    Gem.use_paths(GEMS_LOCATION)
end

$LOAD_PATH << RUBY_LIB_LOCATION

require 'net/http'
require 'uri'
require 'json'
require 'base64'
require 'rexml/document'
require 'nokogiri'
require 'digest/md5'

def unindent(str)
    m = str.match(/^(\s*)/)
    spaces = m[1].size
    str.gsub!(/^ {#{spaces}}/, '')
end

#-------------------------------------------------------------------------------
#  This class is used to query the TurnkeyLinux repository
#-------------------------------------------------------------------------------
class TurnkeyLinux

    #---------------------------------------------------------------------------
    # Default Configuration parameters for the Driver
    #---------------------------------------------------------------------------
    DEFAULTS = {
        :url      => 'http://mirror.turnkeylinux.org',
        :platform => '/turnkeylinux/images/proxmox/',
        :sizemb   => 5120,
        :fs       => 'ext4',
        :format   => 'raw',
        :agent    => 'OpenNebula'
    }

    TEMPLATE = unindent(<<-EOS)
        HYPERVISOR = \"lxd\"
        CPU = \"1\"
        MEMORY = \"768\"
        LXD_SECURITY_PRIVILEGED = \"true\"
        GRAPHICS = [
            LISTEN  =\"0.0.0.0\",
            TYPE  =\"vnc\"
        ]
        CONTEXT = [
            NETWORK  =\"YES\",
            SSH_PUBLIC_KEY  =\"$USER[SSH_PUBLIC_KEY]\",
            SET_HOSTNAME  =\"$NAME\"
        ]"
    EOS

    #---------------------------------------------------------------------------
    # Configuration varibales
    #   :url of linuxcontainers market place
    #   :sizemb default size for container images
    #   :fs filesystem for the image file
    #   :format for the image file, qcow2, raw
    #   :agent for HTTP client
    #---------------------------------------------------------------------------
    def initialize(options = {})
        @options = DEFAULTS
        @options.merge!(options)

        vp = File.dirname(__FILE__) + '/../../VERSION'

        @options[:agent] = "OpenNebula #{File.read(vp)}" if File.exist? vp
    end

    #---------------------------------------------------------------------------
    # Fetch URL
    #---------------------------------------------------------------------------
    def get(path)
        # Get proxy params (needed for ruby 1.9.3)
        http_proxy = ENV['http_proxy'] || ENV['HTTP_PROXY']

        p_host  = nil
        p_port  = nil

        if http_proxy
            p_uri   = URI(http_proxy)
            p_host  = p_uri.host
            p_port  = p_uri.port
        end

        uri = URI(@options[:url] + path)
        req = Net::HTTP::Get.new(uri.request_uri)

        req['User-Agent'] = @options[:agent] if @options[:agent]

        rc = Net::HTTP.start(uri.hostname, uri.port, p_host, p_port) do |http|
            http.request(req)
        end

        return [rc.code.to_i, rc.msg] unless rc.is_a? Net::HTTPSuccess

        [0, rc.body]
    end

    #---------------------------------------------------------------------------
    # Get appliance list
    #---------------------------------------------------------------------------
    def appliances
        try    = 0
        appstr = ''
        apps   = []

        loop do
            rc, body = get(@options[:platform])

            return [rc, body] if rc != 0

            parsed = Nokogiri::HTML.parse(body)
            apps   = parsed.css('pre').text.lines

            break unless apps.empty?

            try += 1

            return [0, ''] if try > 5
        end

        # Line example
        # debian-9-turnkey-zurmo_15.2-1_amd64.tar.gz.hash 2018-11-29 09:45  2.2K
        apps[1..-1].each do |l|
            f = l.split

            next if f[0] =~ /\.hash$/

            image = f[0].split('-')
            time  = f[1].split('-')
            regt  = Time.new(time[0], time[1], time[2]).to_i

            next if image[1].to_i < 9

            data = {
                'NAME'        => "#{image[3].strip} - LXD",
                'SOURCE'      => app_url(f[0]),
                'IMPORT_ID'   => -1,
                'ORIGIN_ID'   => -1,
                'TYPE'        => 'IMAGE',
                'PUBLISHER'   => 'turnkeylinux.org',
                'MD5'         => md5(regt),
                'FORMAT'      => 'raw',
                'VERSION'     => '1.0',
                'TAGS'        => '',
                'REGTIME'     => regt,
                'SIZE'        => @options[:sizemb],
                'DESCRIPTION' => "Based on #{image[0]}-#{image[1]}"
            }

            tmpl = ''
            data.each {|key, val| print_var(tmpl, key, val) }

            tmpl64 = ''
            print_var(tmpl64, 'DRIVER', 'raw')

            data = { 'APPTEMPLATE64' => tmpl64, 'VMTEMPLATE64' => TEMPLATE }
            data.each do |key, val|
                print_var(tmpl, key, Base64.strict_encode64(val))
            end

            appstr << "APP=\"#{Base64.strict_encode64(tmpl)}\"\n"
        end

        [0, appstr]
    end

    def app_url(path)
        "lxd://#{@options[:url]}/#{@options[:platform]}/#{path}?" \
        "size=#{@options[:sizemb]}&filesystem=#{@options[:fs]}&" \
        "format=#{@options[:format]}"
    end

    def print_var(str, name, val)
        return if val.nil?
        return if val.class == String && val.empty?

        str << "#{name}=\"#{val}\"\n"
    end

    # Returns an md5 from a combination of the @option hash and an input string
    def md5(string)
        Digest::MD5.hexdigest("#{@options} #{string}")
    end

end

################################################################################
# Main Program. Outpust the list of marketplace appliances
################################################################################
def set_option(opt, doc, name, path)
    opt[name] = doc.elements[path].text if doc.elements[path]
end

begin
    options     = {}
    drv_message = Base64.decode64(ARGV[0])

    doc = REXML::Document.new(drv_message).root

    set_option(options, doc, :url, 'MARKETPLACE/TEMPLATE/ENDPOINT')
    set_option(options, doc, :sizemb, 'MARKETPLACE/TEMPLATE/IMAGE_SIZE_MB')
    set_option(options, doc, :fs, 'MARKETPLACE/TEMPLATE/FILESYSTEM')
    set_option(options, doc, :format, 'MARKETPLACE/TEMPLATE/FORMAT')

    rc, str = TurnkeyLinux.new(options).appliances

    if rc != 0
        STDERR.puts str
        exit(-1)
    end

    puts str
end
