#!/usr/bin/env ruby

#--
# Copyright 2012 Red Hat, Inc.
#
# 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 'getoptlong'
require 'socket'

def usage
    puts <<USAGE
== Synopsis

oo-admin-chk: Check all user applications

== Usage

oo-admin-chk OPTIONS

Options:
-v|--verbose
    Print information about each check being performed
-h|--help
    Show Usage info
USAGE
end

opts = GetoptLong.new(
    ["--verbose",          "-v", GetoptLong::NO_ARGUMENT],
    ["--help",             "-h", GetoptLong::NO_ARGUMENT]
)

args = {}
begin
    opts.each{ |k,v| args[k]=v }
rescue GetoptLong::Error => e
    usage
    exit -100
end

verbose = args["--verbose"]


require "/var/www/openshift/broker/config/environment"
# Disable analytics for admin scripts
Rails.configuration.analytics[:enabled] = false

errors = false
summary = []


# start with validation of the nodes

localhost = %w[127.0.0.1 ::1]
# get public_hostname of all nodes
hosts = Hash.new {|h,k| h[k]=[]}
OpenShift::MCollectiveApplicationContainerProxy.rpc_get_fact("public_hostname") do |s,host|
  hosts[host] << s
  begin # test host resolution
    # public_hostname must resolve as a FQDN, so should be the full name
    # (the "." at the end blocks adding a search domain)
    resolved_host = IPSocket.getaddress(host + ".")
    if localhost.member? resolved_host
      errors = true
      summary << "FAIL - PUBLIC_HOSTNAME #{host} for #{s} should be public, not localhost"
    else
      puts "OK - PUBLIC_HOSTNAME #{host} for #{s} resolves to #{resolved_host}" if verbose
    end
  rescue Exception => e
    errors = true
    summary << "FAIL - PUBLIC_HOSTNAME #{host} for #{s} does not resolve as a FQDN (#{e})"
  end
end

# get public_ip of all nodes
ips = Hash.new {|h,k| h[k]=[]}
OpenShift::MCollectiveApplicationContainerProxy.rpc_get_fact("public_ip") do |s,ip|
  ips[ip] << s
  if localhost.member? ip
    errors = true
    summary << "FAIL - PUBLIC_IP #{ip} should be public, not localhost"
  else
    puts "OK - PUBLIC_IP #{ip} for #{s}" if verbose
  end
end

hosts.each do |host,ids|
  if ids.length > 1
      errors = true
      summary << "FAIL - multiple node hosts have public_hostname #{host}: #{ids.join ','}"
  end
end
ips.each do |ip,ids|
  if ids.length > 1
      errors = true
      summary << "FAIL - multiple node hosts have public_ip #{ip}: #{ids.join ','}"
  end
end

puts summary.join "\n" if errors && verbose

# now do validation of all gears
node_hash = OpenShift::ApplicationContainerProxy.get_all_gears

mongo_hash = {}
CloudUser.find_all(nil) {|hash|
  user = CloudUser.hash_to_obj(hash)
  gear_count = 0
  user.applications.each { |app|
    app.group_instances.uniq.each { |gi|
      gi.gears.each { |gear|
        gear_count += 1
        mongo_hash[gear.uuid] = app.name
      }
    }
  }
  if user.consumed_gears != gear_count
    msg = "FAIL - user #{user.login} has a mismatch in consumed gears (#{user.consumed_gears}) and actual gears (#{gear_count})!"
    puts msg if verbose
    summary << msg
    errors = true
  else
    puts "OK - user #{user.login} has consumed_gears equal to actual gears (#{gear_count})!" if verbose
  end
}

# now check
puts "Checking application gears in respective nodes :" if verbose
mongo_hash.each { |mk,mv|
  print "#{mk}...\t" if verbose
  if not node_hash.has_key? mk
    errors = true
    puts "FAIL" if verbose
    summary << "Gear #{mk} in #{mv} does not exist on any node"
  else
    puts "OK" if verbose
  end
}

# now check reverse
puts "Checking node gears in application database:" if verbose
node_hash.each { |nk, nv|
  print "#{nk}...\t" if verbose
  if not mongo_hash.has_key? nk
    errors = true
    puts "FAIL" if verbose
    summary << "Gear #{nk} exists on node #{nv} but does not exist in mongo database"
  else
    puts "OK" if verbose
  end
}

puts errors ? "Check failed.\n #{summary.join("\n")}" : "Success"
exit (errors ? 1 : 0)
