UNPKG

hana-cli

Version:
341 lines (300 loc) 10.2 kB
// @ts-check import * as baseLite from '../utils/base-lite.js' import { buildDocEpilogue } from '../utils/doc-linker.js' export const command = 'alerts' export const aliases = ['a', 'alert'] export const describe = baseLite.bundle.getText("alerts") export const builder = (yargs) => yargs.options(baseLite.getBuilder({ limit: { alias: ['l'], type: 'number', default: 100, desc: baseLite.bundle.getText("limit") }, severity: { alias: ['s'], type: 'string', default: 'all', choices: ['all', 'CRITICAL', 'WARNING', 'INFO'], desc: baseLite.bundle.getText("alertSeverity") }, acknowledge: { alias: ['ack'], type: 'string', desc: 'Acknowledge alert by ID' }, delete: { alias: ['del'], type: 'string', desc: 'Delete alert by ID' } })).wrap(160).example('hana-cli alerts --severity CRITICAL', baseLite.bundle.getText("alertsExample")).wrap(160).epilog(buildDocEpilogue('alerts', 'system-tools', ['healthCheck', 'systemInfo'])) export let inputPrompts = { limit: { description: baseLite.bundle.getText("limit"), type: 'number', required: true }, severity: { description: baseLite.bundle.getText("alertSeverity"), type: 'string', required: true } } /** * Command handler function * @param {object} argv - Command line arguments from yargs * @returns {Promise<void>} */ export async function handler(argv) { const base = await import('../utils/base.js') // Handle acknowledge or delete operations if (argv.acknowledge) { base.setPrompts({}) await acknowledgeAlert(argv.acknowledge) return } if (argv.delete) { base.setPrompts({}) await deleteAlert(argv.delete) return } base.promptHandler(argv, listAlerts, inputPrompts) } /** * List and manage database alerts * @param {object} prompts - Input prompts * @returns {Promise<void>} */ export async function listAlerts(prompts) { const base = await import('../utils/base.js') base.debug('listAlerts') try { base.setPrompts(prompts) const db = await base.createDBConnection() try { const limit = base.validateLimit(prompts.limit) const severity = prompts.severity === 'all' ? '%' : prompts.severity let query = ` SELECT MESSAGE_ID as "Alert ID", MESSAGE_CODE as "Code", SEVERITY, ALERT_RATING as "Rating", DESCRIPTION, INSERT_TIME as "Timestamp", IS_ACKNOWLEDGED as "Acknowledged", RECOMMENDED_ACTION as "Recommended Action", COMPONENT FROM SYS.M_ALERTS WHERE SEVERITY LIKE ? ORDER BY INSERT_TIME DESC ` if (limit && base.sqlInjection.isAcceptableParameter(limit.toString())) { query += ` LIMIT ${limit.toString()}` } let results = await db.statementExecPromisified( await db.preparePromisified(query), [severity] ) // Get alert summary let summaryQuery = ` SELECT SEVERITY, COUNT(*) as "Count", COUNT(CASE WHEN IS_ACKNOWLEDGED = 'TRUE' THEN 1 END) as "Acknowledged" FROM SYS.M_ALERTS GROUP BY SEVERITY ORDER BY CASE WHEN SEVERITY = 'CRITICAL' THEN 1 WHEN SEVERITY = 'WARNING' THEN 2 WHEN SEVERITY = 'INFO' THEN 3 ELSE 4 END ` const summaryResults = await db.execSQL(summaryQuery) if (!results || results.length === 0) { base.output(base.bundle.getText('noAlertsFound')) if (summaryResults && summaryResults.length > 0) { base.output('') base.output(base.bundle.getText('alertsByProperty') + ':') base.outputTableFancy(summaryResults) } await base.end() return } base.output('') base.output(base.colors.bold(base.bundle.getText('databaseAlertsHeader'))) base.output(`${base.bundle.getText('total')}: ${results.length}`) base.output('') // Display summary if (summaryResults && summaryResults.length > 0) { base.output(base.colors.bold(base.bundle.getText('alertsByProperty') + ':')) base.outputTableFancy(summaryResults) base.output('') } // Display alert details const displayResults = results.map(row => ({ 'ID': row['Alert ID'], 'Severity': row.SEVERITY, 'Code': row.Code, 'Rating': row.Rating, 'Component': row.COMPONENT, 'Timestamp': row.Timestamp, 'Ack': row.Acknowledged })) base.outputTableFancy(displayResults) base.output('') base.output(base.bundle.getText('alertDetailsHeader') + ':') results.forEach((alert, idx) => { base.output(`${idx + 1}. [${alert.SEVERITY}] ${alert.DESCRIPTION}`) if (alert['Recommended Action']) { base.output(` ${base.bundle.getText('action')}: ${alert['Recommended Action']}`) } base.output(` ID: ${alert['Alert ID']}`) base.output('') }) base.output('To acknowledge an alert: hana-cli alerts --acknowledge <alert_id>') base.output('To delete an alert: hana-cli alerts --delete <alert_id>') base.output('') await base.end() } catch (innerError) { // M_ALERTS view not accessible in this HDI container context base.output(base.bundle.getText('databaseAlertsHeader')) base.output('') base.output(base.colors.yellow('⚠️ Database alerts are not accessible from this schema context.')) base.output('This command requires access to system monitoring views (M_ALERTS) which are') base.output('not available in HDI container schemas. Connect to the SYSTEMDB to view alerts.') base.output('') await base.end() } } catch (error) { await base.error(error) } } /** * Acknowledge an alert * @param {string} alertId - Alert ID to acknowledge * @returns {Promise<void>} */ async function acknowledgeAlert(alertId) { const base = await import('../utils/base.js') base.debug(`acknowledgeAlert: ${alertId}`) try { const db = await base.createDBConnection() try { // Get alert details const getAlertQuery = ` SELECT MESSAGE_ID as "Alert ID", SEVERITY, DESCRIPTION, IS_ACKNOWLEDGED as "Acknowledged" FROM SYS.M_ALERTS WHERE MESSAGE_ID = ? ` const alertDetails = await db.statementExecPromisified( await db.preparePromisified(getAlertQuery), [alertId] ) if (!alertDetails || alertDetails.length === 0) { base.output(base.colors.red(base.bundle.getText('alertNotFoundMsg', [alertId]))) await base.end() return } const alert = alertDetails[0] base.output('') base.output(base.colors.bold(base.bundle.getText('alertDetailsHeader') + ':')) base.output(` ${base.bundle.getText('alertSeverity')}: ${alert.SEVERITY}`) base.output(` ${base.bundle.getText('description')}: ${alert.DESCRIPTION}`) base.output(` ${base.bundle.getText('currentStatus')}: ${alert['Acknowledged']}`) base.output('') // Acknowledge the alert try { const updateQuery = ` UPDATE M_ALERTS SET IS_ACKNOWLEDGED = 'TRUE' WHERE MESSAGE_ID = ? ` await db.statementExecPromisified( await db.preparePromisified(updateQuery), [alertId] ) base.output(base.colors.green(`✓ ${base.bundle.getText('alertAcknowledgedMsg', [alertId])}`)) await base.end() } catch (updateError) { base.debug(`Acknowledge failed: ${updateError.message}`) base.output(base.colors.yellow(`⚠ Warning: Unable to acknowledge alert. ${updateError.message}`)) await base.end() } } catch (alertError) { // M_ALERTS not accessible base.output(base.colors.yellow('⚠️ Database alerts are not accessible from this schema context.')) base.output('This command requires system level access which is not available in HDI containers.') await base.end() } } catch (error) { await base.error(error) } } /** * Delete an alert * @param {string} alertId - Alert ID to delete * @returns {Promise<void>} */ async function deleteAlert(alertId) { const base = await import('../utils/base.js') base.debug(`deleteAlert: ${alertId}`) try { const db = await base.createDBConnection() try { // Get alert details first const getAlertQuery = ` SELECT MESSAGE_ID as "Alert ID", SEVERITY, DESCRIPTION FROM M_ALERTS WHERE MESSAGE_ID = ? ` const alertDetails = await db.statementExecPromisified( await db.preparePromisified(getAlertQuery), [alertId] ) if (!alertDetails || alertDetails.length === 0) { base.output(base.colors.red(`Alert with ID ${alertId} not found.`)) await base.end() return } const alert = alertDetails[0] base.output('') base.output(base.colors.bold('Alert Details:')) base.output(` Severity: ${alert.SEVERITY}`) base.output(` Description: ${alert.DESCRIPTION}`) base.output('') // Delete the alert try { const deleteQuery = ` DELETE FROM SYS.M_ALERTS WHERE MESSAGE_ID = ? ` await db.statementExecPromisified( await db.preparePromisified(deleteQuery), [alertId] ) base.output(base.colors.green(`✓ ${base.bundle.getText('alertDeletedMsg', [alertId])}`)) await base.end() } catch (deleteError) { base.debug(`Delete failed: ${deleteError.message}`) base.output(base.colors.yellow(`⚠ Warning: Unable to delete alert. ${deleteError.message}`)) await base.end() } } catch (alertError) { // M_ALERTS not accessible base.output(base.colors.yellow('⚠️ Database alerts are not accessible from this schema context.')) base.output('This command requires system level access which is not available in HDI containers.') await base.end() } } catch (error) { await base.error(error) } }