diff --git a/src/components/AppNavigation/AppNavigationMixin.js b/src/components/AppNavigation/AppNavigationMixin.js index edeabc52f4..ad9af89914 100644 --- a/src/components/AppNavigation/AppNavigationMixin.js +++ b/src/components/AppNavigation/AppNavigationMixin.js @@ -123,6 +123,11 @@ const AppNavigationMixin = { label: this.$t('appNavigation.powerRestorePolicy'), route: '/settings/power-restore-policy', }, + { + id: 'snmp-alerts', + label: this.$t('appNavigation.snmpAlerts'), + route: '/settings/snmp-alerts', + }, ], }, { diff --git a/src/env/components/AppNavigation/ibm.js b/src/env/components/AppNavigation/ibm.js index 32f5c9f47c..233c3f7565 100644 --- a/src/env/components/AppNavigation/ibm.js +++ b/src/env/components/AppNavigation/ibm.js @@ -118,6 +118,11 @@ const AppNavigationMixin = { label: this.$t('appNavigation.powerRestorePolicy'), route: '/settings/power-restore-policy', }, + { + id: 'snmp-alerts', + label: this.$t('appNavigation.snmpAlerts'), + route: '/settings/snmp-alerts', + }, ], }, { diff --git a/src/env/router/ibm.js b/src/env/router/ibm.js index c6ac61f79d..2eacbdca89 100644 --- a/src/env/router/ibm.js +++ b/src/env/router/ibm.js @@ -25,6 +25,7 @@ import SerialOverLanConsole from '@/views/Operations/SerialOverLan/SerialOverLan import ServerPowerOperations from '@/views/Operations/ServerPowerOperations'; import Certificates from '@/views/SecurityAndAccess/Certificates'; import Power from '@/views/ResourceManagement/Power'; +import SnmpAlerts from '@/views/Settings/SnmpAlerts'; import i18n from '@/i18n'; // Custom components @@ -206,6 +207,14 @@ const routes = [ title: i18n.t('appPageTitle.powerRestorePolicy'), }, }, + { + path: '/settings/snmp-alerts', + name: 'snmp-alerts', + component: SnmpAlerts, + meta: { + title: i18n.t('appPageTitle.snmpAlerts'), + }, + }, { path: '/resource-management/power', name: 'power', diff --git a/src/locales/en-US.json b/src/locales/en-US.json index afe886dcf2..1edc03c77a 100644 --- a/src/locales/en-US.json +++ b/src/locales/en-US.json @@ -126,6 +126,7 @@ "serverPowerOperations": "@:appPageTitle.serverPowerOperations", "certificates": "@:appPageTitle.certificates", "virtualMedia": "@:appPageTitle.virtualMedia", + "snmpAlerts": "@:appPageTitle.snmpAlerts", "power": "@:appPageTitle.power" }, "appPageTitle": { @@ -154,7 +155,8 @@ "serialOverLan": "Serial over LAN (SOL) console", "serverPowerOperations": "Server power operations", "certificates": "Certificates", - "virtualMedia": "Virtual media" + "virtualMedia": "Virtual media", + "snmpAlerts":"SNMP Alerts" }, "pageChangePassword": { "changePassword": "Change password", @@ -806,6 +808,32 @@ "successSaveSettings": "Successfully saved settings." } }, + "pageSnmpAlerts": { + "addDestination": "Add destination", + "deleteDestination": "Delete destination | Delete destinations", + "pageDescription": "Set the Simple Network Management Protocol (SNMP) traps with an IP address and a port.", + "modal": { + "addSnmpDestinationTitle": "Add SNMP alert destination", + "batchDeleteConfirmMessage": "Are you sure you want to delete the SNMP alert destination? This action cannot be undone. | Are you sure you want to delete %{count} SNMP alert destinations? This action cannot be undone.", + "deleteConfirmMessage": "Are you sure you want to delete the SNMP alert destination? This action cannot be undone.", + "deleteSnmpDestinationTitle": "Delete SNMP alert destination | Delete SNMP alert destinations", + "ipaddress": "IP Address", + "port": "Port" + }, + "table": { + "ipaddress": "IP Address", + "port": "Port" + }, + "toast": { + "errorAddDestination":"Error in adding SNMP alert destination", + "errorBatchDelete": "Error in deleting SNMP alert destination. | Error in deleting SNMP alert destinations.", + "errorDeleteDestination": "Error deleting SNMP alert destination.", + "errorLoadSnmpDetails": "Error loading SNMP alert details.", + "successAddDestination":"Successfully added SNMP alert destination.", + "successBatchDelete": "Successfully deleted SNMP alert destination. | Successfully deleted %{count} SNMP alert destinations.", + "successDeleteDestination": "Successfully deleted SNMP alert destination." + } + }, "pageCertificates": { "addNewCertificate": "Add new certificate", "caCertificate": "CA Certificate", diff --git a/src/router/routes.js b/src/router/routes.js index b99aac5166..48c3f42c4a 100644 --- a/src/router/routes.js +++ b/src/router/routes.js @@ -28,6 +28,7 @@ import ServerPowerOperations from '@/views/Operations/ServerPowerOperations'; import Certificates from '@/views/SecurityAndAccess/Certificates'; import VirtualMedia from '@/views/Operations/VirtualMedia'; import Power from '@/views/ResourceManagement/Power'; +import SnmpAlerts from '@/views/Settings/SnmpAlerts'; import i18n from '@/i18n'; const routes = [ @@ -182,6 +183,14 @@ const routes = [ title: i18n.t('appPageTitle.dateTime'), }, }, + { + path: '/settings/snmp-alerts', + name: 'snmp-alerts', + component: SnmpAlerts, + meta: { + title: i18n.t('appPageTitle.snmpAlerts'), + }, + }, { path: '/operations/factory-reset', name: 'factory-reset', diff --git a/src/store/index.js b/src/store/index.js index d7c1b22d03..3f9cf17904 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -27,7 +27,7 @@ import AssemblyStore from './modules/HardwareStatus/AssemblyStore'; import PostCodeLogsStore from './modules/Logs/PostCodeLogsStore'; import PoliciesStore from './modules/SecurityAndAccess/PoliciesStore'; import FactoryResetStore from './modules/Operations/FactoryResetStore'; - +import SnmpAlertsStore from './modules/Settings/SnmpAlertsStore'; import WebSocketPlugin from './plugins/WebSocketPlugin'; import DateTimeStore from './modules/Settings/DateTimeStore'; import VirtualMediaStore from './modules/Operations/VirtualMediaStore'; @@ -55,6 +55,7 @@ export default new Vuex.Store({ eventLog: EventLogStore, sensors: SensorsStore, serverLed: ServerLedStore, + snmpAlerts: SnmpAlertsStore, certificates: CertificatesStore, system: SystemStore, memory: MemoryStore, diff --git a/src/store/modules/Settings/SnmpAlertsStore.js b/src/store/modules/Settings/SnmpAlertsStore.js new file mode 100644 index 0000000000..17b91dfa36 --- /dev/null +++ b/src/store/modules/Settings/SnmpAlertsStore.js @@ -0,0 +1,116 @@ +import api, { getResponseCount } from '@/store/api'; +import i18n from '@/i18n'; +const SnmpAlertsStore = { + namespaced: true, + state: { + allSnmpDetails: [], + }, + getters: { + allSnmpDetails(state) { + return state.allSnmpDetails; + }, + }, + mutations: { + setSnmpDetails(state, allSnmpDetails) { + state.allSnmpDetails = allSnmpDetails; + }, + }, + actions: { + async getSnmpAlertUrl() { + return await api + .get('/redfish/v1/') + .then((response) => api.get(response.data.EventService['@odata.id'])) + .then((response) => api.get(response.data.Subscriptions['@odata.id'])) + .then((response) => response.data['@odata.id']) + .catch((error) => console.log('Error', error)); + }, + async getSnmpDetails({ commit, dispatch }) { + const snmpAlertUrl = await dispatch('getSnmpAlertUrl'); + return await api + .get(snmpAlertUrl) + .then((response) => + response.data.Members.map((user) => user['@odata.id']) + ) + .then((userIds) => api.all(userIds.map((user) => api.get(user)))) + .then((users) => { + const snmpDetailsData = users.map((user) => user.data); + commit('setSnmpDetails', snmpDetailsData); + }) + .catch((error) => { + console.log(error); + const message = i18n.t('pageSnmpAlerts.toast.errorLoadSnmpDetails'); + throw new Error(message); + }); + }, + async deleteDestination({ dispatch }, id) { + const snmpAlertUrl = await dispatch('getSnmpAlertUrl'); + return await api + .delete(`${snmpAlertUrl}/${id}`) + .then(() => dispatch('getSnmpDetails')) + .then(() => + i18n.t('pageSnmpAlerts.toast.successDeleteDestination', { + id, + }) + ) + .catch((error) => { + console.log(error); + const message = i18n.t( + 'pageSnmpAlerts.toast.errorDeleteDestination', + { + id, + } + ); + throw new Error(message); + }); + }, + async deleteMultipleDestinations({ dispatch }, destination) { + const snmpAlertUrl = await dispatch('getSnmpAlertUrl'); + const promises = destination.map(({ id }) => { + return api.delete(`${snmpAlertUrl}/${id}`).catch((error) => { + console.log(error); + return error; + }); + }); + return await api + .all(promises) + .then((response) => { + dispatch('getSnmpDetails'); + return response; + }) + .then( + api.spread((...responses) => { + const { successCount, errorCount } = getResponseCount(responses); + let toastMessages = []; + if (successCount) { + const message = i18n.tc( + 'pageSnmpAlerts.toast.successBatchDelete', + successCount + ); + toastMessages.push({ type: 'success', message }); + } + if (errorCount) { + const message = i18n.tc( + 'pageSnmpAlerts.toast.errorBatchDelete', + errorCount + ); + toastMessages.push({ type: 'error', message }); + } + return toastMessages; + }) + ); + }, + async addDestination({ dispatch }, { data }) { + const snmpAlertUrl = await dispatch('getSnmpAlertUrl'); + return await api + .post(snmpAlertUrl, data) + .then(() => dispatch('getSnmpDetails')) + .then(() => i18n.t('pageSnmpAlerts.toast.successAddDestination')) + .catch((error) => { + console.log(error); + const message = i18n.t('pageSnmpAlerts.toast.errorAddDestination'); + throw new Error(message); + }); + }, + }, +}; +export default SnmpAlertsStore; diff --git a/src/views/Settings/SnmpAlerts/ModalAddDestination.vue b/src/views/Settings/SnmpAlerts/ModalAddDestination.vue new file mode 100644 index 0000000000..1d14117809 --- /dev/null +++ b/src/views/Settings/SnmpAlerts/ModalAddDestination.vue @@ -0,0 +1,142 @@ + + diff --git a/src/views/Settings/SnmpAlerts/SnmpAlerts.vue b/src/views/Settings/SnmpAlerts/SnmpAlerts.vue new file mode 100644 index 0000000000..08b3c114e1 --- /dev/null +++ b/src/views/Settings/SnmpAlerts/SnmpAlerts.vue @@ -0,0 +1,272 @@ + + diff --git a/src/views/Settings/SnmpAlerts/index.js b/src/views/Settings/SnmpAlerts/index.js new file mode 100644 index 0000000000..f27ed4aa46 --- /dev/null +++ b/src/views/Settings/SnmpAlerts/index.js @@ -0,0 +1,2 @@ +import SnmpAlerts from './SnmpAlerts.vue'; +export default SnmpAlerts; diff --git a/tests/unit/__snapshots__/AppNavigation.spec.js.snap b/tests/unit/__snapshots__/AppNavigation.spec.js.snap index 37609d3992..e25f9c16ed 100644 --- a/tests/unit/__snapshots__/AppNavigation.spec.js.snap +++ b/tests/unit/__snapshots__/AppNavigation.spec.js.snap @@ -453,6 +453,15 @@ exports[`AppNavigation.vue should render correctly 1`] = ` appNavigation.powerRestorePolicy + + + appNavigation.snmpAlerts + + @@ -1114,6 +1123,15 @@ exports[`AppNavigation.vue should render with nav-container open 1`] = ` appNavigation.powerRestorePolicy + + + appNavigation.snmpAlerts + +