#!/bin/bash
#############################################################################
#
# Copyright (c) 2011, Dell Inc. All Rights Reserved.
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# Dell Oracle Deployment Utility
#
# Adam Miller - a_j_miller@dell.com
# Copyright (C) 2010-2011 Dell Inc.
#
# This utility is written in order to supplement the Oracle Validated RPM
# configurations as are distributed by Oracle Inc.
#
# NOTE: We have forked the Oracle Validated RPM as it has a hard requirement on
# Oracle UEK Kernel platform and we want to keep Red Hat and Oracle support.
# Dell Validated RPM is now the dependency.
#
# Here you will find Dell Value Adds and implementations of best practices
# configurations that are created to make the lives easier of those who are
# deploying Oracle DB in an array of configurations upon Dell Solutions.
#
# This script is written and tested for Bash version 3.0 and above
#
# Script version = $GBL_dodeploy_version
#############################################################################


# Define global variables, prefixed with GBL_ to note they are global
GBL_grid_infra=""
GBL_ora_ver="" # This will hold user defined oracle version
GBL_ora_supported_ver=( "11gR2" )
GBL_group_id="54325"
GBL_disable_selinux=""
GBL_dodeploy_version="0.1"

function fn_usage {
    printf "Dell Oracle Deployment Utility\n"
    printf "Usage: dodeploy [OPTION]\n"
    printf "\t-h\t\t\tDisplay this help dialog\n"
    printf "\t-r [ORA_REL]\t\tOracle Release, Valid options: ${GBL_ora_supported_ver[*]}\n"
    printf "\t-g\t\t\tSetup environment for Grid Infrastructure\n"
    printf "\t-s\t\t\tDisable SELinux\n"
    printf "\t-v\t\t\tPrint Version and exit\n"
    exit 2
}

function fn_check_oracle_release {
    for i in ${GBL_ora_supported_ver[@]}; do
        if [[ "$GBL_ora_ver" == "$i" ]]; then
            return 0
        fi
    done
    
    return 1
}

function fn_add_group {
    # Add groups required for Oracle DB
    group_id=""

    if [[ -n $1 ]] && [[ -n $2 ]]; then
        group_name=$1
        group_id="-g $2"
    else
        # Error: not enough parameters passed, return the fail
        return 1
    fi
    
    printf "Trying to add new group: $group_name ...\n"
    grep "^$group_name:" /etc/group > /dev/null 2>&1

    if [[ $? -ne 0 ]]; then
        # Add the group since it does not exist
        groupadd $group_id $group_name > /dev/null 2>&1
        printf "\tAdded group: $group_name with id: $2\n" 
    else
        printf "\tgroup: $group_name - already exists\n"
    fi

}

function fn_set_pam_limits {
    grep -e "session.*required.*pam_limits.so" /etc/pam.d/login > /dev/null
    if [[ $? != 0 ]]; then
        printf "session required pam_limits.so\n" >> /etc/pam.d/login
        return 0
    else
        return 1
    fi
}


# FIXME - do we want to disable SELinux anymore?
function fn_disable_selinux {
    selinux_config="/etc/selinux/config"
    selinux_config_bkup=$selinux_config.bkup

    test ! -e "$selinux_config_bkup" && cp $selinux_config $selinux_config_bkup
    printf  "Disabling SELINUX, if not already disabled in $selinux_config file\n"
    selinux_val=$( grep "^SELINUX=" $selinux_config | cut -d"=" -f2 | 
                                                        sed 's/[ \t]*$//' )
    if [[ -z "$selinux_val" ]]; then
        printf "SELINUX=disabled\n" >> ${selinux_config}
        return 0  
    fi
    
    if [[ "$selinux_val" != "disabled" ]]; then
        test -e $selinux_config.tmp && rm -f $selinux_config.tmp
        touch $selinux_config.tmp
        while read line
        do
            printf "$line\n" | grep -e "^#" > /dev/null
            if [[ $? == 0 ]]; then
                printf "$line\n" >> $selinux_config.tmp
            elif [[ "$line" =~ "SELINUX=" ]]; then
                printf "SELINUX=disabled\n" >> $selinux_config.tmp
            else
                printf "$line\n" >> $selinux_config.tmp
            fi
            
        done < $selinux_config
        mv ${selinux_config}.tmp ${selinux_config}
        return 0

    else
        return 1
    fi
}



function fn_setup_grid_infrastructure {

    # We can assume the oracle user exists because the RPM package containing 
    # this utility depends on the Oracle Validated RPM which creates that user
    # for us.
    

    # Create necessary groups

    printf "Creating groups required for Oracle Database with Grid Infrastructure...\n" 
    
    # Oracle Validate RPM should have provided the oinstall and dba groups 
    # already so we need to concern ourselves with those 
    
    if [[ "$GBL_ora_ver" == "11gR1" ]] || [[ "$GBL_ora_ver" == "11gR2" ]]; then
        groupnum=$(cat /etc/group | sort -t: -g +2 -3 | grep -v nfsnobody | 
                                                    cut -f3 -d":" | tail -1)
        
        if [[ "$groupnum" -ge "$GBL_group_id" ]]; then
            GBL_group_id=$(( $groupnum + 1 ))
        fi

        fn_add_group asmadmin $GBL_group_id
        fn_add_group asmdba $(( $GBL_group_id + 1 ))
        fn_add_group asmoper $(( $GBL_group_id + 2 ))
   
        printf "Creating grid user...\n"
        grep "^grid:" /etc/passwd   > /dev/null 2>&1

        # NOTE - we do not create the dba group as that is handled by the
        #        Oracle Validated RPM.
        if [[ $? -ne 0 ]]; then
            
            useradd -g oinstall -G asmadmin,asmoper,asmdba,dba grid  > /dev/null 2>&1
            printf "oracle" | passwd --stdin grid
            printf "Added user: grid\n"
            printf "\t- primary group: oinstall\n"
            printf "\t- secondary group(s): dba,asmdba,asmadmin,asmoper\n" 

            printf "grid\thard\tnofile\t131072\n" >> /etc/security/limits.conf
            printf "grid\tsoft\tnproc\t131072\n" >> /etc/security/limits.conf
            printf "\t- pam limits set\n"
        else
            printf "user: grid - already exists\n"
        fi

        if id oracle | grep "(asmoper)" > /dev/null 2>&1; then
            printf "Oracle user already a member of asmoper group.\n"
        else
            printf "Adding Oracle user to asmoper group... "
            usermod -a -G asmoper,asmdba oracle
            printf "Done\n"
        fi


        # Need to perform directory setup and permissions
        if [[ "$GBL_ora_ver" == "11gR1" ]]; then
            fn_setup_grid_dir "11.1.0" ; # Function call
        elif [[ "$GBL_ora_ver" == "11gR2" ]]; then
            fn_setup_grid_dir "11.2.0" ; # Function call
        fi
    fi

    if grep '6.' /etc/redhat-release; then

        ## NOTE: This is specific to RHEL6 because Upstart replaced the legacy 
        ##       SysV init system and therefore the line from the grid installer
        ##       that is placed in /etc/inittab is no longer applicable.
        printf "Setting up ohasd Upstart configuration in: /etc/init/dell-ora.conf\n"
        cat << EOF
# Dell Oracle Grid Infrastructure ohasd config
#
# As of Oracle 11gR2 there is the following line placed in /etc/inittab but is
# no longer in use as of RHEL6 because of the switch from SysV init to Upstart
#
# h1:35:respawn:/etc/init.d/init.ohasd run >/dev/null 2>&1 </dev/null
#
# The following configuration is simply the migration of that line to the new
# syntax

start on stopped rc RUNLEVEL=[35]
stop on runlevel [S01246]

respawn
script 
    /etc/init.d/init.ohasd run >/dev/null 2>&1 </dev/null
end script
EOF
fi

}

# function fn_configure_sysctl
#
# @param - sysctl_key
# @param - sysctl_val
#
# This function will check for a value in /etc/sysctl.conf and write the 
# desired value in its place if it exists or will simply write the desired
# value in the event it did not already exist.
function fn_configure_sysctl {
    
    # create a temp file for working with the sysctl.conf bits
    tmp_file=$(mktemp)
    
    # Check for params passed
    if [[ -z "$1" ]] || [[ -z "$2" ]]; then
        return 1
    fi

    # Check for already set parameters in /etc/sysctl.conf and nuke them
    grep "$1" /etc/sysctl.conf > /dev/null 2>&1 
    if [[ "$?" -eq "0" ]]; then
        grep -v "$1" /etc/sysctl.conf > $tmp_file
        cp -f $tmp_file /etc/sysctl.conf
    fi
    printf "\n" >> /etc/sysctl.conf # Format looks strange without whitespace
    printf "# Dell Oracle Deployment Setting\n" >> /etc/sysctl.conf
    printf "$1 = $2\n" >> /etc/sysctl.conf

}

#Setup the ORACLE_BASE directory for installation with correct perms
function fn_setup_oracle_dir {

    printf "Setting up Ownerships and Permissions for Oracle Directory... \n"
    
    # Provide the user a default but replace with user provided if necessary
    oracle_base="/u01/app/oracle"
    printf "Enter ORACLE_BASE path/partition [$oracle_base]: "
    read in_oracle_base
    
    # If the user offers a value, use it instead
    if [[ -n "$in_oracle_base" ]]; then
        oracle_base="$in_oracle_base"
    fi
    
    # Create the directory tree if necessary
    if [[ ! -d "$oracle_base" ]]; then
        mkdir -p $oracle_base
    fi

    # Apply appropriate ownership
    chown -R oracle.oinstall $oracle_base
    
    
    # Get oracle root dir. If default, root dir is /u01 
    ora_root_dir="/$(printf "$oracle_base" | cut -d/ -f2)"
    chmod -R 775 $ora_root_dir

    printf "DONE\n"

}

# function fn_setup_grid_dir
#
# @param - ora_version
# 
# This function will setup the grid directories needed, passing in the oracle
# release version as the parameter to be used for the creation of sections
# of the directory structure.
function fn_setup_grid_dir {

    # Since there are more than one version of oracle that can have grid
    # setup, we need to read that parameter in and error if not provided
    if [[ -z "$1" ]]; then
        return 1
    fi
    
    ora_version="$1"


    printf "Setting up Ownerships and Permissions for Grid Directory... \n"

    # Provide the user a default but replace with user provided if necessary
    grid_base="/u01/app/grid"
    printf "Enter Oracle Grid installation base path/partition [$grid_base]: "
    read in_grid_base
    
    # If the user offers a value, use it instead
    if [[ -n "$in_grid_base" ]]; then
        grid_base="$in_grid_base"
    fi
   
    # Create the directory tree if necessary
    if [[ ! -d "${grid_base}" ]]; then
        mkdir -p $grid_base
    fi

    # Provide the user a default but replace with user provided if necessary
    grid_home="/u01/app/$ora_version/grid"
    printf "Enter Oracle Grid Home path [$grid_home]: "
    read in_grid_home
    
    # If the user offers a value, use it instead
    if [[ -n "$in_grid_home" ]]; then
        grid_base="$in_grid_home"
    fi
   
    # Create the directory tree if necessary
    if [[ ! -d "$grid_home" ]]; then
        mkdir -p $grid_home
    fi

    # Get the grid root dir. If default, root dir is /u01   
    grid_root_dir="/$(printf "$grid_base" | cut -d/ -f2)"
    
    # Apply appropriate ownership
    chown -R grid.oinstall $grid_root_dir
}

function fn_setup_10gR2 {
    
    # Function to perform Oracle 10gR2 specific operations

    # Set sysctl parameters for 10gR2

    kernel_shmmni="4096"
    kernel_sem="250 32000 100 128"
    fs_filemax="65536"
    net_ipv4_ip_local_port_range="9000 65500"
    net_core_rmem_default="262144"
    net_core_rmem_max="2097152"
    net_core_wmem_default="262144"
    net_core_wmem_max="262144"
    kernel_shmall="2097152" # Correct for most systems, Metalink Note 301830.1
    
    # shmmax should equal 1/2 of physical memory but not greater than 4GB 
    # This measurement as per Oracle documentation is done in bytes
    tmp_var=$(grep MemTotal /proc/meminfo | awk '{ print int(($2*1024)/2) }')
    if [[ "$tmp_var" -gt "4294967296" ]]; then
        kernel_shmmax="$tmp_var"
    else
        kernel_shmmax="4294967296"
    fi

    kernel_shmmni="4096"
    kernel_sem="250 32000 100 128"
    
    
    # Perform all the processing down here so we can easily change params in 
    # one location as needed.

    fn_configure_sysctl "fs.file-max" "$fs_filemax"
    fn_configure_sysctl "net.ipv4.ip_local_port_range" "$net_ipv4_ip_local_port_range"
    fn_configure_sysctl "net.core.rmem_default" "$net_core_rmem_default"
    fn_configure_sysctl "net.core.rmem_max" "$net_core_rmem_max"
    fn_configure_sysctl "net.core.wmem_default" "$net_core_wmem_default"
    fn_configure_sysctl "net.core.wmem_max" "$net_core_wmem_max"
    fn_configure_sysctl "kernel.shmall" "$kernel_shmall"
    fn_configure_sysctl "kernel.shmmax" "$kernel_shmmax"
    fn_configure_sysctl "kernel.shmmni" "$kernel_shmmni"
    fn_configure_sysctl "kernel.sem" "$kernel_sem"

    # load all the settings that were just placed.
    sysctl -p > /dev/null 2>&1

}

function fn_setup_11gR1 {
    
    # Function to perform Oracle 11gR1 specific operations
    
    # Set sysctl parameters for 11gR1

    kernel_shmmni="4096"
    kernel_sem="250 32000 100 128"
    fs_filemax="6815744"
    net_ipv4_ip_local_port_range="9000 65500"
    net_core_rmem_default="262144"
    net_core_rmem_max="4194304"
    net_core_wmem_default="262144"
    net_core_wmem_max="1048576"
    kernel_shmall="2097152" # Correct for most systems, Metalink Note 301830.1
    
    # shmmax should equal 1/2 of physical memory but not greater than 4GB 
    # This measurement as per Oracle documentation is done in bytes
    tmp_var=$(grep MemTotal /proc/meminfo | awk '{ print int(($2*1024)/2) }')
    if [[ "$tmp_var" -gt "4294967296" ]]; then
        kernel_shmmax="$tmp_var"
    else
        kernel_shmmax="4294967296"
    fi

    # calculate shmall based on shmmax, Metalink Note: 301830.1
    tmp_ps=$( getconf PAGE_SIZE )
    if [[ -n $tmp_ps ]]; then
        tmp_shmall=$(( $kernel_shmmax / $tmp_ps ))
    fi

    if [[ -n $tmp_shmall ]]; then
        if [[ $tmp_shmall -gt $kernel_shmall ]]; then
            kernel_shmall=$tmp_shmall
        fi
    fi

    kernel_shmmni="4096"
    kernel_sem="250 32000 100 128"
    
    # Perform all the processing down here so we can easily change params in 
    # one location as needed.

    fn_configure_sysctl "fs.file-max" "$fs_filemax"
    fn_configure_sysctl "net.ipv4.ip_local_port_range" "$net_ipv4_ip_local_port_range"
    fn_configure_sysctl "net.core.rmem_default" "$net_core_rmem_default"
    fn_configure_sysctl "net.core.rmem_max" "$net_core_rmem_max"
    fn_configure_sysctl "net.core.wmem_default" "$net_core_wmem_default"
    fn_configure_sysctl "net.core.wmem_max" "$net_core_wmem_max"
    fn_configure_sysctl "kernel.shmall" "$kernel_shmall"
    fn_configure_sysctl "kernel.shmmax" "$kernel_shmmax"
    fn_configure_sysctl "kernel.shmmni" "$kernel_shmmni"
    fn_configure_sysctl "kernel.sem" "$kernel_sem"

    # load all the settings that were just placed.
    sysctl -p > /dev/null 2>&1

}

function fn_setup_11gR2 {
    
    # Function to perform Oracle 11gR2 specific operations
    
    # Set sysctl parameters for 11gR2
    

    kernel_shmmni="4096"
    kernel_sem="250 32000 100 128"
    fs_filemax="6815744"
    net_ipv4_ip_local_port_range="9000 65500"
    net_core_rmem_default="262144"
    net_core_rmem_max="4194304"
    net_core_wmem_default="262144"
    net_core_wmem_max="1048576"
    kernel_shmall="2097152" # Correct for most systems, Metalink Note 301830.1
    
    # shmmax should equal 1/2 of physical memory but not greater than 4GB 
    # This measurement as per Oracle documentation is done in bytes
    tmp_var=$(grep MemTotal /proc/meminfo | awk '{ print int(($2*1024)/2) }')
    if [[ "$tmp_var" -gt "4294967296" ]]; then
        kernel_shmmax="$tmp_var"
    else
        kernel_shmmax="4294967296"
    fi
    
    # calculate shmall based on shmmax, Metalink Note: 301830.1
    tmp_ps=$( getconf PAGE_SIZE )
    if [[ -n $tmp_ps ]]; then
        tmp_shmall=$(( $kernel_shmmax / $tmp_ps ))
    fi

    if [[ -n $tmp_shmall ]]; then
        if [[ $tmp_shmall -gt $kernel_shmall ]]; then
            kernel_shmall=$tmp_shmall
        fi
    fi

    kernel_shmmni="4096"
    kernel_sem="250 32000 100 128"
    
    
    # Perform all the processing down here so we can easily change params in 
    # one location as needed.

    fn_configure_sysctl "fs.file-max" "$fs_filemax"
    fn_configure_sysctl "net.ipv4.ip_local_port_range" "$net_ipv4_ip_local_port_range"
    fn_configure_sysctl "net.core.rmem_default" "$net_core_rmem_default"
    fn_configure_sysctl "net.core.rmem_max" "$net_core_rmem_max"
    fn_configure_sysctl "net.core.wmem_default" "$net_core_wmem_default"
    fn_configure_sysctl "net.core.wmem_max" "$net_core_wmem_max"
    fn_configure_sysctl "kernel.shmall" "$kernel_shmall"
    fn_configure_sysctl "kernel.shmmax" "$kernel_shmmax"
    fn_configure_sysctl "kernel.shmmni" "$kernel_shmmni"
    fn_configure_sysctl "kernel.sem" "$kernel_sem"

    # load all the settings that were just placed.
    sysctl -p > /dev/null 2>&1

}


# Check to make sure we're running as root.
if [[ $(id -u) -ne 0 ]]; then
    printf "ERROR: Script must be run as root\n"
    exit 1
fi

# Check for parameters, we require at least one.
if [[ $# -lt 1 ]]; then
    fn_usage ;
    exit 0
fi

# Obtain all of our opts from the command line
while getopts ":hVgsr:" opt; do
    case $opt in
        h)
            fn_usage ;
            exit 0
            ;;
        g)
            GBL_grid_infra="true"
            ;;
        s)
            GBL_disable_selinux="true"
            ;;
        r)
            GBL_ora_ver="$OPTARG"
            ;;
        V)
            printf "dodeploy version: $GBL_dodeploy_version\n"
            exit 0
            ;;
        *)
            fn_usage ;
            exit 101
            ;;
    esac
done


fn_check_oracle_release ; # Function call to check for valid Oracle Release
if [[ "$?" -eq "1" ]]; then
    printf "ERROR: Invalid Oracle Release version provided or none specified.\n"
    printf "Supported Releases: ${GBL_ora_supported_ver[*]}\n"
    exit 3
fi

# Semi-dirty hack to call correct Oracle release specific config function
## Here we are somewhat exploiting bash variable expansion in that the variable
## will be expanded before the function is called so this will call the 
## appropriate function based on the Oracle version passed.
fn_setup_$GBL_ora_ver ;
printf "System's sysctl paramters successfully configured for Oracle $GBL_ora_ver\n"

if [[ "$GBL_grid_infra" == "true" ]]; then
    # We need to perform this outside the getopts loop so we can obtain all
    # the opts before launching the function.
    fn_setup_grid_infrastructure ; # Function call
fi

# Setup Oracle directory and permissions
## This needs to run *after* the grid setup so that the permissions are correct
## since grid owns everything else in (default) /u01/app/
fn_setup_oracle_dir ; # Function call

if [[ "$GBL_disable_selinux" == "true" ]]; then
    fn_disable_selinux ;
fi

fn_set_pam_limits ;  # This will be needed universally and is for some reason not 
                # handled by the oracle-validated rpm  
