Skip to content

Commit

Permalink
Add SNMP alerts page and test hooks (#8)
Browse files Browse the repository at this point in the history
* Add SNMP alerts page and test hooks

This page will be included in the Settings section of the primary
navigation. The user will be able to delete and add alert destination.

Signed-off-by: Sandeepa Singh <[email protected]>
  • Loading branch information
Sandeepa Singh authored and rfrandse committed Jan 28, 2022
1 parent 4aac0d5 commit d02d3ff
Show file tree
Hide file tree
Showing 11 changed files with 609 additions and 2 deletions.
5 changes: 5 additions & 0 deletions src/components/AppNavigation/AppNavigationMixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
},
],
},
{
Expand Down
5 changes: 5 additions & 0 deletions src/env/components/AppNavigation/ibm.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
},
],
},
{
Expand Down
9 changes: 9 additions & 0 deletions src/env/router/ibm.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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',
Expand Down
30 changes: 29 additions & 1 deletion src/locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@
"serverPowerOperations": "@:appPageTitle.serverPowerOperations",
"certificates": "@:appPageTitle.certificates",
"virtualMedia": "@:appPageTitle.virtualMedia",
"snmpAlerts": "@:appPageTitle.snmpAlerts",
"power": "@:appPageTitle.power"
},
"appPageTitle": {
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
9 changes: 9 additions & 0 deletions src/router/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
Expand Down Expand Up @@ -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',
Expand Down
3 changes: 2 additions & 1 deletion src/store/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -55,6 +55,7 @@ export default new Vuex.Store({
eventLog: EventLogStore,
sensors: SensorsStore,
serverLed: ServerLedStore,
snmpAlerts: SnmpAlertsStore,
certificates: CertificatesStore,
system: SystemStore,
memory: MemoryStore,
Expand Down
116 changes: 116 additions & 0 deletions src/store/modules/Settings/SnmpAlertsStore.js
Original file line number Diff line number Diff line change
@@ -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;
142 changes: 142 additions & 0 deletions src/views/Settings/SnmpAlerts/ModalAddDestination.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
<template>
<b-modal id="add-destination" ref="modal" @ok="onOk" @hidden="resetForm">
<template #modal-title>
{{ $t('pageSnmpAlerts.modal.addSnmpDestinationTitle') }}
</template>
<b-form id="form-destination">
<b-container>
<b-row>
<b-col sm="6">
<!-- Add new SNMP alert destination type -->
<b-form-group
:label="$t('pageSnmpAlerts.modal.ipaddress')"
label-for="ip-address"
>
<b-form-input
id="ip-Address"
v-model="form.ipAddress"
:state="getValidationState($v.form.ipAddress)"
data-test-id="snmpAlerts-input-ipAddress"
type="text"
@blur="$v.form.ipAddress.$touch()"
/>
<b-form-invalid-feedback role="alert">
<template v-if="!$v.form.ipAddress.required">
{{ $t('global.form.fieldRequired') }}
</template>
<template v-if="!$v.form.ipAddress.ipAddress">
{{ $t('global.form.invalidFormat') }}
</template>
</b-form-invalid-feedback>
</b-form-group>
</b-col>
<b-col>
<b-form-group label-for="port">
<template #label>
{{ $t('pageSnmpAlerts.modal.port') }} -
<span class="form-text d-inline">
{{ $t('global.form.optional') }}
</span>
</template>
<b-form-input
id="port"
v-model="form.port"
type="text"
:state="getValidationState($v.form.port)"
data-test-id="snmpAlerts-input-port"
@blur="$v.form.port.$touch()"
/>
<b-form-invalid-feedback role="alert">
<template
v-if="!$v.form.port.minLength || !$v.form.port.maxLength"
>
{{
$t('global.form.valueMustBeBetween', {
min: 0,
max: 65535,
})
}}
</template>
</b-form-invalid-feedback>
</b-form-group>
</b-col>
</b-row>
</b-container>
</b-form>
<template #modal-footer="{ cancel }">
<b-button variant="secondary" @click="cancel()">
{{ $t('global.action.cancel') }}
</b-button>
<b-button
form="form-user"
type="submit"
variant="primary"
data-test-id="snmpAlerts-button-ok"
@click="onOk"
>
{{ $t('pageSnmpAlerts.addDestination') }}
</b-button>
</template>
</b-modal>
</template>
<script>
import {
required,
ipAddress,
minValue,
maxValue,
} from 'vuelidate/lib/validators';
import VuelidateMixin from '@/components/Mixins/VuelidateMixin.js';
export default {
mixins: [VuelidateMixin],
data() {
return {
form: {
ipaddress: null,
port: null,
},
};
},
validations() {
return {
form: {
ipAddress: {
required,
ipAddress,
},
port: {
minValue: minValue(0),
maxValue: maxValue(65535),
},
},
};
},
methods: {
handleSubmit() {
this.$v.$touch();
if (this.$v.$invalid) return;
this.$emit('ok', {
ipAddress: this.form.ipAddress,
port: this.form.port,
});
this.closeModal();
},
closeModal() {
this.$nextTick(() => {
this.$refs.modal.hide();
});
},
resetForm() {
this.form.ipAddress = '';
this.form.port = '';
this.$v.$reset();
this.$emit('hidden');
},
onOk(bvModalEvt) {
// prevent modal close
bvModalEvt.preventDefault();
this.handleSubmit();
},
},
};
</script>
Loading

0 comments on commit d02d3ff

Please sign in to comment.