#!/bin/sh
#
#
# Manage Secondary Private IP in Oracle Cloud Infrastructure with Pacemaker
#
#
# Copyright 2016-2018 Lorenzo Garuti <garuti.lorenzo@gmail.com>
#
# 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.
#
#

#
#  Prerequisites:
#
#  - OCI CLI installed (https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/climanualinst.htm)
#  - jq installed
#  - dynamic group with a policy attacched
#  - the policy must have this statement:
#    allow dynamic-group <GROUP_NAME> to use virtual-network-family in compartment id <COMPARTMENT_ID>
#  - a reserved secondary private IP address for Compute Instances high availability
#

#######################################################################
# Initialization:

: ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat}
. ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs

#######################################################################

#
# Defaults
#
OCF_RESKEY_ocicli_default="/usr/local/bin/oci"
OCF_RESKEY_api_delay_default="3"
OCF_RESKEY_cidr_netmask_default="24"
OCF_RESKEY_interface_alias_default="0"
export OCI_CLI_AUTH=instance_principal

: ${OCF_RESKEY_ocicli=${OCF_RESKEY_ocicli_default}}
: ${OCF_RESKEY_api_delay=${OCF_RESKEY_api_delay_default}}
: ${OCF_RESKEY_cidr_netmask=${OCF_RESKEY_cidr_netmask_default}}
: ${OCF_RESKEY_interface_alias=${OCF_RESKEY_interface_alias_default}}

meta_data() {
    cat <<END
<?xml version="1.0"?>
<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
<resource-agent name="ocivip">
<version>1.0</version>

<longdesc lang="en">
Resource Agent for OCI Compute instance Secondary Private IP Addresses.

It manages OCI Secondary Private IP Addresses for Compute instances with oci cli.

See https://docs.oracle.com/en-us/iaas/Content/API/Concepts/cliconcepts.htm for more information about oci cli.

Prerequisites:

- OCI CLI installed (https://docs.oracle.com/en-us/iaas/Content/API/SDKDocs/climanualinst.htm)
- jq installed
- dynamic group with a policy attacched
- the policy must have this statement: allow dynamic-group GROUP_NAME to use virtual-network-family in compartment id COMPARTMENT_ID
- a reserved secondary private IP address for Compute Instances high availability

</longdesc>
<shortdesc lang="en">OCI Secondary Private IP Address for Compute instances Resource Agent</shortdesc>

<parameters>

<parameter name="ocicli" unique="0">
<longdesc lang="en">
OCI Command line interface (CLI) tools
</longdesc>
<shortdesc lang="en">OCI cli tools</shortdesc>
<content type="string" default="${OCF_RESKEY_ocicli_default}" />
</parameter>

<parameter name="secondary_private_ip" unique="1" required="1">
<longdesc lang="en">
reserved secondary private ip for compute instance
</longdesc>
<shortdesc lang="en">reserved secondary private ip for compute instance</shortdesc>
<content type="string" default="" />
</parameter>

<parameter name="cidr_netmask" unique="0">
<longdesc lang="en">
netmask for the secondary_private_ip
</longdesc>
<shortdesc lang="en">netmask for the secondary_private_ip</shortdesc>
<content type="integer" default="${OCF_RESKEY_cidr_netmask_default}" />
</parameter>

<parameter name="interface_alias" unique="0">
<longdesc lang="en">
numeric alias for the interface
</longdesc>
<shortdesc lang="en">numeric alias for the interface</shortdesc>
<content type="integer" default="${OCF_RESKEY_interface_alias_default}" />
</parameter>

<parameter name="api_delay" unique="0">
<longdesc lang="en">
a short delay between API calls, to avoid sending API too quick
</longdesc>
<shortdesc lang="en">a short delay between API calls</shortdesc>
<content type="integer" default="${OCF_RESKEY_api_delay_default}" />
</parameter>

</parameters>

<actions>
<action name="start"        timeout="30s" />
<action name="stop"         timeout="30s" />
<action name="monitor"      timeout="30s" interval="20s" depth="0" />
<action name="migrate_to"   timeout="30s" />
<action name="migrate_from" timeout="30s" />
<action name="meta-data"    timeout="5s" />
<action name="validate"     timeout="10s" />
<action name="validate-all" timeout="10s" />
</actions>
</resource-agent>
END
}

#######################################################################

ocivip_usage() {
    cat <<END
usage: $0 {start|stop|monitor|migrate_to|migrate_from|validate|validate-all|meta-data}

Expects to have a fully populated OCF RA-compliant environment set.
END
}

ocivip_start() {
    ocivip_monitor && return $OCF_SUCCESS

    $OCICLI network vnic assign-private-ip --vnic-id $VNIC_ID \
        --unassign-if-already-assigned \
        --ip-address ${SECONDARY_PRIVATE_IP}
    RETOCI=$?
    ip addr add ${SECONDARY_PRIVATE_IP}/${CIDR_NETMASK} dev ${PRIMARY_IFACE} label ${PRIMARY_IFACE}:${INTERFACE_ALIAS}
    RETIP=$?

    # delay to avoid sending request too fast
    sleep ${OCF_RESKEY_api_delay}

    if [ $RETOCI -ne 0 ] || [ $RETIP -ne 0 ]; then
        return $OCF_NOT_RUNNING
    fi

    ocf_log info "secondary_private_ip has been successfully brought up (${SECONDARY_PRIVATE_IP})"
    return $OCF_SUCCESS
}

ocivip_stop() {
    ocivip_monitor || return $OCF_SUCCESS

    $OCICLI network vnic unassign-private-ip --vnic-id $VNIC_ID \
        --ip-address ${SECONDARY_PRIVATE_IP}
    RETOCI=$?
    ip addr del ${SECONDARY_PRIVATE_IP}/${CIDR_NETMASK} dev ${PRIMARY_IFACE}:${INTERFACE_ALIAS}
    RETIP=$?

    # delay to avoid sending request too fast
    sleep ${OCF_RESKEY_api_delay}

    if [ $RETOCI -ne 0 ] || [ $RETIP -ne 0 ]; then
        return $OCF_NOT_RUNNING
    fi

    ocf_log info "secondary_private_ip has been successfully brought down (${SECONDARY_PRIVATE_IP})"
    return $OCF_SUCCESS
}

ocivip_monitor() {
    $OCICLI network private-ip list --vnic-id $VNIC_ID | grep -q "${SECONDARY_PRIVATE_IP}"
    RETOCI=$?

    if [ $RETOCI -ne 0 ]; then
        return $OCF_NOT_RUNNING
    fi
    return $OCF_SUCCESS
}

ocivip_validate() {
    check_binary ${OCICLI}
    check_binary jq

    if [ -z "${VNIC_ID}" ]; then
        ocf_exit_reason "vnic_id not found. Is this a Compute instance?"
        return $OCF_ERR_GENERIC
    fi

    return $OCF_SUCCESS
}

case $__OCF_ACTION in
    meta-data)
        meta_data
        exit $OCF_SUCCESS
        ;;
esac

OCICLI="${OCF_RESKEY_ocicli}"
SECONDARY_PRIVATE_IP="${OCF_RESKEY_secondary_private_ip}"
CIDR_NETMASK="${OCF_RESKEY_cidr_netmask}"
INTERFACE_ALIAS="${OCF_RESKEY_interface_alias}"
VNIC_ID="$(curl -s -H "Authorization: Bearer Oracle" -L http://169.254.169.254/opc/v2/vnics/ | jq -r '.[0].vnicId')"
PRIMARY_IFACE=$(ip -4 route ls | grep default | grep -Po '(?<=dev )(\S+)' | head -n1)

case $__OCF_ACTION in
    start)
        ocivip_validate || exit $?
        ocivip_start
        ;;
    stop)
        ocivip_stop
        ;;
    monitor)
        ocivip_monitor
        ;;
    migrate_to)
        ocf_log info "Migrating ${OCF_RESOURCE_INSTANCE} to ${OCF_RESKEY_CRM_meta_migrate_target}."
        ocivip_stop
        ;;
    migrate_from)
        ocf_log info "Migrating ${OCF_RESOURCE_INSTANCE} from ${OCF_RESKEY_CRM_meta_migrate_source}."
        ocivip_start
        ;;
    reload)
        ocf_log info "Reloading ${OCF_RESOURCE_INSTANCE} ..."
        ;;
    validate|validate-all)
        ocivip_validate
        ;;
    usage|help)
        ocivip_usage
        exit $OCF_SUCCESS
        ;;
    *)
        ocivip_usage
        exit $OCF_ERR_UNIMPLEMENTED
        ;;
esac

rc=$?
ocf_log debug "${OCF_RESOURCE_INSTANCE} $__OCF_ACTION : $rc"
exit $rc