Compare commits

...

36 Commits

Author SHA1 Message Date
1b446860e6 Added config_paths.json to .gitignore 2024-12-02 03:39:40 +01:00
68057dd986 added sga and pga diagnostics and db parameters 2024-11-29 04:59:34 -05:00
root
b1ffd59111 Bot short name in apexBots.sh 2024-11-28 17:03:36 +06:00
root
3461a4582c added password hide 2024-11-28 16:58:38 +06:00
root
1b4c924584 dynamic port through TICKET_AI_PORT environment variable in service 2024-11-26 13:22:13 +01:00
root
b7a31e7c01 apex and apache sessions etc 2024-11-26 13:13:21 +01:00
66e20186f2 tags improvments and SQL formatting 2024-11-21 07:43:56 -05:00
9d0b6b7a9e Fix mariadb log path 2024-11-20 03:33:10 -05:00
fe39829fa2 Serveral improvements, including sorting of output tags, ignore error if any tag fails, add apex mail queue, smtp settings, top redo generating sql and apex bots usage 2024-11-20 03:20:49 -05:00
f54a39aee8 tag wise 2024-11-14 02:47:38 -05:00
b3ee1052c7 touched tmp file and ownership to oracle 2024-11-07 04:30:03 -05:00
e319eedf14 Delete 'src/config_demo.json' 2024-11-07 10:11:08 +01:00
ed7e9a7f00 Delete 'src/config_cloud_db.json' 2024-11-07 10:10:55 +01:00
daaf5c74a5 Delete 'src/config_23ai.json' 2024-11-07 10:10:49 +01:00
c244ff148e Delete 'src/config_18c.json' 2024-11-07 10:10:40 +01:00
11647d2293 Delete 'src/config_11g.json' 2024-11-07 10:10:31 +01:00
44be61274f made /var/log/secure lines 0 to wallets 2024-11-07 01:30:21 -05:00
d6e72f638c added custom.json option and introduced placeholdr 2024-11-07 01:24:26 -05:00
root
be168c03ba more logs 2024-11-05 05:17:09 -05:00
root
df8e6bb79b adding more scripts 2024-11-05 05:12:26 -05:00
root
4d3caf98c1 Addming more functionality 2024-11-05 05:11:32 -05:00
zulqarnainhabib
cc218af2e0 included apache error logs 2024-10-18 14:50:41 +05:00
zulqarnainhabib
32cca430c3 added tablespace info, top query processes, add date/time, ords config and db parameters 2024-10-18 11:57:46 +05:00
Abdur Rehman
0c59f9f5c5 add df command and increase log size 2024-09-19 16:00:03 +05:00
Abdur Rehman
13d395a49b add 11g config 2024-09-19 15:24:27 +05:00
Abdur Rehman
3094f1a07a update 23ai config path 2024-09-18 12:39:47 +05:00
Abdur Rehman
22769a4a3a separate demo config 2024-09-16 15:41:03 +05:00
Abdur Rehman
0b4c2b1a64 increase catalina logs lines 2024-09-16 15:24:24 +05:00
Abdur Rehman
375d58876f update config files baed on oracle versions 2024-09-13 12:53:12 +05:00
5057d6ca51 Delete 'installer.sh' 2024-09-11 13:11:18 +02:00
4288eaea31 Delete 'src/config.json' 2024-09-11 13:09:13 +02:00
cf7940eeff Add 'src/config_new_ora.json' 2024-09-11 13:02:15 +02:00
cd60e6024f Add 'src/config_old_ora.json' 2024-09-11 13:01:52 +02:00
a3604cf80d Update 'src/config.json' 2024-09-11 13:00:43 +02:00
acb1239e2c update prod and dev ips 2024-08-30 09:55:51 +02:00
fe45a2ec09 Delete 'hooks/hook_maxapex_tickets_ai.php' 2024-02-28 07:26:22 +01:00
22 changed files with 1616 additions and 429 deletions

4
.gitignore vendored
View File

@ -1,3 +1,5 @@
.vscode/ .vscode/
playground/ playground/
venv/ venv/
src/custom.json
src/config_paths.json

View File

@ -1,190 +0,0 @@
<?php
// Import necessary WHMCS classes
use WHMCS\Database\Capsule;
use GuzzleHttp\Client;
// Function to get ticket data and related information
function getTicketData($tid) {
try {
// Perform database query
$result = Capsule::table('tbltickets as tt')
->join('tblhosting as th', Capsule::raw('SUBSTRING(tt.service, 2)'), '=', 'th.id')
->join('tblservers as ts', 'th.server', '=', 'ts.id')
->leftJoin('tblcustomfieldsvalues as cfv', function($join) {
$join->on('tt.id', '=', 'cfv.relid')
->where('cfv.fieldid', '=', function($query) {
$query->select('id')
->from('tblcustomfields')
->where('fieldname', 'Issue Type (*)')
->where('type', 'support')
->limit(1);
});
})
->where('tt.id', $tid)
->select('tt.title', 'tt.message', 'th.domain', 'ts.name', 'ts.ipaddress', 'ts.hostname', 'cfv.value as issueType')
->get();
if ($result->isEmpty()) {
return "No data found for ticket ID: " . htmlspecialchars($tid);
}
// Add ticket information to data array
$data = [];
foreach ($result as $row) {
$data[] = [
'Title' => $row->title,
'Message' => $row->message,
'Domain' => $row->domain,
'ServerName' => $row->name,
'IPAddress' => $row->ipaddress,
'Hostname' => $row->hostname,
'IssueType' => $row->issueType
];
}
} catch (\Exception $e) {
return "An error occurred: " . $e->getMessage();
}
return $data;
}
// Get logs
function getLogs($server, $issueType) {
$client = new Client();
try {
// GET request with a 10-second timeout
$response = $client->get('http://' . urlencode($server) . ':5000/get_logs?issue_type=' . urlencode($issueType), [
'headers' => [
'Content-Type' => 'application/json',
],
'timeout' => 10, // Timeout set to 10 seconds
]);
$responseBody = json_decode($response->getBody(), true);
return $responseBody;
} catch (Exception $e) {
logActivity('error getLogsApi: '. $e->getMessage() );
return null; // or return a specific structure indicating failure
}
}
// Get OpenAI response
function callOpenAI($apiKey, $apiEngine, $subject, $message, $logsData) {
try {
// Constructing the prompt
$prompt = "We have received a Technical Support Ticket from a client. Having subject: $subject, ticket content: $message. Following are the server logs with file paths: $logsData. Please guide us solution of this with explanations.";
// Data for the OpenAI API call
$requestData = array(
"model" => $apiEngine,
// "response_format" => array("type" => "json_object"),
"messages" => array(
array("role" => "system", "content" => "You are a helpful sysops. You have to guide a effective solution."),
array("role" => "user", "content" => $prompt) // Using constructed prompt
)
);
$client = new Client();
$response = $client->post('https://api.openai.com/v1/chat/completions', [
'headers' => [
'Content-Type' => 'application/json',
"Authorization" => "Bearer $apiKey"
],
'json' => $requestData,
]);
$responseArray = json_decode($response->getBody(), true);
// Accessing choices
$choice = $responseArray['choices'][0];
$responseBody = $choice['message']['content'];
return $responseBody;
} catch (Exception $e) {
logActivity('error callOpenAI: '. $e->getMessage() );
// return "Error: " . $e->getMessage();
}
}
// Add ticket notes
function addNotesToTicket($ticketId, $note) {
try {
$command = 'AddTicketNote';
$postData = array(
'ticketid' => $ticketId,
'message' => "Suggestion by ChatGPT.
PLEASE NOTE THAT THIS MAY NOT BE CORRECT THEREFORE PLEASE READ AND UNDERSTAND CAREFULLY BEFORE DOING ANYTHING." . $note,
'markdown' => true
//,
//'attachments' => base64_encode(json_encode([['name' => 'sample_text_file.txt', 'data' => base64_encode('This is a sample text file contents')]])),
);
$results = localAPI($command, $postData);
logActivity("Ticket note added for ticket ID: {$ticketId}");
} catch (Exception $e) {
logActivity("Error adding ticket note: " . $e->getMessage());
}
}
function openTicketHookFunc($vars) {
// Retrieve the Ticket ID and Display Data
$tid = $vars['ticketid'] ?? '';
// Retrieve and display module configuration values
$aiApiKey = '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';
$aiEngine = "gpt-3.5-turbo-1106";
if (!empty($tid)) {
$ticketData = getTicketData($tid);
if (is_array($ticketData)) {
foreach ($ticketData as $row) {
// get logs
// $server = "localhost";
// $issueType = "Demo";
$server = htmlspecialchars($row['ServerName']);
$issueType = htmlspecialchars($row['IssueType']);
$logsDataArray = getLogs($server, $issueType);
if ($logsDataArray !== null) {
$logsData = '';
foreach ($logsDataArray as $key => $value) {
$logsData .= "\n" . "file: " . $key . "\n" . "logs: " . $value;
}
// Call the ChatGPT API
$openAIResponse = callOpenAI($aiApiKey, $aiEngine, $row['Title'], $row['Message'], $logsData);
addNotesToTicket($tid, $openAIResponse);
}
}
}
}
};
add_hook('TicketOpenAdmin', 1, function($vars) {
// logActivity('before hooks from ARH - TicketOpenAdmin');
openTicketHookFunc($vars);
// logActivity('after hooks from ARH - TicketOpenAdmin');
});
add_hook('TicketOpen', 1, function($vars) {
// logActivity('hooks from ARH - TicketOpen');
openTicketHookFunc($vars);
});

View File

@ -1,68 +0,0 @@
#!/bin/bash
# Define variables for easier modifications and readability
GIT_REPO="https://git.maxprint.io/MaxApex/ticket_ai_flask_app.git"
APP_DIR="/opt/ticket-ai"
VENV_DIR="$APP_DIR/venv"
SERVICE_FILE="/etc/systemd/system/ticket-ai.service"
PYTHON_BIN="$VENV_DIR/bin/python3"
ACTIVATE_SCRIPT="$VENV_DIR/bin/activate"
IPTABLES_RULES=("83.136.253.122") # Add IPs as needed
# Ensure the script is run as root
if [ "$(id -u)" != "0" ]; then
echo "This script must be run as root" 1>&2
exit 1
fi
# Installing git
yum install -y git || { echo "Failed to install git. Exiting."; exit 1; }
# Clone the repository if it doesn't exist
if [ ! -d "$APP_DIR" ]; then
git clone $GIT_REPO $APP_DIR || { echo "Failed to clone repository. Exiting."; exit 1; }
else
echo "$APP_DIR already exists. Skipping clone."
fi
# Navigate to the repo directory
cd $APP_DIR || { echo "Failed to navigate to $APP_DIR. Exiting."; exit 1; }
# Create virtual environment if it doesn't exist
if [ ! -d "$VENV_DIR" ]; then
python3 -m venv $VENV_DIR
else
echo "$VENV_DIR already exists. Skipping virtual environment creation."
fi
# Activate virtual environment and install dependencies
source $ACTIVATE_SCRIPT
pip install Flask flask_jwt_extended || { echo "Failed to install Flask or flask_jwt_extended. Exiting."; exit 1; }
# Create systemd service file
cat <<EOF > $SERVICE_FILE
[Unit]
Description=Ticket AI
[Service]
Type=simple
ExecStart=$PYTHON_BIN $APP_DIR/src/app.py
[Install]
WantedBy=multi-user.target
EOF
# Reload systemd to recognize the new service and start it
systemctl daemon-reload
systemctl start ticket-ai.service || { echo "Failed to start ticket-ai.service. Exiting."; exit 1; }
# Add firewall rules to accept traffic
for IP in "${IPTABLES_RULES[@]}"; do
iptables -A INPUT -p tcp -m tcp -s $IP --dport 5000 -j ACCEPT
done
# Save iptables rules and restart the service to apply changes
service iptables save
service iptables restart || { echo "Failed to restart iptables. Exiting."; exit 1; }
echo "Setup completed successfully."

View File

@ -1,59 +1,103 @@
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
access_token = create_access_token(identity=username) @app.route('/get_logs', methods=['GET'])
return jsonify(access_token=access_token) def get_logs():
tags = request.args.getlist("tags") # Get tags in the order they appear in the query
@app.route('/get_logs', methods=['GET']) try:
def get_logs(): # Load main config.json
issue_type = request.args.get("issue_type") with open('/opt/ticket-ai/src/config.json') as config_file:
try: config = json.load(config_file)
with open('/opt/ticket-ai/src/config.json') as config_file:
config = json.load(config_file) # Check for custom.json and merge if it exists
if issue_type not in config: custom_config_path = '/opt/ticket-ai/src/custom.json'
return jsonify({"error": "Invalid issue type"}) if os.path.exists(custom_config_path):
issue_config = config[issue_type] with open(custom_config_path) as custom_file:
response = {log.get('log_file').split('*')[0]: read_log_from_dir(*split_directory_and_file(log.get('log_file')), log['lines']) if '*' in log.get('log_file') else read_log(log['log_file'], log['lines']) for log in issue_config['logs']} custom_config = json.load(custom_file)
response.update({comm.get('tag'): os.popen(comm.get('comm')).read() for comm in issue_config['commands']}) for custom_tag in custom_config["tags"]:
except Exception as e: # Remove matching tags in config if custom.json supersedes
return jsonify({"error": str(e)}) config["tags"] = [tag for tag in config["tags"] if tag["tag"] != custom_tag["tag"]]
return jsonify(response) config["tags"].append(custom_tag)
if __name__ == '__main__': response_text = "" # Initialize an empty string for the plain text response
app.run(debug=True, host="0.0.0.0") tags_found = False
# Process each tag in the given order
for tag in tags:
try:
# Search for the tag in commands and logs
tag_data = next((item for item in config["tags"] if item["tag"] == tag), None)
if not tag_data:
response_text += f"Tag '{tag}' not found.\n\n"
continue
tags_found = True # At least one valid tag is found
# Determine if the tag is a command or a log
response_text += f"===== {tag.upper()} =====\n"
if tag_data["type"] == "command":
response_text += f"{os.popen(tag_data['content']).read()}\n"
elif tag_data["type"] == "log":
log_file = tag_data["content"]
# Replace any placeholders in the log file path
log_file = replace_placeholders(log_file)
if "*" in log_file:
# Handle wildcard in log file path
response_text += f"{read_log_from_dir(*split_directory_and_file(log_file), tag_data['lines'])}\n"
else:
# Regular log file
response_text += f"{read_log(log_file, tag_data['lines'])}\n"
response_text += "\n" # Add spacing between entries
except Exception as e:
# Log the error and proceed
response_text += f"Error for tag '{tag}': {str(e)}\n\n"
# If no tags were found, return HTTP 400 with an error message
if not tags_found:
return "None of the requested tags were found.", 400
except Exception as e:
return f"Error: {str(e)}", 500
# Return the plain text response
return response_text, 200, {'Content-Type': 'text/plain'}
if __name__ == '__main__':
port = int(os.environ.get("TICKET_AI_PORT", 5000)) # Default to 5000 if no environment variable is set
app.run(debug=True, host="0.0.0.0", port=port)

View File

@ -1,111 +1,213 @@
{ {
"--Select Issue Type--": { "tags": [
"commands": [], {
"logs": [] "tag": "disk_usage",
}, "type": "command",
"Password Reset Request": { "content": "df -h | head -n 1 && df -h | grep '^/dev/sd'"
"commands": [], },
"logs": [] {
}, "tag": "tablespace_usage",
"Domain Mapping": { "type": "command",
"commands": [], "content": "output=$(/opt/ticket-ai/src/scripts/tablespaceUsage.sh) && echo \"$output\""
"logs": [] },
}, {
"Wallet/Reverse Proxy Required": { "tag": "datafile_usage",
"commands": [], "type": "command",
"logs": [] "content": "output=$(/opt/ticket-ai/src/scripts/datafileUsage.sh) && echo \"$output\""
}, },
"Others": { {
"commands": [], "tag": "db_version_info",
"logs": [] "type": "command",
}, "content": "output=$(/opt/ticket-ai/src/scripts/db_version_info.sh) && echo \"$output\""
"Email Problem": { },
"commands": [], {
"logs": [ "tag": "pga_diagnostics",
{ "type": "command",
"log_file": "/var/log/messages", "content": "output=$(/opt/ticket-ai/src/scripts/pga_diagnostics.sh) && echo \"$output\""
"lines": 50 },
} {
] "tag": "sga_diagnostics",
}, "type": "command",
"Jasper Reports": { "content": "output=$(/opt/ticket-ai/src/scripts/sga_diagnostics.sh) && echo \"$output\""
"commands": [ },
{
"comm": "top -b -n 1", {
"tag": "top" "tag": "apex_usage",
} "type": "command",
], "content": "output=$(/opt/ticket-ai/src/scripts/apexUsage.sh) && echo \"$output\""
"logs": [ },
{ {
"log_file": "/opt/tomcat/logs/catalina.out", "tag": "apex_mail_queue",
"lines": 50 "type": "command",
}, "content": "output=$(/opt/ticket-ai/src/scripts/apex_mail_queue.sh) && echo \"$output\""
{ },
"log_file": "/var/log/messages", {
"lines": 50 "tag": "apex_smtp_settings",
} "type": "command",
] "content": "output=$(/opt/ticket-ai/src/scripts/apex_smtp_settings.sh) && echo \"$output\""
}, },
"Server Unavailable": { {
"commands": [], "tag": "apex_bot_hits",
"logs": [ "type": "command",
{ "content": "output=$(/opt/ticket-ai/src/scripts/apexBots.sh) && echo \"$output\""
"log_file": "/opt/tomcat/logs/catalina.out", },
"lines": 50 {
}, "tag": "top_redo_sql",
{ "type": "command",
"log_file": "/opt/oracle/diag/rdbms/xe/XE/trace/alert_XE.log", "content": "output=$(/opt/ticket-ai/src/scripts/topRedoSQL.sh) && echo \"$output\""
"lines": 50 },
}, {
{ "tag": "dbparameters",
"log_file": "/var/log/messages", "type": "command",
"lines": 50 "content": "output=$(/opt/ticket-ai/src/scripts/showdbparameter.sh) && echo \"$output\""
} },
] {
}, "tag": "top",
"Demo1": { "type": "command",
"commands": [], "content": "top -b -n 1 | head -n 27"
"logs": [ },
{ {
"log_file": "/home/arehman/Documents/Projects/Python/Maxapex/ticket_ai_flask_app/logs/*_error.log", "tag": "iostat",
"lines": 50 "type": "command",
} "content": "iostat -x 1 3"
] },
}, {
"Demo2": { "tag": "process_finder",
"commands": [], "type": "command",
"logs": [ "content": "output=$(/opt/ticket-ai/src/scripts/processfinder.sh) && echo \"$output\""
{ },
"log_file": "/home/arehman/Documents/Projects/Python/Maxapex/ticket_ai_flask_app/logs/a_error.log", {
"lines": 50 "tag": "ords_config",
} "type": "command",
] "content": "cat /opt/ords/config/databases/default/pool.xml"
}, },
"Demo3": { {
"commands": [ "tag": "date_time",
{ "type": "command",
"comm": "top -b -n 1", "content": "echo Current Date and time on server: ; date"
"tag": "top" },
} {
], "tag": "apache_error_logs",
"logs": [] "type": "command",
}, "content": "find {virtualmin_error_log} -type f -name '*error_log*' -mmin -15 -exec sh -c 'echo \"===== {} =====\"; tail -n 50 {}' \\;"
"Demo": { },
"commands": [ {
{ "tag": "relay_connection",
"comm": "top -b -n 1", "type": "command",
"tag": "top" "content": "timeout 5 bash -c 'echo > /dev/tcp/relay.maxapex.net/2525' && echo 'Relay Connected' || echo 'Relay Connection failed'"
} },
], {
"logs": [ "tag": "banned_ips",
{ "type": "command",
"log_file": "/home/arehman/Documents/Projects/Python/Maxapex/ticket_ai_flask_app/logs/a_error.log", "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"
"lines": 50 },
}, {
{ "tag": "wallet_certs",
"log_file": "/home/arehman/Documents/Projects/Python/Maxapex/ticket_ai_flask_app/logs/*_error.log", "type": "command",
"lines": 50 "content": "output=$(su - oracle -s /bin/bash -c 'orapki wallet display -wallet /home/oracle/wallet/') && echo \"$output\""
} },
] {
} "tag": "journalctl",
} "type": "command",
"content": "output=$(journalctl -p 3 -xb | tail -n 200) && echo \"$output\""
},
{
"tag": "apache_connections",
"type": "command",
"content": "output=$(/opt/ticket-ai/src/scripts/apache_connections.sh) && echo \"$output\""
},
{
"tag": "os_session_summary",
"type": "command",
"content": "output=$(/opt/ticket-ai/src/scripts/os_session_summary.sh) && echo \"$output\""
},
{
"tag": "check_email_ports",
"type": "command",
"content": "output=$(/opt/ticket-ai/src/scripts/check_email_ports.sh) && echo \"$output\""
},
{
"tag": "apex_activity",
"type": "command",
"content": "output=$(/opt/ticket-ai/src/scripts/apex_activity.sh) && echo \"$output\""
},
{
"tag": "postqueue",
"type": "command",
"content": "output=$(/usr/sbin/postqueue -p) && echo \"$output\""
},
{
"tag": "postfix_status",
"type": "command",
"content": "output=$(systemctl is-active postfix) && echo \"$output\""
},
{
"tag": "dovecot_status",
"type": "command",
"content": "output=$(systemctl is-active dovecot) && echo \"$output\""
},
{
"tag": "ipv6_status",
"type": "command",
"content": "output=$(ip -6 addr show | grep -q 'inet6' && echo 'IPv6 is active' || echo 'IPv6 is not active') && echo \"$output\""
},
{
"tag": "iptables",
"type": "command",
"content": "output=$(iptables -L -n) && echo \"$output\""
},
{
"tag": "java_heap",
"type": "command",
"content": "output=$(ps aux | grep java | grep -E 'Xms|Xmx') && echo \"$output\""
},
{
"tag": "oracle_alert_logs",
"type": "log",
"content": "{oracle_alert_logs}",
"lines": 50
},
{
"tag": "ords_logs",
"type": "log",
"content": "{ords_log}",
"lines": 150
},
{
"tag": "var_log_messages",
"type": "log",
"content": "/var/log/messages",
"lines": 150
},
{
"tag": "var_log_secure",
"type": "log",
"content": "/var/log/secure",
"lines": 150
},
{
"tag": "os_release",
"type": "log",
"content": "/etc/os-release",
"lines": 150
},
{
"tag": "var_log_maillog",
"type": "log",
"content": "/var/log/maillog",
"lines": 150
},
{
"tag": "var_log_fail2ban",
"type": "log",
"content": "/var/log/fail2ban.log",
"lines": 150
},
{
"tag": "var_log_mariadb",
"type": "log",
"content": "/var/log/mariadb/mariadb.log",
"lines": 150
}
]
}

5
src/config_paths.json Normal file
View File

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

View File

@ -0,0 +1,9 @@
#!/bin/bash
# Count established connections on ports 80 and 443
connections=$(netstat -an | grep ':80 ' | grep ESTABLISHED | wc -l)
https_connections=$(netstat -an | grep ':443 ' | grep ESTABLISHED | wc -l)
echo "Active HTTP connections: $connections"
echo "Active HTTPS connections: $https_connections"

112
src/scripts/apexBots.sh Executable file
View File

@ -0,0 +1,112 @@
#!/bin/bash
# File paths for output
OUTPUT_FILE="/tmp/apex_bots.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 Bot Hits 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 ON
SET HEADING ON
SET UNDERLINE '='
SET FEEDBACK OFF
SET LINESIZE 150
SET PAGESIZE 100
COLUMN AGENT FORMAT A100
COLUMN HIT_COUNT FORMAT 999999
-- Switch to the specified container
ALTER SESSION SET CONTAINER = $container_name;
-- Print the container name for clarity
PROMPT Bot Hits on APEX in last 1 hour for container: $container_name;
SELECT
CASE
WHEN UPPER(AGENT) LIKE '%AMAZON%' THEN 'Amazon Bot'
WHEN UPPER(AGENT) LIKE '%GOOGLE%' THEN 'Google Bot'
WHEN UPPER(AGENT) LIKE '%FACEBOOK%' THEN 'Facebook Bot'
WHEN UPPER(AGENT) LIKE '%BING%' THEN 'Bing Bot'
WHEN UPPER(AGENT) LIKE '%YANDEX%' THEN 'Yandex Bot'
WHEN UPPER(AGENT) LIKE '%MJ12BOT%' THEN 'MJ12Bot'
WHEN UPPER(AGENT) LIKE '%INDEX%' THEN 'Index Bot'
WHEN UPPER(AGENT) LIKE '%DOTBOT%' THEN 'DotBot'
WHEN UPPER(AGENT) LIKE '%SEMRUSH%' THEN 'Semrush Bot'
ELSE 'Other Bot'
END AS AGENT_NAME,
COUNT(*) AS HIT_COUNT
FROM
APEX_WORKSPACE_ACTIVITY_LOG
WHERE
VIEW_DATE BETWEEN SYSDATE - (1/24) AND SYSDATE
AND (
UPPER(AGENT) LIKE '%BOT%' OR
UPPER(AGENT) LIKE '%YANDEX%' OR
UPPER(AGENT) LIKE '%MJ12BOT%' OR
UPPER(AGENT) LIKE '%INDEX%' OR
UPPER(AGENT) LIKE '%.RU%' OR
UPPER(AGENT) LIKE '%GOOGLE%' OR
UPPER(AGENT) LIKE '%FACEBOOK%'
)
GROUP BY
CASE
WHEN UPPER(AGENT) LIKE '%AMAZON%' THEN 'Amazon Bot'
WHEN UPPER(AGENT) LIKE '%GOOGLE%' THEN 'Google Bot'
WHEN UPPER(AGENT) LIKE '%FACEBOOK%' THEN 'Facebook Bot'
WHEN UPPER(AGENT) LIKE '%BING%' THEN 'Bing Bot'
WHEN UPPER(AGENT) LIKE '%YANDEX%' THEN 'Yandex Bot'
WHEN UPPER(AGENT) LIKE '%MJ12BOT%' THEN 'MJ12Bot'
WHEN UPPER(AGENT) LIKE '%INDEX%' THEN 'Index Bot'
WHEN UPPER(AGENT) LIKE '%DOTBOT%' THEN 'DotBot'
WHEN UPPER(AGENT) LIKE '%SEMRUSH%' THEN 'Semrush Bot'
ELSE 'Other Bot'
END
ORDER BY
HIT_COUNT DESC;
exit;
EOF
"
}
# Run the query for the CDB (root container)
# echo "Gathering Bot Hits 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 Bot Hits 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
cat "$OUTPUT_FILE"
# Clean up the temporary file
rm -f "$PDB_LIST_FILE"

81
src/scripts/apexUsage.sh Executable file
View File

@ -0,0 +1,81 @@
#!/bin/bash
# File paths for output
OUTPUT_FILE="/tmp/apex_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 APEX 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 ON
SET HEADING ON
SET UNDERLINE '='
SET FEEDBACK OFF
SET LINESIZE 150
SET PAGESIZE 100
COLUMN OWNER FORMAT A20
COLUMN MB_USED FORMAT 9999999.99
-- Switch to the specified container
ALTER SESSION SET CONTAINER = $container_name;
-- Print the container name for clarity
PROMPT APEX usage for container: $container_name;
SELECT
OWNER,
SUM(BYTES) / (1024 * 1024) AS MB_USED
FROM
DBA_SEGMENTS
WHERE
OWNER LIKE 'APEX%' -- Replace with your APEX schema name
GROUP BY
OWNER;
exit;
EOF
"
}
# Run the query for the CDB (root container)
# echo "Gathering APEX 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 APEX 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
cat "$OUTPUT_FILE"
# Clean up the temporary file
rm -f "$PDB_LIST_FILE"

82
src/scripts/apex_activity.sh Executable file
View File

@ -0,0 +1,82 @@
#!/bin/bash
# File paths for output
OUTPUT_FILE="/tmp/apex_activity.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 APEX User Hits 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 ON
SET HEADING ON
SET UNDERLINE '='
SET FEEDBACK OFF
SET LINESIZE 150
SET PAGESIZE 100
COLUMN WORKSPACE FORMAT A40
COLUMN HIT_COUNT FORMAT 999999
-- Switch to the specified container
ALTER SESSION SET CONTAINER = $container_name;
-- Print the container name for clarity
PROMPT APEX Session Count in last 1 hour for container: $container_name;
SELECT
WORKSPACE,
COUNT(*) AS HIT_COUNT
FROM
APEX_WORKSPACE_ACTIVITY_LOG
WHERE
VIEW_DATE BETWEEN SYSDATE - (1/24) AND SYSDATE
GROUP BY
WORKSPACE
ORDER BY
HIT_COUNT DESC;
exit;
EOF
"
}
# Run the query for the CDB (root container)
# echo "Gathering Hits 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 APEX User Hits 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
cat "$OUTPUT_FILE"
rm -f "$PDB_LIST_FILE"

80
src/scripts/apex_mail_queue.sh Executable file
View File

@ -0,0 +1,80 @@
#!/bin/bash
# File paths for output
OUTPUT_FILE="/tmp/apex_mail_queue.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 APEX Mail Queue 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 ON
SET HEADING ON
SET UNDERLINE '='
SET FEEDBACK OFF
SET LINESIZE 150
SET PAGESIZE 100
COLUMN WORKSPACE_NAME FORMAT A30
COLUMN TOTAL_EMAILS FORMAT 99999999
COLUMN UNSENT_EMAILS FORMAT 99999999
COLUMN EMAILS_WITH_ERRORS FORMAT 99999999
-- Switch to the specified container
ALTER SESSION SET CONTAINER = $container_name;
-- Print the container name for clarity
PROMPT APEX Mail Queue for container: $container_name;
SELECT
WORKSPACE_NAME,
COUNT(*) AS TOTAL_EMAILS,
SUM(CASE WHEN MAIL_SEND_COUNT = 0 THEN 1 ELSE 0 END) AS UNSENT_EMAILS,
SUM(CASE WHEN MAIL_SEND_ERROR IS NOT NULL THEN 1 ELSE 0 END) AS EMAILS_WITH_ERRORS
FROM
APEX_MAIL_QUEUE
GROUP BY
WORKSPACE_NAME
ORDER BY
WORKSPACE_NAME;
exit;
EOF
"
}
# 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 Mail Queue 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
cat "$OUTPUT_FILE"
# Clean up the temporary file
rm -f "$PDB_LIST_FILE"

View File

@ -0,0 +1,93 @@
#!/bin/bash
# File paths for output
OUTPUT_FILE="/tmp/apex_smtp_settings.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 SMTP Settings 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 ON
SET HEADING ON
SET UNDERLINE '='
SET FEEDBACK OFF
SET LINESIZE 150
SET PAGESIZE 100
COLUMN PARAMETER_NAME FORMAT A20
COLUMN PARAMETER_VALUE FORMAT A50
-- Switch to the specified container
ALTER SESSION SET CONTAINER = $container_name;
-- Print the container name for clarity
PROMPT APEX SMTP Settings for container: $container_name;
SELECT
'SMTP_HOST_ADDRESS' AS PARAMETER_NAME, APEX_INSTANCE_ADMIN.GET_PARAMETER('SMTP_HOST_ADDRESS') AS PARAMETER_VALUE
FROM
DUAL
UNION ALL
SELECT
'SMTP_PORT', APEX_INSTANCE_ADMIN.GET_PARAMETER('SMTP_HOST_PORT')
FROM
DUAL
UNION ALL
SELECT
'SMTP_USERNAME', APEX_INSTANCE_ADMIN.GET_PARAMETER('SMTP_USERNAME')
FROM
DUAL
UNION ALL
SELECT
'SMTP_PASSWORD' AS PARAMETER_NAME,
CASE
WHEN APEX_INSTANCE_ADMIN.GET_PARAMETER('SMTP_PASSWORD') IS NOT NULL THEN 'ABCDEFGHIJKLM'
ELSE NULL
END AS PARAMETER_VALUE
FROM
DUAL
UNION ALL
SELECT
'SMTP_TLS_MODE', APEX_INSTANCE_ADMIN.GET_PARAMETER('SMTP_TLS_MODE')
FROM
DUAL;
exit;
EOF
"
}
# 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 SMTP Settings 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
cat "$OUTPUT_FILE"
# Clean up the temporary file
rm -f "$PDB_LIST_FILE"

View File

@ -0,0 +1,13 @@
#!/bin/bash
ports="25 465 587 143 110 993 995"
file="/etc/sysconfig/iptables"
echo "Checking iptables rules for required ports:"
for port in $ports; do
if grep -q -- "--dport $port" $file; then
echo "Port $port is allowed in $file"
else
echo "Port $port is NOT allowed in $file"
fi
done

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

@ -0,0 +1,88 @@
#!/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 ON
SET HEADING ON
SET UNDERLINE '='
SET FEEDBACK OFF
SET LINESIZE 200
SET PAGESIZE 100
COLUMN datafile_path FORMAT A50
COLUMN tablespace_name FORMAT A20
COLUMN allocated_mb FORMAT 9999999.99
COLUMN used_mb FORMAT 9999999.99
COLUMN free_mb FORMAT 9999999.99
COLUMN pct_used FORMAT 999.99
-- 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
cat "$OUTPUT_FILE"
# Clean up the temporary file
rm -f "$PDB_LIST_FILE"

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

@ -0,0 +1,97 @@
#!/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 ON
SET HEADING ON
SET UNDERLINE '='
SET FEEDBACK OFF
SET LINESIZE 150
SET PAGESIZE 100
COLUMN BANNER FORMAT A80
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
cat "$OUTPUT_FILE"
# Clean up the temporary file
rm -f "$PDB_LIST_FILE"

View File

@ -0,0 +1,76 @@
#!/bin/bash
# Declare an associative array for grouping
declare -A summary
# Iterate through each running process
while read -r process; do
if [[ $process == ora_* || $process == oracle* ]]; then
summary["Oracle"]=$((summary["Oracle"] + 1))
elif [[ $process == php-fpm* ]]; then
summary["PHP-FPM"]=$((summary["PHP-FPM"] + 1))
elif [[ $process == httpd* ]]; then
summary["HTTPD"]=$((summary["HTTPD"] + 1))
elif [[ $process == sshd* ]]; then
summary["SSHD"]=$((summary["SSHD"] + 1))
elif [[ $process == systemd* ]]; then
summary["SystemD"]=$((summary["SystemD"] + 1))
elif [[ $process == zabbix* ]]; then
summary["Zabbix"]=$((summary["Zabbix"] + 1))
elif [[ $process == python* || $process == python3* ]]; then
summary["Python"]=$((summary["Python"] + 1))
elif [[ $process == node* ]]; then
summary["Node.js"]=$((summary["Node.js"] + 1))
elif [[ $process == bash* || $process == sh* ]]; then
summary["Shell"]=$((summary["Shell"] + 1))
elif [[ $process == java* ]]; then
summary["Java"]=$((summary["Java"] + 1))
elif [[ $process == tnslsnr* ]]; then
summary["TNS Listener"]=$((summary["TNS Listener"] + 1))
elif [[ $process == dovecot* || $process == postfix* || $process == saslauthd* ]]; then
summary["Mail"]=$((summary["Mail"] + 1))
elif [[ $process == NetworkManager* ]]; then
summary["NetworkManager"]=$((summary["NetworkManager"] + 1))
elif [[ $process == cron* || $process == crond* ]]; then
summary["Cron"]=$((summary["Cron"] + 1))
else
summary["Uncategorized"]=$((summary["Uncategorized"] + 1))
fi
done < <(ps -eo comm=)
# Print the summary
echo "Program-wise session summary:"
for program in "${!summary[@]}"; do
printf "%-15s %s\n" "$program" "${summary[$program]}"
done
# Declare an associative array for user sessions
declare -A user_sessions
declare -A active_users
# Fetch loginctl session details and process them
while read -r session user_id state; do
# Get username from user ID using getent
username=$(getent passwd "$user_id" | cut -d: -f1)
# If username is valid, count sessions
if [[ -n $username ]]; then
user_sessions["$username"]=$((user_sessions["$username"] + 1))
# Check if the session is active
if [[ $state == "active" ]]; then
active_users["$username"]=1
fi
fi
done < <(loginctl list-sessions --no-legend | awk '{print $1, $2, $3}')
# Print the user-wise session summary
echo "User-wise session summary from loginctl (with usernames):"
printf "%-15s %-10s %-10s\n" "Username" "Sessions" "Connected"
for username in "${!user_sessions[@]}"; do
connected="No"
if [[ ${active_users[$username]} -eq 1 ]]; then
connected="Yes"
fi
printf "%-15s %-10s %-10s\n" "$username" "${user_sessions[$username]}" "$connected"
done

139
src/scripts/pga_diagnostics.sh Executable file
View File

@ -0,0 +1,139 @@
#!/bin/bash
# High PGA-Consuming Sessions
echo "=== High PGA-Consuming Sessions and SQL Statements ==="
echo "This query shows sessions and SQL statements consuming the most PGA memory, with values in MB."
su - oracle -s /bin/bash -c "
sqlplus -s / as sysdba <<EOF
SET LINESIZE 200
COLUMN USERNAME FORMAT A20
COLUMN MODULE FORMAT A30
COLUMN PGA_ALLOC_MB FORMAT 99999.99
COLUMN PGA_USED_MB FORMAT 99999.99
COLUMN SQL_TEXT FORMAT A50
SELECT
s.SID,
s.SERIAL#,
s.USERNAME,
s.MODULE,
ROUND(p.PGA_ALLOC_MEM / 1024 / 1024, 2) AS PGA_ALLOC_MB,
ROUND(p.PGA_USED_MEM / 1024 / 1024, 2) AS PGA_USED_MB,
q.SQL_TEXT
FROM
V\\\$SESSION s
JOIN
V\\\$PROCESS p
ON
s.PADDR = p.ADDR
LEFT JOIN
V\\\$SQL q
ON
s.SQL_ID = q.SQL_ID
WHERE
p.PGA_ALLOC_MEM > (SELECT VALUE FROM V\\\$PGASTAT WHERE NAME = 'total PGA allocated') / 10
ORDER BY
p.PGA_ALLOC_MEM DESC;
EOF
"
# PGA Usage Overview
echo "=== PGA Usage Overview ==="
echo "This query provides a summary of current PGA usage statistics, with values in MB."
su - oracle -s /bin/bash -c "
sqlplus -s / as sysdba <<EOF
SET LINESIZE 200
COLUMN NAME FORMAT A40
COLUMN VALUE_MB FORMAT 999999.99
SELECT
NAME,
ROUND(VALUE / 1024 / 1024, 2) AS VALUE_MB
FROM
V\\\$PGASTAT
WHERE
NAME IN ('total PGA allocated', 'total PGA inuse',
'maximum PGA allocated', 'over allocation count');
EOF
"
# Sessions Using Temporary Tablespace
echo "=== Sessions Using Temporary Tablespace ==="
echo "This query identifies sessions that are spilling to temporary tablespaces, with values in MB."
su - oracle -s /bin/bash -c "
sqlplus -s / as sysdba <<EOF
SET LINESIZE 200
COLUMN USERNAME FORMAT A20
COLUMN TABLESPACE FORMAT A20
COLUMN TEMP_MB FORMAT 99999.99
SELECT
s.SID,
s.SERIAL#,
s.USERNAME,
tsu.TABLESPACE,
ROUND(tsu.BLOCKS * (SELECT VALUE FROM V\\\$PARAMETER WHERE NAME = 'db_block_size') / 1024 / 1024, 2) AS TEMP_MB
FROM
V\\\$SESSION s
JOIN
V\\\$TEMPSEG_USAGE tsu
ON
s.SID = tsu.SESSION_NUM
ORDER BY
TEMP_MB DESC;
EOF
"
# Active Sessions Using Large Work Areas
echo "=== Active Sessions Using Large Work Areas ==="
echo "This query lists sessions using significant PGA memory for sorts, hash joins, or other large work areas."
su - oracle -s /bin/bash -c "
sqlplus -s / as sysdba <<EOF
SET LINESIZE 200
COLUMN SID FORMAT 99999
COLUMN OPERATION_TYPE FORMAT A20
COLUMN WORK_AREA_SIZE_MB FORMAT 99999.99
COLUMN ACTUAL_MEM_USED_MB FORMAT 99999.99
COLUMN NUMBER_PASSES FORMAT 99999
SELECT
SID,
OPERATION_TYPE,
ROUND(WORK_AREA_SIZE / 1024 / 1024, 2) AS WORK_AREA_SIZE_MB,
ROUND(ACTUAL_MEM_USED / 1024 / 1024, 2) AS ACTUAL_MEM_USED_MB,
NUMBER_PASSES
FROM
V\\\$SQL_WORKAREA_ACTIVE
WHERE
ACTUAL_MEM_USED > 0
ORDER BY
ACTUAL_MEM_USED_MB DESC;
EOF
"
# Top Resource-Consuming SQL Statements
echo "=== Top Resource-Consuming SQL Statements ==="
echo "This query highlights the SQL statements with the highest disk reads, buffer gets, and elapsed times, all in MB."
su - oracle -s /bin/bash -c "
sqlplus -s / as sysdba <<EOF
SET LINESIZE 200
COLUMN SQL_ID FORMAT A15
COLUMN DISK_READS_MB FORMAT 99999.99
COLUMN BUFFER_GETS_MB FORMAT 999999.99
COLUMN ELAPSED_TIME_SEC FORMAT 99999.99
COLUMN SQL_TEXT FORMAT A50
SELECT
SQL_ID,
ROUND(DISK_READS / 1024 / 1024, 2) AS DISK_READS_MB,
ROUND(BUFFER_GETS / 1024 / 1024, 2) AS BUFFER_GETS_MB,
ROUND(ELAPSED_TIME / 1000000, 2) AS ELAPSED_TIME_SEC,
SQL_TEXT
FROM
V\\\$SQL
WHERE
DISK_READS > 100
ORDER BY
DISK_READS DESC FETCH FIRST 10 ROWS ONLY;
EOF
"

55
src/scripts/processfinder.sh Executable file
View File

@ -0,0 +1,55 @@
#!/bin/bash
# Define the CPU usage threshold
CPU_THRESHOLD=50
# File path for output
OUTPUT_FILE="/tmp/findsql.tmp"
touch $OUTPUT_FILE
chown oracle $OUTPUT_FILE
# Capture the PIDs of Oracle processes consuming over the specified CPU threshold
pid=($(top -b -n 1 | awk -v threshold="$CPU_THRESHOLD" '$12 ~ /^oracle/ && $9 > threshold {print $1}'))
echo $pid
# Check if there are any PIDs in the array
if [ ${#pid[@]} -gt 0 ]; then
echo "Following may be the SQL statements consuming top CPU" > "$OUTPUT_FILE"
chown oracle "$OUTPUT_FILE"
# Construct a comma-separated list of PIDs
pid_list=$(IFS=,; echo "${pid[*]}")
# Execute the SQL command as the oracle user
su - oracle -s /bin/bash -c "
sqlplus -s / as sysdba <<'EOF' >> \"$OUTPUT_FILE\"
whenever sqlerror exit sql.sqlcode;
SET ECHO ON
SET HEADING ON
SET UNDERLINE '='
SET FEEDBACK OFF
SET LINESIZE 200
SET PAGESIZE 100
COLUMN datafile_path FORMAT A50
COLUMN tablespace_name FORMAT A20
COLUMN allocated_mb FORMAT 9999999.99
COLUMN used_mb FORMAT 9999999.99
COLUMN free_mb FORMAT 9999999.99
COLUMN pct_used FORMAT 999.99
select DISTINCT
s.CLIENT_IDENTIFIER, s.schemaname, s.module, s.action, q.sql_id, q.sql_text
from v\$session s, v\$process p, v\$sql q
where s.paddr = p.addr
and p.spid IN ($pid_list)
and q.sql_text is not null
and s.sql_id = q.sql_id(+);
exit;
EOF
"
else
echo "No Oracle processes consuming significant CPU" > "$OUTPUT_FILE"
fi
cat "$OUTPUT_FILE"

110
src/scripts/sga_diagnostics.sh Executable file
View File

@ -0,0 +1,110 @@
#!/bin/bash
# Overall SGA Usage
echo "=== Overall SGA Usage ==="
echo "This query shows the sizes of all major SGA components (e.g., buffer cache, shared pool, redo log) in MB."
su - oracle -s /bin/bash -c "
sqlplus -s / as sysdba <<EOF
SET LINESIZE 200
COLUMN NAME FORMAT A40
COLUMN VALUE_MB FORMAT 999999999.99
SELECT
NAME,
ROUND(BYTES / 1024 / 1024, 2) AS VALUE_MB
FROM
V\\\$SGAINFO;
EOF
"
# Shared Pool Usage
echo "=== Shared Pool Usage ==="
echo "This query provides details on the shared pool memory usage (session memory, UGA, shared pool) in MB."
su - oracle -s /bin/bash -c "
sqlplus -s / as sysdba <<EOF
SET LINESIZE 200
COLUMN NAME FORMAT A40
COLUMN VALUE_MB FORMAT 9999999.99
SELECT
NAME,
ROUND(VALUE / 1024 / 1024, 2) AS VALUE_MB
FROM
V\\\$SYSSTAT
WHERE
NAME LIKE 'session%memory%'
OR NAME LIKE 'session uga memory%'
OR NAME LIKE 'shared pool%';
EOF
"
# Buffer Cache Statistics
echo "=== Buffer Cache Statistics ==="
echo "This query shows buffer cache usage and performance metrics, including logical and physical reads."
su - oracle -s /bin/bash -c "
sqlplus -s / as sysdba <<EOF
SET LINESIZE 200
COLUMN NAME FORMAT A40
COLUMN VALUE FORMAT 9999999.99
SELECT
NAME,
ROUND(VALUE / 1024 / 1024, 2) AS VALUE_MB
FROM
V\\\$SYSSTAT
WHERE
NAME LIKE 'physical reads%'
OR NAME LIKE 'buffer%'
OR NAME LIKE 'cache%';
EOF
"
# Library Cache Usage
echo "=== Library Cache Usage ==="
echo "This query analyzes memory usage in the library cache, focusing on SQL and PL/SQL execution and reload rates."
su - oracle -s /bin/bash -c "
sqlplus -s / as sysdba <<EOF
SET LINESIZE 200
COLUMN NAMESPACE FORMAT A25
COLUMN GETS FORMAT 999999999
COLUMN PINS FORMAT 999999999
COLUMN RELOADS FORMAT 999999999
COLUMN PIN_HIT_RATIO FORMAT 999.99
SELECT
NAMESPACE,
ROUND(GETS / 1024 / 1024, 2) AS GETS_MB,
ROUND(PINS / 1024 / 1024, 2) AS PINS_MB,
ROUND(RELOADS / 1024 / 1024, 2) AS RELOADS_MB,
ROUND((PINS - RELOADS) / DECODE(PINS, 0, 1, PINS) * 100, 2) AS PIN_HIT_RATIO
FROM
V\\\$LIBRARYCACHE
ORDER BY
PINS DESC;
EOF
"
# Top SQL Area Memory Consumers
echo "=== Top SQL Area Memory Consumers ==="
echo "This query lists SQL statements that consume the most memory in the shared pool, with disk reads and buffer gets in MB."
su - oracle -s /bin/bash -c "
sqlplus -s / as sysdba <<EOF
SET LINESIZE 200
COLUMN SQL_TEXT FORMAT A80
COLUMN PARSE_CALLS FORMAT 9999999
COLUMN DISK_READS_MB FORMAT 9999999.99
COLUMN BUFFER_GETS_MB FORMAT 9999999.99
SELECT
SQL_TEXT,
PARSE_CALLS,
ROUND(DISK_READS / 1024 / 1024, 2) AS DISK_READS_MB,
ROUND(BUFFER_GETS / 1024 / 1024, 2) AS BUFFER_GETS_MB
FROM
V\\\$SQLAREA
WHERE
BUFFER_GETS > 10000
ORDER BY
BUFFER_GETS DESC FETCH FIRST 10 ROWS ONLY;
EOF
"

58
src/scripts/showdbparameter.sh Executable file
View File

@ -0,0 +1,58 @@
#!/bin/bash
# File path for output
OUTPUT_FILE="/tmp/database_parameters.tmp"
> "$OUTPUT_FILE" # Clear the output file before starting
touch $OUTPUT_FILE
chown oracle $OUTPUT_FILE
# Execute the query to retrieve all database parameters
su - oracle -s /bin/bash -c "
sqlplus -s / as sysdba <<EOF >> \"$OUTPUT_FILE\"
whenever sqlerror exit sql.sqlcode;
-- Set SQL*Plus Formatting
SET LINESIZE 200
SET PAGESIZE 50
COLUMN parameter_name FORMAT A35
COLUMN current_value FORMAT A25
COLUMN is_default FORMAT A12
COLUMN is_sys_modifiable FORMAT A18
COLUMN description FORMAT A80
-- SQL Query for Important Parameters
SELECT
name AS parameter_name,
value AS current_value,
isdefault AS is_default,
issys_modifiable AS is_sys_modifiable,
description
FROM
v\\\$parameter
WHERE
name IN (
-- Memory Management
'sga_target', 'sga_max_size', 'pga_aggregate_target', 'memory_target',
'memory_max_target', 'db_cache_size', 'shared_pool_size', 'large_pool_size',
'log_buffer',
-- Sessions and Processes
'sessions', 'processes', 'transactions',
-- Cursors
'open_cursors', 'session_cached_cursors', 'cursor_sharing',
-- Redo and Logs
'log_checkpoint_interval', 'log_checkpoint_timeout', 'log_buffer',
-- Other Important Performance Parameters
'db_block_size', 'optimizer_mode', 'parallel_max_servers'
)
ORDER BY name;
exit;
EOF
"
# Notify user about the location of the output file
echo "Database parameters have been saved to $OUTPUT_FILE"
cat "$OUTPUT_FILE"

91
src/scripts/tablespaceUsage.sh Executable file
View File

@ -0,0 +1,91 @@
#!/bin/bash
# File paths for output
OUTPUT_FILE="/tmp/tablespace_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 tablespace 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 ON
SET HEADING ON
SET UNDERLINE '='
SET FEEDBACK OFF
SET LINESIZE 150
SET PAGESIZE 100
COLUMN tablespace_name FORMAT A25
COLUMN allocated FORMAT 9999999.99
COLUMN used FORMAT 9999999.99
COLUMN free FORMAT 9999999.99
COLUMN PCT_USED FORMAT 999.99
-- Switch to the specified container
ALTER SESSION SET CONTAINER = $container_name;
-- Print the container name for clarity
PROMPT Tablespace usage for container: $container_name;
SELECT a.tablespace_name,
c.bytes/1024/1024 allocated,
round(c.bytes/1024/1024 - nvl(b.bytes,0)/1024/1024, 2) used,
round(nvl(b.bytes,0)/1024/1024, 2) free,
ROUND(((c.bytes - nvl(b.bytes,0)) / c.bytes) * 100, 2) PCT_USED
FROM dba_tablespaces a,
(SELECT tablespace_name, SUM(bytes) bytes
FROM dba_free_space
GROUP BY tablespace_name) b,
(SELECT COUNT(1) datafiles, SUM(bytes) bytes, tablespace_name
FROM dba_data_files
GROUP BY tablespace_name) c
WHERE b.tablespace_name(+) = a.tablespace_name
AND c.tablespace_name(+) = a.tablespace_name
AND a.tablespace_name NOT LIKE 'UNDO%'
AND a.tablespace_name NOT LIKE 'TEMP%'
ORDER BY tablespace_name;
exit;
EOF
"
}
# Run the query for the CDB (root container)
echo "Gathering tablespace 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 tablespace 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
cat "$OUTPUT_FILE"
# Clean up the temporary file
rm -f "$PDB_LIST_FILE"

108
src/scripts/topRedoSQL.sh Executable file
View File

@ -0,0 +1,108 @@
#!/bin/bash
# File paths for output
OUTPUT_FILE="/tmp/top_redo_sql.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 Redo Size 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 ON
SET HEADING ON
SET UNDERLINE '='
SET FEEDBACK OFF
SET LINESIZE 200
SET PAGESIZE 100
COLUMN SID FORMAT 99999
COLUMN SERIAL# FORMAT 999999
COLUMN SQL_TEXT FORMAT A100
COLUMN USERNAME FORMAT A20
COLUMN REDO_SIZE_MB FORMAT 999999.99
-- Switch to the specified container
ALTER SESSION SET CONTAINER = $container_name;
-- Print the container name for clarity
PROMPT Top redo size sessions for container: $container_name;
SELECT
S.SID,
S.SERIAL#,
Q.SQL_TEXT,
S.USERNAME,
ROUND(R.VALUE / (1024 * 1024), 2) AS REDO_SIZE_MB
FROM
V\\\$SESSION S
JOIN
V\\\$SQL Q ON S.SQL_ADDRESS = Q.ADDRESS
JOIN
V\\\$SESSTAT R ON S.SID = R.SID
JOIN
V\\\$STATNAME N ON R.STATISTIC# = N.STATISTIC#
WHERE
N.NAME = 'redo size'
AND NVL( R.VALUE, 0 ) <> 0
AND S.LAST_CALL_ET < 7200 -- Filter sessions active within last 2 hours
ORDER BY
R.VALUE DESC
FETCH FIRST 10 ROWS ONLY;
SELECT
Q.PARSING_SCHEMA_NAME AS USERNAME,
Q.EXECUTIONS,
Q.ROWS_PROCESSED,
Q.SQL_TEXT
FROM
V\\\$SQL Q
WHERE
Q.ROWS_PROCESSED > 10
AND UPPER(Q.SQL_TEXT) NOT LIKE 'SELECT%'
AND Q.PARSING_USER_ID != 0 -- Ignore SYS
AND Q.COMMAND_TYPE != 47 -- Ignore PL/SQL
ORDER BY
Q.ROWS_PROCESSED DESC
FETCH FIRST 10 ROWS ONLY;
exit;
EOF
"
}
# 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 top redo size sessions 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
cat "$OUTPUT_FILE"
# Clean up the temporary file
rm -f