Remove oracle_funcs.sh for RSC
This commit is contained in:
210
README.md
Normal file
210
README.md
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
# RSC Oracle Database Clone Script
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The `rsc_clone.sh` script performs Oracle database clone operations using the Rubrik Security Cloud (RSC) GraphQL API. It allows you to create a clone of an existing Oracle database to a target host with customizable recovery points and advanced Oracle configuration options.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Rubrik Security Cloud (RSC) access with valid credentials
|
||||||
|
- Source Oracle database protected by Rubrik
|
||||||
|
- Target Oracle host configured and registered in RSC
|
||||||
|
- Required configuration files:
|
||||||
|
- `rbk_api.conf` - RSC API credentials
|
||||||
|
- Oracle cloning options file
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### RSC API Configuration (`rbk_api.conf`)
|
||||||
|
|
||||||
|
Create a configuration file with your RSC credentials:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Rubrik Security Cloud (RSC) Configuration
|
||||||
|
RSC_HOST=your-organization.my.rubrik.com
|
||||||
|
RSC_ID="client|your-client-id-here"
|
||||||
|
RSC_SECRET=your-secret-key-here
|
||||||
|
```
|
||||||
|
|
||||||
|
### Oracle Cloning Options File
|
||||||
|
|
||||||
|
Create a text file with Oracle-specific cloning parameters. Each line should contain `KEY=VALUE` pairs for Oracle initialization parameters:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Example: SHED_to_SCLONE.txt
|
||||||
|
CONTROL_FILES='/u01/app/oracle/oradata/SCLONE/control01.ctl, /u01/app/oracle/fast_recovery_area/SCLONE/control02.ctl'
|
||||||
|
DB_CREATE_FILE_DEST=/u01/app/oracle/oradata/SCLONE/
|
||||||
|
AUDIT_FILE_DEST='/u01/app/oracle/admin/SCLONE/adump'
|
||||||
|
DB_FILE_NAME_CONVERT='SHED','SCLONE'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./rsc_clone.sh -n <newname> -o <optionsfile> -h <targethost> [-s sourcehost] [-t "YYYY-MM-DD HH:MM:SS"] [-d] <srcdb>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Required Parameters
|
||||||
|
|
||||||
|
- `-n <newname>` - Database name/SID for the cloned database
|
||||||
|
- `-o <optionsfile>` - Path to Oracle cloning options file
|
||||||
|
- `-h <targethost>` - Target host where the database will be cloned
|
||||||
|
- `<srcdb>` - Source database name to clone
|
||||||
|
|
||||||
|
### Optional Parameters
|
||||||
|
|
||||||
|
- `-s <sourcehost>` - Source host name (use when there are multiple databases with the same name on different hosts)
|
||||||
|
- `-t "YYYY-MM-DD HH:MM:SS"` - Recovery point timestamp (defaults to latest point-in-time)
|
||||||
|
- `-d` - Dry-run mode (shows mutation variables without executing)
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Basic Clone Operation
|
||||||
|
|
||||||
|
Clone database `SHED` to `SCLONE` on target host `pve-ora19c-3`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./rsc_clone.sh -n SCLONE -o SHED_to_SCLONE.txt -h pve-ora19c-3 SHED
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dry-Run Mode
|
||||||
|
|
||||||
|
Preview the clone operation without executing it:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./rsc_clone.sh -n SCLONE -o SHED_to_SCLONE.txt -h pve-ora19c-3 -d SHED
|
||||||
|
```
|
||||||
|
|
||||||
|
**Output:**
|
||||||
|
```
|
||||||
|
DEBUG: DB ID is b4194205-b7d6-5f0b-8360-e6f349b9fd82
|
||||||
|
INFO: No time specified, using latest PIT
|
||||||
|
Latest PIT (ISO8601): 2025-10-15T08:32:49.000Z
|
||||||
|
Latest PIT unixtime (ms): 1760517169000
|
||||||
|
Target Host ID is 26008fd4-9a96-582e-86b7-4da31584a7ad
|
||||||
|
|
||||||
|
=== DRY-RUN MODE ===
|
||||||
|
Would execute the following GraphQL mutation:
|
||||||
|
|
||||||
|
QUERY:
|
||||||
|
mutation OracleDatabaseExportMutation($input: ExportOracleDatabaseInput!) {
|
||||||
|
exportOracleDatabase(input: $input) {
|
||||||
|
id
|
||||||
|
links {
|
||||||
|
href
|
||||||
|
rel
|
||||||
|
__typename
|
||||||
|
}
|
||||||
|
__typename
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VARIABLES:
|
||||||
|
{
|
||||||
|
"input": {
|
||||||
|
"request": {
|
||||||
|
"id": "b4194205-b7d6-5f0b-8360-e6f349b9fd82",
|
||||||
|
"config": {
|
||||||
|
"targetOracleHostOrRacId": "26008fd4-9a96-582e-86b7-4da31584a7ad",
|
||||||
|
"shouldRestoreFilesOnly": false,
|
||||||
|
"recoveryPoint": {
|
||||||
|
"timestampMs": 1760517169000
|
||||||
|
},
|
||||||
|
"cloneDbName": "SCLONE",
|
||||||
|
"shouldAllowRenameToSource": true,
|
||||||
|
"shouldSkipDropDbInUndo": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"advancedRecoveryConfigMap": [
|
||||||
|
{
|
||||||
|
"key": "CONTROL_FILES",
|
||||||
|
"value": "'/u01/app/oracle/oradata/SCLONE/control01.ctl, /u01/app/oracle/fast_recovery_area/SCLONE/control02.ctl'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "DB_CREATE_FILE_DEST",
|
||||||
|
"value": "/u01/app/oracle/oradata/SCLONE/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "AUDIT_FILE_DEST",
|
||||||
|
"value": "'/u01/app/oracle/admin/SCLONE/adump'"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "DB_FILE_NAME_CONVERT",
|
||||||
|
"value": "'SHED','SCLONE'"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
=== END DRY-RUN ===
|
||||||
|
```
|
||||||
|
|
||||||
|
### Point-in-Time Recovery
|
||||||
|
|
||||||
|
Clone database to a specific point in time:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./rsc_clone.sh -n SCLONE -o SHED_to_SCLONE.txt -h pve-ora19c-3 -t "2025-10-15 08:00:00" SHED
|
||||||
|
```
|
||||||
|
|
||||||
|
### Specify Source Host
|
||||||
|
|
||||||
|
When multiple databases with the same name exist on different hosts:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./rsc_clone.sh -n SCLONE -o SHED_to_SCLONE.txt -h pve-ora19c-3 -s pve-ora19c-1 SHED
|
||||||
|
```
|
||||||
|
|
||||||
|
## Script Workflow
|
||||||
|
|
||||||
|
1. **Parameter Validation** - Validates required parameters and options file
|
||||||
|
2. **Database Discovery** - Locates source database in RSC
|
||||||
|
3. **Host Resolution** - Resolves target host ID in RSC
|
||||||
|
4. **Recovery Point** - Determines recovery timestamp (latest or specified)
|
||||||
|
5. **Options Processing** - Converts options file to JSON format
|
||||||
|
6. **Clone Execution** - Submits GraphQL mutation to RSC API
|
||||||
|
7. **Job Monitoring** - Tracks clone job status until completion
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
The script includes comprehensive error handling for:
|
||||||
|
|
||||||
|
- Missing required parameters
|
||||||
|
- Database not found or ambiguous matches
|
||||||
|
- Target host not found
|
||||||
|
- Invalid recovery timestamps
|
||||||
|
- RSC API errors
|
||||||
|
- Job failures
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- `rsc_ops.sh` - RSC API operations and utility functions
|
||||||
|
- `rbk_api.conf` - RSC credentials configuration
|
||||||
|
- `jq` - JSON processing
|
||||||
|
- `curl` - HTTP requests
|
||||||
|
- `date`/`gdate` - Date/time operations
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
1. **Database not found** - Verify database name and use `-s` if multiple databases exist
|
||||||
|
2. **Host not found** - Check target host name is registered in RSC
|
||||||
|
3. **Permission errors** - Ensure RSC service account has sufficient privileges
|
||||||
|
4. **Invalid timestamp** - Use format "YYYY-MM-DD HH:MM:SS" for `-t` parameter
|
||||||
|
|
||||||
|
### Debug Information
|
||||||
|
|
||||||
|
The script provides debug output including:
|
||||||
|
- Source database ID
|
||||||
|
- Target host ID
|
||||||
|
- Recovery point details
|
||||||
|
- Job status updates
|
||||||
|
|
||||||
|
Use dry-run mode (`-d`) to validate configuration before executing actual clones.
|
||||||
|
|
||||||
|
## Version History
|
||||||
|
|
||||||
|
- v0.1 - Initial release with basic clone functionality
|
||||||
|
- Added dry-run mode for validation
|
||||||
|
- Improved error handling and database selection logic
|
||||||
@@ -1,9 +1,6 @@
|
|||||||
# Example Rubrik API Configuration File
|
# Example Rubrik API Configuration File
|
||||||
# Copy this file to rbk_api.conf and fill in your actual values
|
# Copy this file to rbk_api.conf and fill in your actual values
|
||||||
|
|
||||||
# IP Address (or DNS name) of Rubrik CDM
|
|
||||||
RUBRIK_IP=your.rubrik.cluster.ip
|
|
||||||
|
|
||||||
# Rubrik Security Cloud (RSC) Configuration
|
# Rubrik Security Cloud (RSC) Configuration
|
||||||
RSC_HOST=your-organization.my.rubrik.com
|
RSC_HOST=your-organization.my.rubrik.com
|
||||||
RSC_ID="client|your-client-id-here"
|
RSC_ID="client|your-client-id-here"
|
||||||
|
|||||||
42
rsc_clone.sh
42
rsc_clone.sh
@@ -5,7 +5,7 @@
|
|||||||
#
|
#
|
||||||
# Perfoms a database clone operation
|
# Perfoms a database clone operation
|
||||||
#
|
#
|
||||||
# usage: rsc_clone.sh -n <newname> -o <optionsfile> -h <targethost> [-s sourcehost] [-t "YYYY-MM-DD HH:MM:SS"] <srcdb>
|
# usage: rsc_clone.sh -n <newname> -o <optionsfile> -h <targethost> [-s sourcehost] [-t "YYYY-MM-DD HH:MM:SS"] [-d] <srcdb>
|
||||||
#
|
#
|
||||||
# Options:
|
# Options:
|
||||||
# -n <newname> : db_name / SID of the new cloned database
|
# -n <newname> : db_name / SID of the new cloned database
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
# -h <targethost> : Target host where the cloned database will be created
|
# -h <targethost> : Target host where the cloned database will be created
|
||||||
# -s <sourcehost> : Source host where the original database is located (optional, use when there is ambiguity)
|
# -s <sourcehost> : Source host where the original database is located (optional, use when there is ambiguity)
|
||||||
# -t "YYYY-MM-DD HH:MM:SS" : Optional timestamp for the recovery point, defaults to latest PIT
|
# -t "YYYY-MM-DD HH:MM:SS" : Optional timestamp for the recovery point, defaults to latest PIT
|
||||||
|
# -d : Dry-run mode - show mutation variables without executing the clone
|
||||||
# <srcdb> : Source database name or RSC dbid (if known, can be used directly)
|
# <srcdb> : Source database name or RSC dbid (if known, can be used directly)
|
||||||
#
|
#
|
||||||
# Example options file content:
|
# Example options file content:
|
||||||
@@ -22,14 +23,13 @@
|
|||||||
# DB_CREATE_FILE_DEST=/u01/app/oracle/oradata/NEWNAME/
|
# DB_CREATE_FILE_DEST=/u01/app/oracle/oradata/NEWNAME/
|
||||||
# AUDIT_FILE_DEST='/u01/app/oracle/admin/NEWNAME/adump'
|
# AUDIT_FILE_DEST='/u01/app/oracle/admin/NEWNAME/adump'
|
||||||
|
|
||||||
usage() { echo "Usage: $0 -n <newname> -o <optionsfile> -h <targethost> [-s sourcehost] [-t "YYYY-MM-DD HH:MM:SS"] <srcdb>" 1>&2; exit 1; }
|
usage() { echo "Usage: $0 -n <newname> -o <optionsfile> -h <targethost> [-s sourcehost] [-t \"YYYY-MM-DD HH:MM:SS\"] [-d] <srcdb>" 1>&2; exit 1; }
|
||||||
|
|
||||||
MYDIR="$(dirname "$(realpath "$0")")"
|
MYDIR="$(dirname "$(realpath "$0")")"
|
||||||
|
|
||||||
source $MYDIR/oracle_funcs.sh
|
|
||||||
source $MYDIR/rsc_ops.sh
|
source $MYDIR/rsc_ops.sh
|
||||||
|
|
||||||
while getopts "n:o:t:h:s:" o; do
|
while getopts "n:o:t:h:s:d" o; do
|
||||||
case "${o}" in
|
case "${o}" in
|
||||||
n)
|
n)
|
||||||
newName=${OPTARG}
|
newName=${OPTARG}
|
||||||
@@ -46,6 +46,9 @@ while getopts "n:o:t:h:s:" o; do
|
|||||||
s)
|
s)
|
||||||
node_name=${OPTARG}
|
node_name=${OPTARG}
|
||||||
;;
|
;;
|
||||||
|
d)
|
||||||
|
dryrun=true
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
usage
|
usage
|
||||||
;;
|
;;
|
||||||
@@ -258,6 +261,8 @@ oracleDatabases(filter: $filter) {
|
|||||||
gqlVars="$(echo $variables)"
|
gqlVars="$(echo $variables)"
|
||||||
rsc_gql_query
|
rsc_gql_query
|
||||||
|
|
||||||
|
if [[ -n "$node_name" ]]; then
|
||||||
|
# Filter by source host if specified
|
||||||
dbid=$(cat /tmp/rbkresponse.$$ | jq -r --arg NODE "$node_name" '.data.oracleDatabases.nodes[] | select(.logicalPath[]?.name | test("^" + $NODE + "(\\.|$)")) | .id')
|
dbid=$(cat /tmp/rbkresponse.$$ | jq -r --arg NODE "$node_name" '.data.oracleDatabases.nodes[] | select(.logicalPath[]?.name | test("^" + $NODE + "(\\.|$)")) | .id')
|
||||||
cdmId=$(cat /tmp/rbkresponse.$$ | jq -r --arg NODE "$node_name" '.data.oracleDatabases.nodes[] | select(.logicalPath[]?.name | test("^" + $NODE + "(\\.|$)")) | .cluster.id')
|
cdmId=$(cat /tmp/rbkresponse.$$ | jq -r --arg NODE "$node_name" '.data.oracleDatabases.nodes[] | select(.logicalPath[]?.name | test("^" + $NODE + "(\\.|$)")) | .cluster.id')
|
||||||
|
|
||||||
@@ -268,6 +273,19 @@ oracleDatabases(filter: $filter) {
|
|||||||
cleanup
|
cleanup
|
||||||
exit 4
|
exit 4
|
||||||
fi
|
fi
|
||||||
|
else
|
||||||
|
# No source host specified, get all matching databases
|
||||||
|
dbid=$(cat /tmp/rbkresponse.$$ | jq -r '.data.oracleDatabases.nodes[].id')
|
||||||
|
cdmId=$(cat /tmp/rbkresponse.$$ | jq -r '.data.oracleDatabases.nodes[].cluster.id')
|
||||||
|
|
||||||
|
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:"
|
||||||
|
cat /tmp/rbkresponse.$$ | jq -r '.data.oracleDatabases.nodes[] | "\(.dbUniqueName) \(.logicalPath[0].name) \(.id)"'
|
||||||
|
cleanup
|
||||||
|
exit 4
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
echo "DEBUG: DB ID is $dbid"
|
echo "DEBUG: DB ID is $dbid"
|
||||||
fi
|
fi
|
||||||
@@ -334,6 +352,22 @@ gqlClone='mutation OracleDatabaseExportMutation($input: ExportOracleDatabaseInpu
|
|||||||
|
|
||||||
gqlQuery="$(echo $gqlClone)"
|
gqlQuery="$(echo $gqlClone)"
|
||||||
gqlVars="$(echo $variables)"
|
gqlVars="$(echo $variables)"
|
||||||
|
|
||||||
|
if [ "$dryrun" = true ]; then
|
||||||
|
echo "=== DRY-RUN MODE ==="
|
||||||
|
echo "Would execute the following GraphQL mutation:"
|
||||||
|
echo
|
||||||
|
echo "QUERY:"
|
||||||
|
echo "$gqlQuery"
|
||||||
|
echo
|
||||||
|
echo "VARIABLES:"
|
||||||
|
echo "$gqlVars" | jq .
|
||||||
|
echo
|
||||||
|
echo "=== END DRY-RUN ==="
|
||||||
|
cleanup
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
rsc_gql_query
|
rsc_gql_query
|
||||||
cat /tmp/rbkresponse.$$ | jq
|
cat /tmp/rbkresponse.$$ | jq
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ fi
|
|||||||
|
|
||||||
MYDIR="$(dirname "$(realpath "$0")")"
|
MYDIR="$(dirname "$(realpath "$0")")"
|
||||||
|
|
||||||
source $MYDIR/oracle_funcs.sh
|
|
||||||
source $MYDIR/rsc_ops.sh
|
source $MYDIR/rsc_ops.sh
|
||||||
|
|
||||||
RBK_HOST=$1
|
RBK_HOST=$1
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ usage() { echo "Usage: $0 [filter]" 1>&2; exit 1; }
|
|||||||
|
|
||||||
MYDIR="$(dirname "$(realpath "$0")")"
|
MYDIR="$(dirname "$(realpath "$0")")"
|
||||||
# source $MYDIR/rbk_api.conf
|
# source $MYDIR/rbk_api.conf
|
||||||
source $MYDIR/oracle_funcs.sh
|
|
||||||
source $MYDIR/rsc_ops.sh
|
source $MYDIR/rsc_ops.sh
|
||||||
|
|
||||||
gql_SLAListQuery='query SLAListQuery($after: String, $first: Int, $filter: [GlobalSlaFilterInput!], $sortBy: SlaQuerySortByField, $sortOrder: SortOrder) {
|
gql_SLAListQuery='query SLAListQuery($after: String, $first: Int, $filter: [GlobalSlaFilterInput!], $sortBy: SlaQuerySortByField, $sortOrder: SortOrder) {
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ fi
|
|||||||
MYDIR="$(dirname "$(realpath "$0")")"
|
MYDIR="$(dirname "$(realpath "$0")")"
|
||||||
|
|
||||||
# source $MYDIR/rbk_api.conf
|
# source $MYDIR/rbk_api.conf
|
||||||
source $MYDIR/oracle_funcs.sh
|
|
||||||
source $MYDIR/rsc_ops.sh
|
source $MYDIR/rsc_ops.sh
|
||||||
|
|
||||||
gql_DBListQuery='query OracleDatabases($filter: [Filter!]) {
|
gql_DBListQuery='query OracleDatabases($filter: [Filter!]) {
|
||||||
|
|||||||
30
rsc_ops.sh
30
rsc_ops.sh
@@ -8,7 +8,35 @@
|
|||||||
#--------------------------------------------------------------------------------------------------------
|
#--------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
MYDIR="$(dirname "$(realpath "$0")")"
|
MYDIR="$(dirname "$(realpath "$0")")"
|
||||||
#source $MYDIR/rbk_api.conf
|
source $MYDIR/rbk_api.conf
|
||||||
|
|
||||||
|
# Set DATE command based on OS
|
||||||
|
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||||
|
DATE=gdate
|
||||||
|
else
|
||||||
|
DATE=date
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Utility functions
|
||||||
|
exit_with_error () {
|
||||||
|
rm -f /tmp/rbkresponse.$$
|
||||||
|
echo Aborting Script!
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup () {
|
||||||
|
rm -f /tmp/mountedDBs.$$
|
||||||
|
rm -f /tmp/rbkresponse.$$
|
||||||
|
rm -f /tmp/payload.$$
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
check_get_rsc_token () {
|
check_get_rsc_token () {
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user