#!/bin/bash # # Author: Georg Voell - georg.voell@oracle.com # Version: @(#)instance 1.0.1 01.09.2020 (c)2020 Oracle # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ # #@ Do some jobs with instances #@ #@Usage: instance [options] [action] #@ Options: -h, --help : Displays helptext. #@ -v, --version : Displays the version of the script. #@ -i, --instance-id : Instance OCID. #@ -c, --compartment-id : Compartment OCID. #@ -v, --vnic-id : VNIC (Virtual Network Interface Card) OCID. #@ Action: #@ status: List only lifecycle-state of instance specified with instance-id. #@ list : List all instances or one instance specified with instance-id. #@ stop : Stop instance specified with instance-id. #@ start : Start instance specified with instance-id. #@ open : Attach reserved public ip and allow access for ssh. #@ close : Detach reserved public ip and deny access for ssh. #@ #@Examples: #@ instance #@ List compartments and all instances running in these compartments. #@ instance list --compartment-id ocid1.tenancy.oc1..xxxxxxxxx #@ List all instances in root compartment. #@ instance stop --instance-id ocid1.instance.oc1.xxxxxxxxx #@ Stop instance specifies with instance-id. # # Exit codes: # 01: Unknown or wrong parameter. # 02: Unknown action. # 03: No **instance-id** specified with start or stop. # 04: Instance was already running or stopped. # 05: Instance was already opened or closed. # 06: Could not attach / detach public ip (or unexpected error) # 99: User interrupt. # # See also: # **install-scripts**(1) # # ToDo: # # - Check also nested compartments. # - We currently only detach public ip from primary vnic. Parameter --vnic-id has no usage yet. # - Check if we can stop internet access by detaching public ip - OAC may need it also # Alternative: Working with Whitelist (access only from OAC or the world) or Securitylist (open or close port 22) # # Update history: # # V 1.0.0 11.06.2020 Using library # V 1.0.1 01.09.2020 Changed parameter v to n for vnic-id # script=${0} # Name of this script progstr=`basename "$script"` # Basename of the script progdir=`dirname "$script"` # Dirname of the script libfile=`filecheck -x lib.bash` # Find location of library source "$libfile" # Include default bash library PATH="$WORKPATH" # Set PATH to something useful # Do extra cleanup function ExtraCleanup() { filecheck -rm ${scratchfile}.comp filecheck -rm ${scratchfile}.inst filecheck -rm ${scratchfile}.vnic filecheck -rm ${scratchfile}.ip } # Display root compartment and the childs (no grand children) function DisplayCompartments() { comp=${1} if [ "$comp" = "" ]; then # Get root compartment comp=`$oci iam compartment list | browse-json compartment-id` fi printf "id\tlifecycle-state\tname\n" > ${scratchfile}.comp printf "%s\tACTIVE\troot\n" $comp >> ${scratchfile}.comp $oci iam compartment list --compartment-id $comp > $scratchfile result=`filecheck -sl $scratchfile` if [ "$result" = "$scratchfile" ]; then cat $scratchfile | convert-json --fields "id,lifecycle-state,name" --noheader --output text >> ${scratchfile}.comp fi printf "\nCompartments:\n\n" print-table ${scratchfile}.comp --green "ACTIVE" --yellow "DELETED" --red "INACTIVE" } # Display all instances in root compartment and the childs (no grand children) function DisplayInstances() { comp=${1} if [ "$comp" = "" ]; then # Create list of compartments if it doesn't exists yet if [ ! -r "${scratchfile}.comp" ]; then DisplayCompartments fi printf "id\tlifecycle-state\tdisplay-name\tcompartment\n" > ${scratchfile}.inst while read -r line; do comp=`echo "$line" | cut -d' ' -f1` stat=`echo "$line" | cut -d' ' -f2` name=`echo "$line" | cut -d' ' -f3` if [ "$comp" != "id" ]; then $oci compute instance list --compartment-id $comp > $scratchfile result=`filecheck -sl $scratchfile` if [ "$result" = "$scratchfile" ]; then cat $scratchfile \ | convert-json --fields "id,lifecycle-state,display-name,compartment-id" --noheader --output text \ | sed 's|'$comp'|'$name'|' >> ${scratchfile}.inst # Replace compartment-id with compartment-name fi fi done < ${scratchfile}.comp else $oci compute instance list --compartment-id $comp > $scratchfile result=`filecheck -sl $scratchfile` if [ "$result" != "" ]; then cat $scratchfile \ | convert-json --fields "id,lifecycle-state,display-name" --output text > ${scratchfile}.inst fi fi result=`filecheck -sl ${scratchfile}.inst` if [ "$result" != "" ]; then printf "\nInstances:\n\n" print-table ${scratchfile}.inst --green "RUNNING" --red "STOPPED" --yellow "TERMINATED" else printf "\nNo instancesfound.\n" fi } # Display one instance specified with instance-id function GetInstance() { inst=${1} $oci compute instance get --instance-id $inst > $scratchfile result=`filecheck -sl $scratchfile` if [ "$result" != "" ]; then cat $scratchfile \ | browse-json data --output json | jq -M .[] \ | convert-json --fields "id,lifecycle-state,display-name,shape" --output text > ${scratchfile}.inst printf "\nResult:\n\n" print-table ${scratchfile}.inst --list --green "RUNNING" --red "STOPPED" --yellow "TERMINATED" # Get attached VNIC $oci compute instance list-vnics --instance-id $inst > $scratchfile result=`filecheck -sl $scratchfile` if [ "$result" != "" ]; then cat $scratchfile \ | convert-json --fields "id,lifecycle-state,is-primary,private-ip,public-ip" --output text > ${scratchfile}.vnic printf "\nAttached VNICs:\n\n" print-table ${scratchfile}.vnic --green "AVAILABLE" --red "TERMINATED" --yellow "TERMINATING" fi fi } # Return lifecycle-state of instance specified with instance-id # lifecycle-state could be: PROVISIONING, RUNNING, STARTING, STOPPING, STOPPED, CREATING_IMAGE, TERMINATING, TERMINATED function GetState() { inst=${1} # Get current state state=`$oci compute instance get --instance-id $inst | browse-json data/lifecycle-state` # return lifecycle-state echo "$state" } # Stop instance specified with instance-id function StopInstance() { inst=${1} stat=0 # Get current state state=`GetState $inst` if [ "$state" = "STOPPED" ]; then stat=4 errormsg $stat "($progstr) Instance '$inst' already stopped." else printf "\nStopping Instance.\n" $oci compute instance action --action stop --wait-for-state "STOPPED" --instance-id $inst \ | browse-json data --output json | jq -M .[] \ | convert-json --fields "id,lifecycle-state,display-name" --output text > $scratchfile printf "Result:\n\n" print-table $scratchfile --list --green "RUNNING" --red "STOPPED" --yellow "TERMINATED" fi return $stat } # Start instance specified with instance-id function StartInstance() { inst=${1} stat=0 # Get current state state=`GetState $inst` if [ "$state" = "RUNNING" ]; then stat=3 errormsg $stat "($progstr) Instance '$inst' already running." else printf "\nStarting Instance.\n" $oci compute instance action --action start --wait-for-state "RUNNING" --instance-id $inst \ | browse-json data --output json | jq -M .[] \ | convert-json --fields "id,lifecycle-state,display-name" --output text > $scratchfile printf "Result:\n\n" print-table $scratchfile --list --green "RUNNING" --red "STOPPED" --yellow "TERMINATED" fi return $stat } # Open or close public access function TogglePublicIP() { proc=${1} inst=${2} stat=0 if [ "$proc" = "close" ]; then printf "\nClosing instance.\n" else printf "\nOpening instance.\n" fi # Get attached vnics from instance $oci compute instance list-vnics --instance-id $inst \ | convert-json --fields "id,is-primary,compartment-id,public-ip,display-name" --output text > ${scratchfile}.vnic result=`filecheck -sl ${scratchfile}.vnic` if [ "$result" = "" ]; then stat=6 errormsg $stat "($progstr) Instance '$inst': Unexpected error." else grep "Yes" ${scratchfile}.vnic > $scratchfile # Fetching primary vnic result=`filecheck -s $scratchfile` if [ "$result" = "" ]; then stat=6 errormsg $stat "($progstr) Instance '$inst': Unexpected error." else vnic_id=`head -n 1 $scratchfile | cut -d' ' -f1` compartment_id=`head -n 1 $scratchfile | cut -d' ' -f3` public_ip=`head -n 1 $scratchfile | cut -d' ' -f4` vnic_display_name=`head -n 1 $scratchfile | cut -d' ' -f5` if [ "$proc" = "close" ]; then if [ "$public_ip" = "None" ]; then stat=5 errormsg $stat "($progstr) Instance '$inst' already closed." fi else if [ "$public_ip" != "None" ]; then stat=5 errormsg $stat "($progstr) Instance '$inst' already opened." fi fi if [ $stat -eq 0 ]; then private_ip_id=`$oci network private-ip list --vnic-id $vnic_id | convert-json --output json | browse-json id` # Make a list of all reserved public ip $oci network public-ip list -c $compartment_id --scope REGION --all \ | convert-json --fields "id,lifecycle-state,lifetime,ip-address,display-name,private-ip-id" --output text > ${scratchfile}.ip result=`filecheck -sl ${scratchfile}.ip` if [ "$result" = "" ]; then stat=6 errormsg $stat "($progstr) Instance '$inst': Unexpected error." else if [ "$proc" = "close" ]; then grep "$public_ip" ${scratchfile}.ip > $scratchfile # Fetching public-ip from list else grep "$private_ip_id" ${scratchfile}.ip > $scratchfile # Fetching private-ip-id (stored in display-name) from list fi result=`filecheck -s $scratchfile` if [ "$result" = "" ]; then stat=6 errormsg $stat "($progstr) Instance '$inst': Unexpected error." else public_ip_id=`head -n 1 $scratchfile | cut -d' ' -f1` lifecycle_state=`head -n 1 $scratchfile | cut -d' ' -f2` lifetime=`head -n 1 $scratchfile | cut -d' ' -f3` ip_address=`head -n 1 $scratchfile | cut -d' ' -f4` display_name=`head -n 1 $scratchfile | cut -d' ' -f5` check=`head -n 1 $scratchfile | cut -d' ' -f6` if [ "$proc" = "close" ]; then if [ "$lifetime" != "RESERVED" -o "$lifecycle_state" != "ASSIGNED" -o "$private_ip_id" != "$check" ]; then stat=6 errormsg $stat "($progstr) Cannot detach ip '$ip_address': IP may is not an reserved ip." fi else if [ "$lifetime" != "RESERVED" -o "$lifecycle_state" != "AVAILABLE" -o "$check" != "None" ]; then stat=6 errormsg $stat "($progstr) Cannot attach ip '$ip_address': IP may is not an reserved ip." fi fi if [ $stat -eq 0 ]; then if [ "$proc" = "close" ]; then # Detach the public ip and rename display-name to private_ip_id $oci network public-ip update --public-ip-id $public_ip_id --display-name "$private_ip_id" --private-ip-id "" > $scratchfile stat=$? else # Attach the public ip and rename display-name to vnic_display_name $oci network public-ip update --public-ip-id $public_ip_id --display-name "$vnic_display_name" --private-ip-id "$private_ip_id" > $scratchfile stat=$? fi result=`filecheck -sl $scratchfile` if [ $stat -ne 0 -o "$result" = "" ]; then stat=6 errormsg $stat "($progstr) Instance '$inst': Unexpected error." else result=`cat $scratchfile | browse-json data/private-ip-id` if [ "$proc" = "close" ]; then if [ "$result" = "$private_ip_id" ]; then echo "Removing of public ip '$ip_address' from instance '$inst' failed." stat=6 else echo "Removed public ip '$ip_address' from instance '$inst'." fi else if [ "$result" != "$private_ip_id" ]; then echo "Attaching of public ip '$ip_address' to instance '$inst' failed." stat=6 else echo "Attached public ip '$ip_address' to instance '$inst'." fi fi fi fi fi fi fi fi fi return $stat } # Open or close public access function ToggleWhitelist() { proc=${1} inst=${2} stat=0 if [ "$proc" = "close" ]; then printf "\nClosing instance.\n" else printf "\nOpening instance.\n" fi # Code comes later return $stat } # Preset compartment_id="" instance_id="" vnic_id="" # Check parameters: Loop until all parameters are used up while [ $# -gt 0 ]; do pname=${1} case "$pname" in -c | --compartment-id) shift if [ "$1" != "" ]; then compartment_id="$1" result=`echo $compartment_id | grep '^ocid1.tenancy.oc1.'` if [ "$result" = "" ]; then result=`echo $compartment_id | grep '^ocid1.compartment.oc1.'` if [ "$result" = "" ]; then errstr="Invalid compartment ocid '$compartment_id' specified after parameter '$pname'." fi fi shift else errstr="Please specify a compartment ocid after parameter '$pname'." fi ;; -i | --instance-id) shift if [ "$1" != "" ]; then instance_id="$1" result=`echo $instance_id | grep '^ocid1.instance.oc1.'` if [ "$result" = "" ]; then errstr="Invalid instance ocid '$instance_id' specified after parameter '$pname'." fi shift else errstr="Please specify an instance ocid after parameter '$pname'." fi ;; -n | --vnic-id) shift if [ "$1" != "" ]; then vnic_id="$1" result=`echo $vnic_id | grep '^ocid1.vnic.oc1.'` if [ "$result" = "" ]; then errstr="Invalid vnic ocid '$vnic_id' specified after parameter '$pname'." fi shift else errstr="Please specify an vnic ocid after parameter '$pname'." fi ;; -v | --version) shift showversion=1 ;; -h | --help) shift showhelp=1 ;; *) paramck=`echo "$1" | grep '^-'` # Types don't begin with '-' if [ "$param" = "" -a "$paramck" = "" ]; then param=`echo "$1" | tr "[:upper:]" "[:lower:]"` else errstr="Unknown second parameter: '$1'." fi shift esac done # Check if OCI is configured oci=`filecheck -x oci` if [ "$oci" = "" ]; then errstr="OCI CLI 'oci' not installed." else $oci os ns get > $scratchfile 2>&1 stat=$? if [ $stat -eq 0 ]; then namespace=`cat $scratchfile | browse-json data` if [ "$namespace" == "" ]; then errstr="OCI CLI 'oci' not configured correct." fi else errstr="OCI CLI 'oci' not configured." fi fi # Display help or error message DisplayHelp # Main if [ "$param" = "list" -o "$param" = "" -o "$instance_id" = "" ]; then if [ "$instance_id" != "" ]; then GetInstance $instance_id else if [ "$compartment_id" = "" ]; then DisplayInstances else DisplayInstances $compartment_id fi fi else if [ "$instance_id" = "" ]; then exitcode=3 errormsg $exitcode "($progstr) No instance-id specified." else case "$param" in status) GetState "$instance_id" exitcode=$? ;; start) StartInstance "$instance_id" exitcode=$? ;; stop) StopInstance "$instance_id" exitcode=$? ;; open) TogglePublicIP "open" "$instance_id" # ToggleWhitelist "open" "$instance_id" exitcode=$? ;; close) TogglePublicIP "close" "$instance_id" # ToggleWhitelist "close" "$instance_id" exitcode=$? ;; *) exitcode=2 errormsg $exitcode "($progstr) Unknown action '$param'." esac fi fi # Cleanup and exit Cleanup exit $exitcode