Compare commits

...

42 Commits

Author SHA1 Message Date
d8752eb721 feat(hanatool): Add path discovery for hdbsql 2025-11-20 13:46:03 +01:00
668f56e56c feat(keymanager): Add alternative path detection for the tools needed to manage users 2025-11-20 13:25:40 +01:00
9acf30da12 func(b1conf): Update config generator to include AD integration 2025-10-16 14:12:34 +02:00
46673d88d2 feat(hanatool): Add NO DEPENDENCIES to export function 2025-10-15 15:03:11 +02:00
557cb807dd fix(backup): Fix an issue where backup script would try to create an obsolete dir for the systemdb backup 2025-10-09 10:15:34 +02:00
4274f4d01d chore: Add signature to scripts 2025-10-08 21:45:11 +02:00
66da934be2 fix(backup): Fixed obsolete config option. 2025-10-08 21:18:47 +02:00
3355727a9b refactor(backup): Rewrite backup tool to use hanatool instead of its own implementation. 2025-10-08 21:14:02 +02:00
6a94e19a94 fix(hanatool): Improve backup notifications and bump version 2025-10-08 20:09:38 +02:00
c35f2528cf fix(keymanager): Fixed SYSTEMDB user creation 2025-10-08 20:03:40 +02:00
8a5f76bbe4 feat(hanatool): Include HANA tenant name in backup file names 2025-10-08 19:56:24 +02:00
d428def0a2 feat(hanatool): Improve compressed tenant backup logic
Refactored the compressed tenant backup process to use a temporary directory within the specified TARGET_PATH, aligning its behavior with schema exports. This change avoids the use of the /tmp directory, which can be too small for large backups.

- Create a temporary directory in TARGET_PATH for the backup.
- Perform the tenant backup into this temporary directory.
- Compress the backup files to a .tar.gz archive in TARGET_PATH.
- Clean up the temporary backup directory after compression.
- Bumped hanatool.sh version to 1.5.2.
- Updated hanatool version in packages.conf to 1.5.2.
2025-10-08 19:26:39 +02:00
2fe4ba0fd2 feat(b1.gen): Add B1 config generator 2025-10-08 17:55:29 +02:00
b801c2c002 feat(backup): Introduce backup hook script and adjust configuration 2025-10-06 11:22:59 +02:00
80fd12f0f9 fix(monitor): Remove incorrect folder from path 2025-10-06 11:21:53 +02:00
f597ae09aa feat(monitor): Implement conditional configuration in monitor.hook.sh 2025-10-06 10:55:10 +02:00
bd35ddbab6 fix(monitor): Resolve sed delimiter issue in monitor.hook.sh
Updated the sed commands in monitor.hook.sh to use # as a delimiter instead of / when updating SAPCONTROL_PATH and HDBSQL_PATH. This resolves "unknown option to s" errors caused by path slashes conflicting with the sed syntax.
2025-10-06 10:13:57 +02:00
1bd67d3613 chore(monitor): Update monitor.conf paths to placeholders 2025-10-06 10:09:37 +02:00
1c254115c4 fix(monitor): Adjust monitor.hook.sh to correctly update monitor.conf 2025-10-06 10:07:32 +02:00
b0553c5826 refactor(install): Use curl for install script execution and revert monitor hook URL 2025-10-06 10:04:45 +02:00
56e781996a fix(packages): Simplify monitor hook URL in packages.conf 2025-10-06 09:59:50 +02:00
4e98731bd1 fix(packages): Correct quoting for monitor hook curl execution 2025-10-06 09:54:59 +02:00
a2579ab3d5 feat(packages): Update monitor hook to use direct curl execution 2025-10-06 09:53:19 +02:00
b983b9e953 feat(monitor): Introduce monitor.hook.sh and update version to 1.3.1 2025-10-06 09:49:43 +02:00
1c4c7ebcc6 feat(install): Add optional install script execution for packages
This commit introduces the ability for packages to define an optional install script
within the `packages.conf` file. The `install.sh` script will now parse this
new field and execute the provided script after all associated package files
have been downloaded.

The `packages.conf` documentation has been updated to reflect the new format,
including the optional fifth field for the install script.
2025-10-06 09:41:12 +02:00
52bc1ed352 fix(hanatool): Initialize import_options and bump version to 1.5.1 2025-10-03 16:08:04 +02:00
ec0c686a3c chore(packages): Update Monitor Suite version to 1.3.0
This commit updates the version of the Monitor Suite in packages.conf to 1.3.0, aligning with the recent feature additions and refactoring in the monitoring scripts.
2025-10-01 13:11:34 +02:00
bb0531aeea feat(monitor): Add HANA statement queue monitoring
This commit introduces a new feature to monitor the HANA statement queue.

Added STATEMENT_QUEUE_THRESHOLD and STATEMENT_QUEUE_CONSECUTIVE_RUNS to monitor/monitor.conf.
Implemented logic in monitor/monitor.sh to query the statement queue length, track consecutive breaches of the defined threshold, and send notifications.
Updated the script version to 1.3.0.
Refactored log segment checks to only run when segments are found.
2025-10-01 13:10:57 +02:00
92a2b963c4 refactor(readme): Improved README, make it way more readable 2025-09-26 15:19:31 +02:00
a8fc2c07e8 refactor(readme): Improve README.md structure, update tool descriptions, and remove non-HANA related scripts. 2025-09-26 15:05:41 +02:00
6b2132a7ab feat(monitor): Bump version to 1.2.3 and refactor notification logic
Updated monitor/monitor.sh to version 1.2.3.
Removed the redundant else block in the send_notification_if_changed() function in monitor/monitor.sh as it provided no functional change.
Updated the "Monitor Suite" version in packages.conf to 1.2.3 to reflect the script update.
2025-09-25 18:44:59 +02:00
2549ccf250 feat: Remove verbose "state unchanged" messages and bump version to 1.2.2 2025-09-25 18:42:43 +02:00
e083c5b749 fix(monitor): Remove 'local' from global variables and bump version to 1.2.1
Removed incorrect 'local' keyword from global variable declarations in monitor/monitor.sh as it's only valid within functions. Updated version to 1.2.1 in monitor/monitor.sh and packages.conf.
2025-09-25 18:38:41 +02:00
eeb5b2eb7b feat(monitor): Implement state-based notifications to prevent alert spam
Introduces state management to 'monitor.sh' to send notifications only when a monitored status changes (e.g., from healthy to alert, or alert to resolved). This prevents repetitive alerts for persistent issues. Creates a 'monitor_state' directory for tracking. Updates script version to 1.2.0.
2025-09-25 18:34:40 +02:00
a6150467e5 fix(monitor): Correct hdbsql command in backup monitoring
Fixes an issue in 'monitor.sh' where the HANA backup monitoring used incorrect variable names ('HANA_USERKEY' instead of 'HANA_USER_KEY') and did not explicitly use the configured 'HDBSQL_PATH' for the 'hdbsql' command. Updates script version to 1.1.1.
2025-09-25 18:33:58 +02:00
2424d55426 feat(monitor): Add HANA data backup age monitoring
Introduces a new monitoring check in 'monitor.sh' to verify the age of the last successful SAP HANA data backup. Alerts are sent if the backup age exceeds a configurable 'BACKUP_THRESHOLD_HOURS'. Updates script version to 1.1.0.
2025-09-25 18:33:49 +02:00
408f2396da refactor: Rename cleaner.sh and keymanager.sh scripts
Renames 'clean.sh' to 'cleaner.sh' and 'hdb_keymanager.sh' to 'keymanager.sh' for consistency. Updates corresponding file paths in 'packages.conf'.
2025-09-25 18:33:37 +02:00
a16b8aa42b feat(installer): Rework install.sh for non-interactive mode and improved UX
Performs a major refactoring of 'install.sh' to introduce non-interactive installation via command-line arguments (e.g., '--overwrite-config'). Enhances the interactive menu with detailed package information (display name, description, version, update status) and improves config file handling with diff previews. Updates 'packages.conf' format to support new package metadata and uses short, lowercase keys.
2025-09-25 18:33:24 +02:00
d9760b9072 docs(readme): Update Aurora script usage to reflect automation
Updates 'README.md' to reflect the new, automated execution model of 'aurora.sh', removing references to manual 'new', 'complete', and 'info' arguments. The script is now configured via 'aurora.conf' and typically run via cron.
2025-09-25 18:33:09 +02:00
229683dfa5 feat(aurora): Dynamically fetch and update company name in refreshed schema
Enhances 'aurora.sh' to fetch the original company name from the source schema and use it to construct a descriptive company name for the new Aurora schema (e.g., "AURORA - [Original Name] - [Date]"). Updates script version to 2.1.0.
2025-09-25 18:31:51 +02:00
2d5d2dfa9c refactor(aurora): Complete rewrite of Aurora refresh script to v2.0.0
Performs a major refactoring of 'aurora.sh' and 'aurora.conf'. The script is rewritten for improved clarity, error handling, and a streamlined, single-purpose execution flow for automated HANA schema refreshes. Configuration variables are renamed for better understanding. Updates script version to 2.0.0.
2025-09-25 18:31:35 +02:00
61e44106e5 feat(hanatool): Add --replace flag for schema imports
Introduces a new '--replace' option to 'hanatool.sh' to allow replacing existing objects during schema import operations. Updates script version to 1.5.0.
2025-09-25 18:30:45 +02:00
15 changed files with 987 additions and 547 deletions

156
README.md
View File

@@ -1,122 +1,82 @@
# SAP HANA Automation Scripts # 🚀 SAP HANA Automation Scripts
This repository contains a collection of Bash and Batch scripts designed to automate various administrative and monitoring tasks for SAP HANA databases. A collection of powerful Bash scripts designed to automate and simplify SAP HANA administration, monitoring, and management tasks.
## Installation ## ✨ Key Features
To get started, you can use the `install.sh` script to download and set up the tools: * **Automate Everything**: Schedule routine backups, file cleanups, and schema refreshes.
* **Monitor Proactively**: Keep an eye on system health, disk space, and backup status with automated alerts.
* **Simplify Management**: Use powerful command-line tools and interactive menus for common tasks.
* **Secure**: Integrates with SAP's secure user store (`hdbuserstore`) for credential management.
* **Get Notified**: Receive completion and failure alerts via `ntfy.sh`.
## ⚙️ Quick Install
Get started in seconds. The interactive installer will guide you through selecting the tools you need.
```sh ```sh
bash -c "$(curl -sSL https://install.technopunk.space)" bash -c "$(curl -sSL https://install.technopunk.space)"
``` ```
This command will download and execute the `install.sh` script, which provides an interactive menu to select and install individual tools. ## 🛠️ Tools Overview
## Tools Overview The following scripts and suites are included. Suites are configured via a `.conf` file in their respective directories.
Here's a breakdown of the scripts included in this repository: | Tool | Purpose & Core Function |
| :------------- | :------------------------------------------------------------------------------------------------------------------------------------------- |
| **`cleaner`** 🧹 | **File Cleaner**: Deletes files older than a specified retention period. Ideal for managing logs and temporary files. |
| **`hanatool`** 🗄️ | **HANA Management**: A powerful CLI tool to export/import schemas, perform full tenant backups, and compress artifacts. |
| **`keymanager`** 🔑 | **Key Manager**: An interactive menu to easily create, delete, and test `hdbuserstore` keys with an automatic rollback safety feature. |
| **`aurora`** 🌅 | **Schema Refresh Suite**: Automates refreshing a non-production schema from a production source. |
| **`backup`** 💾 | **Backup Suite**: A complete, cron-friendly solution for scheduling schema exports and/or full tenant backups with configurable compression. |
| **`monitor`** 📊 | **Monitoring Suite**: Continuously checks HANA process status, disk usage, log segments, and backup age, sending alerts when thresholds are breached. |
### 1. `install.sh` (Script Downloader) ## 📖 Tool Details
* **Purpose**: An interactive script to download and manage other scripts from a remote `packages.conf` file. ### 1\. `cleaner.sh` (File Cleaner) 🧹
* **Features**:
* Presents a menu of available tools.
* Checks for updates to installed scripts.
* Shows a `diff` for configuration files before overwriting.
* Automatically makes downloaded shell scripts executable.
* **Usage**: Run the script and follow the on-screen prompts.
### 2. `packages.conf` (Configuration for `install.sh`) * **Purpose**: Deletes files older than a specified retention period from given directories to help manage disk space.
* **Purpose**: Defines the list of available scripts, their versions, and their download URLs for `install.sh`. ### 2\. `hanatool.sh` (SAP HANA Schema & Tenant Management) 🗄️
* **Format**: An associative array `SCRIPT_PACKAGES` where keys are package names and values are `version|URL1 URL2...`.
### 3. `clean.sh` (File Cleaner) * **Purpose**: A versatile command-line utility for SAP HANA, enabling quick exports and imports of schemas, as well as full tenant backups.
* **Features**:
* **Purpose**: Deletes files older than a specified retention period in given directories. * Export/Import schemas (with optional renaming).
* **Usage**: `./clean.sh <retention_days>:<path> [<retention_days>:<path> ...]` * Perform full tenant backups.
* **Example**: `./clean.sh 30:/var/log 7:/tmp/downloads`
### 4. `hanatool.sh` (SAP HANA Schema and Tenant Management Tool)
* **Purpose**: A versatile tool for exporting/importing HANA schemas and performing tenant backups.
* **Features**:
* Export a schema.
* Import a schema (with optional renaming).
* Perform a full tenant backup.
* Supports compression (`tar.gz`).
* Dry-run mode to preview commands. * Dry-run mode to preview commands.
* Ntfy.sh notifications for completion/failure. * `ntfy.sh` notifications for task completion/failure.
* Custom `hdbsql` path. * **Options**: `-t, --threads N`, `-c, --compress`, `-n, --dry-run`, `--ntfy <token>`, `--replace`, `--hdbsql <path>`, `-h, --help`
* **Usage**:
* Schema: `./hanatool.sh [USER_KEY] export|import [SCHEMA_NAME] [PATH] [OPTIONS]`
* Schema Rename: `./hanatool.sh [USER_KEY] import-rename [SCHEMA_NAME] [NEW_SCHEMA_NAME] [PATH] [OPTIONS]`
* Tenant: `./hanatool.sh [USER_KEY] backup [PATH] [OPTIONS]`
* **Options**: `-t, --threads N`, `-c, --compress`, `-n, --dry-run`, `--ntfy <token>`, `--hdbsql <path>`, `-h, --help`
### 5. `hdb_keymanager.sh` (SAP HANA Secure User Store Key Manager) ### 3\. `keymanager.sh` (Secure User Store Key Manager) 🔑
* **Purpose**: An interactive script to manage SAP HANA `hdbuserstore` keys. * **Purpose**: An interactive script to simplify the creation, deletion, and testing of SAP HANA `hdbuserstore` keys.
* **Features**: * **Features**:
* Create new secure keys with interactive prompts. * Interactive menu for easy key management.
* Delete existing keys from a selection list. * Connection testing for existing keys.
* Test connections for existing keys. * Automatic rollback of a newly created key if its connection test fails.
* Includes rollback functionality if a newly created key fails to connect.
* **Usage**: `./hdb_keymanager.sh` (runs an interactive menu)
### 6. `update.bat` (Git Update Script for Windows) ### 4\. `aurora.sh` (HANA Aurora Refresh Suite) 🌅
* **Purpose**: A simple Windows Batch script to stage all changes, commit with a user-provided message, and push to a Git repository. * **Purpose**: Automates the refresh of a "copy" schema from a production source, ensuring non-production environments stay up-to-date.
* **Usage**: `update.bat` * **Process**:
1. Drops the existing target schema (optional).
2. Exports the source schema from production.
3. Imports and renames the data to the target schema.
4. Runs post-import configurations and grants privileges.
### 7. `aurora/aurora.sh` (HANA Aurora Refresh Script) ### 5\. `backup.sh` (SAP HANA Automated Backup Suite) 💾
* **Purpose**: Automates the refresh of a "Aurora" schema (a copy of a production schema) for testing or development. * **Purpose**: Provides automated, scheduled backups for SAP HANA databases.
* **Configuration**: Uses `aurora/aurora.conf`. * **Features**:
* **Features**: * Supports schema exports, full tenant data backups, or both.
* Exports a source schema. * Configurable compression to save disk space.
* Imports and renames it to an Aurora schema. * Uses secure `hdbuserstore` keys for connections.
* Updates company name fields in the imported schema.
* Can drop existing Aurora schema before refresh.
* Grants privileges to a specified user.
* Runs post-import SQL scripts.
* **Usage**: `./aurora/aurora.sh [new | complete | info]`
* `new`: Export, import, and rename (no privileges or post-scripts).
* `complete`: Drop, export, import, grant privileges, and run post-scripts.
* `info`: Show configuration information.
### 8. `aurora/aurora.conf` (Configuration for `aurora.sh`) ### 6\. `monitor.sh` (SAP HANA Monitoring Suite) 📊
* **Purpose**: Configures the `aurora.sh` script, including source schema, target user, backup directory, `hdbsql` path, and post-import SQL scripts. * **Purpose**: Continuously monitors critical aspects of SAP HANA and sends proactive alerts via `ntfy.sh` when predefined thresholds are exceeded.
* **Checks Performed**:
### 9. `backup/backup.sh` (SAP HANA Automated Backup Script) * Verifies all HANA processes have a 'GREEN' status.
* Monitors disk usage against a set threshold.
* **Purpose**: Performs automated schema exports and/or tenant backups for SAP HANA databases, typically via cronjobs. * Analyzes log segment state.
* **Configuration**: Uses `backup/backup.conf`. * Checks the age of the last successful data backup.
* **Features**:
* Supports schema exports for multiple schemas.
* Performs full tenant data backups.
* Optionally backs up the SYSTEMDB.
* Supports compression for both schema exports and tenant backups.
* Configurable backup types (`schema`, `tenant`, `all`).
* **Usage**: `./backup/backup.sh` (all settings are read from `backup.conf`)
### 10. `backup/backup.conf` (Configuration for `backup.sh`)
* **Purpose**: Configures the `backup.sh` script, including `hdbsql` path, user keys, base backup directory, backup type, compression settings, and schema names to export.
### 11. `monitor/monitor.sh` (SAP HANA Monitoring Script)
* **Purpose**: Monitors SAP HANA processes, disk usage, and log segment states, sending ntfy.sh notifications for alerts.
* **Configuration**: Uses `monitor/monitor.conf`.
* **Features**:
* Checks if all HANA processes are running (GREEN status).
* Monitors disk usage for specified directories against a threshold.
* Analyzes HANA log segments for 'Truncated' and 'Free' percentages against thresholds.
* Sends detailed notifications via ntfy.sh.
* Uses a lock file to prevent multiple instances from running.
* **Usage**: `./monitor/monitor.sh` (all settings are read from `monitor.conf`)
### 12. `monitor/monitor.conf` (Configuration for `monitor.sh`)
* **Purpose**: Configures the `monitor.sh` script, including company name, ntfy.sh settings, HANA connection details, monitoring thresholds (disk usage, log segments), and directories to monitor.

View File

@@ -1,31 +1,40 @@
# Configuration for the HANA Aurora Refresh Script # Configuration for the Aurora Refresh Script (aurora_refresh.sh)
# Place this file in the same directory as the aurora.sh script. # Place this file in the same directory as the script.
# Author: Tomi Eckert
# --- Main Settings --- # --- Main Settings ---
# The source production schema to be copied. # The source production schema to be copied.
SCHEMA="SBO_DEMO" # Example: "SBO_COMPANY_PROD"
SOURCE_SCHEMA="SBODEMOHU"
# The user who will be granted privileges on the new Aurora schema. # The HANA user that will be granted read/write access to the new Aurora schema.
AURORA_SCHEMA_USER="B1_53424F5F4348494D5045585F4155524F5241_RW" # This is typically a technical user for the application.
# Example: "B1_..._RW"
AURORA_USER="B1_XXXXXXXXX_RW"
# The database user for performing backup and administrative tasks. # The secure user store key for the HANA database user with privileges to
BACKOP_USER="CRONKEY" # perform EXPORT, IMPORT, DROP SCHEMA, and GRANT commands (e.g., SYSTEM).
# Using a key (hdbuserstore) is more secure than hardcoding a password.
# Example: "CRONKEY"
DB_ADMIN_KEY="CRONKEY"
# --- Paths and Files --- # --- Paths ---
# The base directory for storing the temporary schema export. # The base directory where the temporary schema export folder will be created.
BACKUP_DIR="/hana/shared/backup/schema" # Ensure the <sid>adm user has write permissions here.
BACKUP_BASE_DIR="/hana/shared/backup/schema"
# The full path to the HANA hdbsql executable. # The full path to the HANA hdbsql executable.
HDBSQL="/usr/sap/NDB/HDB00/exe/hdbsql" HDBSQL="/usr/sap/NDB/HDB00/exe/hdbsql"
# The root directory where post-import SQL scripts are located.
SQL_SCRIPTS_ROOT="/usr/sap/NDB/home/tools/sql"
# --- Post-Import Scripts --- # --- Post-Import Scripts (Optional) ---
# The root directory where the SQL script and its associated files are located. # A space-separated list of SQL script filenames to run after the import is complete.
SQL_ROOT="/usr/sap/NDB/home/tools" # The script will look for these files inside the SQL_SCRIPTS_ROOT directory.
# Leave empty ("") if no scripts are needed.
# A space-separated list of SQL script files to run after the import is complete. # Example: "update_user_emails.sql cleanup_tables.sql"
# These scripts should be located in the SCRIPT_ROOT directory. POST_IMPORT_SQL=""
POST_SQL=""

View File

@@ -1,115 +1,120 @@
#!/bin/sh #!/bin/sh
# Version: 1.2.6 # Version: 2.1.0
# Exit immediately if a command exits with a non-zero status. # Author: Tomi Eckert
set -e #
# Purpose: Performs an automated refresh of a SAP HANA schema. It exports a
# production schema and re-imports it under a new name ("Aurora")
# to create an up-to-date, non-production environment for testing.
# Designed to be run via cron, typically in the early morning.
#
# -----------------------------------------------------------------------------
# === SETUP === # --- Basic Setup ---
# Determine script's directory and source the configuration file. # Exit immediately if any command fails or if an unset variable is used.
set -eu
# --- Configuration ---
# Load the configuration file located in the same directory as the script.
SCRIPT_DIR=$(dirname "$0") SCRIPT_DIR=$(dirname "$0")
CONFIG_FILE="${SCRIPT_DIR}/aurora.conf" CONFIG_FILE="${SCRIPT_DIR}/aurora.conf"
if [ ! -f "$CONFIG_FILE" ]; then if [ ! -f "$CONFIG_FILE" ]; then
echo "Error: Configuration file not found at '${CONFIG_FILE}'" >&2 echo "FATAL: Configuration file not found at '${CONFIG_FILE}'" >&2
exit 1 exit 1
fi fi
# shellcheck source=aurora.conf # shellcheck source=aurora.conf
. "$CONFIG_FILE" . "$CONFIG_FILE"
# === DERIVED VARIABLES === # --- Validate Configuration ---
TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S") if [ ! -x "$HDBSQL" ]; then
AURORA="${SCHEMA}_AURORA" echo "❌ FATAL: hdbsql is not found or not executable at '${HDBSQL}'" >&2
AURORA_TEMP_DIR="${BACKUP_DIR}/${AURORA}" exit 1
COMPNYNAME="${SCHEMA#SBO_}"
[[ "$COMPNYNAME" == *_PROD ]] && COMPNYNAME="${COMPNYNAME%_PROD}" # Remove _PROD suffix if it exists
# === FUNCTIONS ===
run_sql() {
echo " Executing: $1"
"$HDBSQL" -U "${BACKOP_USER}" "$1" >/dev/null
}
show_info() {
echo "Source Schema: ${SCHEMA}"
echo "Target Schema: ${AURORA}"
echo "Target Schema User: ${AURORA_SCHEMA_USER}"
echo "Company Name: ${COMPNYNAME}"
echo "Export Directory: ${AURORA_TEMP_DIR}"
}
usage() {
echo "Usage: $0 [--info]"
echo " --info : Show configuration information."
echo " (No argument) : Drop, export, import, grant privileges, and run post-scripts."
}
export_schema() {
echo "⬇️ Starting schema export for '${SCHEMA}'..."
mkdir -p "$AURORA_TEMP_DIR"
run_sql "EXPORT \"${SCHEMA}\".\"*\" AS BINARY INTO '$AURORA_TEMP_DIR' WITH REPLACE;"
echo "✅ Schema export completed."
}
import_and_rename() {
echo "⬆️ Starting import and rename to '${AURORA}'..."
run_sql "IMPORT \"${SCHEMA}\".\"*\" FROM '$AURORA_TEMP_DIR' WITH IGNORE EXISTING RENAME SCHEMA \"${SCHEMA}\" TO \"${AURORA}\";"
echo " Updating company name fields..."
"$HDBSQL" -U "${BACKOP_USER}" -c ";" -I - <<EOF
UPDATE \"${AURORA}\".CINF SET \"CompnyName\"='AURORA ${COMPNYNAME} ${TIMESTAMP}';
UPDATE \"${AURORA}\".OADM SET \"CompnyName\"='AURORA ${COMPNYNAME} ${TIMESTAMP}';
UPDATE \"${AURORA}\".OADM SET \"PrintHeadr\"='AURORA ${COMPNYNAME} ${TIMESTAMP}';
EOF
echo "✅ Import and rename completed."
}
grant_privileges() {
echo "🔑 Granting privileges on '${AURORA}' to '${AURORA_SCHEMA_USER}'..."
run_sql "GRANT ALL PRIVILEGES ON SCHEMA \"${AURORA}\" TO \"${AURORA_SCHEMA_USER}\";"
echo "✅ Privileges granted."
}
drop_aurora_schema() {
echo "🗑️ Dropping existing '${AURORA}' schema..."
run_sql "DROP SCHEMA \"${AURORA}\" CASCADE;" 2>/dev/null || echo "⚠️ Could not drop schema '${AURORA}'. It might not exist." >&2
echo "✅ Old schema dropped."
}
run_post_scripts() {
echo "⚙️ Running post-import SQL scripts: ${POST_SQL}..."
for sql_file in $POST_SQL; do
echo " - Running script: ${sql_file}"
"$HDBSQL" -U "${BACKOP_USER}" -I "${SCRIPT_ROOT}/${sql_file}"
done
echo "✅ All post-import scripts completed."
}
cleanup_exported_files() {
echo "🧹 Cleaning up exported files from '${AURORA_TEMP_DIR}'..."
rm -rf "$AURORA_TEMP_DIR"
echo "✅ Exported files cleaned up."
}
# === SCRIPT EXECUTION ===
if [ $# -eq 0 ]; then
echo "🚀 Starting 'complete' operation (default)..."
drop_aurora_schema
export_schema
import_and_rename
grant_privileges
run_post_scripts
cleanup_exported_files
echo "🎉 'Complete' operation finished successfully!"
exit 0
fi fi
case "$1" in # --- Derived Variables (Do Not Edit) ---
--info) TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S")
show_info AURORA_SCHEMA="${SOURCE_SCHEMA}_AURORA"
;; EXPORT_DIR="${BACKUP_BASE_DIR}/${AURORA_SCHEMA}_TEMP_EXPORT"
*) COMPANY_NAME_BASE=$(echo "${SOURCE_SCHEMA}" | sed 's/^SBO_//' | sed 's/_PROD$//')
echo "❌ Error: Invalid argument '$1'." >&2
usage # --- Main Execution ---
exit 1 echo
;; echo "🚀 [$(date "+%T")] Starting Aurora Refresh for '${SOURCE_SCHEMA}'"
esac echo "--------------------------------------------------------"
echo " Source Schema: ${SOURCE_SCHEMA}"
echo " Target Aurora Schema: ${AURORA_SCHEMA}"
echo " Temp Export Path: ${EXPORT_DIR}"
echo "--------------------------------------------------------"
# 1. Drop the old Aurora schema if it exists.
echo "🗑️ Dropping old schema '${AURORA_SCHEMA}' (if it exists)..."
"$HDBSQL" -U "$DB_ADMIN_KEY" "DROP SCHEMA \"${AURORA_SCHEMA}\" CASCADE" >/dev/null 2>&1 || echo " -> Schema did not exist. Continuing."
# 2. Prepare the temporary export directory.
echo "📁 Preparing temporary export directory..."
rm -rf "$EXPORT_DIR"
mkdir -p "$EXPORT_DIR"
# 3. Export the source schema.
echo "⬇️ Exporting source schema '${SOURCE_SCHEMA}' to binary files..."
"$HDBSQL" -U "$DB_ADMIN_KEY" "EXPORT \"${SOURCE_SCHEMA}\".\"*\" AS BINARY INTO '${EXPORT_DIR}' WITH REPLACE;" >/dev/null
echo " -> Export complete."
# 4. Import the data into the new Aurora schema.
echo "⬆️ Importing data and renaming schema to '${AURORA_SCHEMA}'..."
"$HDBSQL" -U "$DB_ADMIN_KEY" "IMPORT \"${SOURCE_SCHEMA}\".\"*\" FROM '${EXPORT_DIR}' WITH IGNORE EXISTING RENAME SCHEMA \"${SOURCE_SCHEMA}\" TO \"${AURORA_SCHEMA}\";" >/dev/null
echo " -> Import complete."
# 5. Update company name in CINF and OADM tables.
echo "✍️ Updating company name fields in the new schema..."
# First, get the original company name from the source schema.
# The query returns a header and the name in quotes. sed gets the second line, tr removes the quotes, xargs trims whitespace.
echo " -> Fetching original company name from '${SOURCE_SCHEMA}'..."
ORIGINAL_COMPNY_NAME=$("$HDBSQL" -U "$DB_ADMIN_KEY" "SELECT \"CompnyName\" FROM \"${SOURCE_SCHEMA}\".\"CINF\"" | sed -n '2p' | tr -d '"' | xargs)
# Construct the new name in the desired format.
DATE_STAMP=$(date "+%Y-%m-%d")
NEW_COMPNY_NAME="AURORA - ${ORIGINAL_COMPNY_NAME} - ${DATE_STAMP}"
echo " -> New company name set to: '${NEW_COMPNY_NAME}'"
echo " -> Updating CINF table..."
"$HDBSQL" -U "$DB_ADMIN_KEY" "UPDATE \"${AURORA_SCHEMA}\".CINF SET \"CompnyName\" = '${NEW_COMPNY_NAME}';" >/dev/null
echo " -> Updating OADM table..."
"$HDBSQL" -U "$DB_ADMIN_KEY" "UPDATE \"${AURORA_SCHEMA}\".OADM SET \"CompnyName\" = '${NEW_COMPNY_NAME}', \"PrintHeadr\" = '${NEW_COMPNY_NAME}';" >/dev/null
echo " -> Company info updated."
# 6. Grant privileges to the read/write user.
echo "🔑 Granting ALL privileges on '${AURORA_SCHEMA}' to '${AURORA_USER}'..."
"$HDBSQL" -U "$DB_ADMIN_KEY" "GRANT ALL PRIVILEGES ON SCHEMA \"${AURORA_SCHEMA}\" TO \"${AURORA_USER}\";" >/dev/null
echo " -> Privileges granted."
# 7. Run post-import SQL scripts, if any are defined.
if [ -n "$POST_IMPORT_SQL" ]; then
echo "⚙️ Running post-import SQL scripts..."
# Use word splitting intentionally here
# shellcheck disable=SC2086
for sql_file in $POST_IMPORT_SQL; do
full_path="${SQL_SCRIPTS_ROOT}/${sql_file}"
if [ -f "$full_path" ]; then
echo " -> Executing: ${sql_file}"
"$HDBSQL" -U "$DB_ADMIN_KEY" -I "$full_path"
else
echo " -> ⚠️ WARNING: Script not found: ${full_path}" >&2
fi
done
else
echo " No post-import SQL scripts to run."
fi
# 8. Clean up the temporary export files.
echo "🧹 Cleaning up temporary directory '${EXPORT_DIR}'..."
rm -rf "$EXPORT_DIR"
echo " -> Cleanup complete."
echo "--------------------------------------------------------"
echo "✅ [$(date "+%T")] Aurora Refresh finished successfully!"
echo
exit 0

256
b1.gen.sh Normal file
View File

@@ -0,0 +1,256 @@
#!/bin/bash
# Author: Tomi Eckert
# ==============================================================================
# SAP Business One for HANA Silent Installation Configurator
# ==============================================================================
# This script interactively collects necessary details to customize the
# silent installation properties file for SAP Business One on HANA.
# It provides sensible defaults and generates the final 'install.properties'.
# ==============================================================================
# --- Function to display a welcome header ---
print_header() {
echo "======================================================"
echo " SAP Business One for HANA Installation Configurator "
echo "======================================================"
echo "Please provide the following details. Defaults are in [brackets]."
echo ""
}
# --- Function to read password securely (single entry) ---
read_password() {
local prompt_text=$1
local -n pass_var=$2 # Use a nameref to pass the variable name
# Loop until the entered password is not empty
while true; do
read -s -p "$prompt_text: " pass_var
echo
if [ -z "$pass_var" ]; then
echo "Password cannot be empty. Please try again."
else
break
fi
done
}
# --- Function to read and verify password securely ---
read_password_verify() {
local prompt_text=$1
local -n pass_var=$2 # Use a nameref to pass the variable name
local pass_verify
# Loop until the entered passwords match and are not empty
while true; do
read -s -p "$prompt_text: " pass_var
echo
if [ -z "$pass_var" ]; then
echo "Password cannot be empty. Please try again."
continue
fi
read -s -p "Confirm password: " pass_verify
echo
if [ "$pass_var" == "$pass_verify" ]; then
break
else
echo "Passwords do not match. Please try again."
echo ""
fi
done
}
# --- Main configuration logic ---
print_header
# --- Installation Type ---
echo "--- Installation Type ---"
read -p "Is this a new installation or are you reconfiguring an existing instance? (new/reconfigure) [new]: " install_type
install_type=${install_type:-new}
if [[ "$install_type" == "reconfigure" ]]; then
LANDSCAPE_INSTALL_ACTION="connect"
B1S_SHARED_FOLDER_OVERWRITE="false"
else
LANDSCAPE_INSTALL_ACTION="create"
B1S_SHARED_FOLDER_OVERWRITE="true"
fi
echo ""
# 1. Get Hostname/IP Details
# Default to the current machine's hostname.
DEFAULT_HOSTNAME=$(hostname)
read -p "Enter HANA Database Server Hostname or IP [${DEFAULT_HOSTNAME}]: " HANA_DATABASE_SERVERS
HANA_DATABASE_SERVERS=${HANA_DATABASE_SERVERS:-$DEFAULT_HOSTNAME}
# 2. Get HANA Instance Details
read -p "Enter HANA Database Instance Number [00]: " HANA_DATABASE_INSTANCE
HANA_DATABASE_INSTANCE=${HANA_DATABASE_INSTANCE:-00}
# 3. Get HANA SID to construct the admin user
read -p "Enter HANA SID (Tenant Name) [NDB]: " HANA_SID
HANA_SID=${HANA_SID:-NDB}
# Convert SID to lowercase and append 'adm'
HANA_DATABASE_ADMIN_ID=$(echo "${HANA_SID}" | tr '[:upper:]' '[:lower:]')adm
# 4. Get Passwords
echo ""
echo "--- Secure Password Entry ---"
read_password "Enter password for HANA Admin ('${HANA_DATABASE_ADMIN_ID}')" HANA_DATABASE_ADMIN_PASSWD
# 5. Get HANA Database User
read -p "Enter HANA Database User ID [SYSTEM]: " HANA_DATABASE_USER_ID
HANA_DATABASE_USER_ID=${HANA_DATABASE_USER_ID:-SYSTEM}
# 6. Get HANA User Password
read_password "Enter password for HANA User ('${HANA_DATABASE_USER_ID}')" HANA_DATABASE_USER_PASSWORD
# 7. Get SLD and Site User Details
echo ""
echo "--- System Landscape Directory (SLD) ---"
read -p "Enter SLD Service Port [40000]: " SERVICE_PORT
SERVICE_PORT=${SERVICE_PORT:-40000}
read -p "Enter SLD Site User ID [B1SiteUser]: " SITE_USER_ID
SITE_USER_ID=${SITE_USER_ID:-B1SiteUser}
read_password_verify "Enter password for Site User ('${SITE_USER_ID}')" SITE_USER_PASSWORD
# --- SLD Single Sign-On (SSO) Settings ---
echo ""
echo "--- SLD Single Sign-On (SSO) Settings ---"
read -p "Do you want to configure Active Directory SSO? [y/N]: " configure_sso
if [[ "$configure_sso" =~ ^[yY]$ ]]; then
SLD_WINDOWS_DOMAIN_ACTION="use"
read -p "Enter AD Domain Controller: " SLD_WINDOWS_DOMAIN_CONTROLLER
read -p "Enter AD Domain Name: " SLD_WINDOWS_DOMAIN_NAME
read -p "Enter AD Domain User ID: " SLD_WINDOWS_DOMAIN_USER_ID
read_password "Enter password for AD Domain User ('${SLD_WINDOWS_DOMAIN_USER_ID}')" SLD_WINDOWS_DOMAIN_USER_PASSWORD
else
SLD_WINDOWS_DOMAIN_ACTION="skip"
SLD_WINDOWS_DOMAIN_CONTROLLER=""
SLD_WINDOWS_DOMAIN_NAME=""
SLD_WINDOWS_DOMAIN_USER_ID=""
SLD_WINDOWS_DOMAIN_USER_PASSWORD=""
fi
# 10. & 11. Get Service Layer Load Balancer Details
echo ""
echo "--- Service Layer ---"
read -p "Enter Service Layer Load Balancer Port [50000]: " SL_LB_PORT
SL_LB_PORT=${SL_LB_PORT:-50000}
read -p "How many Service Layer member nodes should be configured? [2]: " SL_MEMBER_COUNT
SL_MEMBER_COUNT=${SL_MEMBER_COUNT:-2}
# Generate the SL_LB_MEMBERS string
SL_LB_MEMBERS=""
for (( i=1; i<=SL_MEMBER_COUNT; i++ )); do
port=$((50000 + i))
member="${HANA_DATABASE_SERVERS}:${port}"
if [ -z "$SL_LB_MEMBERS" ]; then
SL_LB_MEMBERS="$member"
else
SL_LB_MEMBERS="$SL_LB_MEMBERS,$member"
fi
done
# 12. Display Summary and Ask for Confirmation
clear
echo "======================================================"
echo " Configuration Summary"
echo "======================================================"
echo ""
echo " --- Installation & System Details ---"
echo " INSTALLATION_FOLDER=/usr/sap/SAPBusinessOne"
echo " LANDSCAPE_INSTALL_ACTION=${LANDSCAPE_INSTALL_ACTION}"
echo " B1S_SHARED_FOLDER_OVERWRITE=${B1S_SHARED_FOLDER_OVERWRITE}"
echo ""
echo " --- SAP HANA Database Server Details ---"
echo " HANA_DATABASE_SERVERS=${HANA_DATABASE_SERVERS}"
echo " HANA_DATABASE_INSTANCE=${HANA_DATABASE_INSTANCE}"
echo " HANA_DATABASE_ADMIN_ID=${HANA_DATABASE_ADMIN_ID}"
echo " HANA_DATABASE_ADMIN_PASSWD=[hidden]"
echo ""
echo " --- SAP HANA Database User ---"
echo " HANA_DATABASE_USER_ID=${HANA_DATABASE_USER_ID}"
echo " HANA_DATABASE_USER_PASSWORD=[hidden]"
echo ""
echo " --- System Landscape Directory (SLD) Details ---"
echo " SERVICE_PORT=${SERVICE_PORT}"
echo " SITE_USER_ID=${SITE_USER_ID}"
echo " SITE_USER_PASSWORD=[hidden]"
echo ""
echo " --- SLD Single Sign-On (SSO) ---"
echo " SLD_WINDOWS_DOMAIN_ACTION=${SLD_WINDOWS_DOMAIN_ACTION}"
if [ "$SLD_WINDOWS_DOMAIN_ACTION" == "use" ]; then
echo " SLD_WINDOWS_DOMAIN_CONTROLLER=${SLD_WINDOWS_DOMAIN_CONTROLLER}"
echo " SLD_WINDOWS_DOMAIN_NAME=${SLD_WINDOWS_DOMAIN_NAME}"
echo " SLD_WINDOWS_DOMAIN_USER_ID=${SLD_WINDOWS_DOMAIN_USER_ID}"
echo " SLD_WINDOWS_DOMAIN_USER_PASSWORD=[hidden]"
fi
echo ""
echo " --- Service Layer ---"
echo " SL_LB_PORT=${SL_LB_PORT}"
echo " SL_LB_MEMBERS=${SL_LB_MEMBERS}"
echo ""
echo "======================================================"
read -p "Save this configuration to 'install.properties'? [y/N]: " confirm
echo ""
if [[ ! "$confirm" =~ ^[yY]$ ]]; then
echo "Configuration cancelled by user."
exit 1
fi
# --- Write the final install.properties file ---
# Using a HEREDOC to write the configuration file with the variables collected.
cat > install.properties << EOL
# SAP Business One for HANA Silent Installation Properties
# Generated by configuration script on $(date)
INSTALLATION_FOLDER=/usr/sap/SAPBusinessOne
HANA_DATABASE_SERVERS=${HANA_DATABASE_SERVERS}
HANA_DATABASE_INSTANCE=${HANA_DATABASE_INSTANCE}
HANA_DATABASE_ADMIN_ID=${HANA_DATABASE_ADMIN_ID}
HANA_DATABASE_ADMIN_PASSWD=${HANA_DATABASE_ADMIN_PASSWD}
HANA_DATABASE_USER_ID=${HANA_DATABASE_USER_ID}
HANA_DATABASE_USER_PASSWORD=${HANA_DATABASE_USER_PASSWORD}
SERVICE_PORT=${SERVICE_PORT}
SLD_DATABASE_NAME=SLDDATA
SLD_CERTIFICATE_ACTION=self
CONNECTION_SSL_CERTIFICATE_VERIFICATION=false
SLD_DATABASE_ACTION=create
SLD_SERVER_PROTOCOL=https
SITE_USER_ID=${SITE_USER_ID}
SITE_USER_PASSWORD=${SITE_USER_PASSWORD}
# --- SLD Single Sign-On (SSO) Settings ---
SLD_WINDOWS_DOMAIN_ACTION=${SLD_WINDOWS_DOMAIN_ACTION}
SLD_WINDOWS_DOMAIN_CONTROLLER=${SLD_WINDOWS_DOMAIN_CONTROLLER}
SLD_WINDOWS_DOMAIN_NAME=${SLD_WINDOWS_DOMAIN_NAME}
SLD_WINDOWS_DOMAIN_USER_ID=${SLD_WINDOWS_DOMAIN_USER_ID}
SLD_WINDOWS_DOMAIN_USER_PASSWORD=${SLD_WINDOWS_DOMAIN_USER_PASSWORD}
SL_LB_MEMBER_ONLY=false
SL_LB_PORT=${SL_LB_PORT}
SL_LB_MEMBERS=${SL_LB_MEMBERS}
SL_THREAD_PER_SERVER=10
SELECTED_FEATURES=B1ServerTools,B1ServerToolsLandscape,B1ServerToolsSLD,B1ServerToolsLicense,B1ServerToolsJobService,B1ServerToolsXApp,B1SLDAgent,B1BackupService,B1Server,B1ServerSHR,B1ServerHelp,B1AnalyticsPlatform,B1ServerCommonDB,B1ServiceLayerComponent
B1S_SAMBA_AUTOSTART=true
B1S_SHARED_FOLDER_OVERWRITE=${B1S_SHARED_FOLDER_OVERWRITE}
LANDSCAPE_INSTALL_ACTION=${LANDSCAPE_INSTALL_ACTION}
EOL
echo "Success! The configuration file 'install.properties' has been created in the current directory."
exit 0

View File

@@ -1,32 +1,30 @@
# ============================================================================== # ==============================================================================
# Configuration for HANA Backup Script (backup.sh) # Configuration for HANA Backup Script (backup.sh)
# ============================================================================== # ==============================================================================
# Author: Tomi Eckert
# --- Connection Settings --- # --- Connection Settings ---
# Full path to the SAP HANA hdbsql executable.
HDBSQL_PATH="/usr/sap/hdbclient/hdbsql"
# User key name from the hdbuserstore. # User key name from the hdbuserstore.
# This key should be configured to connect to the target tenant database. # This key should be configured to connect to the target tenant database.
USER_KEY="CRONKEY" USER_KEY="CRONKEY"
# hdbuserstore key for the SYSTEMDB user # hdbuserstore key for the SYSTEMDB user
SYSTEMDB_USER_KEY="SYSTEMDB_KEY" SYSTEMDB_USER_KEY="SYSTEMKEY"
# --- Backup Settings --- # --- Backup Settings ---
# The base directory where all backup files and directories will be stored. # The base directory where all backup files and directories will be stored.
# Ensure this directory exists and that the OS user running the script has # Ensure this directory exists and that the OS user running the script has
# write permissions to it. # write permissions to it.
BACKUP_BASE_DIR="/hana/backups/automated" BACKUP_BASE_DIR="/hana/shared/backup"
# Specify the type of backup to perform on script execution. # Specify the type of backup to perform on script execution.
# Options are: # Options are:
# 'schema' - Performs only the schema export. # 'schema' - Performs only the schema export.
# 'tenant' - Performs only the tenant data backup. # 'tenant' - Performs only the tenant data backup.
# 'all' - Performs both the schema export and the tenant backup. # 'all' - Performs both the schema export and the tenant backup.
BACKUP_TYPE="all" BACKUP_TYPE="tenant"
# Set to 'true' to also perform a backup of the SYSTEMDB # Set to 'true' to also perform a backup of the SYSTEMDB
BACKUP_SYSTEMDB=true BACKUP_SYSTEMDB=true

17
backup/backup.hook.sh Normal file
View File

@@ -0,0 +1,17 @@
#!/bin/bash
# Author: Tomi Eckert
# This script helps to configure backup.conf
# Source the backup.conf to get current values
source backup.conf
HDBSQL_PATH_INPUT=$(which hdbsql)
# Default values if not found
HDBSQL_PATH_INPUT=${HDBSQL_PATH_INPUT:-"/usr/sap/hdbclient/hdbsql"}
# Update backup.conf
sed -i "s#^HDBSQL_PATH=\".*\"#HDBSQL_PATH=\"$HDBSQL_PATH_INPUT\"#" backup.conf
echo "backup.conf updated successfully!"

View File

@@ -1,18 +1,20 @@
#!/bin/bash #!/bin/bash
# Version: 1.0.5 # Version: 1.0.8
# Author: Tomi Eckert
# ============================================================================== # ==============================================================================
# SAP HANA Backup Script # SAP HANA Backup Script
# #
# Performs schema exports for one or more schemas and/or tenant backups for a # Performs schema exports for one or more schemas and/or tenant backups for a
# SAP HANA database. Designed to be executed via a cronjob. # SAP HANA database using hanatool.sh. Designed to be executed via a cronjob.
# Reads all settings from the backup.conf file in the same directory. # Reads all settings from the backup.conf file in the same directory.
# ============================================================================== # ==============================================================================
# --- Configuration and Setup --- # --- Configuration and Setup ---
# Find the script's own directory to locate the config file # Find the script's own directory to locate the config file and hanatool.sh
SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &> /dev/null && pwd) SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &> /dev/null && pwd)
CONFIG_FILE="${SCRIPT_DIR}/backup.conf" CONFIG_FILE="${SCRIPT_DIR}/backup.conf"
HANATOOL_PATH="${SCRIPT_DIR}/hanatool.sh" # Assuming hanatool.sh is in the parent directory
# Check for config file and source it # Check for config file and source it
if [[ -f "$CONFIG_FILE" ]]; then if [[ -f "$CONFIG_FILE" ]]; then
@@ -22,176 +24,101 @@ else
exit 1 exit 1
fi fi
# Check if hdbsql executable exists # Check if hanatool.sh executable exists
if [[ ! -x "$HDBSQL_PATH" ]]; then if [[ ! -x "$HANATOOL_PATH" ]]; then
echo "❌ Error: hdbsql not found or not executable at '${HDBSQL_PATH}'" echo "❌ Error: hanatool.sh not found or not executable at '${HANATOOL_PATH}'"
exit 1 exit 1
fi fi
# Calculate threads to use (half of the available cores, but at least 1)
TOTAL_THREADS=$(nproc --all)
THREADS=$((TOTAL_THREADS / 2))
if [[ "$THREADS" -eq 0 ]]; then
THREADS=1
fi
# --- Functions ---
# Performs a binary export of a specific schema.
# Accepts the schema name as its first argument.
perform_schema_export() {
local schema_name="$1"
if [[ -z "$schema_name" ]]; then
echo " ❌ Error: No schema name provided to perform_schema_export function."
return 1
fi
echo "⬇️ Starting schema export for '${schema_name}'..."
local timestamp
timestamp=$(date +%Y%m%d_%H%M%S)
local export_base_dir="${BACKUP_BASE_DIR}/schema"
local export_path="${export_base_dir}/${schema_name}_${timestamp}"
local query_export_path="$export_path"
if [[ "$COMPRESS_SCHEMA" == "true" ]]; then
export_path="${export_base_dir}/tmp/${schema_name}_${timestamp}"
query_export_path="$export_path"
echo " Compression enabled. Using temporary export path: ${export_path}"
fi
local archive_file="${export_base_dir}/${schema_name}_${timestamp}.tar.gz"
mkdir -p "$(dirname "$export_path")"
local query="EXPORT \"${schema_name}\".\"*\" AS BINARY INTO '${query_export_path}' WITH REPLACE THREADS ${THREADS};"
"$HDBSQL_PATH" -U "$USER_KEY" "$query" > /dev/null 2>&1
local exit_code=$?
if [[ "$exit_code" -eq 0 ]]; then
echo " ✅ Successfully exported schema '${schema_name}'."
if [[ "$COMPRESS_SCHEMA" == "true" ]]; then
echo " 🗜️ Compressing exported files..."
tar -czf "$archive_file" -C "$(dirname "$export_path")" "$(basename "$export_path")"
local tar_exit_code=$?
if [[ "$tar_exit_code" -eq 0 ]]; then
echo " ✅ Successfully created archive '${archive_file}'."
echo " 🧹 Cleaning up temporary directory..."
rm -rf "$export_path"
rmdir --ignore-fail-on-non-empty "$(dirname "$export_path")"
echo " ✨ Cleanup complete."
else
echo " ❌ Error: Failed to compress '${export_path}'."
fi
else
echo " Compression disabled. Raw export files are located at '${export_path}'."
fi
else
echo " ❌ Error: Failed to export schema '${schema_name}' (hdbsql exit code: ${exit_code})."
fi
}
# Loops through the schemas in the config file and runs an export for each.
run_all_schema_exports() {
if [[ -z "$SCHEMA_NAMES" ]]; then
echo " ⚠️ Warning: SCHEMA_NAMES variable is not set in config. Skipping schema export."
return
fi
echo "🔎 Found schemas to export: ${SCHEMA_NAMES}"
for schema in $SCHEMA_NAMES; do
perform_schema_export "$schema"
echo "--------------------------------------------------"
done
}
# REFACTORED: Generic function to back up any database (Tenant or SYSTEMDB).
# Arguments: 1:Backup Name (for logging), 2:User Key, 3:Base Directory, 4:Compression Flag
perform_database_backup() {
local backup_name="$1"
local user_key="$2"
local backup_base_dir="$3"
local compress_enabled="$4"
echo "⬇️ Starting ${backup_name} backup..."
local timestamp
timestamp=$(date +%Y%m%d_%H%M%S)
local backup_path_prefix
local backup_target_dir
if [[ "$compress_enabled" == "true" ]]; then
backup_target_dir="${backup_base_dir}/tmp"
backup_path_prefix="${backup_target_dir}/backup_${timestamp}"
echo " Compression enabled. Using temporary backup path: ${backup_path_prefix}"
else
backup_target_dir="$backup_base_dir"
backup_path_prefix="${backup_target_dir}/backup_${timestamp}"
fi
mkdir -p "$backup_target_dir"
local query="BACKUP DATA USING FILE ('${backup_path_prefix}')"
"$HDBSQL_PATH" -U "$user_key" "$query" > /dev/null 2>&1
local exit_code=$?
if [[ "$exit_code" -eq 0 ]]; then
echo " ✅ Successfully initiated ${backup_name} backup with prefix '${backup_path_prefix}'."
if [[ "$compress_enabled" == "true" ]]; then
local archive_file="${backup_base_dir}/backup_${timestamp}.tar.gz"
echo " 🗜️ Compressing backup files..."
tar -czf "$archive_file" -C "$backup_target_dir" .
local tar_exit_code=$?
if [[ "$tar_exit_code" -eq 0 ]]; then
echo " ✅ Successfully created archive '${archive_file}'."
echo " 🧹 Cleaning up temporary directory..."
rm -rf "$backup_target_dir"
echo " ✨ Cleanup complete."
else
echo " ❌ Error: Failed to compress backup files in '${backup_target_dir}'."
fi
fi
else
echo " ❌ Error: Failed to initiate ${backup_name} backup (hdbsql exit code: ${exit_code})."
fi
}
# --- Main Execution --- # --- Main Execution ---
echo "⚙️ Starting HANA backup process..." echo "⚙️ Starting HANA backup process using hanatool.sh..."
mkdir -p "$BACKUP_BASE_DIR" mkdir -p "$BACKUP_BASE_DIR"
SCHEMA_EXPORT_OPTIONS=""
case "$BACKUP_TYPE" in case "$BACKUP_TYPE" in
schema) schema)
run_all_schema_exports if [[ -z "$SCHEMA_NAMES" ]]; then
echo " ⚠️ Warning: SCHEMA_NAMES variable is not set in config. Skipping schema export."
else
echo "🔎 Found schemas to export: ${SCHEMA_NAMES}"
for schema in $SCHEMA_NAMES; do
echo "⬇️ Starting schema export for '${schema}'..."
SCHEMA_EXPORT_OPTIONS="$COMMON_OPTIONS"
if [[ "$COMPRESS_SCHEMA" == "true" ]]; then
SCHEMA_EXPORT_OPTIONS+=" --compress"
fi
"$HANATOOL_PATH" "$USER_KEY" export "$schema" "${BACKUP_BASE_DIR}/schema" $SCHEMA_EXPORT_OPTIONS
if [[ $? -ne 0 ]]; then
echo "❌ Error: Schema export for '${schema}' failed."
fi
echo "--------------------------------------------------"
done
fi
;; ;;
tenant) tenant)
perform_database_backup "Tenant" "$USER_KEY" "${BACKUP_BASE_DIR}/tenant" "$COMPRESS_TENANT" echo "⬇️ Starting Tenant backup..."
TENANT_BACKUP_OPTIONS="$COMMON_OPTIONS"
if [[ "$COMPRESS_TENANT" == "true" ]]; then
TENANT_BACKUP_OPTIONS+=" --compress"
fi
"$HANATOOL_PATH" "$USER_KEY" backup "${BACKUP_BASE_DIR}/tenant" $TENANT_BACKUP_OPTIONS
if [[ $? -ne 0 ]]; then
echo "❌ Error: Tenant backup failed."
fi
;; ;;
all) all)
run_all_schema_exports if [[ -z "$SCHEMA_NAMES" ]]; then
perform_database_backup "Tenant" "$USER_KEY" "${BACKUP_BASE_DIR}/tenant" "$COMPRESS_TENANT" echo " ⚠️ Warning: SCHEMA_NAMES variable is not set in config. Skipping schema export."
else
echo "🔎 Found schemas to export: ${SCHEMA_NAMES}"
for schema in $SCHEMA_NAMES; do
echo "⬇️ Starting schema export for '${schema}'..."
SCHEMA_EXPORT_OPTIONS="$COMMON_OPTIONS"
if [[ "$COMPRESS_SCHEMA" == "true" ]]; then
SCHEMA_EXPORT_OPTIONS+=" --compress"
fi
"$HANATOOL_PATH" "$USER_KEY" export "$schema" "${BACKUP_BASE_DIR}/schema" $SCHEMA_EXPORT_OPTIONS
if [[ $? -ne 0 ]]; then
echo "❌ Error: Schema export for '${schema}' failed."
fi
echo "--------------------------------------------------"
done
fi
echo "⬇️ Starting Tenant backup..."
TENANT_BACKUP_OPTIONS="$COMMON_OPTIONS"
if [[ "$COMPRESS_TENANT" == "true" ]]; then
TENANT_BACKUP_OPTIONS+=" --compress"
fi
"$HANATOOL_PATH" "$USER_KEY" backup "${BACKUP_BASE_DIR}/tenant" $TENANT_BACKUP_OPTIONS
if [[ $? -ne 0 ]]; then
echo "❌ Error: Tenant backup failed."
fi
;; ;;
*) *)
echo " ❌ Error: Invalid BACKUP_TYPE '${BACKUP_TYPE}' in config. Use 'schema', 'tenant', or 'all'." echo " ❌ Error: Invalid BACKUP_TYPE '${BACKUP_TYPE}' in config. Use 'schema', 'tenant', or 'all'."
;; ;;
esac esac
# NEW: Check if SYSTEMDB backup is enabled, regardless of BACKUP_TYPE (as long as it's not 'schema' only) # Check if SYSTEMDB backup is enabled, regardless of BACKUP_TYPE (as long as it's not 'schema' only)
if [[ "$BACKUP_TYPE" == "tenant" || "$BACKUP_TYPE" == "all" ]]; then if [[ "$BACKUP_TYPE" == "tenant" || "$BACKUP_TYPE" == "all" ]]; then
if [[ "$BACKUP_SYSTEMDB" == "true" ]]; then if [[ "$BACKUP_SYSTEMDB" == "true" ]]; then
echo "--------------------------------------------------" echo "--------------------------------------------------"
if [[ -z "$SYSTEMDB_USER_KEY" ]]; then if [[ -z "$SYSTEMDB_USER_KEY" ]]; then
echo " ❌ Error: BACKUP_SYSTEMDB is true, but SYSTEMDB_USER_KEY is not set in config." echo " ❌ Error: BACKUP_SYSTEMDB is true, but SYSTEMDB_USER_KEY is not set in config."
else else
perform_database_backup "SYSTEMDB" "$SYSTEMDB_USER_KEY" "${BACKUP_BASE_DIR}/systemdb" "$COMPRESS_TENANT" echo "⬇️ Starting SYSTEMDB backup..."
SYSTEMDB_BACKUP_OPTIONS="$COMMON_OPTIONS"
if [[ "$COMPRESS_TENANT" == "true" ]]; then # SYSTEMDB compression uses COMPRESS_TENANT setting
SYSTEMDB_BACKUP_OPTIONS+=" --compress"
fi
"$HANATOOL_PATH" "$SYSTEMDB_USER_KEY" backup "${BACKUP_BASE_DIR}/tenant" $SYSTEMDB_BACKUP_OPTIONS
if [[ $? -ne 0 ]]; then
echo "❌ Error: SYSTEMDB backup failed."
fi
fi fi
fi fi
fi fi

View File

@@ -1,5 +1,6 @@
#!/bin/bash #!/bin/bash
# Version: 1.1.0 # Version: 1.1.0
# Author: Tomi Eckert
# Check if any arguments were provided # Check if any arguments were provided
if [ "$#" -eq 0 ]; then if [ "$#" -eq 0 ]; then

View File

@@ -1,5 +1,6 @@
#!/bin/bash #!/bin/bash
# Version: 1.4.6 # Version: 1.5.6
# Author: Tomi Eckert
# ============================================================================== # ==============================================================================
# SAP HANA Schema and Tenant Management Tool (hanatool.sh) # SAP HANA Schema and Tenant Management Tool (hanatool.sh)
# #
@@ -7,11 +8,27 @@
# ============================================================================== # ==============================================================================
# --- Default Settings --- # --- Default Settings ---
HDBSQL_PATH="/usr/sap/hdbclient/hdbsql" # Define potential HDB client paths
HDB_CLIENT_PATH_1="/usr/sap/hdbclient"
HDB_CLIENT_PATH_2="/usr/sap/NDB/HDB00/exe"
# Determine the correct HDB_CLIENT_PATH
if [ -d "$HDB_CLIENT_PATH_1" ]; then
HDB_CLIENT_PATH="$HDB_CLIENT_PATH_1"
elif [ -d "$HDB_CLIENT_PATH_2" ]; then
HDB_CLIENT_PATH="$HDB_CLIENT_PATH_2"
else
echo "❌ Error: Neither '$HDB_CLIENT_PATH_1' nor '$HDB_CLIENT_PATH_2' found."
echo "Please install the SAP HANA client or adjust the paths in the script."
exit 1
fi
HDBSQL_PATH="${HDB_CLIENT_PATH}/hdbsql"
COMPRESS=false COMPRESS=false
THREADS=0 # 0 means auto-calculate later THREADS=0 # 0 means auto-calculate later
DRY_RUN=false DRY_RUN=false
NTFY_TOKEN="" NTFY_TOKEN=""
IMPORT_REPLACE=false
# --- Help/Usage Function --- # --- Help/Usage Function ---
usage() { usage() {
@@ -38,6 +55,7 @@ usage() {
echo " -c, --compress Enable tar.gz compression for exports and backups." echo " -c, --compress Enable tar.gz compression for exports and backups."
echo " -n, --dry-run Show what commands would be executed without running them." echo " -n, --dry-run Show what commands would be executed without running them."
echo " --ntfy <token> Send a notification via ntfy.sh upon completion/failure." echo " --ntfy <token> Send a notification via ntfy.sh upon completion/failure."
echo " --replace Use the 'REPLACE' option for imports instead of 'IGNORE EXISTING'."
echo " --hdbsql <path> Specify a custom path for the hdbsql executable." echo " --hdbsql <path> Specify a custom path for the hdbsql executable."
echo " -h, --help Show this help message." echo " -h, --help Show this help message."
echo "" echo ""
@@ -48,8 +66,8 @@ usage() {
echo " # Import MYSCHEMA from a compressed archive" echo " # Import MYSCHEMA from a compressed archive"
echo " $0 MY_SCHEMA_KEY import MYSCHEMA /hana/backups/MYSCHEMA_20240101.tar.gz -c" echo " $0 MY_SCHEMA_KEY import MYSCHEMA /hana/backups/MYSCHEMA_20240101.tar.gz -c"
echo "" echo ""
echo " # Import MYSCHEMA as MYSCHEMA_TEST using a custom hdbsql path" echo " # Import MYSCHEMA as MYSCHEMA_TEST, replacing any existing objects"
echo " $0 MY_SCHEMA_KEY import-rename MYSCHEMA MYSCHEMA_TEST /hana/backups/temp_export --hdbsql /sap/custom/hdbsql" echo " $0 MY_SCHEMA_KEY import-rename MYSCHEMA MYSCHEMA_TEST /hana/backups/temp_export --replace"
} }
# --- Notification Function --- # --- Notification Function ---
@@ -63,6 +81,28 @@ send_notification() {
fi fi
} }
# --- Function to get HANA tenant name ---
get_hana_tenant_name() {
local user_key="$1"
local hdbsql_path="$2"
local dry_run="$3"
local query="SELECT DATABASE_NAME FROM SYS.M_DATABASES;"
local tenant_name=""
if [[ "$dry_run" == "true" ]]; then
echo "[DRY RUN] Would execute hdbsql to get tenant name: \"$hdbsql_path\" -U \"$user_key\" \"$query\""
tenant_name="DRYRUN_TENANT"
else
tenant_name=$("$hdbsql_path" -U "$user_key" "$query" | tail -n +2 | head -n 1 | tr -d '[:space:]' | tr -d '"')
if [[ -z "$tenant_name" ]]; then
echo "❌ Error: Could not retrieve HANA tenant name using user key '${user_key}'."
exit 1
fi
fi
echo "$tenant_name"
}
# --- Argument Parsing --- # --- Argument Parsing ---
POSITIONAL_ARGS=() POSITIONAL_ARGS=()
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
@@ -83,6 +123,10 @@ while [[ $# -gt 0 ]]; do
NTFY_TOKEN="$2" NTFY_TOKEN="$2"
shift 2 shift 2
;; ;;
--replace)
IMPORT_REPLACE=true
shift
;;
--hdbsql) --hdbsql)
HDBSQL_PATH="$2" HDBSQL_PATH="$2"
shift 2 shift 2
@@ -142,28 +186,29 @@ case "$ACTION" in
echo " - Path: ${TARGET_PATH}" echo " - Path: ${TARGET_PATH}"
echo " - Compress: ${COMPRESS}" echo " - Compress: ${COMPRESS}"
TENANT_NAME=$(get_hana_tenant_name "$USER_KEY" "$HDBSQL_PATH" "$DRY_RUN")
echo " - Tenant Name: ${TENANT_NAME}"
timestamp=$(date +%Y%m%d_%H%M%S) timestamp=$(date +%Y%m%d_%H%M%S)
backup_target_dir="" backup_target_dir="$TARGET_PATH" # Initialize with TARGET_PATH
backup_path_prefix="" backup_path_prefix=""
if [[ "$COMPRESS" == "true" ]]; then if [[ "$COMPRESS" == "true" ]]; then
if [[ "$DRY_RUN" == "true" ]]; then if [[ "$DRY_RUN" == "true" ]]; then
backup_target_dir="/tmp/tenant_backup_DRYRUN_TEMP" backup_target_dir="${TARGET_PATH}/${TENANT_NAME}_backup_DRYRUN_TEMP" # Use TARGET_PATH
else else
backup_target_dir=$(mktemp -d "/tmp/tenant_backup_${timestamp}_XXXXXXXX") backup_target_dir=$(mktemp -d "${TARGET_PATH}/${TENANT_NAME}_backup_${timestamp}_XXXXXXXX") # Use TARGET_PATH
fi fi
echo " Using temporary backup directory: ${backup_target_dir}" echo " Using temporary backup directory: ${backup_target_dir}"
else
backup_target_dir="$TARGET_PATH"
fi fi
if [[ "$DRY_RUN" == "true" && "$COMPRESS" == "false" ]]; then if [[ "$DRY_RUN" == "true" ]]; then
echo "[DRY RUN] Would create directory: mkdir -p \"$backup_target_dir\"" echo "[DRY RUN] Would create directory: mkdir -p \"$backup_target_dir\""
else else
mkdir -p "$backup_target_dir" mkdir -p "$backup_target_dir"
fi fi
backup_path_prefix="${backup_target_dir}/backup_${timestamp}" backup_path_prefix="${backup_target_dir}/backup_${TENANT_NAME}_${timestamp}"
QUERY="BACKUP DATA USING FILE ('${backup_path_prefix}')" QUERY="BACKUP DATA USING FILE ('${backup_path_prefix}')"
@@ -178,7 +223,7 @@ case "$ACTION" in
if [[ "$EXIT_CODE" -eq 0 ]]; then if [[ "$EXIT_CODE" -eq 0 ]]; then
echo "✅ Successfully initiated tenant backup with prefix '${backup_path_prefix}'." echo "✅ Successfully initiated tenant backup with prefix '${backup_path_prefix}'."
if [[ "$COMPRESS" == "true" ]]; then if [[ "$COMPRESS" == "true" ]]; then
ARCHIVE_FILE="${TARGET_PATH}/tenant_backup_${timestamp}.tar.gz" ARCHIVE_FILE="${TARGET_PATH}/${TENANT_NAME}_backup_${timestamp}.tar.gz"
echo "🗜️ Compressing backup files to '${ARCHIVE_FILE}'..." echo "🗜️ Compressing backup files to '${ARCHIVE_FILE}'..."
TAR_EXIT_CODE=0 TAR_EXIT_CODE=0
@@ -201,10 +246,10 @@ case "$ACTION" in
echo "❌ Error: Failed to create archive from '${backup_target_dir}'." echo "❌ Error: Failed to create archive from '${backup_target_dir}'."
fi fi
fi fi
send_notification "Tenant backup for user key '${USER_KEY}' completed successfully." send_notification "HANA tenant '${TENANT_NAME}' backup completed successfully."
else else
echo "❌ Error: Failed to initiate tenant backup (hdbsql exit code: ${EXIT_CODE})." echo "❌ Error: Failed to initiate tenant backup (hdbsql exit code: ${EXIT_CODE})."
send_notification "Tenant backup for user key '${USER_KEY}' FAILED." send_notification "HANA tenant '${TENANT_NAME}' backup FAILED."
if [[ "$COMPRESS" == "true" && "$DRY_RUN" == "false" ]]; then rm -rf "$backup_target_dir"; fi if [[ "$COMPRESS" == "true" && "$DRY_RUN" == "false" ]]; then rm -rf "$backup_target_dir"; fi
fi fi
;; ;;
@@ -241,7 +286,7 @@ case "$ACTION" in
mkdir -p "$EXPORT_DIR" mkdir -p "$EXPORT_DIR"
fi fi
QUERY="EXPORT \"${SCHEMA_NAME}\".\"*\" AS BINARY INTO '${EXPORT_DIR}' WITH REPLACE THREADS ${THREADS};" QUERY="EXPORT \"${SCHEMA_NAME}\".\"*\" AS BINARY INTO '${EXPORT_DIR}' WITH REPLACE THREADS ${THREADS} NO DEPENDENCIES;"
EXIT_CODE=0 EXIT_CODE=0
if [[ "$DRY_RUN" == "true" ]]; then if [[ "$DRY_RUN" == "true" ]]; then
@@ -350,12 +395,20 @@ case "$ACTION" in
exit 1 exit 1
fi fi
QUERY_RENAME_PART="" import_options=""
if [[ "$ACTION" == "import-rename" ]]; then if [[ "$IMPORT_REPLACE" == "true" ]]; then
QUERY_RENAME_PART="RENAME SCHEMA \"${SCHEMA_NAME}\" TO \"${NEW_SCHEMA_NAME}\"" import_options="REPLACE"
echo " - Mode: REPLACE"
else
import_options="IGNORE EXISTING"
echo " - Mode: IGNORE EXISTING (default)"
fi fi
QUERY="IMPORT \"${SCHEMA_NAME}\".\"*\" AS BINARY FROM '${IMPORT_DIR}' WITH IGNORE EXISTING THREADS ${THREADS} ${QUERY_RENAME_PART};" if [[ "$ACTION" == "import-rename" ]]; then
import_options="${import_options} RENAME SCHEMA \"${SCHEMA_NAME}\" TO \"${NEW_SCHEMA_NAME}\""
fi
QUERY="IMPORT \"${SCHEMA_NAME}\".\"*\" AS BINARY FROM '${IMPORT_DIR}' WITH ${import_options} THREADS ${THREADS};"
EXIT_CODE=0 EXIT_CODE=0
if [[ "$DRY_RUN" == "true" ]]; then if [[ "$DRY_RUN" == "true" ]]; then

View File

@@ -1,39 +1,28 @@
#!/bin/bash #!/bin/bash
# Author: Tomi Eckert
# --- Main Script --- # --- Main Script ---
# This script presents a menu of software packages defined in a remote # This script presents a menu of software packages, or installs them
# configuration file. The user can select one or more packages, and the # non-interactively via command-line arguments. It downloads files from a
# script will download the corresponding files. It includes a feature to show # remote configuration, shows a diff for config updates, and checks versions.
# a diff and ask for confirmation before overwriting existing config files.
# It can also check for updates to already-installed scripts.
# --- Functions --- # --- Functions ---
# A simple function to log messages with a consistent format.
log() {
echo "[$1] $2"
}
# Get the version from a local script file. # Get the version from a local script file.
# It reads the first 5 lines and extracts the version number.
get_local_version() { get_local_version() {
local file_path="$1" local file_path="$1"
if [[ -f "${file_path}" ]]; then if [[ -f "${file_path}" ]]; then
# Grep for the version line, then use awk to get the last field.
head -n 5 "${file_path}" | grep -m 1 "^# Version:" | awk '{print $NF}' head -n 5 "${file_path}" | grep -m 1 "^# Version:" | awk '{print $NF}'
else else
echo "0.0.0" # Return a base version if file doesn't exist. echo "0.0.0" # Return a base version if file doesn't exist.
fi fi
} }
# Compare two version strings (e.g., "1.2.0" vs "1.10.0"). # Compare two version strings. Returns 0 if v1 is newer.
# Returns 0 if v1 is newer, 1 if they are the same or v2 is newer.
is_version_greater() { is_version_greater() {
local v1=$1 local v1=$1
local v2=$2 local v2=$2
# Use sort's version sorting capability to find the "highest" version.
# If the highest version is v1, then v1 > v2.
if [[ "$(printf '%s\n' "$v1" "$v2" | sort -V | head -n 1)" != "$v1" ]]; then if [[ "$(printf '%s\n' "$v1" "$v2" | sort -V | head -n 1)" != "$v1" ]]; then
return 0 # v1 is greater return 0 # v1 is greater
else else
@@ -41,181 +30,212 @@ is_version_greater() {
fi fi
} }
# New function to process a single selected package. # Process a single selected package.
process_package() { process_package() {
local choice="$1" local choice_key="$1"
# Check if the choice is a valid package name. local force_overwrite="$2" # Expects "true" or "false"
if [[ -z "${SCRIPT_PACKAGES[$choice]}" ]]; then
log "❌" "Invalid package name provided: '${choice}'" if [[ -z "${SCRIPT_PACKAGES[$choice_key]}" ]]; then
echo "[❌] Invalid package name provided: '${choice_key}'"
return return
fi fi
echo echo
log "⬇️" "Processing package: '${choice}'..." echo "[⬇️] Processing package: '${choice_key}'..."
# Get the config value and split it into version and URLs # Parse the new config format
config_value="${SCRIPT_PACKAGES[$choice]}" config_value="${SCRIPT_PACKAGES[$choice_key]}"
remote_version=$(echo "${config_value}" | cut -d'|' -f1) display_name=$(echo "${config_value}" | cut -d'|' -f1)
urls_to_download=$(echo "${config_value}" | cut -d'|' -f2-) remote_version=$(echo "${config_value}" | cut -d'|' -f2)
description=$(echo "${config_value}" | cut -d'|' -f3)
urls_to_download=$(echo "${config_value}" | cut -d'|' -f4)
install_script=$(echo "${config_value}" | cut -d'|' -f5) # Optional install script
read -r -a urls_to_download_array <<< "$urls_to_download" read -r -a urls_to_download_array <<< "$urls_to_download"
for url in "${urls_to_download_array[@]}"; do for url in "${urls_to_download_array[@]}"; do
filename=$(basename "${url}") filename=$(basename "${url}")
# If it's a .conf file AND it already exists, ask to overwrite. # Handle config file overwrites
if [[ "${filename}" == *.conf && -f "${filename}" ]]; then if [[ "${filename}" == *.conf && -f "${filename}" ]]; then
log "->" "Found existing config file: '${filename}'." if [[ "$force_overwrite" == "true" ]]; then
tmp_file=$(mktemp) echo "[⚠️] Overwriting '${filename}' due to --overwrite-config flag."
if ! curl -fsSL -o "${filename}" "${url}"; then
echo "[❌] Error: Failed to download '${filename}'."
fi
continue
fi
echo "[->] Found existing config file: '${filename}'."
tmp_file=$(mktemp)
if curl -fsSL -o "${tmp_file}" "${url}"; then if curl -fsSL -o "${tmp_file}" "${url}"; then
log "🔎" "Comparing versions..." echo "[🔎] Comparing versions..."
echo "-------------------- DIFF START --------------------" echo "-------------------- DIFF START --------------------"
if command -v colordiff &> /dev/null; then if command -v colordiff &> /dev/null; then
colordiff -u "${filename}" "${tmp_file}" colordiff -u "${filename}" "${tmp_file}"
else else
# Attempt to use diff's color option, which is common.
diff --color=always -u "${filename}" "${tmp_file}" 2>/dev/null || diff -u "${filename}" "${tmp_file}" diff --color=always -u "${filename}" "${tmp_file}" 2>/dev/null || diff -u "${filename}" "${tmp_file}"
fi fi
echo "--------------------- DIFF END ---------------------" echo "--------------------- DIFF END ---------------------"
read -p "Do you want to overwrite '${filename}'? (y/N) " -n 1 -r REPLY read -p "Do you want to overwrite '${filename}'? (y/N) " -n 1 -r REPLY
echo echo
if [[ $REPLY =~ ^[Yy]$ ]]; then if [[ $REPLY =~ ^[Yy]$ ]]; then
mv "${tmp_file}" "${filename}" mv "${tmp_file}" "${filename}"
log "✅" "Updated '${filename}'." echo "[✅] Updated '${filename}'."
else else
rm "${tmp_file}" rm "${tmp_file}"
log "🤷" "Kept existing version of '${filename}'." echo "[🤷] Kept existing version of '${filename}'."
fi fi
else else
log "❌" "Error: Failed to download new version of '${filename}' for comparison." echo "[❌] Error downloading new version of '${filename}' for comparison."
rm -f "${tmp_file}" rm -f "${tmp_file}"
fi fi
else else
# Original download logic for all other files. # Original download logic for all other files.
log "->" "Downloading '${filename}'..." echo "[->] Downloading '${filename}'..."
if curl -fsSL -o "${filename}" "${url}"; then if curl -fsSL -o "${filename}" "${url}"; then
log "✅" "Successfully downloaded '${filename}'." echo "[✅] Successfully downloaded '${filename}'."
if [[ "${filename}" == *.sh || "${filename}" == *.bash ]]; then if [[ "${filename}" == *.sh || "${filename}" == *.bash ]]; then
chmod +x "${filename}" chmod +x "${filename}"
log "🤖" "Made '${filename}' executable." echo "[🤖] Made '${filename}' executable."
fi fi
else else
log "❌" "Error: Failed to download '${filename}'." echo "[❌] Error: Failed to download '${filename}'."
fi fi
fi fi
done done
log "📦" "Package processing complete for '${choice}'."
}
if [[ -n "${install_script}" ]]; then
echo "[⚙️] Running install script for '${choice_key}'..."
#eval "${install_script}"
bash -c "$(curl -sSL $install_script)"
if [ $? -eq 0 ]; then
echo "[✅] Install script completed successfully."
else
echo "[❌] Install script failed with exit code $?."
fi
fi
echo "[📦] Package processing complete for '${choice_key}'."
}
# --- Main Logic --- # --- Main Logic ---
# Generate a unique temporary filename with a timestamp.
conf_file="packages.conf.$(date +%Y%m%d%H%M%S)" conf_file="packages.conf.$(date +%Y%m%d%H%M%S)"
# Set up a trap to delete the temporary file on exit.
trap 'rm -f "${conf_file}"' EXIT trap 'rm -f "${conf_file}"' EXIT
# Download the configuration file. echo "[🔄] Downloading configuration file..."
log "🔄" "Downloading configuration file..."
if ! curl -fsSL -o "${conf_file}" "https://git.technopunk.space/tomi/Scripts/raw/branch/main/packages.conf"; then if ! curl -fsSL -o "${conf_file}" "https://git.technopunk.space/tomi/Scripts/raw/branch/main/packages.conf"; then
log "❌" "Error: Failed to download packages.conf. Exiting." echo "[❌] Error: Failed to download packages.conf. Exiting."
exit 1 exit 1
fi fi
log "✅" "Configuration file downloaded successfully." echo "[✅] Configuration file downloaded successfully."
# Source the configuration file to load the SCRIPT_PACKAGES associative array.
source "${conf_file}" source "${conf_file}"
# --- Update Check & User Interface --- # --- Argument Parsing for Non-Interactive Mode ---
if [ "$#" -gt 0 ]; then
declare -a packages_to_install
overwrite_configs=false
for arg in "$@"; do
case $arg in
--overwrite-config)
overwrite_configs=true
;;
-*)
echo "[❌] Unknown flag: $arg" >&2
exit 1
;;
*)
packages_to_install+=("$arg")
;;
esac
done
# Create an array of options from the package names. if [ ${#packages_to_install[@]} -eq 0 ]; then
# We will modify this array to show installation and update status. echo "[❌] Flag provided with no package names. Exiting."
declare -a options exit 1
package_keys=("${!SCRIPT_PACKAGES[@]}")
log "🔎" "Checking for updates..."
for key in "${package_keys[@]}"; do
# The config format is now "VERSION|URL1 URL2..."
config_value="${SCRIPT_PACKAGES[$key]}"
remote_version=$(echo "${config_value}" | cut -d'|' -f1)
# Get just the URLs and assume the first URL is the main script to check.
urls=$(echo "${config_value}" | cut -d'|' -f2-)
read -r -a url_array <<< "$urls"
main_script_filename=$(basename "${url_array[0]}")
# Get the local version of the main script file.
local_version=$(get_local_version "${main_script_filename}")
status=""
if [[ -f "${main_script_filename}" ]]; then
status=" (Installed: v${local_version})"
# Compare versions
if is_version_greater "$remote_version" "$local_version"; then
status+=" [Update available: v${remote_version}]"
fi fi
echo "[🚀] Running in non-interactive mode."
for pkg_key in "${packages_to_install[@]}"; do
if [[ -n "${SCRIPT_PACKAGES[$pkg_key]}" ]]; then
process_package "$pkg_key" "$overwrite_configs"
else
echo "[⚠️] Unknown package: '$pkg_key'. Skipping."
fi fi
options+=("${key}${status}") done
done echo "[🏁] Non-interactive run complete."
exit 0
fi
options+=("Quit") # Add a Quit option to the menu. # --- Interactive Mode ---
declare -a ordered_keys
package_keys_sorted=($(for k in "${!SCRIPT_PACKAGES[@]}"; do echo $k; done | sort))
ordered_keys=("${package_keys_sorted[@]}")
# --- User Interaction --- # --- Display Menu ---
# Manually display the options with numbers.
echo echo
echo "-------------------------------------" echo "-------------------------------------"
echo " Script Downloader " echo " Script Downloader "
echo "-------------------------------------" echo "-------------------------------------"
for i in "${!options[@]}"; do echo "[🔎] Checking for updates..."
printf "%d) %s\n" "$((i+1))" "${options[$i]}"
done
echo echo
# Prompt the user for one or more choices. for i in "${!ordered_keys[@]}"; do
key="${ordered_keys[$i]}"
config_value="${SCRIPT_PACKAGES[$key]}"
display_name=$(echo "${config_value}" | cut -d'|' -f1)
remote_version=$(echo "${config_value}" | cut -d'|' -f2)
description=$(echo "${config_value}" | cut -d'|' -f3)
urls=$(echo "${config_value}" | cut -d'|' -f4)
# install_script=$(echo "${config_value}" | cut -d'|' -f5) # Not used for display in menu
read -r -a url_array <<< "$urls"
main_script_filename=$(basename "${url_array[0]}")
local_version=$(get_local_version "${main_script_filename}")
# Print main package line
echo -e "\033[1m$((i+1))) $key - $display_name (v$remote_version)\033[0m"
# Print description
echo " $description"
# Print status
if [[ -f "${main_script_filename}" ]]; then
if is_version_greater "$remote_version" "$local_version"; then
echo -e " \033[33m[Update available: v${local_version} -> v${remote_version}]\033[0m"
else
echo -e " \033[32m[Installed: v${local_version}]\033[0m"
fi
fi
echo
done
quit_num=$((${#ordered_keys[@]} + 1))
echo -e "\033[1m${quit_num}) Quit\033[0m"
echo
# --- Handle User Input ---
read -p "Please enter your choice(s) (e.g., 1 3 4), or press Enter to quit: " -r -a user_choices read -p "Please enter your choice(s) (e.g., 1 3 4), or press Enter to quit: " -r -a user_choices
# If no choices are made, exit gracefully.
if [ ${#user_choices[@]} -eq 0 ]; then if [ ${#user_choices[@]} -eq 0 ]; then
log "👋" "No selection made. Exiting." echo "[👋] No selection made. Exiting."
exit 0 exit 0
fi fi
# Loop through the user's selections and process each one.
for choice_num in "${user_choices[@]}"; do for choice_num in "${user_choices[@]}"; do
# Validate that the input is a number.
if ! [[ "$choice_num" =~ ^[0-9]+$ ]]; then if ! [[ "$choice_num" =~ ^[0-9]+$ ]]; then
log "⚠️" "Skipping invalid input: '${choice_num}'. Not a number." echo "[⚠️] Skipping invalid input: '${choice_num}'. Not a number."
continue continue
fi fi
if [ "$choice_num" -eq "$quit_num" ]; then
# Convert selection number to array index (0-based). echo "[👋] Quit selected. Exiting."
index=$((choice_num - 1))
# Validate that the index is within the bounds of the options array.
if [[ -z "${options[$index]}" ]]; then
log "⚠️" "Skipping invalid choice: '${choice_num}'. Out of range."
continue
fi
# Get the choice text from the array.
choice_with_status="${options[$index]}"
# Strip the status message to get the package key.
choice=$(echo "${choice_with_status}" | sed 's/ (.*//')
# Handle the "Quit" option.
if [[ "${choice}" == "Quit" ]]; then
log "👋" "Quit selected. Exiting now."
exit 0 exit 0
fi fi
index=$((choice_num - 1))
# Process the selected package. if [[ -z "${ordered_keys[$index]}" ]]; then
process_package "${choice}" echo "[⚠️] Skipping invalid choice: '${choice_num}'. Out of range."
continue
fi
choice_key="${ordered_keys[$index]}"
process_package "$choice_key" "false" # Never force overwrite in interactive mode
done done
echo echo
log "🏁" "All selected packages have been processed." echo "[🏁] All selected packages have been processed."

View File

@@ -1,5 +1,6 @@
#!/bin/bash #!/bin/bash
# Version: 1.2.1 # Version: 1.2.3
# Author: Tomi Eckert
# A script to interactively manage SAP HANA hdbuserstore keys, with testing. # A script to interactively manage SAP HANA hdbuserstore keys, with testing.
@@ -12,7 +13,20 @@ COLOR_NC='\033[0m' # No Color
# --- Configuration --- # --- Configuration ---
# Adjust these paths if your HANA client is installed elsewhere. # Adjust these paths if your HANA client is installed elsewhere.
HDB_CLIENT_PATH="/usr/sap/hdbclient" # Define potential HDB client paths
HDB_CLIENT_PATH_1="/usr/sap/hdbclient"
HDB_CLIENT_PATH_2="/usr/sap/NDB/HDB00/exe"
# Check which path exists and set HDB_CLIENT_PATH accordingly
if [ -d "$HDB_CLIENT_PATH_1" ]; then
HDB_CLIENT_PATH="$HDB_CLIENT_PATH_1"
elif [ -d "$HDB_CLIENT_PATH_2" ]; then
HDB_CLIENT_PATH="$HDB_CLIENT_PATH_2"
else
echo -e "${COLOR_RED}❌ Error: Neither '$HDB_CLIENT_PATH_1' nor '$HDB_CLIENT_PATH_2' found.${COLOR_NC}"
echo -e "${COLOR_RED}Please install the SAP HANA client or adjust the paths in the script.${COLOR_NC}"
exit 1
fi
HDB_USERSTORE_EXEC="${HDB_CLIENT_PATH}/hdbuserstore" HDB_USERSTORE_EXEC="${HDB_CLIENT_PATH}/hdbuserstore"
HDB_SQL_EXEC="${HDB_CLIENT_PATH}/hdbsql" HDB_SQL_EXEC="${HDB_CLIENT_PATH}/hdbsql"
@@ -65,7 +79,7 @@ create_new_key() {
# Conditionally build the connection string # Conditionally build the connection string
if [[ "$is_systemdb" =~ ^[Yy]$ ]]; then if [[ "$is_systemdb" =~ ^[Yy]$ ]]; then
CONNECTION_STRING="${hdb_host}:3${hdb_instance}15" CONNECTION_STRING="${hdb_host}:3${hdb_instance}13"
echo -e "${COLOR_YELLOW}💡 Connecting to SYSTEMDB. Tenant name will be omitted from the connection string.${COLOR_NC}" echo -e "${COLOR_YELLOW}💡 Connecting to SYSTEMDB. Tenant name will be omitted from the connection string.${COLOR_NC}"
else else
read -p "Enter the Tenant DB [NDB]: " hdb_tenant read -p "Enter the Tenant DB [NDB]: " hdb_tenant

View File

@@ -1,20 +1,21 @@
# Configuration for SAP HANA Monitoring Script # Configuration for SAP HANA Monitoring Script
# Author: Tomi Eckert
# --- Company Information --- # --- Company Information ---
# Used to identify which company the alert is for. # Used to identify which company the alert is for.
COMPANY_NAME="Your Company Name" COMPANY_NAME="Company"
# --- Notification Settings --- # --- Notification Settings ---
# Your ntfy.sh topic URL # Your ntfy.sh topic URL
NTFY_TOPIC_URL="https://ntfy.technopunk.space/sap" NTFY_TOPIC_URL="https://ntfy.technopunk.space/sap"
# Your ntfy.sh bearer token (if required) # Your ntfy.sh bearer token (if required)
NTFY_TOKEN="your_ntfy_token_here" NTFY_TOKEN="tk_xxxxx"
# --- HANA Connection Settings --- # --- HANA Connection Settings ---
# Full path to the sapcontrol executable # Full path to the sapcontrol executable
SAPCONTROL_PATH="/usr/sap/NDB/HDB00/exe/sapcontrol" SAPCONTROL_PATH="<sapcontrol_path>"
# Full path to the hdbsql executable # Full path to the hdbsql executable
HDBSQL_PATH="/usr/sap/hdbclient/hdbsql" HDBSQL_PATH="<hdbsql_path>"
# HANA user key for authentication # HANA user key for authentication
HANA_USER_KEY="CRONKEY" HANA_USER_KEY="CRONKEY"
# HANA Instance Number for sapcontrol # HANA Instance Number for sapcontrol
@@ -27,8 +28,13 @@ DISK_USAGE_THRESHOLD=80
TRUNCATED_PERCENTAGE_THRESHOLD=50 TRUNCATED_PERCENTAGE_THRESHOLD=50
# Percentage of 'Free' log segments below which an alert is triggered # Percentage of 'Free' log segments below which an alert is triggered
FREE_PERCENTAGE_THRESHOLD=25 FREE_PERCENTAGE_THRESHOLD=25
# Maximum age of the last successful full data backup in hours.
BACKUP_THRESHOLD_HOURS=25
# Statement queue length that triggers a check
STATEMENT_QUEUE_THRESHOLD=100
# Number of consecutive runs the queue must be over threshold to trigger an alert
STATEMENT_QUEUE_CONSECUTIVE_RUNS=3
# --- Monitored Directories --- # --- Monitored Directories ---
# List of directories to check for disk usage (space-separated) # List of directories to check for disk usage (space-separated)
DIRECTORIES_TO_MONITOR=("/hana/log" "/hana/shared" "/hana/data" "/usr/sap") DIRECTORIES_TO_MONITOR=("/hana/log" "/hana/shared" "/hana/data" "/usr/sap")

56
monitor/monitor.hook.sh Normal file
View File

@@ -0,0 +1,56 @@
#!/bin/bash
# Author: Tomi Eckert
# This script helps to configure monitor.conf
# Source the monitor.conf to get current values
source monitor.conf
# Check if COMPANY_NAME or NTFY_TOKEN are still default
if [ "$COMPANY_NAME" = "Company" ] || [ "$NTFY_TOKEN" = "tk_xxxxx" ]; then
echo "Default COMPANY_NAME or NTFY_TOKEN detected. Running configuration..."
else
echo "COMPANY_NAME and NTFY_TOKEN are already configured. Exiting."
exit 0
fi
# Prompt for COMPANY_NAME
read -p "Enter Company Name (e.g., MyCompany): " COMPANY_NAME_INPUT
COMPANY_NAME_INPUT=${COMPANY_NAME_INPUT:-"$COMPANY_NAME"} # Default to current value if not provided
# Prompt for NTFY_TOKEN
read -p "Enter ntfy.sh token (e.g., tk_xxxxx): " NTFY_TOKEN_INPUT
NTFY_TOKEN_INPUT=${NTFY_TOKEN_INPUT:-"$NTFY_TOKEN"} # Default to current value if not provided
# Define HANA client paths
HDB_CLIENT_PATH="/usr/sap/hdbclient"
HDB_USERSTORE_EXEC="${HDB_CLIENT_PATH}/hdbuserstore"
# List HANA user keys and prompt for selection
echo "Available HANA User Keys:"
HANA_KEYS=$("$HDB_USERSTORE_EXEC" list 2>/dev/null | tail -n +3 | grep '^KEY ' | awk '{print $2}')
if [ -z "$HANA_KEYS" ]; then
echo "No HANA user keys found. Please create one using keymanager.sh or enter manually."
read -p "Enter HANA User Key (e.g., CRONKEY): " HANA_USER_KEY_INPUT
else
echo "$HANA_KEYS"
read -p "Enter HANA User Key from the list above (e.g., CRONKEY): " HANA_USER_KEY_INPUT
fi
HANA_USER_KEY_INPUT=${HANA_USER_KEY_INPUT:-"CRONKEY"} # Default value
# Find paths for sapcontrol and hdbsql
SAPCONTROL_PATH_INPUT=$(which sapcontrol)
HDBSQL_PATH_INPUT=$(which hdbsql)
# Default values if not found
SAPCONTROL_PATH_INPUT=${SAPCONTROL_PATH_INPUT:-"/usr/sap/NDB/HDB00/exe/sapcontrol"}
HDBSQL_PATH_INPUT=${HDBSQL_PATH_INPUT:-"/usr/sap/hdbclient/hdbsql"}
# Update monitor.conf
sed -i "s/^COMPANY_NAME=\".*\"/COMPANY_NAME=\"$COMPANY_NAME_INPUT\"/" monitor.conf
sed -i "s/^NTFY_TOKEN=\".*\"/NTFY_TOKEN=\"$NTFY_TOKEN_INPUT\"/" monitor.conf
sed -i "s#^SAPCONTROL_PATH=\".*\"#SAPCONTROL_PATH=\"$SAPCONTROL_PATH_INPUT\"#" monitor.conf
sed -i "s#^HDBSQL_PATH=\".*\"#HDBSQL_PATH=\"$HDBSQL_PATH_INPUT\"#" monitor.conf
sed -i "s/^HANA_USER_KEY=\".*\"/HANA_USER_KEY=\"$HANA_USER_KEY_INPUT\"/" monitor.conf
echo "monitor.conf updated successfully!"

View File

@@ -1,9 +1,10 @@
#!/bin/bash #!/bin/bash
# Version: 1.0.5 # Version: 1.3.1
# Author: Tomi Eckert
# ============================================================================= # =============================================================================
# SAP HANA Monitoring Script # SAP HANA Monitoring Script
# #
# Checks HANA processes, disk usage, and log segment state. # Checks HANA processes, disk usage, log segments, and statement queue.
# Sends ntfy.sh notifications if thresholds are exceeded. # Sends ntfy.sh notifications if thresholds are exceeded.
# ============================================================================= # =============================================================================
@@ -28,21 +29,68 @@ if [ ! -f "$CONFIG_FILE" ]; then
fi fi
source "$CONFIG_FILE" source "$CONFIG_FILE"
STATE_DIR="${SCRIPT_DIR}/monitor_state"
mkdir -p "${STATE_DIR}"
# Helper functions for state management
get_state() {
local key="$1"
if [ -f "${STATE_DIR}/${key}.state" ]; then
cat "${STATE_DIR}/${key}.state"
else
echo ""
fi
}
set_state() {
local key="$1"
local value="$2"
echo "$value" > "${STATE_DIR}/${key}.state"
}
HOSTNAME=$(hostname) HOSTNAME=$(hostname)
SQL_QUERY="SELECT b.host, b.service_name, a.state, count(*) FROM PUBLIC.M_LOG_SEGMENTS a JOIN PUBLIC.M_SERVICES b ON (a.host = b.host AND a.port = b.port) GROUP BY b.host, b.service_name, a.state;" SQL_QUERY="SELECT b.host, b.service_name, a.state, count(*) FROM PUBLIC.M_LOG_SEGMENTS a JOIN PUBLIC.M_SERVICES b ON (a.host = b.host AND a.port = b.port) GROUP BY b.host, b.service_name, a.state;"
send_notification() { send_notification_if_changed() {
local title="$1" local alert_key="$1"
local message="$2" local title_prefix="$2" # e.g., "HANA Process"
local full_message="[${COMPANY_NAME} | ${HOSTNAME}] ${message}" local current_message="$3"
curl -H "Authorization: Bearer ${NTFY_TOKEN}" -H "Title: ${title}" -d "${full_message}" "${NTFY_TOPIC_URL}" > /dev/null 2>&1 local is_alert_condition="$4" # "true" or "false"
local current_value="$5" # The value to store as state (e.g., "85%", "GREEN", "ALERT")
local previous_value=$(get_state "${alert_key}")
if [ "$current_value" != "$previous_value" ]; then
local full_title=""
local full_message=""
if [ "$is_alert_condition" == "true" ]; then
full_title="${title_prefix} Alert"
full_message="🚨 Critical: ${current_message}"
else
# Check if it was previously an alert (i.e., previous_value was not "OK")
if [ -n "$previous_value" ] && [ "$previous_value" != "OK" ]; then
full_title="${title_prefix} Resolved"
full_message="✅ Resolved: ${current_message}"
else
# No alert, and no previous alert to resolve, so just update state silently
set_state "${alert_key}" "$current_value"
return
fi
fi
local final_message="[${COMPANY_NAME} | ${HOSTNAME}] ${full_message}"
curl -H "Authorization: Bearer ${NTFY_TOKEN}" -H "Title: ${full_title}" -d "${final_message}" "${NTFY_TOPIC_URL}" > /dev/null 2>&1
set_state "${alert_key}" "$current_value"
echo "🔔 Notification sent for ${alert_key}: ${full_message}"
fi
} }
# --- HANA Process Status --- # --- HANA Process Status ---
echo "⚙️ Checking HANA process status..." echo "⚙️ Checking HANA process status..."
if [ ! -x "$SAPCONTROL_PATH" ]; then if [ ! -x "$SAPCONTROL_PATH" ]; then
echo "❌ Error: sapcontrol not found or not executable at ${SAPCONTROL_PATH}" >&2 echo "❌ Error: sapcontrol not found or not executable at ${SAPCONTROL_PATH}" >&2
send_notification "HANA Monitor Error" "❌ Error: sapcontrol not found or not executable at ${SAPCONTROL_PATH}" send_notification_if_changed "hana_sapcontrol_path" "HANA Monitor Error" "sapcontrol not found or not executable at ${SAPCONTROL_PATH}" "true" "SAPCONTROL_ERROR"
exit 1 exit 1
fi fi
@@ -51,23 +99,28 @@ non_green_processes=$("${SAPCONTROL_PATH}" -nr "${HANA_INSTANCE_NR}" -function G
if [ -n "$non_green_processes" ]; then if [ -n "$non_green_processes" ]; then
echo "🚨 Alert: One or more HANA processes are not running!" >&2 echo "🚨 Alert: One or more HANA processes are not running!" >&2
echo "$non_green_processes" >&2 echo "$non_green_processes" >&2
send_notification "HANA Process Alert" "🚨 Critical: One or more HANA processes are not GREEN. Problem processes: ${non_green_processes}" send_notification_if_changed "hana_processes" "HANA Process" "One or more HANA processes are not GREEN. Problem processes: ${non_green_processes}" "true" "PROCESS_ALERT:${non_green_processes}"
exit 1 # Exit early as other checks might fail exit 1 # Exit early as other checks might fail
else
send_notification_if_changed "hana_processes" "HANA Process" "All HANA processes are GREEN." "false" "OK"
echo "✅ Success! All HANA processes are GREEN."
fi fi
echo "✅ Success! All HANA processes are GREEN."
# --- Disk Space Monitoring --- # --- Disk Space Monitoring ---
echo " Checking disk usage..." echo " Checking disk usage..."
for dir in "${DIRECTORIES_TO_MONITOR[@]}"; do for dir in "${DIRECTORIES_TO_MONITOR[@]}"; do
if [ ! -d "$dir" ]; then if [ ! -d "$dir" ]; then
echo "⚠️ Warning: Directory '$dir' not found. Skipping." >&2 echo "⚠️ Warning: Directory '$dir' not found. Skipping." >&2
send_notification_if_changed "disk_dir_not_found_${dir//\//_}" "HANA Disk Warning" "Directory '$dir' not found." "true" "DIR_NOT_FOUND"
continue continue
fi fi
usage=$(df -h "$dir" | awk 'NR==2 {print $5}' | sed 's/%//') usage=$(df -h "$dir" | awk 'NR==2 {print $5}' | sed 's/%//')
echo " - ${dir} is at ${usage}%" echo " - ${dir} is at ${usage}%"
if (( $(echo "$usage > $DISK_USAGE_THRESHOLD" | bc -l) )); then if (( $(echo "$usage > $DISK_USAGE_THRESHOLD" | bc -l) )); then
echo "🚨 Alert: ${dir} usage is at ${usage}% which is above the ${DISK_USAGE_THRESHOLD}% threshold." >&2 echo "🚨 Alert: ${dir} usage is at ${usage}% which is above the ${DISK_USAGE_THRESHOLD}% threshold." >&2
send_notification "HANA Disk Alert" "🚨 Critical: Disk usage for ${dir} is at ${usage}%." send_notification_if_changed "disk_usage_${dir//\//_}" "HANA Disk" "Disk usage for ${dir} is at ${usage}%." "true" "${usage}%"
else
send_notification_if_changed "disk_usage_${dir//\//_}" "HANA Disk" "Disk usage for ${dir} is at ${usage}% (below threshold)." "false" "OK"
fi fi
done done
@@ -75,14 +128,14 @@ done
echo "⚙️ Executing HANA SQL query..." echo "⚙️ Executing HANA SQL query..."
if [ ! -x "$HDBSQL_PATH" ]; then if [ ! -x "$HDBSQL_PATH" ]; then
echo "❌ Error: hdbsql not found or not executable at ${HDBSQL_PATH}" >&2 echo "❌ Error: hdbsql not found or not executable at ${HDBSQL_PATH}" >&2
send_notification "HANA Monitor Error" "❌ Error: hdbsql not found or not executable at ${HDBSQL_PATH}" send_notification_if_changed "hana_hdbsql_path" "HANA Monitor Error" "hdbsql not found or not executable at ${HDBSQL_PATH}" "true" "HDBSQL_ERROR"
exit 1 exit 1
fi fi
readarray -t sql_output < <("$HDBSQL_PATH" -U "$HANA_USER_KEY" -c ";" "$SQL_QUERY" 2>&1) readarray -t sql_output < <("$HDBSQL_PATH" -U "$HANA_USER_KEY" -c ";" "$SQL_QUERY" 2>&1)
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo "❌ Failure! The hdbsql command failed. Please check logs." >&2 echo "❌ Failure! The hdbsql command failed. Please check logs." >&2
error_message=$(printf '%s\n' "${sql_output[@]}") error_message=$(printf '%s\n' "${sql_output[@]}")
send_notification "HANA Monitor Error" "❌ Failure! The hdbsql command failed. Details: ${error_message}" send_notification_if_changed "hana_hdbsql_command" "HANA Monitor Error" "The hdbsql command failed. Details: ${error_message}" "true" "HDBSQL_COMMAND_FAILED"
exit 1 exit 1
fi fi
@@ -108,20 +161,84 @@ echo " Free Segments: ${free_segments}"
if [ $total_segments -eq 0 ]; then if [ $total_segments -eq 0 ]; then
echo "⚠️ Warning: No log segments found. Skipping percentage checks." >&2 echo "⚠️ Warning: No log segments found. Skipping percentage checks." >&2
exit 0 send_notification_if_changed "hana_log_segments_total" "HANA Log Segment Warning" "No log segments found. Skipping percentage checks." "true" "NO_LOG_SEGMENTS"
fi else
send_notification_if_changed "hana_log_segments_total" "HANA Log Segment" "Log segments found." "false" "OK"
truncated_percentage=$((truncated_segments * 100 / total_segments)) truncated_percentage=$((truncated_segments * 100 / total_segments))
if (( $(echo "$truncated_percentage > $TRUNCATED_PERCENTAGE_THRESHOLD" | bc -l) )); then if (( $(echo "$truncated_percentage > $TRUNCATED_PERCENTAGE_THRESHOLD" | bc -l) )); then
echo "🚨 Alert: ${truncated_percentage}% of log segments are 'Truncated'." >&2 echo "🚨 Alert: ${truncated_percentage}% of log segments are 'Truncated'." >&2
send_notification "HANA Log Segment Alert" "🚨 Alert: ${truncated_percentage}% of HANA log segments are in 'Truncated' state." send_notification_if_changed "hana_log_truncated" "HANA Log Segment" "${truncated_percentage}% of HANA log segments are in 'Truncated' state." "true" "${truncated_percentage}%"
else
send_notification_if_changed "hana_log_truncated" "HANA Log Segment" "${truncated_percentage}% of HANA log segments are in 'Truncated' state (below threshold)." "false" "OK"
fi
free_percentage=$((free_segments * 100 / total_segments))
if (( $(echo "$free_percentage < $FREE_PERCENTAGE_THRESHOLD" | bc -l) )); then
echo "🚨 Alert: Only ${free_percentage}% of log segments are 'Free'." >&2
send_notification_if_changed "hana_log_free" "HANA Log Segment" "Only ${free_percentage}% of HANA log segments are in 'Free' state." "true" "${free_percentage}%"
else
send_notification_if_changed "hana_log_free" "HANA Log Segment" "Only ${free_percentage}% of HANA log segments are in 'Free' state (above threshold)." "false" "OK"
fi
fi fi
free_percentage=$((free_segments * 100 / total_segments)) # --- HANA Statement Queue Monitoring ---
if (( $(echo "$free_percentage < $FREE_PERCENTAGE_THRESHOLD" | bc -l) )); then echo "⚙️ Checking HANA statement queue..."
echo "🚨 Alert: Only ${free_percentage}% of log segments are 'Free'." >&2 STATEMENT_QUEUE_SQL="SELECT COUNT(*) FROM M_SERVICE_THREADS WHERE THREAD_TYPE = 'SqlExecutor' AND THREAD_STATE = 'Queueing';"
send_notification "HANA Log Segment Alert" "🚨 Alert: Only ${free_percentage}% of HANA log segments are in 'Free' state." queue_count=$("$HDBSQL_PATH" -U "$HANA_USER_KEY" -j -a -x "$STATEMENT_QUEUE_SQL" 2>/dev/null | tr -d '"')
if ! [[ "$queue_count" =~ ^[0-9]+$ ]]; then
echo "⚠️ Warning: Could not retrieve HANA statement queue count. Skipping check." >&2
send_notification_if_changed "hana_statement_queue_check_fail" "HANA Monitor Warning" "Could not retrieve statement queue count." "true" "QUEUE_CHECK_FAIL"
else
send_notification_if_changed "hana_statement_queue_check_fail" "HANA Monitor Warning" "Statement queue check is working." "false" "OK"
echo " Current statement queue length: ${queue_count}"
breach_count=$(get_state "statement_queue_breach_count")
breach_count=${breach_count:-0}
if (( queue_count > STATEMENT_QUEUE_THRESHOLD )); then
breach_count=$((breach_count + 1))
echo "📈 Statement queue is above threshold. Consecutive breach count: ${breach_count}/${STATEMENT_QUEUE_CONSECUTIVE_RUNS}."
else
breach_count=0
fi
set_state "statement_queue_breach_count" "$breach_count"
if (( breach_count >= STATEMENT_QUEUE_CONSECUTIVE_RUNS )); then
message="Statement queue has been over ${STATEMENT_QUEUE_THRESHOLD} for ${breach_count} checks. Current count: ${queue_count}."
send_notification_if_changed "hana_statement_queue_status" "HANA Statement Queue" "${message}" "true" "ALERT:${queue_count}"
else
message="Statement queue is normal. Current count: ${queue_count}."
send_notification_if_changed "hana_statement_queue_status" "HANA Statement Queue" "${message}" "false" "OK"
fi
fi
# --- HANA Backup Status Monitoring ---
echo " Checking last successful data backup status..."
last_backup_date=$("$HDBSQL_PATH" -U "$HANA_USER_KEY" -j -a -x \
"SELECT TOP 1 SYS_START_TIME FROM M_BACKUP_CATALOG WHERE ENTRY_TYPE_NAME = 'complete data backup' AND STATE_NAME = 'successful' ORDER BY SYS_START_TIME DESC" 2>/dev/null | tr -d "\"" | sed 's/\..*//')
if [[ -z "$last_backup_date" ]]; then
message="No successful complete data backup found for ${COMPANY_NAME} HANA."
echo "🚨 Critical: ${message}"
send_notification_if_changed "hana_backup_status" "HANA Backup" "${message}" "true" "NO_BACKUP"
else
last_backup_epoch=$(date -d "$last_backup_date" +%s)
current_epoch=$(date +%s)
threshold_seconds=$((BACKUP_THRESHOLD_HOURS * 3600))
age_seconds=$((current_epoch - last_backup_epoch))
age_hours=$((age_seconds / 3600))
if (( age_seconds > threshold_seconds )); then
message="Last successful HANA backup for ${COMPANY_NAME} is ${age_hours} hours old, which exceeds the threshold of ${BACKUP_THRESHOLD_HOURS} hours. Last backup was on: ${last_backup_date}."
echo "🚨 Critical: ${message}"
send_notification_if_changed "hana_backup_status" "HANA Backup" "${message}" "true" "${age_hours}h"
else
message="Last successful backup is ${age_hours} hours old (Threshold: ${BACKUP_THRESHOLD_HOURS} hours)."
echo "✅ Success! ${message}"
send_notification_if_changed "hana_backup_status" "HANA Backup" "${message}" "false" "OK"
fi
fi fi
echo "✅ Success! HANA monitoring check complete." echo "✅ Success! HANA monitoring check complete."

View File

@@ -1,17 +1,18 @@
#!/bin/bash #!/bin/bash
# Author: Tomi Eckert
# #
# This file contains the configuration for the script downloader. # This file contains the configuration for the script downloader.
# The `SCRIPT_PACKAGES` associative array maps a package name to a # The `SCRIPT_PACKAGES` associative array maps a short package name
# pipe-separated string: "<version>|<space-separated list of URLs>". # to a pipe-separated string with the following format:
# "<Display Name>|<Version>|<Description>|<Space-separated list of URLs>|[Install Script (optional)]"
# The Install Script will be executed after all files for the package are downloaded.
declare -A SCRIPT_PACKAGES declare -A SCRIPT_PACKAGES
# The version should match the "# Version: x.x.x" line in the main script file. # Format: short_name="Display Name|Version|Description|URL1 URL2..."
SCRIPT_PACKAGES["Aurora Suite"]="1.2.6|https://git.technopunk.space/tomi/Scripts/raw/branch/main/aurora/aurora.sh https://git.technopunk.space/tomi/Scripts/raw/branch/main/aurora/aurora.conf" SCRIPT_PACKAGES["aurora"]="Aurora Suite|2.1.0|A collection of scripts for managing Aurora database instances.|https://git.technopunk.space/tomi/Scripts/raw/branch/main/aurora/aurora.sh https://git.technopunk.space/tomi/Scripts/raw/branch/main/aurora/aurora.conf"
SCRIPT_PACKAGES["Backup Suite"]="1.0.5|https://git.technopunk.space/tomi/Scripts/raw/branch/main/backup/backup.sh https://git.technopunk.space/tomi/Scripts/raw/branch/main/backup/backup.conf" SCRIPT_PACKAGES["backup"]="Backup Suite|1.0.8|A comprehensive script for backing up system files and databases.|https://git.technopunk.space/tomi/Scripts/raw/branch/main/backup/backup.sh https://git.technopunk.space/tomi/Scripts/raw/branch/main/backup/backup.conf"
SCRIPT_PACKAGES["Monitor Suite"]="1.0.5|https://git.technopunk.space/tomi/Scripts/raw/branch/main/monitor/monitor.sh https://git.technopunk.space/tomi/Scripts/raw/branch/main/monitor/monitor.conf" SCRIPT_PACKAGES["monitor"]="Monitor Suite|1.3.1|Scripts for monitoring system health and performance metrics.|https://git.technopunk.space/tomi/Scripts/raw/branch/main/monitor/monitor.sh https://git.technopunk.space/tomi/Scripts/raw/branch/main/monitor/monitor.conf|https://git.technopunk.space/tomi/Scripts/raw/branch/main/monitor/monitor.hook.sh"
SCRIPT_PACKAGES["Key Manager"]="1.2.1|https://git.technopunk.space/tomi/Scripts/raw/branch/main/hdb_keymanager.sh" SCRIPT_PACKAGES["keymanager"]="Key Manager|1.2.3|A utility for managing HDB user keys for SAP HANA.|https://git.technopunk.space/tomi/Scripts/raw/branch/main/keymanager.sh"
SCRIPT_PACKAGES["File Cleaner"]="1.1.0|https://git.technopunk.space/tomi/Scripts/raw/branch/main/clean.sh" SCRIPT_PACKAGES["cleaner"]="File Cleaner|1.1.0|A simple script to clean up temporary files and logs.|https://git.technopunk.space/tomi/Scripts/raw/branch/main/cleaner.sh"
SCRIPT_PACKAGES["HANA Tool"]="1.4.6|https://git.technopunk.space/tomi/Scripts/raw/branch/main/hanatool.sh" SCRIPT_PACKAGES["hanatool"]="HANA Tool|1.5.6|A command-line tool for various SAP HANA administration tasks.|https://git.technopunk.space/tomi/Scripts/raw/branch/main/hanatool.sh"
# Example: Add a new script with its version.
# SCRIPT_PACKAGES["My Other Script"]="1.0.0|https://path/to/my-other-script.sh"