#!/bin/sh
#
# uln-yum-mirror
#
# Updated: 9/19/2013

# Source yum-uln-mirror configuration.
. /etc/sysconfig/uln-yum-mirror

# utilities to check for before checking the release
PRERELEASE_UTILS="awk cat comm find grep gunzip id mkdir rpm sed sort uname"

# utilities to check for after checking the release
RELEASE_UTILS="reposync createrepo modifyrepo yum-arch"

# Maximum number of packages to download at once
MAXPKG=1000

export PATH=/bin:/usr/bin:/usr/sbin

# check for basic utilities needed for all releases, we'll check for 
# release-specific utils further down
for cmd in $PRERELEASE_UTILS ; do
    if ! type $cmd >/dev/null 2>&1 ; then 
        echo "$0: Needed command not found: $cmd" >&2
        exit 1
    fi
done

# check for root
if [ $(id -u) -ne 0 ] ; then
    echo "$0: You must be root to run this script" >&2
    exit 1
fi

# The script has the option to be able to download the src rpms alongside
# the binary rpms.  The default, however is to NOT download them.  So, set the
# default and then check if the user asked for the src rpms.
if [ $# -eq 1 ] ; then
    if [ "$1" = "src" ] ; then
        SRC=1
    else
        echo "$0: unknown argument \"$1\"" >&2
        echo "usage: $0 [src]" >&2
        exit 1
    fi
fi

# Identify what OS the script is running on.
# If the OS is EL or RHEL, save version and arch info.
if rpm -q --whatprovides redhat-release >/dev/null 2>&1 ; then
    EL_VER=`rpm -qf /etc/redhat-release --qf '%{VERSION}' | sed 's/[^0-9]*//g'`
    EL_ARCH=`uname -i`
else
    echo "$0: Script must be run on Enterprise Linux OS" >&2
    exit 1
fi

ADDONS_CHANNEL="el${EL_VER}_${EL_ARCH}_addons"
LATEST_CHANNEL="el${EL_VER}_${EL_ARCH}_latest"

# release-specific processing
case $EL_VER in
    [5-6])
	# We need 6GB of memory, but check if it's greater than 5000 to allow for reserved memory and kdump
	# Anything less and createrepo --update is very likely to trigger the OOM-killer for large repos
	# This will cause the createrepo process to take longer, but at least it will finish
	if [ "$(free -m | awk '/Mem/{print $2}')" -ge 5000 ]; then
	    CREATEREPO_EXTRA_ARGS="--update"
	fi
        RELEASE_UTILS+=" reposync"
        LATEST_CHANNEL="ol${EL_VER}_${EL_ARCH}_latest"
        # for OL6, use sha type that's compatible with OL5
        if [ $EL_VER -eq 6 ] ; then
            CREATEREPO_EXTRA_ARGS+=" --checksum=sha"
            MODIFYREPO_EXTRA_ARGS="--checksum=sha"
            REPOSYNC_EXTRA_ARGS="--norepopath --download-metadata"
            ADDONS_CHANNEL="ol${EL_VER}_${EL_ARCH}_addons"
        fi
	# Only download latest packages and remove old packages if ALL_PKGS=0 in the configuration
	if [ $ALL_PKGS -eq 0 ]; then
		REPOSYNC_EXTRA_ARGS="${REPOSYNC_EXTRA_ARGS} -n -d"
	fi
        # make sure yum is installed
        if ! type yum >/dev/null 2>&1 ; then
            echo "$0: Needed command not found: yum" >&2
            exit 1
        fi
        ;;
    *)
        echo "$0: unsupported version ($EL_VER)" >&2
        exit 1
        ;;
esac

# check for utilities that are release-specific or should be installed
# by the uln-yum-mirror rpm
for cmd in $RELEASE_UTILS ; do
    if ! type $cmd >/dev/null 2>&1 ; then 
        echo "$0: Needed command not found: $cmd" >&2
        exit 1
    fi
done

# create the directory for the generated repo files
#mkdir -p $REPO_FILE_DIR 2>/dev/null 
#if [ $? -ne 0 ] ; then
#    echo "$0: Permieeion denied creating $REPO_FILE_DIR"
#    exit 1
#fi

# easiest way to get the list of channel labels/names that this system is subscribed to
# is direct from ULN via python
python -c "
import sys
sys.path.append('/usr/share/rhn/')
from up2date_client import up2dateAuth,rpcServer
sid = up2dateAuth.getSystemId()
if sid is None:
    print 'Error: system not registered'
    sys.exit(-1)
try:
    s = rpcServer.getServer()
    for chan in s.up2date.listChannels(sid):
        print('%s::::%s' % (chan['label'], chan['name']))
except:
    print 'Error: ', sys.exc_info()[1].faultString
    sys.exit(-1)
" | sort | while read LINE; do
  # parse out the channel label and name
  LABEL=$(echo $LINE | awk -F '::::' '{print $1}')
  NAME=$(echo $LINE | awk -F '::::' '{print $2}')

  # set the prefix (OL/EL)
  case $EL_VER in
      [5-6])
          [ $SRC -eq 1 ] && SRCARG="--source" || SRCARG=""
          ;;
      4)
          [ $SRC -eq 1 ] && SRCARG="--src" || SRCARG="--nosrc"
          ;;
  esac

  echo "## BEGIN PROCESSING $LABEL ##"
  # convert the channel label into a path suitable for our yum conventions
  DIR=`echo $LABEL | sed -e 's/x86_64/x86-64/g' \
        -e 's/oracle_addons/oracle-addons/g' -e 's/UEK_/UEK-/g' \
        -e 's/Dtrace_/Dtrace-/g' -e 's/gdm_/gdm-/g' | \
        sed 's/_u\([0-9][0-9]*\)_/_\1_/' | awk -F _ '{if($1!="exadata") \
        {printf("%s/", toupper($1))} else {printf("%s/", $1)};count=1;\
        while (++count<NF){if(count==NF-1) {print $(count+1)"/"$(count)} \
        else{printf("%s/", $(count))}}}' | sed -e 's/x86-64/x86_64/g' \
        -e 's/oracle-addons/oracle_addons/g' -e 's/UEK-/UEK\//g' \
        -e 's/Dtrace-/Dtrace/g' -e 's/gdm-/gdm_/g'`

  # determine the correct yum repository directory
  # set REP_DIR for further use in the repo file creation
  REP_DIR=$REP_UNK/$DIR
  echo $LABEL | grep '^exadata*_' >/dev/null 2>&1 && \
    REP_DIR=$REP_ENG/$DIR
  echo $LABEL | grep '^el[0-9]*_' >/dev/null 2>&1 && \
    REP_DIR=$REP_EL/$DIR
  echo $LABEL | grep '^ol[0-9]*_' >/dev/null 2>&1 && \
    REP_DIR=$REP_OL/$DIR
  echo $LABEL | grep '^ovm[0-9]*_' >/dev/null 2>&1 && \
    REP_DIR=$REP_OVM/$DIR

  # set the full absolute path to the repo on the filesystem
  REP_PATH=$REP_BASE/$REP_DIR

  echo "Channel Dir: $REP_PATH"

  # Create the required yum repository directory.
  # If directory creation failed, print error and exit.
  mkdir -p $REP_PATH

  cd $REP_PATH
  if [ $? -ne 0 ] ; then
      echo "$0: Error creating $REP_PATH" >&2
      exit 1
  fi

  # On OL5 we need to run makecache to pull updateinfo.xml.gz into the local cache
  if [ $EL_VER -eq 5 ] ; then
    yum makecache --disablerepo='*' --enablerepo=$LABEL
  fi

  case $EL_VER in
    [5-6])
      # if the channel is an x86_64 channel and the mirror host is an x86 system
      # then we need to add the -a option to allow for the non-matching-arch rpms
      # to be found and downloaded
      if `echo $LABEL | grep -q x86_64` && [ x`uname -i`x == 'xi386x' ] ; then
          SRCARG="${SRCARG} -a x86_64"
      fi
      reposync -q -g -l -m -p $REP_PATH -r $LABEL $REPOSYNC_EXTRA_ARGS $SRCARG
      ;;
  esac
      
  # run createrepo to generate yum repo metadata
  GRPARG=
  if [ -f ${REP_PATH}/comps.xml ] ; then
      GRPARG="-g ${REP_PATH}/comps.xml"
  else
      # if the comps.xml file wasn't downloaded by reposync, check the
      # global yum cache in /var/cache for the file as well
      CACHED_GROUP_FILE=${YUM_GLOBAL_CACHE}/${LABEL}/comps.xml
      if [ -f ${CACHED_GROUP_FILE} ] ; then
          GRPARG="-g $CACHED_GROUP_FILE"
      fi
  fi
  createrepo -c ${REP_PATH}/.cache $GRPARG $CREATEREPO_EXTRA_ARGS $REP_PATH

  # for OL5, the updateinfo file will not be downloaded by reposync, but it
  # should already be in the global yum cache if it's available for this repo
  if [ ! -f ${REP_PATH}/updateinfo.xml.gz ] && [ -f ${YUM_GLOBAL_CACHE}/${LABEL}/updateinfo.xml.gz ] ; then
      cp ${YUM_GLOBAL_CACHE}/${LABEL}/updateinfo.xml.gz ${REP_PATH}/updateinfo.xml.gz
  fi

  # if there's an updateinfo file for this repo, then patch it into the repo info
  if [ -f ${REP_PATH}/updateinfo.xml.gz ] ; then
      gunzip -fq ${REP_PATH}/updateinfo.xml.gz
      modifyrepo $MODIFYREPO_EXTRA_ARGS ${REP_PATH}/updateinfo.xml ${REP_PATH}/repodata/
  fi

  # determine ip address
  #ip=$(python -c "import sys; sys.path.append('/usr/share/rhn/'); from up2date_client.hardware import findHostByRoute; print findHostByRoute()[1]")

  #REP_URL="http://${ip}/yum/${REP_DIR}"
  #REPO_FILE=$REPO_FILE_DIR/${LABEL}.repo

  # remove any old versions of the repo file
  #rm -f $REPO_FILE 2>/dev/null

  # write the repo file for this repo
  #echo "[${LABEL}]" | sed -e 's/i386//' -e 's/x86_64//' -e 's/ia64//' >>$REPO_FILE
  #echo "name=${NAME}" >> $REPO_FILE
  #echo "baseurl=${REP_URL}" >> $REPO_FILE
  #echo "gpgcheck=1" >> $REPO_FILE
  #echo "enabled=1" >> $REPO_FILE

  # only call yum-arch for repos that are not OL6-based
  if echo $LABEL | grep -q ^ol6 ; then
      # since we don't really need any yum-arch headers for OL6, 
      # remove any headers directory from previous script runs
      rm -rf $REP_PATH/headers >/dev/null 2>&1
  else
      # run yum-arch command to generate up2date repo data
      if [ ${SRC} -eq 1 ] ; then
          yum-arch -q -s $REP_PATH 2>/dev/null
      else
          yum-arch -q $REP_PATH 2>/dev/null
      fi
  fi

  echo "## END PROCESSING $LABEL ##"
done

if [ $HARDLINK_RPMS -eq 1 ]; then
    echo "## START HARDLINK PROCESSING ##"
    # Run the hardlinkpy script over the yum base directory, excluding the metadata
    hardlinkpy -v 0 -f -t -x '.*/\.cache$' -x '.*/repodata$' -x '.*/headers$' ${REP_BASE}
    echo "## END HARDLINK PROCESSING ##"
fi
