diff --git a/rsc_clone.sh b/rsc_clone.sh index 0fdc4a0..a2c5fe0 100755 --- a/rsc_clone.sh +++ b/rsc_clone.sh @@ -23,13 +23,23 @@ # DB_CREATE_FILE_DEST=/u01/app/oracle/oradata/NEWNAME/ # AUDIT_FILE_DEST='/u01/app/oracle/admin/NEWNAME/adump' -usage() { echo "Usage: $0 -n -o -h [-s sourcehost] [-t \"YYYY-MM-DD HH:MM:SS\"] [-d] " 1>&2; exit 1; } +usage() { log_error "Usage: $0 -n -o -h [-s sourcehost] [-t \"YYYY-MM-DD HH:MM:SS\"] [-d] "; exit 1; } MYDIR="$(dirname "$(realpath "$0")")" +# Support long option --verbose by translating to -v before getopts +for arg in "$@"; do + case "$arg" in + --verbose) + set -- "${@/--verbose/-v}" + break + ;; + esac +done + source $MYDIR/rsc_ops.sh -while getopts "n:o:t:h:s:d" o; do +while getopts "n:o:t:h:s:d:v" o; do case "${o}" in n) newName=${OPTARG} @@ -49,6 +59,9 @@ while getopts "n:o:t:h:s:d" o; do d) dryrun=true ;; + v) + VERBOSE=1 + ;; *) usage ;; @@ -64,7 +77,7 @@ fi # Check if optionsFile exists if [[ ! -f "$optionsFile" ]]; then - echo "ERROR: Options file '$optionsFile' does not exist." + log_error "Options file '$optionsFile' does not exist." exit_with_error fi @@ -139,7 +152,7 @@ get_latest_pit() { # Get latest endTime latest_endtime=$(cat /tmp/rbkresponse.$$ | jq -r '.data.oracleRecoverableRanges.data[] | .endTime' | sort -r | head -n 1) - echo "Latest PIT (ISO8601): $latest_endtime" + log_info "Latest PIT (ISO8601): $latest_endtime" # Convert to unixtime in milliseconds latest_unixtime_ms=$(date -d "$latest_endtime" +%s 2>/dev/null) @@ -148,11 +161,11 @@ get_latest_pit() { latest_unixtime_ms=$(gdate -d "$latest_endtime" +%s 2>/dev/null) fi if [[ -z "$latest_unixtime_ms" ]]; then - echo "ERROR: Unable to convert $latest_endtime to unixtime" + log_error "Unable to convert $latest_endtime to unixtime" exit 5 fi latest_unixtime_ms=$((latest_unixtime_ms * 1000)) - echo "Latest PIT unixtime (ms): $latest_unixtime_ms" + log_info "Latest PIT unixtime (ms): $latest_unixtime_ms" export latest_unixtime_ms } @@ -188,7 +201,7 @@ get_oracle_host_id() { host_ids=$(cat /tmp/rbkresponse.$$ | jq -r '.data.oracleTopLevelDescendants.nodes[] | .id') host_count=$(echo "$host_ids" | grep -c .) if [[ $host_count -ne 1 ]]; then - echo "ERROR: Multiple hosts found for '$1':" + log_error "Multiple hosts found for '$1':" cat /tmp/rbkresponse.$$ | jq -r '.data.oracleTopLevelDescendants.nodes[] | "\(.name) \(.id)"' exit_with_error fi @@ -199,7 +212,7 @@ get_oracle_host_id() { # If $1 looks like a dbid (contains hyphens), use it directly and skip DB lookup if [[ "$1" == *-* ]]; then dbid="$1" - echo "INFO: Using provided dbid: $dbid" + log_info "Using provided dbid: $dbid" gql_lookupCdmId='query OracleDatabase($fid: UUID!) { oracleDatabase(fid: $fid) { @@ -217,10 +230,10 @@ if [[ "$1" == *-* ]]; then cdmId=$(cat /tmp/rbkresponse.$$ | jq -r '.data.oracleDatabase.cluster.id') if [[ -z "$cdmId" ]]; then - echo "ERROR: Could not find CDM ID for dbid '$dbid'" + log_error "Could not find CDM ID for dbid '$dbid'" exit 1 fi - echo "CDM ID is $cdmId" + log_info "CDM ID is $cdmId" else gql_DBListQuery='query OracleDatabases($filter: [Filter!]) { @@ -268,7 +281,7 @@ oracleDatabases(filter: $filter) { dbid_count=$(echo "$dbid" | grep -c .) if [[ "$dbid_count" -ne 1 || -z "$dbid" ]]; then - echo "ERROR: Expected exactly one database running on node '$node_name', found $dbid_count:" + log_error "Expected exactly one database running on node '$node_name', found $dbid_count:" cat /tmp/rbkresponse.$$ | jq -r '.data.oracleDatabases.nodes[] | "\(.dbUniqueName) \(.logicalPath[0].name) \(.id)"' cleanup exit 4 @@ -280,7 +293,7 @@ oracleDatabases(filter: $filter) { dbid_count=$(echo "$dbid" | grep -c .) if [[ "$dbid_count" -ne 1 || -z "$dbid" ]]; then - echo "ERROR: Expected exactly one database with name '$1', found $dbid_count:" + log_error "Expected exactly one database with name '$1', found $dbid_count:" cat /tmp/rbkresponse.$$ | jq -r '.data.oracleDatabases.nodes[] | "\(.dbUniqueName) \(.logicalPath[0].name) \(.id)"' cleanup exit 4 @@ -301,7 +314,7 @@ if [[ -n "${datestring:-}" ]]; then unixtime_ms=$((unixtime * 1000)) echo INFO: Requested time is $datestring which is $utctime in UTC, unixtime is $unixtime else - echo INFO: No time specified, using latest PIT + log_info "No time specified, using latest PIT" get_latest_pit unixtime_ms=$latest_unixtime_ms fi @@ -310,11 +323,11 @@ fi get_oracle_host_id "$targetHost" if [[ -z "$targetHostId" ]]; then - echo "ERROR: Could not resolve target host ID for '$targetHost'" + log_error "Could not resolve target host ID for '$targetHost'" exit_with_error fi -echo Target Host ID is $targetHostId +log_info "Target Host ID is $targetHostId" cloningOptions=$(template_to_json $optionsFile) @@ -374,7 +387,7 @@ cat /tmp/rbkresponse.$$ | jq # Save the id from the response job_id=$(cat /tmp/rbkresponse.$$ | jq -r '.data.exportOracleDatabase.id') -echo "DEBUG: Job id is $job_id" +log_info "Job id is $job_id" gqlCheckStatus='query OracleDatabaseAsyncRequestDetails($input: GetOracleAsyncRequestStatusInput!) { oracleDatabaseAsyncRequestDetails(input: $input) { @@ -405,25 +418,17 @@ while true; do status=$(cat /tmp/rbkresponse.$$ | jq -r '.data.oracleDatabaseAsyncRequestDetails.status') progress=$(cat /tmp/rbkresponse.$$ | jq -r '.data.oracleDatabaseAsyncRequestDetails.progress') - echo "Job status: $status $progress percent" + log_info "Job status: $status $progress percent" if [[ "$status" == "FAILED" ]]; then - echo "Database clone FAILED" + log_error "Database clone FAILED" cat /tmp/rbkresponse.$$ | jq cleanup exit 2 elif [[ "$status" == "CANCELLED" ]]; then - echo "Database clone CANCELLED" + log_warn "Database clone CANCELLED" exit 3 elif [[ "$status" == "SUCCEEDED" ]]; then - echo "Database clone SUCCEEDED" - cat /tmp/rbkresponse.$$ | jq - cleanup - exit 0 - fi - sleep 15 - -done - echo "Database clone SUCCEEDED" + log_info "Database clone SUCCEEDED" cat /tmp/rbkresponse.$$ | jq cleanup exit 0 @@ -432,3 +437,4 @@ done done + diff --git a/rsc_host_refresh.sh b/rsc_host_refresh.sh index 4ab8553..e00899b 100755 --- a/rsc_host_refresh.sh +++ b/rsc_host_refresh.sh @@ -10,21 +10,35 @@ # Options: # : Host to perform refresh job -usage() { echo "Usage: $0 " 1>&2; exit 1; } +usage() { log_error "Usage: $0 "; exit 1; } if [[ -z "$1" ]]; then - usage + usage fi MYDIR="$(dirname "$(realpath "$0")")" +# Support -v/--verbose and strip it from positional args +NEWARGS=() +for arg in "$@"; do + case "$arg" in + -v|--verbose) + VERBOSE=1 + ;; + *) + NEWARGS+=("$arg") + ;; + esac +done +set -- "${NEWARGS[@]}" + source $MYDIR/rsc_ops.sh RBK_HOST=$1 rsc_get_host_id -echo "Host ID: $targetHostId" +log_info "Host ID: $targetHostId" gql_refreshHost='mutation RefreshHost($input: RefreshHostInput!) { refreshHost(input: $input) { @@ -48,9 +62,9 @@ variables="{ gqlQuery="$(echo $gql_refreshHost)" gqlVars="$(echo $variables)" -echo "Refreshing host $RBK_HOST (ID: $targetHostId)" +log_info "Refreshing host $RBK_HOST (ID: $targetHostId)" rsc_gql_query -echo "Response:" +log_info "Response:" cat /tmp/rbkresponse.$$ | jq \ No newline at end of file diff --git a/rsc_list_oracle_slas.sh b/rsc_list_oracle_slas.sh index b4e4814..2e4e330 100755 --- a/rsc_list_oracle_slas.sh +++ b/rsc_list_oracle_slas.sh @@ -7,9 +7,23 @@ # # usage: rsc_list_slas.sh [filter] -usage() { echo "Usage: $0 [filter]" 1>&2; exit 1; } +usage() { log_error "Usage: $0 [filter]"; exit 1; } MYDIR="$(dirname "$(realpath "$0")")" +# Support -v/--verbose and strip it from positional args so it doesn't become $1 +NEWARGS=() +for arg in "$@"; do + case "$arg" in + -v|--verbose) + VERBOSE=1 + ;; + *) + NEWARGS+=("$arg") + ;; + esac +done +set -- "${NEWARGS[@]}" + # source $MYDIR/rbk_api.conf source $MYDIR/rsc_ops.sh diff --git a/rsc_log_backup.sh b/rsc_log_backup.sh index c397813..77e2b8c 100755 --- a/rsc_log_backup.sh +++ b/rsc_log_backup.sh @@ -7,7 +7,7 @@ # # usage: rsc_log_backup.sh [filter] -usage() { echo "Usage: $0 [filter]" 1>&2; exit 1; } +usage() { log_error "Usage: $0 [filter]"; exit 1; } if [[ -z "$1" ]]; then usage @@ -15,6 +15,19 @@ fi MYDIR="$(dirname "$(realpath "$0")")" +# Support -v/--verbose and strip it from positional args +NEWARGS=() +for arg in "$@"; do + case "$arg" in + -v|--verbose) + VERBOSE=1 + ;; + *) + NEWARGS+=("$arg") + ;; + esac +done +set -- "${NEWARGS[@]}" # source $MYDIR/rbk_api.conf source $MYDIR/rsc_ops.sh @@ -57,13 +70,13 @@ cdmId=$(cat /tmp/rbkresponse.$$ | jq -r '.data.oracleDatabases.nodes[] | .cluste # Check for multiple dbids dbid_count=$(echo "$dbid" | wc -l) if [[ "$dbid_count" -ne 1 || -z "$dbid" ]]; then - echo "ERROR: Expected exactly one database match! found:" + log_error "Expected exactly one database match! found:" cat /tmp/rbkresponse.$$ | jq -r '.data.oracleDatabases.nodes[] | .dbUniqueName' cleanup exit 4 fi -echo "DEBUG: DB ID is $dbid" +log_info "DB ID is $dbid" variables="{ \"input\": { \"id\": \"$dbid\" @@ -81,8 +94,8 @@ gqlVars="$(echo $variables)" rsc_gql_query # Save the id from the response +log_info "Log backup job id: $(cat /tmp/rbkresponse.$$ | jq -r '.data.takeOnDemandOracleLogSnapshot.id')" log_backup_id=$(cat /tmp/rbkresponse.$$ | jq -r '.data.takeOnDemandOracleLogSnapshot.id') -echo "DEBUG: Job id is $log_backup_id" gqlCheckStatus='query OracleDatabaseAsyncRequestDetails($input: GetOracleAsyncRequestStatusInput!) { oracleDatabaseAsyncRequestDetails(input: $input) { @@ -113,17 +126,17 @@ while true; do status=$(cat /tmp/rbkresponse.$$ | jq -r '.data.oracleDatabaseAsyncRequestDetails.status') progress=$(cat /tmp/rbkresponse.$$ | jq -r '.data.oracleDatabaseAsyncRequestDetails.progress') - echo "Job status: $status $progress percent" + log_info "Job status: $status $progress percent" if [[ "$status" == "FAILED" ]]; then - echo "Log backup FAILED" + log_error "Log backup FAILED" cat /tmp/rbkresponse.$$ | jq cleanup exit 2 elif [[ "$status" == "CANCELLED" ]]; then - echo "Log backup CANCELLED" + log_warn "Log backup CANCELLED" exit 3 elif [[ "$status" == "SUCCEEDED" ]]; then - echo "Log backup SUCCEEDED" + log_info "Log backup SUCCEEDED" cat /tmp/rbkresponse.$$ | jq cleanup exit 0 diff --git a/rsc_ops.sh b/rsc_ops.sh index 0c66814..87f88bd 100755 --- a/rsc_ops.sh +++ b/rsc_ops.sh @@ -9,21 +9,66 @@ MYDIR="$(dirname "$(realpath "$0")")" -# Load RSC configuration from rsc.json +# Logging helpers +VERBOSE=${VERBOSE:-0} +log_info() { + if [ "${VERBOSE}" -eq 1 ]; then + echo "INFO: $*" + fi +} +log_warn() { + echo "WARN: $*" >&2 +} +log_error() { + echo "ERROR: $*" >&2 +} + +# Ensure required commands are available +require_cmds() { + local miss=0 + for cmd in jq curl; do + if ! command -v "$cmd" >/dev/null 2>&1; then + log_error "Required command '$cmd' not found in PATH" + miss=1 + fi + done + if [ "$miss" -ne 0 ]; then + exit 1 + fi +} +require_cmds + +# Load and validate RSC configuration from rsc.json if [ -f "$MYDIR/rsc.json" ]; then - RSC_ID=$(jq -r '.client_id' "$MYDIR/rsc.json") - RSC_SECRET=$(jq -r '.client_secret' "$MYDIR/rsc.json") - RSC_HOST=$(jq -r '.access_token_uri' "$MYDIR/rsc.json" | sed 's|https://||' | sed 's|/api/client_token||') + RSC_ID=$(jq -r '.client_id // empty' "$MYDIR/rsc.json") + RSC_SECRET=$(jq -r '.client_secret // empty' "$MYDIR/rsc.json") + ACCESS_TOKEN_URI=$(jq -r '.access_token_uri // empty' "$MYDIR/rsc.json") + + if [ -z "${RSC_ID}" ] || [ -z "${RSC_SECRET}" ] || [ -z "${ACCESS_TOKEN_URI}" ]; then + log_error "rsc.json is missing required fields: client_id, client_secret or access_token_uri" + log_error "Please populate $MYDIR/rsc.json (see rsc.json.example)" + exit 1 + fi + + # Derive host from access token URI and normalize + RSC_HOST=$(echo "$ACCESS_TOKEN_URI" | sed 's|https://||' | sed 's|/api/client_token||') + + # Restrict config file permissions + if [ -f "$MYDIR/rsc.json" ]; then + chmod 600 "$MYDIR/rsc.json" 2>/dev/null || true + fi + + log_info "Loaded RSC configuration for host: $RSC_HOST" else - echo "ERROR: rsc.json configuration file not found" + log_error "rsc.json configuration file not found at $MYDIR/rsc.json" exit 1 fi -# Set DATE command based on OS -if [[ "$OSTYPE" == "darwin"* ]]; then - DATE=gdate +# Set DATE command based on available binaries (prefer gdate if installed) +if command -v gdate >/dev/null 2>&1; then + DATE=gdate else - DATE=date + DATE=date fi # Utility functions @@ -42,8 +87,11 @@ cleanup () { check_http_error () { # All good responses start with a 2 if [ ${http_response:0:1} != "2" ]; then - echo FATAL: HTTP error from API call: $http_response. The server responded with: - cat /tmp/rbkresponse.$$ ; echo ; exit_with_error + log_error "HTTP error from API call: $http_response" + # Show a short excerpt of the response body for debugging + head -c 4096 /tmp/rbkresponse.$$ 2>/dev/null || true + echo + exit_with_error fi } @@ -85,8 +133,11 @@ get_rsc_token () { RSC_AUTH_TOKEN=$(cat /tmp/rbkresponse.$$ | jq -r '.access_token') SECONDS=$(cat /tmp/rbkresponse.$$ | jq -r '.expires_in') EXPIRATION=$($DATE +%s -d "+${SECONDS} seconds") - #cat /tmp/rbkresponse.$$ | jq - echo "$EXPIRATION $RSC_AUTH_TOKEN" > ~/.rbkRscsession.$id_string + # Save session securely + umask 077 + printf '%s %s\n' "$EXPIRATION" "$RSC_AUTH_TOKEN" > ~/.rbkRscsession.$id_string + chmod 600 ~/.rbkRscsession.$id_string 2>/dev/null || true + log_info "Cached RSC session to ~/.rbkRscsession.$id_string" } @@ -136,11 +187,11 @@ EOF #cat /tmp/payload.$$ | jq -r error=$(cat /tmp/rbkresponse.$$ | jq -r '.errors // empty') if [ "$error" ]; then - echo "ERROR: The last GraphQL API call returned an error" + log_error "The last GraphQL API call returned an error" echo - echo "PAYLOAD:" + log_error "PAYLOAD:" cat /tmp/payload.$$ | jq -r - echo "RESPONSE:" + log_error "RESPONSE:" cat /tmp/rbkresponse.$$ | jq -r '.errors' exit_with_error fi @@ -168,11 +219,11 @@ rsc_get_host_id() { # Get all matching host IDs (portable, no mapfile) host_ids=$(cat /tmp/rbkresponse.$$ | jq -r '.data.physicalHosts.nodes[] | .id') host_count=$(echo "$host_ids" | grep -c .) - if [[ $host_count -ne 1 ]]; then - echo "ERROR: Multiple hosts found for '$1':" - cat /tmp/rbkresponse.$$ | jq -r '.data.physicalHosts.nodes[] | "\(.name) \(.id)"' - exit_with_error - fi + if [[ $host_count -ne 1 ]]; then + log_error "Multiple hosts found for '$1':" + cat /tmp/rbkresponse.$$ | jq -r '.data.physicalHosts.nodes[] | "\(.name) \(.id)"' + exit_with_error + fi # Set the first match (or empty if none) targetHostId=$(echo "$host_ids" | head -n 1) @@ -189,9 +240,9 @@ rsc_find_database () { #echo cluster UUID not, set, getting it get_cluster_uuid #echo Cluster UUID is $cluster_uuid - else - echo Cluster UUID was already $cluster_uuid - fi + else + log_info "Cluster UUID was already $cluster_uuid" + fi variables="{ \"filter\":[{\"field\":\"REGEX\",\"texts\":[\"$RBK_SID\"]},{\"field\":\"IS_GHOST\",\"texts\":[\"false\"]},{\"field\":\"IS_ACTIVE\",\"texts\":[\"true\"]},{\"field\":\"CLUSTER_ID\",\"texts\":[\"$cluster_uuid\"]}], @@ -356,8 +407,8 @@ if [ $num -eq 1 ]; then read name rsc_db_id < <(echo $(cat /tmp/rbkresponse.$$ | jq -r '.data.globalSearchResults.edges[] | select (.node.objectType=="ORACLE_DATA_GUARD_GROUP" and .node.isRelic==false)| .node.name, .node.id')) database_type="Data Guard" elif [ $num -gt 1 ]; then - echo "ERROR: There were $num entries returned for Data Guard databases with name $RBK_SID" - exit_with_error + log_error "There were $num entries returned for Data Guard databases with name $RBK_SID" + exit_with_error fi if [ -z "$rsc_db_id" ]; then @@ -370,8 +421,8 @@ if [ -z "$rsc_db_id" ]; then database_type="Standalone" #echo Good, There is just one Standalone DB with name $name and RSC ID $rsc_db_id else - echo "ERROR: There were $num entries returned from JQ for DB with name $RBK_SID on host $RBK_HOST" - exit_with_error + log_error "There were $num entries returned from JQ for DB with name $RBK_SID on host $RBK_HOST" + exit_with_error fi else num=$(cat /tmp/rbkresponse.$$ | jq -r --arg HOST "$RBK_HOST" '[.data.globalSearchResults.edges[] | select (.node.logicalPath[0].name==$HOST and .node.objectType=="OracleDatabase" and .node.isRelic==false)] | length') @@ -380,8 +431,8 @@ if [ -z "$rsc_db_id" ]; then database_type="Standalone" #echo Good, There is just one Standalone DB with name $name on ${RBK_HOST} and RSC ID $rsc_db_id else - echo "ERROR: There were $num entries returned from for DB with name $RBK_SID on host $RBK_HOST" - exit_with_error + log_error "There were $num entries returned from for DB with name $RBK_SID on host $RBK_HOST" + exit_with_error fi fi fi