This commit is contained in:
Zulqarnain 2024-11-14 02:47:38 -05:00
parent b3ee1052c7
commit f54a39aee8
5 changed files with 423 additions and 285 deletions

View File

@ -1,110 +1,88 @@
from flask import Flask, request, jsonify from flask import Flask, request, jsonify
import json import json
import subprocess import subprocess
from flask_jwt_extended import JWTManager, create_access_token import os
import os import datetime
import datetime
app = Flask(__name__)
app = Flask(__name__)
# Load paths from config_paths.json for placeholder replacement
# Setup the Flask-JWT-Extended extension with open('/opt/ticket-ai/src/config_paths.json') as paths_file:
app.config["JWT_SECRET_KEY"] = os.environ.get("JWT_SECRET_KEY", "default-secret-key") paths = json.load(paths_file)
jwt = JWTManager(app)
# Function to split directory and file
def split_directory_and_file(path): def split_directory_and_file(path):
directory, file_with_wildcard = os.path.split(path) directory, file_with_wildcard = os.path.split(path)
file_name = file_with_wildcard.split('*')[0] # Get the part before the wildcard file_name = file_with_wildcard.split('*')[0] # Get the part before the wildcard
return directory, file_name return directory, file_name
def read_log_from_dir(dir_path, pattern, lines): def replace_placeholders(text):
ret_var = "" """Replace placeholders in the text with paths from config_paths.json."""
three_hours_ago = datetime.datetime.now() - datetime.timedelta(hours=3) for placeholder, actual_path in paths.items():
log_files = [f for f in os.listdir(dir_path) if pattern in f and datetime.datetime.fromtimestamp(os.path.getmtime(os.path.join(dir_path, f))) > three_hours_ago] text = text.replace(f"{{{placeholder}}}", actual_path)
for file_name in log_files: return text
file_path = os.path.join(dir_path, file_name)
ret_var += f"{file_name}\n{subprocess.check_output(['tail', '-n', str(lines), file_path]).decode('utf-8')}\n\n" def read_log_from_dir(dir_path, pattern, lines):
return ret_var ret_var = ""
three_hours_ago = datetime.datetime.now() - datetime.timedelta(hours=3)
def read_log(log_file, lines): log_files = [f for f in os.listdir(dir_path) if pattern in f and datetime.datetime.fromtimestamp(os.path.getmtime(os.path.join(dir_path, f))) > three_hours_ago]
return subprocess.check_output(['tail', '-n', str(lines), log_file]).decode('utf-8') for file_name in log_files:
file_path = os.path.join(dir_path, file_name)
def read_top(): ret_var += f"{file_name}\n{subprocess.check_output(['tail', '-n', str(lines), file_path]).decode('utf-8')}\n\n"
return subprocess.check_output(['top', '-b', '-n', '1']).decode('utf-8') return ret_var
@app.route('/login', methods=['POST']) def read_log(log_file, lines):
def login(): # Replace placeholder in log file path
username = request.json.get("username") log_file = replace_placeholders(log_file)
password = request.json.get("password") return subprocess.check_output(['tail', '-n', str(lines), log_file]).decode('utf-8')
if username != "admin" or password != "password":
return jsonify({"msg": "Bad username or password"}), 401 @app.route('/get_logs', methods=['GET'])
access_token = create_access_token(identity=username) def get_logs():
return jsonify(access_token=access_token) tags = request.args.getlist("tags")
try:
# Function to replace placeholders in the issue configuration # Load main config-tags.json
def replace_placeholders(issue_config, paths): with open('/opt/ticket-ai/src/config.json') as config_file:
for log in issue_config.get('logs', []): config = json.load(config_file)
log_file = log.get('log_file')
if log_file: # Check for custom.json and merge if it exists
for placeholder, actual_path in paths.items(): custom_config_path = '/opt/ticket-ai/src/custom.json'
log['log_file'] = log['log_file'].replace(f"{{{placeholder}}}", actual_path) if os.path.exists(custom_config_path):
for command in issue_config.get('commands', []): with open(custom_config_path) as custom_file:
comm = command.get('comm') custom_config = json.load(custom_file)
if comm: for key, value in custom_config.items():
for placeholder, actual_path in paths.items(): if key in config:
command['comm'] = command['comm'].replace(f"{{{placeholder}}}", actual_path) config[key].extend(value)
else:
# Function to merge custom config into main config config[key] = value
def merge_configs(main_config, custom_config):
for key, value in custom_config.items(): response = {}
if key in main_config:
# Merge commands and logs if issue type already exists # Process each tag in the list
main_config[key]['commands'].extend(value.get('commands', [])) for tag in tags:
main_config[key]['logs'].extend(value.get('logs', [])) # Search for the tag in commands and logs
else: tag_data = next((item for item in config["tags"] if item["tag"] == tag), None)
# Add new issue type if it doesn't exist if not tag_data:
main_config[key] = value response[tag] = "Tag not found"
continue
@app.route('/get_logs', methods=['GET'])
def get_logs(): # Determine if the tag is a command or a log
issue_type = request.args.get("issue_type") if tag_data["type"] == "command":
try: response[tag] = f"```\n{os.popen(tag_data['content']).read()}\n```"
# Load config_paths.json for dynamic paths elif tag_data["type"] == "log":
with open('/opt/ticket-ai/src/config_paths.json') as paths_file: log_file = tag_data["content"]
paths = json.load(paths_file) # Replace any placeholders in the log file path
log_file = replace_placeholders(log_file)
# Load main config.json if "*" in log_file:
with open('/opt/ticket-ai/src/config.json') as config_file: # Handle wildcard in log file path
config = json.load(config_file) response[tag] = f"```\n{read_log_from_dir(*split_directory_and_file(log_file), tag_data['lines'])}\n```"
else:
# Check for custom.json and merge if it exists # Regular log file
custom_config_path = '/opt/ticket-ai/src/custom.json' response[tag] = f"```\n{read_log(log_file, tag_data['lines'])}\n```"
if os.path.exists(custom_config_path):
with open(custom_config_path) as custom_file: except Exception as e:
custom_config = json.load(custom_file) return jsonify({"error": str(e)})
merge_configs(config, custom_config)
return jsonify(response)
if issue_type not in config:
return jsonify({"error": "Invalid issue type"}) if __name__ == '__main__':
app.run(debug=True, host="0.0.0.0")
# Get the specific configuration for the issue type
issue_config = config[issue_type]
# Replace placeholders in logs and commands
replace_placeholders(issue_config, paths)
# Process logs and commands
response = {
log.get('log_file').split('*')[0]: f"```\n{read_log_from_dir(*split_directory_and_file(log.get('log_file')), log['lines'])}\n```" if '*' in log.get('log_file') else f"```\n{read_log(log['log_file'], log['lines'])}\n```"
for log in issue_config['logs']
}
response.update({
comm.get('tag'): f"```\n{os.popen(comm.get('comm')).read()}\n```" for comm in issue_config['commands']
})
except Exception as e:
return jsonify({"error": str(e)})
return jsonify(response)
if __name__ == '__main__':
app.run(debug=True, host="0.0.0.0")

View File

@ -1,175 +1,162 @@
{ {
"APEX Upgrade Request": { "tags": [
"commands": [ {
{ "tag": "disk_usage",
"comm": "df -h | head -n 1 && df -h | grep '^/dev/sd'", "type": "command",
"tag": "disk_usage" "content": "df -h | head -n 1 && df -h | grep '^/dev/sd'"
}, },
{ {
"comm": "/opt/ticket-ai/src/scripts/tablespaceUsage.sh;sleep 5;cat /tmp/tablespace_usage.tmp", "tag": "tablespace_usage",
"tag": "tablespace_usage" "type": "command",
}, "content": "/opt/ticket-ai/src/scripts/tablespaceUsage.sh;sleep 5;cat /tmp/tablespace_usage.tmp"
{ },
"comm": "/opt/ticket-ai/src/scripts/apexUsage.sh;sleep 5;cat /tmp/apex_usage.tmp", {
"tag": "apex_usage" "tag": "datafile_usage",
} "type": "command",
], "content": "/opt/ticket-ai/src/scripts/datafileUsage.sh;sleep 5;cat /tmp/datafile_usage.tmp"
"logs": [ },
{ {
"log_file": "{oracle_diag_log}", "tag": "db_version_info",
"lines": 10 "type": "command",
} "content": "/opt/ticket-ai/src/scripts/db_version_info.sh;sleep 5;cat /tmp/db_version_info.tmp"
] },
}, {
"tag": "apex_usage",
"Server Performance Problem": { "type": "command",
"commands": [ "content": "/opt/ticket-ai/src/scripts/apexUsage.sh;sleep 5;cat /tmp/apex_usage.tmp"
{ },
"comm": "top -b -n 1", {
"tag": "top" "tag": "top",
}, "type": "command",
{ "content": "top -b -n 1"
"comm": "iostat -x 1 3", },
"tag": "iostat" {
}, "tag": "iostat",
{ "type": "command",
"comm": "df -h | head -n 1 && df -h | grep '^/dev/sd'", "content": "iostat -x 1 3"
"tag": "df" },
}, {
{ "tag": "df",
"comm": "/opt/ticket-ai/src/scripts/processfinder.sh;sleep 5;cat /tmp/findsql.tmp", "type": "command",
"tag": "processfinder" "content": "df -h | head -n 1 && df -h | grep '^/dev/sd'"
}, },
{ {
"comm": "/opt/ticket-ai/src/scripts/tablespaceUsage.sh;sleep 5;cat /tmp/tablespace_usage.tmp", "tag": "processfinder",
"tag": "tablespaceusage" "type": "command",
}, "content": "/opt/ticket-ai/src/scripts/processfinder.sh;sleep 5;cat /tmp/findsql.tmp"
{ },
"comm": "cat /opt/ords/config/databases/default/pool.xml", {
"tag": "ordsconfig" "tag": "ordsconfig",
}, "type": "command",
{ "content": "cat /opt/ords/config/databases/default/pool.xml"
"comm": "echo Current Date and time on server: ; date", },
"tag": "datetime" {
}, "tag": "datetime",
{ "type": "command",
"comm": "find {virtualmin_error_log} -type f -name '*error_log*' -mmin -15 -exec sh -c 'echo \"===== {} =====\"; tail -n 50 {}' \\;", "content": "echo Current Date and time on server: ; date"
"tag": "apacheerrorlogs" },
} {
], "tag": "apacheerrorlogs",
"logs": [ "type": "command",
{ "content": "find {virtualmin_error_log} -type f -name '*error_log*' -mmin -15 -exec sh -c 'echo \"===== {} =====\"; tail -n 50 {}' \\;"
"log_file": "{tomcat_catalina_log}", },
"lines": 150 {
}, "tag": "relay_connection",
{ "type": "command",
"log_file": "{oracle_diag_log}", "content": "timeout 5 bash -c 'echo > /dev/tcp/relay.maxapex.net/2525' && echo 'Relay Connected' || echo 'Relay Connection failed'"
"lines": 50 },
}, {
{ "tag": "banned_ips",
"log_file": "/var/log/messages", "type": "command",
"lines": 25 "content": "for jail in $(sudo fail2ban-client status | grep \"Jail list\" | cut -d ':' -f2 | tr ',' ' '); do echo \"$jail:\"; sudo fail2ban-client status \"$jail\" | grep \"Banned IP list\"; done"
} },
] {
}, "tag": "wallet_certs",
"type": "command",
"Server Unavailable": { "content": "output=$(su - oracle -s /bin/bash -c 'orapki wallet display -wallet /home/oracle/wallet/') && echo \"$output\""
"commands": [ },
{ {
"comm": "top -b -n 1", "tag": "journalctl",
"tag": "top" "type": "command",
}, "content": "output=$(journalctl -p 3 -xb) && echo \"$output\""
{ },
"comm": "iostat -x 1 3", {
"tag": "iostat" "tag": "postqueue",
}, "type": "command",
{ "content": "output=$(/usr/sbin/postqueue -p) && echo \"$output\""
"comm": "df -h | head -n 1 && df -h | grep '^/dev/sd'", },
"tag": "df" {
}, "tag": "postfix_status",
{ "type": "command",
"comm": "/opt/ticket-ai/src/scripts/processfinder.sh;sleep 5;cat /tmp/findsql.tmp", "content": "output=$(systemctl is-active postfix) && echo \"$output\""
"tag": "processfinder" },
}, {
{ "tag": "dovecot_status",
"comm": "/opt/ticket-ai/src/scripts/tablespaceUsage.sh;sleep 5;cat /tmp/tablespace_usage.tmp", "type": "command",
"tag": "tablespaceusage" "content": "output=$(systemctl is-active dovecot) && echo \"$output\""
}, },
{ {
"comm": "cat /opt/ords/config/databases/default/pool.xml", "tag": "ipv6_status",
"tag": "ordsconfig" "type": "command",
}, "content": "output=$(ip -6 addr show | grep -q 'inet6' && echo 'IPv6 is active' || echo 'IPv6 is not active') && echo \"$output\""
{ },
"comm": "echo Current Date and time on server: ; date", {
"tag": "datetime" "tag": "iptables",
}, "type": "command",
{ "content": "output=$(iptables -L -n) && echo \"$output\""
"comm": "find {virtualmin_error_log} -type f -name '*error_log*' -mmin -15 -exec sh -c 'echo \"===== {} =====\"; tail -n 50 {}' \\;", },
"tag": "apacheerrorlogs" {
} "tag": "java_heap",
], "type": "command",
"logs": [ "content": "output=$(ps aux | grep java | grep -E 'Xms|Xmx') && echo \"$output\""
{ },
"log_file": "{tomcat_catalina_log}", {
"lines": 150 "tag": "oracle_alert_logs",
}, "type": "log",
{ "content": "{oracle_alert_logs}",
"log_file": "{oracle_diag_log}", "lines": 50
"lines": 50 },
}, {
{ "tag": "ords_logs",
"log_file": "/var/log/messages", "type": "log",
"lines": 25 "content": "{ords_log}",
} "lines": 150
] },
}, {
"tag": "var_log_messages",
"Email Problem": { "type": "log",
"commands": [ "content": "/var/log/messages",
{ "lines": 150
"comm": "timeout 5 bash -c 'echo > /dev/tcp/relay.maxapex.net/2525' && echo 'Relay Connected' || echo 'Relay Connection failed'", },
"tag": "relay_connection" {
}, "tag": "var_log_secure",
{ "type": "log",
"comm": "df -h", "content": "/var/log/secure",
"tag": "df" "lines": 150
} },
], {
"logs": [ "tag": "os-release",
{ "type": "log",
"log_file": "/var/log/messages", "content": "/etc/os-release",
"lines": 150 "lines": 150
} },
] {
}, "tag": "var_log_maillog",
"type": "log",
"Password Problem": { "content": "/var/log/maillog",
"commands": [ "lines": 150
{ },
"comm": "for jail in $(sudo fail2ban-client status | grep \"Jail list\" | cut -d ':' -f2 | tr ',' ' '); do echo \"$jail:\"; sudo fail2ban-client status \"$jail\" | grep \"Banned IP list\"; done", {
"tag": "banned_ips" "tag": "var_log_fail2ban",
} "type": "log",
], "content": "/var/log/fail2ban.log",
"logs": [ "lines": 150
{ },
"log_file": "/var/log/secure", {
"lines": 150 "tag": "var_log_mariadb",
} "type": "log",
] "content": "/var/log/mariadb/mariadb.log",
}, "lines": 150
}
"Wallet Problem": { ]
"commands": [
{
"comm": "output=$(su - oracle -s /bin/bash -c 'orapki wallet display -wallet /home/oracle/wallet/') && echo \"$output\"",
"tag": "wallet_certs"
}
],
"logs": [
{
"log_file": "/var/log/secure",
"lines": 0
}
]
}
} }

View File

@ -1,5 +1,5 @@
{ {
"oracle_diag_log": "/opt/oracle/diag/rdbms/xe/XE/trace/alert_XE.log", "oracle_alert_logs": "/opt/oracle/diag/rdbms/xe/XE/trace/alert_XE.log",
"virtualmin_error_log": "/var/log/virtualmin/error_log", "virtualmin_error_log": "/var/log/virtualmin/error_log",
"tomcat_catalina_log": "/opt/tomcat/logs/catalina.out" "ords_log": "/opt/tomcat/logs/catalina.out"
} }

79
src/scripts/datafileUsage.sh Executable file
View File

@ -0,0 +1,79 @@
#!/bin/bash
# File paths for output
OUTPUT_FILE="/tmp/datafile_usage.tmp"
PDB_LIST_FILE="/tmp/pdb_list.tmp"
> "$OUTPUT_FILE" # Clear the output file before starting
touch $OUTPUT_FILE
chown oracle $OUTPUT_FILE
# Function to execute the datafile usage query in a specific container
run_query() {
local container_name="$1"
su - oracle -s /bin/bash -c "
sqlplus -s / as sysdba <<EOF >> \"$OUTPUT_FILE\"
whenever sqlerror exit sql.sqlcode;
set echo off
set heading on
SET UNDERLINE '='
set feedback off
set linesize 150
-- Switch to the specified container
ALTER SESSION SET CONTAINER = $container_name;
-- Print the container name for clarity
PROMPT Datafile usage for container: $container_name;
SELECT d.file_name AS datafile_path,
d.tablespace_name,
ROUND(d.bytes / 1024 / 1024, 2) AS allocated_mb,
ROUND((d.bytes - NVL(fs.bytes, 0)) / 1024 / 1024, 2) AS used_mb,
ROUND(NVL(fs.bytes, 0) / 1024 / 1024, 2) AS free_mb,
ROUND(((d.bytes - NVL(fs.bytes, 0)) / d.bytes) * 100, 2) AS pct_used
FROM dba_data_files d
LEFT JOIN (
SELECT file_id, SUM(bytes) AS bytes
FROM dba_free_space
GROUP BY file_id
) fs ON d.file_id = fs.file_id
ORDER BY d.tablespace_name, d.file_name;
exit;
EOF
"
}
# Run the query for the CDB (root container)
echo "Gathering datafile usage for CDB (root container)..." >> "$OUTPUT_FILE"
run_query 'CDB\$ROOT'
# Get a list of all PDBs, excluding PDB$SEED, and write to a temporary file
su - oracle -s /bin/bash -c "
sqlplus -s / as sysdba <<'EOF'
set heading off
set feedback off
set pagesize 0
spool $PDB_LIST_FILE
SELECT NAME FROM v\$pdbs WHERE NAME NOT IN ('PDB\$SEED');
spool off
exit;
EOF
"
# Verify that the PDB_LIST_FILE was created and contains data
if [[ -f "$PDB_LIST_FILE" && -s "$PDB_LIST_FILE" ]]; then
# Read each valid PDB name from the temporary file
while IFS= read -r PDB; do
PDB=$(echo "$PDB" | xargs) # Trim any leading/trailing whitespace
if [[ -n "$PDB" ]]; then
echo "Gathering datafile usage for PDB: $PDB..." >> "$OUTPUT_FILE"
run_query "$PDB"
fi
done < "$PDB_LIST_FILE"
else
echo "No PDBs found or could not access v\$pdbs view" >> "$OUTPUT_FILE"
fi
# Clean up the temporary file
rm -f "$PDB_LIST_FILE"

94
src/scripts/db_version_info.sh Executable file
View File

@ -0,0 +1,94 @@
#!/bin/bash
# File path for output
OUTPUT_FILE="/tmp/db_version_info.tmp"
PDB_LIST_FILE="/tmp/pdb_list.tmp"
> "$OUTPUT_FILE" # Clear the output file before starting
touch $OUTPUT_FILE
chown oracle $OUTPUT_FILE
# Function to get the Oracle Database version
get_oracle_version() {
su - oracle -s /bin/bash -c "
sqlplus -s / as sysdba <<'EOF' >> \"$OUTPUT_FILE\"
whenever sqlerror exit sql.sqlcode;
set echo off
set heading on
SET UNDERLINE '='
set feedback off
set linesize 150
PROMPT Oracle Database Version:;
SELECT banner FROM v\$version WHERE banner LIKE 'Oracle%' AND ROWNUM = 1;
exit;
EOF
"
}
# Function to get APEX and ORDS versions within a specific container
get_apex_ords_versions() {
local container_name="$1"
su - oracle -s /bin/bash -c "
sqlplus -s / as sysdba <<EOF >> \"$OUTPUT_FILE\"
whenever sqlerror exit sql.sqlcode;
set echo off
set heading on
SET UNDERLINE '='
set feedback off
set linesize 150
-- Switch to the specified container
ALTER SESSION SET CONTAINER = $container_name;
-- Print the container name for clarity
PROMPT Version information for container: $container_name;
-- APEX version
PROMPT APEX Version:;
SELECT version_no AS version FROM apex_release;
-- ORDS version
PROMPT ORDS Version:;
SELECT 'ORDS is ' || version AS \"Version of ORDS\"
FROM ords_metadata.ords_schema_version;
exit;
EOF
"
}
# Run the Oracle version query
echo "Gathering Oracle Database version..." >> "$OUTPUT_FILE"
get_oracle_version
# Get a list of all PDBs, excluding PDB$SEED, and write to a temporary file
su - oracle -s /bin/bash -c "
sqlplus -s / as sysdba <<'EOF'
set heading off
set feedback off
set pagesize 0
spool $PDB_LIST_FILE
SELECT NAME FROM v\$pdbs WHERE NAME NOT IN ('PDB\$SEED');
spool off
exit;
EOF
"
# Verify that the PDB_LIST_FILE was created and contains data
if [[ -f "$PDB_LIST_FILE" && -s "$PDB_LIST_FILE" ]]; then
# Read each valid PDB name from the temporary file
while IFS= read -r PDB; do
PDB=$(echo "$PDB" | xargs) # Trim any leading/trailing whitespace
if [[ -n "$PDB" ]]; then
echo "Gathering APEX and ORDS versions for PDB: $PDB..." >> "$OUTPUT_FILE"
get_apex_ords_versions "$PDB"
fi
done < "$PDB_LIST_FILE"
else
echo "No PDBs found or could not access v\$pdbs view" >> "$OUTPUT_FILE"
fi
# Clean up the temporary file
rm -f "$PDB_LIST_FILE"