#
# Cookbook Name:: hadoop
# Library:: Helper
#
# Copyright 2013, whitestar
#
# 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.
#

module Hadoop

module Helper
  USERS_V1 = {
    :hadoop => {:name => 'hadoop', :uid => 10001},
    :hdfs   => {:name => 'hdfs',   :uid => 10002},
    :mapred => {:name => 'mapred', :uid => 10003}
  }
  USERS_V2 = USERS_V1.merge(
    {:yarn => {:name => 'yarn', :uid => 10004}}
  )

  LIMITS_FILES_V1 = [
    'hdfs.conf',
    'mapreduce.conf'
  ]
  LIMITS_FILES_V2 = LIMITS_FILES_V1 + ['yarn.conf']


  def parse_version_number(version)
    if /^(\d+)\.(\d+)\.(\d+)\.?(\d*)[-\w]*$/ =~ version then
      major_version = $1
      middle_version = "#{$1}.#{$2}"
    else
      Chef::Application.fatal!("Invalid Hadoop version: #{version}")
    end
  
    unless (('1.0.0' <= version && version < '1.3') \
      || ('2.0' <= middle_version && middle_version < '2.4')) then
      Chef::Application.fatal!("Non supported version: #{version}")
    end

    return [version, major_version, middle_version]
  end


  def parse_cdh_version_number(version)
    if /^(\d+)(u|\.)(\d+)\.?(\d*)$/ =~ version then
      major_version = $1
      middle_version = "#{$1}.#{$3}"
    else
      Chef::Application.fatal!("Invalid Hadoop version: #{version}")
    end
  
    unless (middle_version && middle_version == '5.0' \
      || ('4.2' <= middle_version && middle_version <= '4.4') \
      || ('3.5' <= middle_version && middle_version <= '3.6')) then
      Chef::Application.fatal!("Non supported version: #{version}")
    end

    return [version, major_version, middle_version]
  end


  def parse_hdp_version_number(version)
    if /^(\d+)\.(\d+)\.(\d+)\.?(\d*)$/ =~ version then
      major_version = $1
      middle_version = "#{$1}.#{$2}"
    else
      Chef::Application.fatal!("Invalid Hadoop version: #{version}")
    end
  
    unless (('1.2.0' <= version && version < '1.4') \
      || middle_version == '2.0') then
      Chef::Application.fatal!("Non supported version: #{version}")
    end

    return [version, major_version, middle_version]
  end


  def get_equivalent_apache_hadoop_version_of(distribution, in_version)
    case distribution
    when 'cdh'
      version, major_version, middle_version \
        = parse_cdh_version_number(in_version)

      case middle_version
      when '5.0' then return '2.2'
      when '4.4', '4.3', '4.2' then return '2.0'
      when '3.6', '3.5' then return '1.1'
      end
    when 'hdp'
      version, major_version, middle_version \
        = parse_hdp_version_number(in_version)

      case version
      when '2.0.6.0', '2.0.8.0', '2.0.9.0', '2.0.9.1' then return '2.2'
      when '2.0.5.0', '2.0.4.0' then return '2.1'
      end

      case middle_version
      when '2.0' then return '2.0'
      when '1.3' then return '1.2'
      when '1.2' then return '1.1'
      end
    end
  end


  def get_users(major_version)
    case major_version
    when '1' then return USERS_V1
    when '2' then return USERS_V2
    end
  end


  def get_limits_files(major_version)
    case major_version
    when '1' then return LIMITS_FILES_V1
    when '2' then return LIMITS_FILES_V2
    end
  end


  def setup_directories(major_version)
    users = get_users(major_version)

    active_vol_nums = 0
    node['grid']['max_vol_nums'].to_i.times {|vol_num|
      target_vol_dir = "#{node['grid']['vol_root']}/#{vol_num}"
    
      if vol_num == 0 || FileTest::directory?(target_vol_dir) then
        directory "#{target_vol_dir}/var" do
          owner 'root'
          group 'root'
          mode '0755'
          action :create
          recursive true
        end
    
        %w{lib log}.each {|dir|
          directory "#{target_vol_dir}/var/#{dir}" do
            owner 'root'
            group users[:hadoop][:name]
            mode '0775'
            action :create
            recursive true
          end
        }
    
        if major_version == '2' then
          directory "#{target_vol_dir}/var/log/yarn" do
            owner users[:yarn][:name]
            group users[:hadoop][:name]
            mode '0755'
            action :create
            recursive true
          end
    
          directory "#{target_vol_dir}/var/log/yarn/nm" do
            owner users[:yarn][:name]
            group users[:hadoop][:name]
            mode '0755'
            action :create
            recursive true
          end
    
          directory "#{target_vol_dir}/var/log/yarn/nm/local" do
            owner users[:yarn][:name]
            group users[:hadoop][:name]
            mode '0755'
            action :create
            recursive true
          end
        end
    
        if major_version == '1' then
          directory "#{target_vol_dir}/tmp" do
            owner 'root'
            group 'root'
            mode '1777'
            action :create
            recursive true
          end
        end
=begin NOT necessary. 
        if major_version == '2' then
          directory "#{target_vol_dir}/tmp/mapred" do
            owner 'root'
            group 'root'
            mode '1777'
            action :create
            recursive true
          end
        end
=end
        if vol_num == 0 then
          directory "#{target_vol_dir}/var/run" do
            owner 'root'
            group users[:hadoop][:name]
            mode '0775'
            action :create
            recursive true
          end
    
          directory "#{target_vol_dir}/var/log/hdfs" do
            owner users[:hdfs][:name]
            group users[:hdfs][:name]
            mode '0755'
            action :create
            recursive true
          end
        end
      else
        break
      end
      active_vol_nums = vol_num + 1
    }
    
    return active_vol_nums
  end


  def conf_template(conf_dir, middle_version, conf_files, tpl_vars)
    source_dir = ('2.0' <= middle_version && middle_version < '2.4') \
      ? "etc-#{middle_version}/hadoop" \
      : "conf-#{middle_version}"
  
    conf_files.each {|conf_file|
      template "#{conf_dir}/#{conf_file}" do
        source "#{source_dir}/#{conf_file}"
        owner 'root'
        group 'root'
        mode '0644'
        variables(tpl_vars)
      end
    }
  end


  def get_cgroup_pkg_name
    case node[:platform_family]
    when 'debian'
      if node[:platform] == 'ubuntu' then
        # https://bugs.launchpad.net/ubuntu/+source/libcgroup/+bug/1096771
        return 'cgroup-lite'
      else
        return 'cgroup-bin'
      end
    when 'rhel'
      return 'libcgroup'
    end
  end


  def get_cgconfig_service_name
    if node[:platform] == 'ubuntu' then
      return 'cgroup-lite'
    else
      return 'cgconfig'
    end
  end


  def setup_cgroup(middle_version)
    if node['hadoop']['yarn.nodemanager.linux-container-executor.resources-handler.class'] \
      == 'org.apache.hadoop.yarn.server.nodemanager.util.CgroupsLCEResourcesHandler' then
      package get_cgroup_pkg_name do
        action :install
      end

      cgconfig_service_name = get_cgconfig_service_name
      service cgconfig_service_name do
        action [:enable, :start]
        supports :status => true, :restart => true, :reload => true
      end

      if node[:platform] == 'ubuntu' then
        upstart_job_name = 'cgconfig4yarn'
        service upstart_job_name do
          provider Chef::Provider::Service::Upstart
          action :nothing
        end

        template "/etc/init/#{upstart_job_name}.conf" do
          source "etc-#{middle_version}/init/#{upstart_job_name}.conf"
          owner 'root'
          group 'root'
          mode '0644'
          notifies :start, "service[#{upstart_job_name}]"
        end
      else
        template '/etc/cgconfig.conf' do
          source "etc-#{middle_version}/cgconfig.conf"
          owner 'root'
          group 'root'
          mode '0644'
          notifies :restart, "service[#{cgconfig_service_name}]"
        end
      end
    end
  end
end

end

