Initial commit with project setup and basic structure established.
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
import argparse
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
|
||||
def fetch_config(ip, data_id, group, tenant):
|
||||
url = f"http://{ip}:8848/nacos/v1/cs/configs?dataId={data_id}&group={group}&tenant={tenant}"
|
||||
print(f"Trying to fetch config from {url}...")
|
||||
try:
|
||||
with urllib.request.urlopen(url, timeout=5) as response:
|
||||
if response.status == 200:
|
||||
return response.read().decode('utf-8')
|
||||
except urllib.error.URLError as e:
|
||||
print(f"Failed to connect to {ip}: {e}")
|
||||
except Exception as e:
|
||||
print(f"Error fetching config from {ip}: {e}")
|
||||
return None
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Fetch configuration from Nacos via HTTP.")
|
||||
parser.add_argument("--hosts", nargs='+', default=["192.168.3.200", "192.168.3.230"], help="List of Nacos server IPs")
|
||||
parser.add_argument("--data-id", required=True, help="Nacos Data ID (e.g., redis.yml)")
|
||||
parser.add_argument("--group", default="DEFAULT_GROUP", help="Nacos Group (default: DEFAULT_GROUP)")
|
||||
parser.add_argument("--tenant", default="dev", help="Nacos Tenant (default: dev)")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
config_content = None
|
||||
for ip in args.hosts:
|
||||
config_content = fetch_config(ip, args.data_id, args.group, args.tenant)
|
||||
if config_content:
|
||||
print(f"Successfully fetched config from {ip}")
|
||||
break
|
||||
|
||||
if config_content:
|
||||
print("-" * 40)
|
||||
print(f"CONTENT OF {args.data_id}:")
|
||||
print("-" * 40)
|
||||
print(config_content)
|
||||
print("-" * 40)
|
||||
else:
|
||||
print(f"Failed to fetch config '{args.data_id}' from all provided hosts.")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,115 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
import argparse
|
||||
import paramiko
|
||||
import os
|
||||
|
||||
def fetch_logs(args):
|
||||
ssh = paramiko.SSHClient()
|
||||
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
|
||||
try:
|
||||
# Connect to the server
|
||||
print(f"Connecting to {args.host}...")
|
||||
ssh.connect(
|
||||
hostname=args.host,
|
||||
username=args.username,
|
||||
password=args.password,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
command = ""
|
||||
if args.mode == "docker":
|
||||
since_flag = f'--since "{args.since}"' if args.since else ""
|
||||
until_flag = f'--until "{args.until}"' if args.until else ""
|
||||
tail_flag = f"--tail={args.lines}" if not args.since else ""
|
||||
# docker-compose logs command
|
||||
command = f"docker-compose -f {args.yml_path} logs --no-log-prefix {since_flag} {until_flag} {tail_flag} {args.service}"
|
||||
else:
|
||||
# File mode
|
||||
if args.since or args.until:
|
||||
# Time-based filtering for file mode using awk
|
||||
since_val = args.since if args.since else ""
|
||||
until_val = args.until if args.until else ""
|
||||
|
||||
# Convert relative time (e.g., 10m) to absolute timestamp on server
|
||||
if since_val and any(since_val.endswith(suffix) for suffix in ['s', 'm', 'h', 'd']) and since_val[:-1].isdigit():
|
||||
unit_map = {'s': 'seconds', 'm': 'minutes', 'h': 'hours', 'd': 'days'}
|
||||
unit = unit_map[since_val[-1]]
|
||||
num = since_val[:-1]
|
||||
since_expr = f"\"$(date -d '{num} {unit} ago' '+%Y-%m-%d %H:%M:%S')\""
|
||||
elif since_val:
|
||||
since_expr = f"'{since_val}'"
|
||||
else:
|
||||
since_expr = "''"
|
||||
|
||||
until_expr = f"'{until_val}'" if until_val else "''"
|
||||
|
||||
# awk script that extracts timestamp and compares
|
||||
# It maintains 'in_range' state for multi-line logs (like stack traces)
|
||||
awk_script = (
|
||||
f"awk -v since={since_expr} -v until={until_expr} '"
|
||||
"BEGIN { in_range = (since == \"\"); } "
|
||||
"{ "
|
||||
" if (match($0, /^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}/)) { "
|
||||
" current_time = substr($0, RSTART, 19); "
|
||||
" in_range = (since == \"\" || current_time >= since) && (until == \"\" || current_time <= until); "
|
||||
" } "
|
||||
" if (in_range) print $0; "
|
||||
"}' " + args.log_path
|
||||
)
|
||||
command = awk_script
|
||||
else:
|
||||
# Default tail + grep for errors
|
||||
command = f"tail -n {args.lines} {args.log_path} | grep -E 'Exception|Error|Caused by' -C 50"
|
||||
|
||||
print(f"Executing command: {command}")
|
||||
stdin, stdout, stderr = ssh.exec_command(command)
|
||||
|
||||
output = stdout.read().decode('utf-8', errors='replace')
|
||||
error = stderr.read().decode('utf-8', errors='replace')
|
||||
|
||||
if error and not output:
|
||||
print(f"Error from server:\n{error}")
|
||||
return
|
||||
|
||||
if not output:
|
||||
print("No matching logs found.")
|
||||
return
|
||||
|
||||
# Print a header for the output
|
||||
print("-" * 40)
|
||||
print(f"LOGS FROM {args.host} ({args.service if args.mode == 'docker' else args.log_path})")
|
||||
print("-" * 40)
|
||||
print(output)
|
||||
print("-" * 40)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Failed to fetch logs: {str(e)}")
|
||||
finally:
|
||||
ssh.close()
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Fetch logs from a remote server via SSH.")
|
||||
parser.add_argument("--host", required=True, help="Server IP or hostname")
|
||||
parser.add_argument("--username", required=True, help="SSH username")
|
||||
parser.add_argument("--password", help="SSH password")
|
||||
parser.add_argument("--mode", choices=["docker", "file"], required=True, help="Log extraction mode")
|
||||
parser.add_argument("--service", help="Docker service name (for docker mode)")
|
||||
parser.add_argument("--yml-path", help="Path to docker-compose.yml (for docker mode)")
|
||||
parser.add_argument("--log-path", help="Path to log file (for file mode)")
|
||||
parser.add_argument("--lines", type=int, default=1000, help="Number of lines to fetch/analyze (default 1000)")
|
||||
parser.add_argument("--since", help="Filter logs since this time (e.g., '10m', '2026-01-16 10:10:00')")
|
||||
parser.add_argument("--until", help="Filter logs until this time (e.g., '2026-01-16 10:20:00')")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.mode == "docker" and not (args.service and args.yml_path):
|
||||
parser.error("docker mode requires --service and --yml-path")
|
||||
if args.mode == "file" and not args.log_path:
|
||||
parser.error("file mode requires --log-path")
|
||||
|
||||
fetch_logs(args)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user