Index: /branches/amp_4_0/platform/tools/enable_opensearch_jwt_auth.sh
===================================================================
--- /branches/amp_4_0/platform/tools/enable_opensearch_jwt_auth.sh	(nonexistent)
+++ /branches/amp_4_0/platform/tools/enable_opensearch_jwt_auth.sh	(working copy)
@@ -0,0 +1,88 @@
+#!/bin/bash
+
+OPENSEARCH_CONFIG_DIR="/etc/opensearch/opensearch-security"
+CONFIG_YML="$OPENSEARCH_CONFIG_DIR/config.yml"
+ROLES_YML="$OPENSEARCH_CONFIG_DIR/roles.yml"
+ROLES_MAPPING_YML="$OPENSEARCH_CONFIG_DIR/roles_mapping.yml"
+CERTS_DIR="/etc/opensearch/certs"
+BACKUP_CONFIG_YML="$CONFIG_YML.bak-$(date +%F-%H%M%S)"
+SECURITY_ADMIN_SCRIPT="/usr/share/opensearch/plugins/opensearch-security/tools/securityadmin.sh"
+
+sudo dnf install epel-release -y
+sudo dnf install yq -y
+
+pip3.13 install pyyaml
+
+echo "🔐 Generating a new JWT signing key..."
+BASE64_SIGNING_KEY=$(openssl rand -base64 32 | tr -d '\n"' | tr -d "'")
+
+if [ -z "$BASE64_SIGNING_KEY" ]; then
+    echo "❌ Failed to generate signing key. Exiting."
+    exit 1
+fi
+
+echo "📝 Creating backup of config.yml..."
+cp "$CONFIG_YML" "$BACKUP_CONFIG_YML"
+
+echo "✅ Updating config.yml using yq..."
+command -v yq >/dev/null 2>&1 || { echo "❌ yq not installed."; exit 1; }
+
+# Enable HTTP for JWT domain and set key
+yq eval -i '.config.dynamic.authc.jwt_auth_domain.http_enabled = true' "$CONFIG_YML"
+yq eval -i '.config.dynamic.authc.jwt_auth_domain.http_authenticator.config.signing_key = "'"$BASE64_SIGNING_KEY"'"' "$CONFIG_YML"
+yq eval -i '.config.dynamic.authc.jwt_auth_domain.http_authenticator.config.jwt_url_parameter = "access_token"' "$CONFIG_YML"
+yq eval -i '.config.dynamic.authc.jwt_auth_domain.http_authenticator.config.roles_key = "roles"' "$CONFIG_YML"
+yq eval -i '.config.dynamic.authc.jwt_auth_domain.http_authenticator.config.subject_key = "sub"' "$CONFIG_YML"
+yq eval -i '.config.dynamic.authc.jwt_auth_domain.http_authenticator.config.issuer = "amp.com"' "$CONFIG_YML"
+yq eval -i '.config.dynamic.authc.jwt_auth_domain.order = 1' "$CONFIG_YML"
+
+echo "✅ Ensuring roles.yml contains jwt_users role..."
+if ! grep -q '^jwt_users:' "$ROLES_YML"; then
+    cat >> "$ROLES_YML" <<EOL
+
+jwt_users:
+  cluster_permissions:
+    - "cluster:monitor/*"
+  index_permissions:
+    - index_patterns:
+        - "*"
+      allowed_actions:
+        - "read"
+  tenant_permissions:
+    - tenant_patterns:
+        - "*"
+      allowed_actions:
+        - "kibana_all_write"
+EOL
+fi
+
+echo "✅ Ensuring roles_mapping.yml contains mapping for JWT users..."
+if ! grep -q '^jwt_users:' "$ROLES_MAPPING_YML"; then
+    cat >> "$ROLES_MAPPING_YML" <<EOL
+
+jwt_users:
+  reserved: false
+  hidden: false
+  description: "All JWT authenticated users"
+  users:
+    - "admin"
+EOL
+fi
+
+echo "🔍 Validating YAML syntax..."
+python3 -c "import yaml; yaml.safe_load(open('$CONFIG_YML'))" || { echo "❌ Invalid YAML syntax"; cp "$BACKUP_CONFIG_YML" "$CONFIG_YML"; exit 1; }
+
+echo "✅ Pushing updated security configuration..."
+$SECURITY_ADMIN_SCRIPT -cd "$OPENSEARCH_CONFIG_DIR" \
+    -cacert "$CERTS_DIR/root-ca.pem" \
+    -cert "$CERTS_DIR/admin.pem" \
+    -key "$CERTS_DIR/admin-key.pem" -nhnv
+
+if [ $? -eq 0 ]; then
+    echo "✅ JWT Authentication enabled and role mapping applied successfully."
+    echo "ℹ️ Restart OpenSearch service if needed."
+else
+    echo "❌ Security admin push failed. Restoring backup."
+    cp "$BACKUP_CONFIG_YML" "$CONFIG_YML"
+    exit 1
+fi
Index: /branches/amp_4_0/platform/tools/install_configure_opensearch.sh
===================================================================
--- /branches/amp_4_0/platform/tools/install_configure_opensearch.sh	(revision 2703)
+++ /branches/amp_4_0/platform/tools/install_configure_opensearch.sh	(working copy)
@@ -244,7 +244,6 @@
 
 # 16. Configure OpenSearch
 log_info "Configuring OpenSearch..."
-
 sudo cp "${OPENSEARCH_CONFIG_FILE}" "${OPENSEARCH_CONFIG_FILE}.bak_$(date +%Y%m%d%H%M%S)"
 
 cat << EOF | sudo tee "${OPENSEARCH_CONFIG_FILE}" > /dev/null
@@ -275,7 +274,8 @@
 plugins.security.allow_default_init_securityindex: true
 
 # Allow REST API access to these roles
-plugins.security.restapi.roles_enabled: ["security_rest_api_access"]
+plugins.security.restapi.roles_enabled: ["security_rest_api_access", "all_access"]
+plugins.security.restapi.admin.enabled: true
 
 # Admin DN
 plugins.security.authcz.admin_dn:
@@ -292,7 +292,6 @@
 
 # 17. Configure OpenSearch Dashboards
 log_info "Configuring OpenSearch Dashboards..."
-
 sudo cp "${DASHBOARDS_CONFIG_FILE}" "${DASHBOARDS_CONFIG_FILE}.bak_$(date +%Y%m%d%H%M%S)"
 
 cat << EOF | sudo tee "${DASHBOARDS_CONFIG_FILE}" > /dev/null
@@ -311,6 +310,10 @@
 server.ssl.certificate: ${NODE_CERT}
 server.ssl.key: ${NODE_KEY}
 opensearch.ssl.certificateAuthorities: ["${CA_CERT}"]
+
+server.basePath: "/visualization"
+server.rewriteBasePath: true
+
 EOF
 
 # Set permissions
@@ -320,7 +323,6 @@
 
 # 18. Configure OpenSearch systemd service
 log_info "Generating OpenSearch systemd service file..."
-
 sudo rm -f "${OPENSEARCH_SERVICE_FILE}"
 
 cat << EOF | sudo tee "${OPENSEARCH_SERVICE_FILE}" > /dev/null
@@ -486,79 +488,48 @@
     log_info "Waiting for security index to stabilize..."
     sleep 10 # Give it a moment after plugin initialization
 
-    log_info "Updating admin user roles and password..."
-
-    # Retry updating admin user roles up to 3 times
-    RETRY_COUNT=3
-    ADMIN_UPDATE_SUCCESS=false
-    for j in $(seq 1 $RETRY_COUNT); do
-        UPDATE_RESPONSE=$(curl -s -k -X PUT "https://localhost:9200/_plugins/_security/api/internalusers/admin" \
-            --cert "${ADMIN_CERT}" --key "${ADMIN_KEY}" \
-            -H "Content-Type: application/json" \
-            -d "{\"password\":\"${NEW_ADMIN_PASSWORD}\",\"backend_roles\":[\"security_admin\",\"all_access\", \"security_rest_api_access\"]}" --fail --show-error)
+    # New steps to update password and roles
+    log_info "Updating admin user password and roles based on manual steps..."
+    # Dynamically generate the password hash, filtering out the warning
+    log_info "Dynamically generating hash for the admin password..."
+    ADMIN_HASH=$(sudo -u opensearch /usr/share/opensearch/plugins/opensearch-security/tools/hash.sh -p "${NEW_ADMIN_PASSWORD}" | grep -o '\$2y\$12\$[a-zA-Z0-9./]*' | tr -d '\n')
+    if [ -z "${ADMIN_HASH}" ]; then
+        log_error "Failed to generate password hash. Ensure OpenSearch is running and paths are correct."
+    fi
+    log_success "Password hash generated."
 
-        if echo "$UPDATE_RESPONSE" | grep -q "\"status\":\"OK\""; then
-            log_success "Admin user roles updated and password changed to '${NEW_ADMIN_PASSWORD}'."
-            ADMIN_UPDATE_SUCCESS=true
-            break
-        else
-            log_warn "Attempt $j/$RETRY_COUNT: Failed to update admin user roles. Response: $UPDATE_RESPONSE. Retrying in 5 seconds..."
-            sleep 5
-        fi
-    done
+    # Update internal_users.yml with the newly generated hash
+    log_info "Updating internal_users.yml with new admin password hash..."
+    # Using a more robust sed command with a different delimiter '@'
+    sudo sed -i -E "s@^\s*hash:.*@  hash: \"${ADMIN_HASH}\"@" "${SECURITY_CONFIG_DIR}/internal_users.yml" || log_error "Failed to update admin hash in internal_users.yml."
+    log_success "internal_users.yml updated with new admin hash."
 
-    if [ "$ADMIN_UPDATE_SUCCESS" = false ]; then
-        log_error "Failed to update admin user roles after $RETRY_COUNT attempts."
+    # Re-run securityadmin.sh to apply the new internal_users.yml with the password change.
+    log_info "Applying the updated security configuration..."
+    if JAVA_HOME="${JAVA_HOME_PATH}" sudo -E /usr/share/opensearch/plugins/opensearch-security/tools/securityadmin.sh \
+        -cd "${SECURITY_CONFIG_DIR}" \
+        -icl -nhnv \
+        -cacert "${CA_CERT}" -cert "${ADMIN_CERT}" -key "${ADMIN_KEY}"; then
+        log_success "Updated security configuration applied successfully."
+    else
+        log_error "Failed to apply updated security configuration."
     fi
 
-    echo "[INFO] Mapping backend roles to security roles..."
-    # Add a small delay here to allow the previous update to settle
-    sleep 5
-
-    for role in all_access security_rest_api_access; do
-      echo "  ↳ Mapping backend role: ${role}"
-      MAPPING_RESPONSE=$(curl -s -k \
-        --cert /etc/opensearch/certs/admin.pem \
-        --key /etc/opensearch/certs/admin-key.pem \
-        -X PUT "https://localhost:9200/_plugins/_security/api/rolesmapping/${role}" \
-        -H 'Content-Type: application/json' \
-        -d "{\"backend_roles\": [\"${role}\"]}" | jq)
-      echo "$MAPPING_RESPONSE"
-      if echo "$MAPPING_RESPONSE" | grep -q "status"; then
-          log_success "Successfully mapped backend role: ${role}"
-      else
-          log_warn "Failed to map backend role: ${role}. Response: $MAPPING_RESPONSE"
-      fi
-      sleep 1 # Small delay between role mappings
-    done
-    echo "[SUCCESS] Role mappings completed."
-
-    # 23. Verify admin user roles - AFTER role mappings
-    log_info "Verifying admin user roles after mapping..."
-    # Add a small delay to ensure role mappings are fully applied
-    sleep 10
-    ADMIN_ROLES_VERIFY=$(curl -s -k -u admin:"${NEW_ADMIN_PASSWORD}" "https://localhost:9200/_plugins/_security/api/internalusers/admin?pretty")
-    if echo "$ADMIN_ROLES_VERIFY" | grep -q "security_admin" && \
-       echo "$ADMIN_ROLES_VERIFY" | grep -q "all_access" && \
-       echo "$ADMIN_ROLES_VERIFY" | grep -q "security_rest_api_access"; then
-        log_success "Admin user has all required roles: $(echo "$ADMIN_ROLES_VERIFY" | grep backend_roles)"
+    # 23. Verify admin user roles after changes
+    log_info "Verifying admin user roles after password and configuration update..."
+    sleep 10 # A final delay to ensure changes are live
+    ADMIN_ROLES_VERIFY=$(curl -s -k -u admin:"${NEW_ADMIN_PASSWORD}" "https://localhost:9200/_plugins/_security/api/roles?pretty")
+    # This curl command directly verifies the roles that are available and accessible with the new credentials.
+    if echo "$ADMIN_ROLES_VERIFY" | grep -q "all_access" && echo "$ADMIN_ROLES_VERIFY" | grep -q "security_rest_api_full_access"; then
+        log_success "Admin user can access expected roles with the new password. Validation successful."
     else
-        log_error "Admin user still missing required roles. Actual roles: $(echo "$ADMIN_ROLES_VERIFY" | grep backend_roles || echo 'None'). Please review security configuration."
+        log_error "Validation failed. Admin user could not access all expected roles with the new password. Response: $ADMIN_ROLES_VERIFY"
     fi
 
 else
     log_error "Failed to initialize Security Plugin. Check logs."
 fi
 
-# 23. Verify admin user roles
-log_info "Verifying admin user roles..."
-ADMIN_ROLES=$(curl -s -k -u admin:"${NEW_ADMIN_PASSWORD}" "https://localhost:9200/_plugins/_security/api/internalusers/admin?pretty")
-if echo "$ADMIN_ROLES" | grep -q "all_access"; then
-    log_success "Admin user has required roles: $(echo "$ADMIN_ROLES" | grep backend_roles)"
-else
-    log_error "Admin user missing required roles. Actual roles: $(echo "$ADMIN_ROLES" | grep backend_roles || echo 'None'). Check security configuration."
-fi
-
 # 24. Start OpenSearch Dashboards
 log_info "Enabling and starting OpenSearch Dashboards service..."
 sudo systemctl daemon-reload || log_error "Failed to reload systemd daemon."
@@ -599,4 +570,4 @@
 log_info "Access Dashboards at: https://$(hostname -I | awk '{print $1}'):${DASHBOARDS_PORT} or https://127.0.0.1:${DASHBOARDS_PORT}"
 log_info "Username: admin, Password: ${NEW_ADMIN_PASSWORD}"
 log_warn "Import CA certificate (${CA_CERT}) into your browser's trust store."
-log_warn "For production, use trusted CA certificates and secure passwords."
\ No newline at end of file
+log_warn "For production, use trusted CA certificates and secure passwords."
Index: /branches/amp_4_0/platform/tools/install_nginx.sh
===================================================================
--- /branches/amp_4_0/platform/tools/install_nginx.sh	(revision 2703)
+++ /branches/amp_4_0/platform/tools/install_nginx.sh	(working copy)
@@ -2,10 +2,10 @@
 
 # Script to configure Nginx on Rocky Linux 9.5 as a reverse proxy for:
 # - Custom app (backend at http://127.0.0.1:3033 or static page) on HTTPS (port 443) at root (e.g., /, /login)
-# - Kibana (backend at http://127.0.0.1:5601) accessible via /visualization on HTTPS (port 443)
+# - Opensearch-Dashboards (backend at http://127.0.0.1:5601) accessible via /visualization on HTTPS (port 443)
 # - Grafana (backend at http://127.0.0.1:3000) accessible via /monitoring on HTTPS (port 443)
 # Uses self-signed certificates and the server's IP address instead of a domain name.
-# Kibana paths are scoped under /visualization/ and Grafana under /monitoring/ to avoid conflicts with custom app paths (e.g., /login).
+# Opensearch-Dashboards paths are scoped under /visualization/ and Grafana under /monitoring/ to avoid conflicts with custom app paths (e.g., /login).
 # Fixes duplicate server.host and handles redirects to stay under respective paths.
 
 # --- Variables ---
@@ -21,9 +21,9 @@
 SSL_CRT="${SSL_DIR}/server.crt"
 SSL_COMMON_NAME="${SERVER_IP:-$(ip addr show | grep -oE 'inet [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | awk '{print $2}' | grep -v '127.0.0.1' | head -1)}"
 SSL_DAYS=365
-BACKEND_URL="http://127.0.0.1:3033" # Backend for custom app
-KIBANA_BACKEND_URL="http://127.0.0.1:5601" # Kibana backend
-GRAFANA_BACKEND_URL="http://127.0.0.1:3000" # Grafana backend
+BACKEND_URL="http://127.0.0.1:8000" # Backend for custom app
+OPENSEARCH_BACKEND_URL="https://127.0.0.1:5601" # Opensearch backend
+#GRAFANA_BACKEND_URL="http://127.0.0.1:3000" # Grafana backend
 
 # --- Functions ---
 log_info() {
@@ -43,7 +43,7 @@
     local url=$1
     local name=$2
     log_info "Checking if ${name} is running at ${url}..."
-    if ! curl -s --connect-timeout 5 "${url}" &>/dev/null; then
+    if ! curl -s -k --connect-timeout 5 "${url}" &>/dev/null; then
         log_error "${name} is not running at ${url}. Start the service or update the configuration."
     fi
     log_info "${name} is accessible at ${url}."
@@ -90,8 +90,8 @@
 fi
 
 # --- Check Backend Services ---
-check_service "${KIBANA_BACKEND_URL}" "Kibana"
-check_service "${GRAFANA_BACKEND_URL}" "Grafana"
+check_service "${OPENSEARCH_BACKEND_URL}" "Opensearch-Dashboards"
+#check_service "${GRAFANA_BACKEND_URL}" "Grafana"
 if curl -s --connect-timeout 5 "${BACKEND_URL}" &>/dev/null; then
     log_info "Main application is accessible at ${BACKEND_URL}."
 else
@@ -140,7 +140,7 @@
     sudo semanage fcontext -a -t httpd_config_t "${SSL_DIR}(/.*)?" &>> "${INSTALL_LOG_FILE}"
     sudo restorecon -R -v "${SSL_DIR}" &>> "${INSTALL_LOG_FILE}"
     sudo setsebool -P httpd_can_network_connect 1 &>> "${INSTALL_LOG_FILE}"
-    sudo semanage port -a -t http_port_t -p tcp 3033 2>/dev/null || sudo semanage port -m -t http_port_t -p tcp 3033 &>> "${INSTALL_LOG_FILE}"
+    sudo semanage port -a -t http_port_t -p tcp 8000 2>/dev/null || sudo semanage port -m -t http_port_t -p tcp 8000 &>> "${INSTALL_LOG_FILE}"
     sudo semanage port -a -t http_port_t -p tcp 5601 2>/dev/null || sudo semanage port -m -t http_port_t -p tcp 5601 &>> "${INSTALL_LOG_FILE}"
     sudo semanage port -a -t http_port_t -p tcp 3000 2>/dev/null || sudo semanage port -m -t http_port_t -p tcp 3000 &>> "${INSTALL_LOG_FILE}"
 else
@@ -167,51 +167,25 @@
 fi
 log_info "FirewallD configured to allow HTTP and HTTPS traffic."
 
-# --- Configure Kibana Base Path ---
-log_info "Configuring Kibana with base path /visualization..."
-KIBANA_CONF="/etc/kibana/kibana.yml"
-if [ -f "${KIBANA_CONF}" ]; then
-    # Remove existing server.host, server.port, server.basePath, server.rewriteBasePath to avoid duplicates
-    sudo sed -i '/^server\.host:/d' "${KIBANA_CONF}" &>> "${INSTALL_LOG_FILE}"
-    sudo sed -i '/^server\.port:/d' "${KIBANA_CONF}" &>> "${INSTALL_LOG_FILE}"
-    sudo sed -i '/^server\.basePath:/d' "${KIBANA_CONF}" &>> "${INSTALL_LOG_FILE}"
-    sudo sed -i '/^server\.rewriteBasePath:/d' "${KIBANA_CONF}" &>> "${INSTALL_LOG_FILE}"
-    # Add correct settings
-    cat <<EOK >> "${KIBANA_CONF}"
-server.host: "127.0.0.1"
-server.port: 5601
-server.basePath: "/visualization"
-server.rewriteBasePath: true
-EOK
-    log_info "Restarting Kibana to apply configuration..."
-    sudo systemctl restart kibana &>> "${INSTALL_LOG_FILE}"
-    if [ $? -ne 0 ]; then
-        log_error "Failed to restart Kibana. Check 'sudo systemctl status kibana' or ${INSTALL_LOG_FILE}."
-    fi
-    log_info "Kibana configured with base path /visualization."
-else
-    log_error "Kibana configuration file not found at ${KIBANA_CONF}. Ensure Kibana is installed and configured."
-fi
-
-# --- Configure Grafana Base Path ---
-log_info "Configuring Grafana with base path /monitoring..."
-GRAFANA_CONF="/etc/grafana/grafana.ini"
-if [ -f "${GRAFANA_CONF}" ]; then
-    # Update Grafana configuration for base path
-    sudo sed -i 's|^;\?\s*root_url\s*=.*|root_url = https://'${SSL_COMMON_NAME}'/monitoring/|' "${GRAFANA_CONF}" &>> "${INSTALL_LOG_FILE}"
-    sudo sed -i 's|^;\?\s*serve_from_sub_path\s*=.*|serve_from_sub_path = true|' "${GRAFANA_CONF}" &>> "${INSTALL_LOG_FILE}"
-    log_info "Restarting Grafana to apply configuration..."
-    sudo systemctl restart grafana-server &>> "${INSTALL_LOG_FILE}"
-    if [ $? -ne 0 ]; then
-        log_error "Failed to restart Grafana. Check 'sudo systemctl status grafana-server' or ${INSTALL_LOG_FILE}."
-    fi
-    log_info "Grafana configured with base path /monitoring."
-else
-    log_error "Grafana configuration file not found at ${GRAFANA_CONF}. Ensure Grafana is installed and configured."
-fi
+## --- Configure Grafana Base Path ---
+#log_info "Configuring Grafana with base path /monitoring..."
+#GRAFANA_CONF="/etc/grafana/grafana.ini"
+#if [ -f "${GRAFANA_CONF}" ]; then
+#    # Update Grafana configuration for base path
+#    sudo sed -i 's|^;\?\s*root_url\s*=.*|root_url = https://'${SSL_COMMON_NAME}'/monitoring/|' "${GRAFANA_CONF}" &>> "${INSTALL_LOG_FILE}"
+#    sudo sed -i 's|^;\?\s*serve_from_sub_path\s*=.*|serve_from_sub_path = true|' "${GRAFANA_CONF}" &>> "${INSTALL_LOG_FILE}"
+#    log_info "Restarting Grafana to apply configuration..."
+#    sudo systemctl restart grafana-server &>> "${INSTALL_LOG_FILE}"
+#    if [ $? -ne 0 ]; then
+#        log_error "Failed to restart Grafana. Check 'sudo systemctl status grafana-server' or ${INSTALL_LOG_FILE}."
+#    fi
+#    log_info "Grafana configured with base path /monitoring."
+#else
+#    log_error "Grafana configuration file not found at ${GRAFANA_CONF}. Ensure Grafana is installed and configured."
+#fi
 
 # --- Configure Nginx ---
-log_info "Configuring Nginx for custom app, Kibana, and Grafana."
+log_info "Configuring Nginx for custom app, Opensearch-Dashboards, and Grafana."
 if [ -f "${NGINX_DEFAULT_CONF}" ]; then
     log_info "Removing default Nginx configuration file: ${NGINX_DEFAULT_CONF}"
     sudo rm "${NGINX_DEFAULT_CONF}" &>> "${INSTALL_LOG_FILE}"
@@ -226,6 +200,12 @@
 cat <<EOF | sudo tee "${NGINX_APP_CONF}" &>> "${INSTALL_LOG_FILE}"
 # /etc/nginx/conf.d/app.conf
 
+# Extract access_token from cookie, fallback to empty string
+map \$http_cookie \$jwt_token {
+    default "";
+    "~access_token=([^;]+)" \$1;
+}
+
 # --- HTTP Server Block (Redirects to HTTPS) ---
 server {
     listen 80;
@@ -250,24 +230,61 @@
     ssl_prefer_server_ciphers on;
     ssl_ciphers 'TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305';
 
-    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
+    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;/visualization
 
     # Enable access logging for debugging
     access_log /var/log/nginx/access.log;
 
-    # Kibana at /visualization/
-    location /visualization/ {
-        proxy_pass http://127.0.0.1:5601;  # No trailing slash!
+    # Set access_token cookie and redirect to clean URL without query param
+    location = /visualization {
+        if (\$arg_access_token) {
+            add_header Set-Cookie "access_token=\$arg_access_token; Path=/visualization/; HttpOnly";
+            return 302 /visualization/;
+        }
+        proxy_pass https://127.0.0.1:5601;
         proxy_http_version 1.1;
         proxy_set_header Host \$host;
         proxy_set_header X-Real-IP \$remote_addr;
         proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
         proxy_set_header X-Forwarded-Proto \$scheme;
+        proxy_set_header Authorization "Bearer \$jwt_token";
         proxy_set_header Upgrade \$http_upgrade;
         proxy_set_header Connection "upgrade";
         proxy_cache_bypass \$http_upgrade;
+        proxy_read_timeout 60s;
+        proxy_connect_timeout 60s;
     }
 
+    location ^~ /visualization/ {
+        proxy_pass https://127.0.0.1:5601;
+        proxy_http_version 1.1;
+        proxy_set_header Host \$host;
+        proxy_set_header X-Real-IP \$remote_addr;
+        proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
+        proxy_set_header X-Forwarded-Proto \$scheme;
+        proxy_set_header Authorization "Bearer \$jwt_token";
+        proxy_set_header Upgrade \$http_upgrade;
+        proxy_set_header Connection "upgrade";
+        proxy_cache_bypass \$http_upgrade;
+        proxy_read_timeout 60s;
+        proxy_connect_timeout 60s;
+    }
+
+    location ~* ^/visualization/(app|api|public|built_assets)/ {
+        proxy_pass https://127.0.0.1:5601;
+        proxy_http_version 1.1;
+        proxy_set_header Host \$host;
+        proxy_set_header X-Real-IP \$remote_addr;
+        proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
+        proxy_set_header X-Forwarded-Proto \$scheme;
+        proxy_set_header Authorization "Bearer \$jwt_token";
+        proxy_set_header Upgrade \$http_upgrade;
+        proxy_set_header Connection "upgrade";
+        proxy_cache_bypass \$http_upgrade;
+        proxy_read_timeout 60s;
+        proxy_connect_timeout 60s;
+    }
+
     # Grafana at /monitoring/
     location /monitoring/ {
         proxy_pass http://127.0.0.1:3000;  # No trailing slash!
@@ -328,6 +345,12 @@
 cat <<EOF | sudo tee "${NGINX_TEMPLATE_CONF}" &>> "${INSTALL_LOG_FILE}"
 # /etc/nginx/conf.d/app.conf
 
+# Extract access_token from cookie, fallback to empty string
+map \$\$http_cookie \$\$jwt_token {
+    default "";
+    "~access_token=([^;]+)" \$\$1;
+}
+
 # --- HTTP Server Block (Redirects to HTTPS) ---
 server {
     listen 80;
@@ -357,19 +380,57 @@
     # Enable access logging for debugging
     access_log /var/log/nginx/access.log;
 
-    # Kibana at /visualization/
-    location /visualization/ {
-        proxy_pass http://127.0.0.1:5601;  # No trailing slash!
+    # Opensearch-Dashboards at /visualization/
+    # Set access_token cookie and redirect to clean URL without query param
+    location = /visualization {
+        if (\$\$arg_access_token) {
+            add_header Set-Cookie "access_token=\$\$arg_access_token; Path=/visualization/; HttpOnly";
+            return 302 /visualization/;
+        }
+        proxy_pass https://127.0.0.1:5601;
         proxy_http_version 1.1;
         proxy_set_header Host \$\$host;
         proxy_set_header X-Real-IP \$\$remote_addr;
         proxy_set_header X-Forwarded-For \$\$proxy_add_x_forwarded_for;
         proxy_set_header X-Forwarded-Proto \$\$scheme;
+        proxy_set_header Authorization "Bearer \$\$jwt_token";
         proxy_set_header Upgrade \$\$http_upgrade;
         proxy_set_header Connection "upgrade";
         proxy_cache_bypass \$\$http_upgrade;
+        proxy_read_timeout 60s;
+        proxy_connect_timeout 60s;
     }
 
+    location ^~ /visualization/ {
+        proxy_pass https://127.0.0.1:5601;
+        proxy_http_version 1.1;
+        proxy_set_header Host \$\$host;
+        proxy_set_header X-Real-IP \$\$remote_addr;
+        proxy_set_header X-Forwarded-For \$\$proxy_add_x_forwarded_for;
+        proxy_set_header X-Forwarded-Proto \$\$scheme;
+        proxy_set_header Authorization "Bearer \$\$jwt_token";
+        proxy_set_header Upgrade \$\$http_upgrade;
+        proxy_set_header Connection "upgrade";
+        proxy_cache_bypass \$\$http_upgrade;
+        proxy_read_timeout 60s;
+        proxy_connect_timeout 60s;
+    }
+
+    location ~* ^/visualization/(app|api|public|built_assets)/ {
+        proxy_pass https://127.0.0.1:5601;
+        proxy_http_version 1.1;
+        proxy_set_header Host \$\$host;
+        proxy_set_header X-Real-IP \$\$remote_addr;
+        proxy_set_header X-Forwarded-For \$\$proxy_add_x_forwarded_for;
+        proxy_set_header X-Forwarded-Proto \$\$scheme;
+        proxy_set_header Authorization "Bearer \$\$jwt_token";
+        proxy_set_header Upgrade \$\$http_upgrade;
+        proxy_set_header Connection "upgrade";
+        proxy_cache_bypass \$\$http_upgrade;
+        proxy_read_timeout 60s;
+        proxy_connect_timeout 60s;
+    }
+
     # Grafana at /monitoring/
     location /monitoring/ {
         proxy_pass http://127.0.0.1:3000;  # No trailing slash!
@@ -439,46 +500,46 @@
 log_info "Nginx service started and enabled successfully."
 
 # --- Verify Accessibility ---
-log_info "Verifying Kibana accessibility at https://${SSL_COMMON_NAME}/visualization..."
+log_info "Verifying Opensearch-Dashboards accessibility at https://${SSL_COMMON_NAME}/visualization..."
 if ! curl -s --connect-timeout 5 --insecure "https://${SSL_COMMON_NAME}/visualization" &>/dev/null; then
-    log_warning "Kibana is not accessible at https://${SSL_COMMON_NAME}/visualization. Checking backend directly..."
-    if ! curl -s --connect-timeout 5 "${KIBANA_BACKEND_URL}" &>/dev/null; then
-        log_error "Kibana backend (${KIBANA_BACKEND_URL}) is not responding. Check 'sudo systemctl status kibana' or ${INSTALL_LOG_FILE}."
+    log_warning "Opensearch-Dashboards is not accessible at https://${SSL_COMMON_NAME}/visualization. Checking backend directly..."
+    if ! curl -s --connect-timeout 5 "${OPENSEARCH_BACKEND_URL}" &>/dev/null; then
+        log_error "Opensearch-Dashboards backend (${OPENSEARCH_BACKEND_URL}) is not responding. Check 'sudo systemctl status opensearch-dashboards' or ${INSTALL_LOG_FILE}."
     fi
-    log_warning "Kibana backend is responding, but proxying failed. Check Nginx logs: /var/log/nginx/error.log and /var/log/nginx/access.log"
+    log_warning "Opensearch-Dashboards backend is responding, but proxying failed. Check Nginx logs: /var/log/nginx/error.log and /var/log/nginx/access.log"
 fi
-log_info "Kibana is accessible at https://${SSL_COMMON_NAME}/visualization."
+log_info "Opensearch-Dashboards is accessible at https://${SSL_COMMON_NAME}/visualization."
 
-log_info "Verifying Grafana accessibility at https://${SSL_COMMON_NAME}/monitoring..."
-if ! curl -s --connect-timeout 5 --insecure "https://${SSL_COMMON_NAME}/monitoring" &>/dev/null; then
-    log_warning "Grafana is not accessible at https://${SSL_COMMON_NAME}/monitoring. Checking backend directly..."
-    if ! curl -s --connect-timeout 5 "${GRAFANA_BACKEND_URL}" &>/dev/null; then
-        log_error "Grafana backend (${GRAFANA_BACKEND_URL}) is not responding. Check 'sudo systemctl status grafana-server' or ${INSTALL_LOG_FILE}."
-    fi
-    log_warning "Grafana backend is responding, but proxying failed. Check Nginx logs: /var/log/nginx/error.log and /var/log/nginx/access.log"
-fi
-log_info "Grafana is accessible at https://${SSL_COMMON_NAME}/monitoring."
+#log_info "Verifying Grafana accessibility at https://${SSL_COMMON_NAME}/monitoring..."
+#if ! curl -s --connect-timeout 5 --insecure "https://${SSL_COMMON_NAME}/monitoring" &>/dev/null; then
+#    log_warning "Grafana is not accessible at https://${SSL_COMMON_NAME}/monitoring. Checking backend directly..."
+#    if ! curl -s --connect-timeout 5 "${GRAFANA_BACKEND_URL}" &>/dev/null; then
+#        log_error "Grafana backend (${GRAFANA_BACKEND_URL}) is not responding. Check 'sudo systemctl status grafana-server' or ${INSTALL_LOG_FILE}."
+#    fi
+#    log_warning "Grafana backend is responding, but proxying failed. Check Nginx logs: /var/log/nginx/error.log and /var/log/nginx/access.log"
+#fi
+#log_info "Grafana is accessible at https://${SSL_COMMON_NAME}/monitoring."
 
 # --- Verify Path Accessibility ---
-log_info "Verifying path accessibility for Kibana, Grafana, and custom app..."
+log_info "Verifying path accessibility for Opensearch-Dashboards, Grafana, and custom app..."
 if ! curl -s --connect-timeout 5 --insecure "https://${SSL_COMMON_NAME}/visualization/login" &>/dev/null; then
-    log_warning "Kibana login page not accessible at https://${SSL_COMMON_NAME}/visualization/login. Check Kibana status."
+    log_warning "Opensearch-Dashboards login page not accessible at https://${SSL_COMMON_NAME}/visualization/login. Check Opensearch-Dashboards status."
 fi
-if ! curl -s --connect-timeout 5 --insecure "https://${SSL_COMMON_NAME}/monitoring/login" &>/dev/null; then
-    log_warning "Grafana login page not accessible at https://${SSL_COMMON_NAME}/monitoring/login. Check Grafana status."
-fi
+#if ! curl -s --connect-timeout 5 --insecure "https://${SSL_COMMON_NAME}/monitoring/login" &>/dev/null; then
+#    log_warning "Grafana login page not accessible at https://${SSL_COMMON_NAME}/monitoring/login. Check Grafana status."
+#fi
 if ! curl -s --connect-timeout 5 --insecure "https://${SSL_COMMON_NAME}/login" &>/dev/null; then
     log_warning "Custom app login page not accessible at https://${SSL_COMMON_NAME}/login. Check app backend or Nginx configuration."
 fi
 log_info "Path accessibility check completed."
 
-log_info "Nginx configuration complete. Serving custom app, Kibana, and Grafana on port 443."
+log_info "Nginx configuration complete. Serving custom app, Opensearch-Dashboards, and Grafana on port 443."
 log_info "Ensure your firewall allows inbound traffic to ports 80 and 443."
 log_info "************************************************************************************************"
 log_info "** IMPORTANT ACCESS INFORMATION **"
 log_info "************************************************************************************************"
 log_info "- Custom application: https://${SSL_COMMON_NAME} (e.g., /, /login)"
-log_info "- Kibana: https://${SSL_COMMON_NAME}/visualization (e.g., /visualization/login)"
-log_info "- Grafana: https://${SSL_COMMON_NAME}/monitoring (e.g., /monitoring/login)"
+log_info "- Opensearch-Dashboards: https://${SSL_COMMON_NAME}/visualization (e.g., /visualization/login)"
+#log_info "- Grafana: https://${SSL_COMMON_NAME}/monitoring (e.g., /monitoring/login)"
 log_warning "Browsers will show security warnings for the self-signed certificate. For production, obtain a trusted certificate from Let's Encrypt: https://letsencrypt.org/"
 log_info "Full installation log: ${INSTALL_LOG_FILE}"
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/djproject/urls.py
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/djproject/urls.py	(revision 2703)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/djproject/urls.py	(working copy)
@@ -16,7 +16,8 @@
 from hive.report.generate_report import handle_report_generation
 from hive.controller.backup_controller import handle_backup_req
 from hive.controller.restore_controller import handle_restore_req
-from hive.an_opensearch import opensearch_proxy
+from hive.controller.utils import handle_observability_status_req
+from hive.an_opensearch import opensearch_proxy, get_opensearch_sso_token
 
 js_info_dict = {
     #'packages': ('your.app.package',),
@@ -73,6 +74,8 @@
     # re_path(r'^kibana4/(?P<path>.*)', kibana_proxy),
     re_path(r'^elastic/(?P<path>.*)', elastic_proxy),
     re_path(r'^log-analysis/(?P<path>.*)', opensearch_proxy),
+    re_path(r'^observability-status$', handle_observability_status_req),
+    re_path(r'^opensearch-sso-token$', get_opensearch_sso_token),
     re_path(r'^reporting/(?P<app>\w+)/(?P<filename>.*)$', reporting_downloading_handler),
     re_path(r'^reporting_logo$', reporting_logo_handler),
     re_path(r'^cm/save_setting$', save_setting),
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/observability/observability.html
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/observability/observability.html	(revision 2703)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/observability/observability.html	(working copy)
@@ -19,16 +19,16 @@
           </ng-container>
           <ng-container matColumnDef="name">
             <th mat-header-cell *matHeaderCellDef>Service Name</th>
-            <td mat-cell *matCellDef="let element">{{ element?.name }}</td>
+            <td mat-cell *matCellDef="let element">{{ element?.label }}</td>
           </ng-container>
           <ng-container matColumnDef="status">
             <th mat-header-cell *matHeaderCellDef>Status</th>
             <td mat-cell *matCellDef="let element">
               <div class="row-action a-link">
-                @if (element?.status === 'active') {
-                  <fa-icon [icon]="['far', 'check-circle']" class="success-icon"></fa-icon>
+                @if (element?.value) {
+                  <fa-icon [icon]="['far', 'check-circle']" class="success-icon" matTooltip="Active"></fa-icon>
                 } @else {
-                  <fa-icon [icon]="['far', 'xmark-circle']" class="delete-icon"></fa-icon>
+                  <fa-icon [icon]="['far', 'xmark-circle']" class="delete-icon" matTooltip="It appears to be inactive."></fa-icon>
                 }
               </div>
             </td>
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/observability/observability.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/observability/observability.ts	(revision 2703)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/observability/observability.ts	(working copy)
@@ -6,6 +6,8 @@
 import {Confirmation} from '../../services/confirmation';
 import {MAT_DIALOG_DATA, MatDialog, MatDialogConfig, MatDialogRef} from '@angular/material/dialog';
 import {FormBuilder, FormGroup} from '@angular/forms';
+import {take} from 'rxjs/operators';
+import {MatPaginator} from '@angular/material/paginator';
 
 @Component({
   selector: 'app-observability',
@@ -15,8 +17,11 @@
 })
 export class Observability implements OnInit {
 
+  totalRecords: number = 0;
   dataColumns = ['serial', 'name', 'status'];
   dataSource = new MatTableDataSource([]);
+  @ViewChild(MatPaginator) paginator!: MatPaginator;
+
   dialog = inject(MatDialog);
   dialogConfig = new MatDialogConfig();
 
@@ -29,8 +34,27 @@
   }
 
   ngOnInit() {
+    setTimeout(()=>{
+      this.getObservabilityStatus();
+    })
   }
 
+  getObservabilityStatus() {
+    this.dataSource.data = [];
+    this.totalRecords = 0;
+    this._system.getObservabilityStatus().pipe(take(1)).subscribe({
+      next: (result: any) => {
+        this.dataSource.data = result;
+        this.dataSource.paginator = this.paginator;
+        this.totalRecords = this.dataSource.data.length;
+      },
+      error: error => {
+        this._notification.showError(`Error: ${error?.message}`);
+        console.log(error);
+      }
+    });
+  }
+
   updateLogSettings() {
     // ToDo: Get the current log settings - keep original msg or not.
     this.dialogConfig.data = {
@@ -42,7 +66,20 @@
   }
 
   openSearchDashboard() {
-    // ToDo: Route to OpenSearch Dashboard.
+    this._system.getOpenSearchAuthToken().pipe(take(1)).subscribe({
+      next: (result: any) => {
+        if (result && result.token) {
+          const protocol = window.location.protocol;
+          const hostname =  window.location.hostname;
+          const opensearchUrl = `${protocol}://${hostname}/visualization?access_token=${result?.token}`;
+          window.open(opensearchUrl, '_blank');
+        }
+      },
+      error: error => {
+        this._notification.showError(`Error: ${error?.message}`);
+        console.log(error);
+      }
+    });
   }
 }
 
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/constants/api_urls.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/constants/api_urls.ts	(revision 2703)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/constants/api_urls.ts	(working copy)
@@ -21,6 +21,7 @@
   WEBCONSOLE_HANDLE_REQ_URL: `${PREFIX}/cm/ajax/webconsole/_handle_request`,
   // System
   GET_OBSERVABILITY_STATUS_URL: `${PREFIX}/observability-status`,
+  GET_OPENSEARCH_SSO_TOKEN_URL: `${PREFIX}/opensearch-sso-token`,
   GET_SYSTEM_HOST_CONFIG_URL: `${PREFIX}/api/cm/system/HostSettings/_fields`,
   UPDATE_SYSTEM_HOST_CONFIG_URL: `${PREFIX}/api/cm/system/HostSettings/_update`,
   GET_SYSTEM_LOG_SETTINGS_URL: `${PREFIX}/cm/get_log_basic`,
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/system-service.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/system-service.ts	(revision 2703)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/services/system-service.ts	(working copy)
@@ -445,5 +445,9 @@
 
   getObservabilityStatus() {
     return this.http.get(URLS.GET_OBSERVABILITY_STATUS_URL);
+  }
+
+  getOpenSearchAuthToken() {
+    return this.http.get(URLS.GET_OPENSEARCH_SSO_TOKEN_URL);
   }
 }
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/an_opensearch.py
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/an_opensearch.py	(revision 2703)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/an_opensearch.py	(working copy)
@@ -1,7 +1,14 @@
 from opensearchpy import OpenSearch
-from django.http import HttpResponse
+from django.http import HttpResponse, JsonResponse
 import json
+import jwt
+import datetime
+import base64
+import yaml
+from hive.utils import andebug
 
+CONFIG_YML_PATH = '/etc/opensearch/opensearch-security/config.yml'
+
 HOST = 'localhost'
 PORT = 9200
 AUTH = ('admin', 'Arr@y2050')
@@ -18,3 +25,23 @@
     )
     res = client.search(index='acm-*', body=json.loads(request.body), request_timeout=300)
     return HttpResponse(json.dumps(res), content_type='application/json')
+
+
+def get_opensearch_sso_token(request):
+    # Load the signing key from config.yml
+    with open(CONFIG_YML_PATH, 'r') as file:
+        config = yaml.safe_load(file)
+
+    base64_key = config['config']['dynamic']['authc']['jwt_auth_domain']['http_authenticator']['config']['signing_key']
+    secret_key = base64.b64decode(base64_key)
+
+    # Use timezone-aware UTC datetime objects
+    now = datetime.datetime.now(datetime.timezone.utc)
+
+    # ToDo: Implement RBAC
+    payload = {'sub': 'admin', 'exp': now + datetime.timedelta(hours=1), 'iat': now, }
+
+    # Generate token
+    token = jwt.encode(payload, secret_key, algorithm='HS256')
+
+    return JsonResponse({'token': token})
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/controller/utils.py
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/controller/utils.py	(nonexistent)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/controller/utils.py	(working copy)
@@ -0,0 +1,25 @@
+import json
+
+from django.http import HttpResponse, JsonResponse
+from cm.lib.libbasic_operation import oper_log
+from hive.custom_exceptions import generic_exception as ge
+from hive.services.utils import get_observability_services_status
+
+def handle_observability_status_req(request, path=None):
+    try:
+        if request.method == 'GET':
+            status_data = get_observability_services_status()
+            return JsonResponse(status_data, safe=False)
+        else:
+            return HttpResponse(json.dumps({
+                'error': 400,
+                'message': "Invalid HTTP method"
+            }), content_type='application/json', status=400)
+
+    except ge.GenericError as e:
+        oper_log('error', 'system', e.message)
+        return ge.handle_exception(e)
+    except Exception as e:
+        oper_log('error', 'system', 'Exception while observability services status.,  details: {}'.format(e.message))
+        e.message = 'Exception while observability services status,  details: {}'.format(str(e.message))
+        raise ge.GenericError(500, e.message)
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/services/utils.py
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/services/utils.py	(nonexistent)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/hive/services/utils.py	(working copy)
@@ -0,0 +1,32 @@
+import json
+import os
+
+from django.http import HttpResponse
+
+from hive.utils import andebug
+from hive.custom_exceptions import generic_exception as ge
+from cm.lib.libbasic_operation import oper_log
+
+
+def get_observability_services_status():
+    try:
+        services_list = [
+            {'value': 'opensearch', 'label': 'Search & Analytics Engine'},
+            {'value': 'opensearch-dashboards', 'label': 'Logs Dashboards'},
+            {'value': 'logstash', 'label': 'Syslog Collector'},
+            {'value': 'telegraf', 'label': 'Metrics Collector'},
+        ]
+        result = []
+        for service in services_list:
+            process = len(os.popen('ps aux | grep "' + service[
+                'value'] + '" | grep -v grep | grep -v tail | grep -v keepH5ssAlive').readlines())
+            result.append({'value': process >= 1, 'label': service['label']})
+        return result
+    except Exception as e:
+        oper_log('error', 'system', "Exception while fetching observability services status.")
+        message = str(e.message).replace("'", "")
+        message = 'Error while fetching observability services status, details: {}'.format(message)
+        return HttpResponse(json.dumps({
+            "message": "while fetching observability services status",
+            "details": "{}".format(message)
+        }), content_type="application/json", status=500)
Index: /branches/amp_4_0/tools/requirements.txt
===================================================================
--- /branches/amp_4_0/tools/requirements.txt	(revision 2703)
+++ /branches/amp_4_0/tools/requirements.txt	(working copy)
@@ -1,46 +1,62 @@
-APScheduler==3.3.1
-backports.ssl-match-hostname==3.5.0.1
-configobj==4.7.2
-decorator==3.4.0
-Django==1.5.12
-django-revproxy==0.10.0
-elasticsearch==6.0.0
-flup==1.0.2
-funcsigs==1.0.2
-futures==3.1.1
-influxdb==5.3.1
-iniparse==0.4
-ipaddress==1.0.16
-javapackages==1.0.0
-Jinja2==2.7.2
-lxml==3.2.1
-M2Crypto==0.25.1
-MarkupSafe==0.11
-msgpack==1.0.4
-perf==0.1
-Pillow==4.0.0
-psutil==5.0.0
-psycopg2==2.7.5
-pycurl==7.19.0
-pyftpdlib==1.5.2
-pygobject==3.22.0
-pygpgme==0.3
-pyliblzma==0.5.3
-python-linux-procfs==0.4.9
-pytz==2017.2
-pyudev==0.15
-pyxattr==0.5.1
-reportlab==3.4.0
-requests==2.13.0
-schedule==0.6.0
-schedutils==0.4
-six==1.10.0
-slip==0.4.0
-slip.dbus==0.4.0
-tftpy==0.6.2
-tzlocal==1.4
-urlgrabber==3.10
-urllib3==1.22
+anyio==4.9.0
+APScheduler==3.11.0
+asgiref==3.8.1
+by==0.0.6
+certifi==2025.4.26
+chardet==5.2.0
+charset-normalizer==3.4.2
+dicttoxml==1.7.16
+Django==5.2.1
+django-callback==0.6.1
+dnspython==2.8.0
+dynuipv4update==0.12
+elastic-transport==8.17.1
+elasticsearch==9.0.2
+Events==0.5
+flup==1.0.3
+getpublicipv4==0.12
+h11==0.16.0
+httpcore==1.0.9
+httpx==0.28.1
+idna==3.10
+Jinja2==3.1.6
+joblib==1.5.0
+kthread==0.2.3
+kthread-sleep==0.11
+MarkupSafe==3.0.2
+numpy==2.2.6
+opensearch-py==3.0.0
+ordered-set==4.1.0
+pfehler==0.10
+pillow==11.2.1
+prettytable==3.16.0
+psycopg==3.2.10
+psycopg2-binary==2.9.10
+pycurl==7.45.6
+pyecharts==2.0.8
+PyJWT==2.10.1
+PyLaTeX==1.4.2
+pymongo==4.15.0
+python-dateutil==2.9.0.post0
+PyYAML==6.0.2
+reportlab==4.4.1
+requests==2.32.3
+revproxy==0.12
+rpm==0.4.0
+scikit-learn==1.6.1
+scipy==1.15.3
+simplejson==3.20.1
+six==1.17.0
+sniffio==1.3.1
+sqlparse==0.5.3
+stderrstdoutcapture==0.10
+tftpy==0.8.5
+thread==2.0.5
+threadpoolctl==3.6.0
+touchtouch==0.11
+typing_extensions==4.13.2
+tzlocal==5.3.1
+urllib3==2.4.0
+Wand==0.6.13
+wcwidth==0.2.13
 Whoosh==2.7.4
-yum-metadata-parser==1.1.4
-opensearch-py==3.0.0
\ No newline at end of file
