Getting there
This commit is contained in:
0
clone_oracle_database.py
Normal file → Executable file
0
clone_oracle_database.py
Normal file → Executable file
0
introspect_schema.py
Normal file → Executable file
0
introspect_schema.py
Normal file → Executable file
12
list_oracle_databases.py
Normal file → Executable file
12
list_oracle_databases.py
Normal file → Executable file
@@ -20,6 +20,7 @@ def list_oracle_databases():
|
||||
nodes {
|
||||
dbUniqueName
|
||||
id
|
||||
isRelic
|
||||
cluster {
|
||||
id
|
||||
name
|
||||
@@ -34,13 +35,9 @@ def list_oracle_databases():
|
||||
}
|
||||
"""
|
||||
|
||||
# Variables: exclude relics and replicated databases
|
||||
# Variables: exclude replicated databases only
|
||||
variables = {
|
||||
"filter": [
|
||||
{
|
||||
"texts": ["false"],
|
||||
"field": "IS_RELIC"
|
||||
},
|
||||
{
|
||||
"texts": ["false"],
|
||||
"field": "IS_REPLICATED"
|
||||
@@ -71,11 +68,12 @@ def list_oracle_databases():
|
||||
db['dbUniqueName'],
|
||||
db['id'],
|
||||
cluster_name,
|
||||
host_name
|
||||
host_name,
|
||||
'Yes' if db['isRelic'] else 'No'
|
||||
])
|
||||
|
||||
# Print tabulated output
|
||||
headers = ['Database Name', 'ID', 'Cluster', 'Host']
|
||||
headers = ['Database Name', 'ID', 'Cluster', 'Host', 'Relic']
|
||||
print(tabulate(table_data, headers=headers, tablefmt='grid'))
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -120,7 +120,7 @@ if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description='List Oracle Live Mounts')
|
||||
parser.add_argument('--name', help='Filter by name (mounted database name)')
|
||||
parser.add_argument('--cluster-uuid', help='Filter by cluster UUID')
|
||||
parser.add_argument('--source-db-id', help='Filter by source database ID')
|
||||
parser.add_argument('--source-db-id', help='Filter by source database ID (optional, defaults to local database)')
|
||||
parser.add_argument('--org-id', help='Filter by organization ID')
|
||||
parser.add_argument('--sort-field', choices=['NAME', 'CREATION_DATE', 'STATUS'], help='Sort field')
|
||||
parser.add_argument('--sort-order', choices=['ASC', 'DESC'], default='ASC', help='Sort order')
|
||||
@@ -128,6 +128,12 @@ if __name__ == "__main__":
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if not args.source_db_id and not (args.name or args.cluster_uuid or args.org_id):
|
||||
auth = RSCAuth()
|
||||
gql = RSCGraphQL(auth)
|
||||
args.source_db_id = gql.get_local_database_id()
|
||||
print(f"INFO: No filters specified, using local database ID: {args.source_db_id}")
|
||||
|
||||
try:
|
||||
list_oracle_live_mounts(
|
||||
name=args.name,
|
||||
19
list_db_snapshots.py → list_oracle_snapshots.py
Normal file → Executable file
19
list_db_snapshots.py → list_oracle_snapshots.py
Normal file → Executable file
@@ -86,7 +86,7 @@ def find_database_by_name_or_id(identifier):
|
||||
def format_timestamp(timestamp):
|
||||
"""Format ISO timestamp to readable format"""
|
||||
try:
|
||||
dt = datetime.fromisoformat(timestamp.replace('Z', '+00:00'))
|
||||
dt = datetime.strptime(timestamp.replace('Z', '+0000'), '%Y-%m-%dT%H:%M:%S.%f%z')
|
||||
return dt.strftime('%Y-%m-%d %H:%M:%S UTC')
|
||||
except:
|
||||
return timestamp
|
||||
@@ -217,7 +217,7 @@ def list_snapshots(identifier):
|
||||
format_timestamp(snap['date']),
|
||||
'On-Demand' if snap['isOnDemandSnapshot'] else 'Policy',
|
||||
snap['slaDomain']['name'] if snap['slaDomain'] else 'None',
|
||||
'Local + Replica' if snap['snapshotRetentionInfo']['replicationInfos'] else '✅ Local only'
|
||||
'Local + Replica' if snap['snapshotRetentionInfo']['replicationInfos'] else 'Local only'
|
||||
])
|
||||
|
||||
headers = ['Snapshot ID', 'Date', 'Type', 'SLA Domain', 'Location Status']
|
||||
@@ -245,14 +245,23 @@ def list_snapshots(identifier):
|
||||
sys.exit(1)
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 2:
|
||||
print("Usage: python list_db_snapshots.py <database_name_or_id>")
|
||||
if len(sys.argv) == 2:
|
||||
identifier = sys.argv[1]
|
||||
elif len(sys.argv) == 1:
|
||||
# No argument, find local database
|
||||
auth = RSCAuth()
|
||||
gql = RSCGraphQL(auth)
|
||||
identifier = gql.get_local_database_id()
|
||||
print(f"INFO: Using local database ID: {identifier}")
|
||||
else:
|
||||
print("Usage: python list_db_snapshots.py [<database_name_or_id>]")
|
||||
print("If no database is specified, uses the local database.")
|
||||
print("Examples:")
|
||||
print(" python list_db_snapshots.py SCLONE")
|
||||
print(" python list_db_snapshots.py 2cb7e201-9da0-53f2-8c69-8fc21f82e0d2")
|
||||
print(" python list_db_snapshots.py")
|
||||
sys.exit(1)
|
||||
|
||||
identifier = sys.argv[1]
|
||||
list_snapshots(identifier)
|
||||
|
||||
if __name__ == "__main__":
|
||||
25
mount_oracle_filesonly.py
Normal file → Executable file
25
mount_oracle_filesonly.py
Normal file → Executable file
@@ -5,6 +5,7 @@ import sys
|
||||
import os
|
||||
import argparse
|
||||
import time
|
||||
import socket
|
||||
from datetime import datetime
|
||||
from rsc import RSCAuth, RSCGraphQL
|
||||
|
||||
@@ -58,7 +59,6 @@ def find_database_by_name_or_id(identifier):
|
||||
variables = {
|
||||
"filter": [
|
||||
{"texts": [identifier], "field": "NAME_EXACT_MATCH"},
|
||||
{"texts": ["false"], "field": "IS_RELIC"},
|
||||
{"texts": ["false"], "field": "IS_REPLICATED"}
|
||||
]
|
||||
}
|
||||
@@ -160,7 +160,7 @@ def get_latest_pit(db_id):
|
||||
print(f"INFO: Latest PIT (ISO8601): {latest_endtime}")
|
||||
|
||||
# Convert to datetime and then to milliseconds since epoch
|
||||
dt = datetime.fromisoformat(latest_endtime.replace('Z', '+00:00'))
|
||||
dt = datetime.strptime(latest_endtime.replace('Z', '+0000'), '%Y-%m-%dT%H:%M:%S.%f%z')
|
||||
unixtime_ms = int(dt.timestamp() * 1000)
|
||||
print(f"INFO: Latest PIT unixtime (ms): {unixtime_ms}")
|
||||
|
||||
@@ -304,26 +304,39 @@ def main():
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="""
|
||||
Examples:
|
||||
python mount_oracle_filesonly.py --mountpath /tmp/mount
|
||||
python mount_oracle_filesonly.py --mountpath /tmp/mount SHED
|
||||
python mount_oracle_filesonly.py --targethost target-host --mountpath /tmp/mount SHED
|
||||
python mount_oracle_filesonly.py --targethost target-host --mountpath /tmp/mount --timestamp "2025-11-25 12:00:00" SHED
|
||||
"""
|
||||
)
|
||||
|
||||
parser.add_argument("--targethost", required=True,
|
||||
help="Target host where the files will be mounted")
|
||||
parser.add_argument("--targethost", required=False,
|
||||
help="Target host where the files will be mounted (defaults to local hostname)")
|
||||
parser.add_argument("--mountpath", required=True,
|
||||
help="Target mount path for the files")
|
||||
parser.add_argument("--timestamp",
|
||||
help="Optional timestamp for the recovery point in format 'YYYY-MM-DD HH:MM:SS'")
|
||||
parser.add_argument("srcdb",
|
||||
help="Source database name or RSC database ID")
|
||||
parser.add_argument("srcdb", nargs='?',
|
||||
help="Source database name or RSC database ID (optional, defaults to local database)")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if not args.targethost:
|
||||
args.targethost = socket.gethostname()
|
||||
print(f"INFO: No target host specified, defaulting to local hostname: {args.targethost}")
|
||||
|
||||
try:
|
||||
# Find the source database
|
||||
if args.srcdb:
|
||||
print(f"INFO: Finding source database: {args.srcdb}")
|
||||
db = find_database_by_name_or_id(args.srcdb)
|
||||
else:
|
||||
print("INFO: No source database specified, finding local database")
|
||||
auth = RSCAuth()
|
||||
gql = RSCGraphQL(auth)
|
||||
db_id = gql.get_local_database_id()
|
||||
db = find_database_by_name_or_id(db_id)
|
||||
print(f"INFO: Found database: {db['dbUniqueName']} (ID: {db['id']})")
|
||||
print(f"INFO: Cluster: {db['cluster']['name']} (ID: {db['cluster']['id']})")
|
||||
|
||||
|
||||
61
rsc.py
61
rsc.py
@@ -2,6 +2,7 @@ import json
|
||||
import os
|
||||
import time
|
||||
import requests
|
||||
import socket
|
||||
|
||||
class RSCAuth:
|
||||
def __init__(self, config_file='rsc.json'):
|
||||
@@ -102,6 +103,66 @@ class RSCGraphQL:
|
||||
|
||||
return data
|
||||
|
||||
def get_local_database_id(self):
|
||||
"""Get the ID of the local database on this host, preferring one protected by SLA"""
|
||||
hostname = socket.gethostname()
|
||||
|
||||
query = """
|
||||
query OracleDatabases($filter: [Filter!]) {
|
||||
oracleDatabases(filter: $filter) {
|
||||
nodes {
|
||||
id
|
||||
dbUniqueName
|
||||
isRelic
|
||||
effectiveSlaDomain {
|
||||
id
|
||||
name
|
||||
}
|
||||
cluster {
|
||||
id
|
||||
name
|
||||
}
|
||||
logicalPath {
|
||||
fid
|
||||
name
|
||||
objectType
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
variables = {
|
||||
"filter": [
|
||||
{"texts": ["false"], "field": "IS_REPLICATED"}
|
||||
]
|
||||
}
|
||||
|
||||
response = self.query(query, variables)
|
||||
all_dbs = response['data']['oracleDatabases']['nodes']
|
||||
|
||||
# Filter databases on this host
|
||||
dbs = [db for db in all_dbs if db['logicalPath'] and db['logicalPath'][0]['name'] == hostname]
|
||||
|
||||
if not dbs:
|
||||
raise ValueError(f"No databases found on host {hostname}")
|
||||
|
||||
# Filter databases with SLA protection
|
||||
protected_dbs = [db for db in dbs if db.get('effectiveSlaDomain')]
|
||||
|
||||
if protected_dbs:
|
||||
if len(protected_dbs) == 1:
|
||||
return protected_dbs[0]['id']
|
||||
else:
|
||||
# Multiple protected, use the first one with a warning
|
||||
print(f"WARN: Multiple protected databases on {hostname}, using {protected_dbs[0]['dbUniqueName']}")
|
||||
return protected_dbs[0]['id']
|
||||
else:
|
||||
if len(dbs) == 1:
|
||||
return dbs[0]['id']
|
||||
else:
|
||||
raise ValueError(f"Multiple databases on {hostname}, none protected by SLA")
|
||||
|
||||
def introspect_schema(self):
|
||||
"""Introspect the GraphQL schema to get type information"""
|
||||
introspection_query = """
|
||||
|
||||
Reference in New Issue
Block a user