Skip to content

Commit

Permalink
feat(monitoring): show ip addresses of wan ifaces
Browse files Browse the repository at this point in the history
  • Loading branch information
andre8244 committed Nov 12, 2024
1 parent 61e230d commit 6bc76cb
Show file tree
Hide file tree
Showing 7 changed files with 423 additions and 197 deletions.
184 changes: 104 additions & 80 deletions src/components/standalone/monitoring/ConnectivityMonitor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,16 @@ import InterfaceTrafficCard from './connectivity/InterfaceTrafficCard.vue'
import { isEmpty } from 'lodash-es'
import type { Policy } from '@/composables/useMwan'
import WanConnectionsCard from './connectivity/WanConnectionsCard.vue'
import {
getName,
type DeviceOrIface,
type ZoneWithDeviceNames,
type ZoneWithDevices
} from '@/lib/standalone/network'
import { zonesSorting } from '@/stores/standalone/firewall'
import { useNetworkDevices } from '@/composables/useNetworkDevices'
import { getIpv4Addresses, getIpv6Addresses, getName, isDeviceUp } from '@/lib/standalone/network'
import { useUciNetworkConfig } from '@/composables/useUciNetworkConfig'

export type Wan = {
iface: string
device: string
status?: string
ip4Addresses?: string[]
ip6Addresses?: string[]
}

export type WanEvent = {
Expand All @@ -39,18 +37,24 @@ export type WanEvent = {
}

const { t } = useI18n()
const { allDevices, listDevices, loadingListDevices, errorListDevices, errorListDevicesDetails } =
useNetworkDevices()
const {
networkConfig,
getNetworkConfig,
loadingNetworkConfig,
errorNetworkConfig,
errorNetworkConfigDetails
} = useUciNetworkConfig()

const wans = ref<Wan[]>([])
const mwanEvents = ref<Record<string, any[]>>({})
const mwanPolicies = ref<Policy[]>([])
const allDevices = ref<DeviceOrIface[]>([]) ////
const devicesByZone = ref<ZoneWithDeviceNames[]>([]) ////

let loading = ref({
listWans: false,
getMwanReport: false,
getMwanPolicies: false, ////
listDevices: false ////
getMwanPolicies: false
})

let error = ref({
Expand All @@ -59,69 +63,78 @@ let error = ref({
getMwanReport: '',
getMwanReportDetails: '',
getMwanPolicies: '',
getMwanPoliciesDetails: '',
listDevices: '', ////
listDevicesDetails: '' ////
getMwanPoliciesDetails: ''
})

const wanConnections = computed(() => {
const wanData = []
const loadingData = computed(() => {
return (
loading.value.listWans ||
loading.value.getMwanReport ||
loading.value.getMwanPolicies ||
loadingNetworkConfig.value ||
loadingListDevices.value
)
})

console.log('wans', wans.value) ////
const wanConnections = computed(() => {
const wanData: Wan[] = []

for (const wan of wans.value) {
// get wan status from policy data
let statusFound = false

for (const policy of mwanPolicies.value) {
if (statusFound) {
break
}

for (const policyMembers of Object.values(policy.members)) {
if (mwanPolicies.value.length > 0) {
// multiwan configured
for (const policy of mwanPolicies.value) {
if (statusFound) {
break
}

for (const policyMember of policyMembers) {
if (policyMember.interface == wan.iface) {
wanData.push({
...wan,
status: policyMember.status
})
statusFound = true
for (const policyMembers of Object.values(policy.members)) {
if (statusFound) {
break
}

for (const policyMember of policyMembers) {
if (policyMember.interface == wan.iface) {
const devFound = allDevices.value.find((dev) => getName(dev) === wan.device)

wanData.push({
...wan,
status: policyMember.status,
ip4Addresses: devFound ? getIpv4Addresses(devFound, networkConfig.value) : [],
ip6Addresses: devFound ? getIpv6Addresses(devFound, networkConfig.value) : []
})
statusFound = true
break
}
}
}
}
}
}
return wanData
})
} else {
// multiwan not configured

////
const sortedZonesAndDevices = computed(() => {
const zones: ZoneWithDevices[] = []
devicesByZone.value.forEach((z: ZoneWithDeviceNames) => {
const deviceList: DeviceOrIface[] = []
z.devices.forEach((devName: string) => {
const devFound = allDevices.value.find((dev) => getName(dev) === devName)
const devFound = allDevices.value.find((dev) => getName(dev) === wan.device)

if (devFound) {
deviceList.push(devFound)
wanData.push({
...wan,
status: isDeviceUp(devFound, allDevices.value) ? 'online' : 'offline',
ip4Addresses: getIpv4Addresses(devFound, networkConfig.value),
ip6Addresses: getIpv6Addresses(devFound, networkConfig.value)
})
}
})
const zone = { name: z.name, devices: deviceList }
zones.push(zone)
})

return zones.sort(zonesSorting)
}
}
return wanData.sort(sortByProperty('iface'))
})

onMounted(() => {
listWans()
getMwanReport()
getMwanPolicies()
listDevices()
getNetworkConfig()
})

async function listWans() {
Expand Down Expand Up @@ -187,30 +200,6 @@ async function getMwanPolicies() {
loading.value.getMwanPolicies = false
}
}

////
async function listDevices() {
loading.value.listDevices = true
error.value.listDevices = ''
error.value.listDevicesDetails = ''

try {
const res = await ubusCall('ns.devices', 'list-devices')

const bondDevices = res.data.all_devices
.filter((device: DeviceOrIface) => device.name?.startsWith('bond-'))
.map((device: DeviceOrIface) => device.name?.slice(5))

allDevices.value = res.data.all_devices.filter(
(device: DeviceOrIface) => !bondDevices.includes(device.name ?? device['.name'])
)
devicesByZone.value = res.data.devices_by_zone
} catch (err: any) {
error.value.listDevices = t(getAxiosErrorMessage(err))
error.value.listDevicesDetails = err.toString()
}
loading.value.listDevices = false
}
</script>

<template>
Expand Down Expand Up @@ -254,25 +243,60 @@ async function listDevices() {
{{ error.getMwanPoliciesDetails }}
</template>
</NeInlineNotification>
<div class="grid grid-cols-1 gap-x-6 gap-y-6 xl:grid-cols-2">
<!-- listDevices error notification -->
<NeInlineNotification
v-if="errorListDevices"
kind="error"
:title="t('error.cannot_load_network_devices')"
:description="errorListDevices"
:closeAriaLabel="t('common.close')"
class="mb-4"
>
<template v-if="errorListDevicesDetails" #details>
{{ errorListDevicesDetails }}
</template>
</NeInlineNotification>
<!-- uci network config error notification -->
<NeInlineNotification
v-if="errorNetworkConfig"
kind="error"
:title="t('error.cannot_load_network_config')"
:description="errorNetworkConfig"
:closeAriaLabel="t('common.close')"
class="mb-4"
>
<template v-if="errorNetworkConfigDetails" #details>
{{ errorNetworkConfigDetails }}
</template>
</NeInlineNotification>
<div class="grid grid-cols-1 gap-x-6 gap-y-6 sm:grid-cols-12">
<!-- skeleton -->
<template v-if="loading.listWans || loading.getMwanReport || loading.getMwanPolicies">
<template v-if="loadingData">
<NeCard
loading
:skeletonLines="7"
class="sm:col-span-12 3xl:col-span-8 6xl:col-span-4 7xl:col-span-3"
></NeCard>
<NeCard
v-for="index in 4"
:key="index"
loading
:skeletonLines="5"
class="col-span-1"
:skeletonLines="7"
class="sm:col-span-12 xl:col-span-6 3xl:col-span-4 7xl:col-span-3"
></NeCard>
</template>
<template v-else>
<!-- connections -->
<WanConnectionsCard v-if="wanConnections.length" :wanConnections="wanConnections" />
<WanConnectionsCard
v-if="wanConnections.length"
:wanConnections="wanConnections"
class="sm:col-span-12 3xl:col-span-8 6xl:col-span-4 7xl:col-span-3"
/>
<!-- wan events -->
<NeCard
v-if="isEmpty(mwanEvents)"
:title="t('standalone.real_time_monitor.wan_events')"
class="col-span-1"
class="sm:col-span-12 xl:col-span-6 3xl:col-span-4 7xl:col-span-3"
>
<NeEmptyState
:title="t('standalone.real_time_monitor.no_events_message')"
Expand All @@ -286,15 +310,15 @@ async function listDevices() {
:key="wanName"
:wan="wanName"
:wanEvents="events"
class="col-span-1"
class="sm:col-span-12 xl:col-span-6 3xl:col-span-4 7xl:col-span-3"
/>
<!-- wans traffic -->
<InterfaceTrafficCard
v-for="wan in wans"
:key="wan.device"
:iface="wan.iface"
:device="wan.device"
class="col-span-1"
class="sm:col-span-12 xl:col-span-6 3xl:col-span-4 7xl:col-span-3"
/>
</template>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const { currentPage, paginatedItems } = useItemPagination(() => props.wanConnect
<NeTableHeadCell>{{ t('standalone.real_time_monitor.interface') }}</NeTableHeadCell>
<NeTableHeadCell>{{ t('standalone.real_time_monitor.device') }}</NeTableHeadCell>
<NeTableHeadCell>{{ t('common.status') }}</NeTableHeadCell>
<NeTableHeadCell>{{ t('common.ip_address') }}</NeTableHeadCell>
</NeTableHead>
<NeTableBody>
<NeTableRow v-for="(item, index) in paginatedItems" :key="index">
Expand Down Expand Up @@ -66,6 +67,12 @@ const { currentPage, paginatedItems } = useItemPagination(() => props.wanConnect
}}
</div>
</NeTableCell>
<NeTableCell :data-label="t('common.ip_address')">
<span v-if="item.ip4Addresses.length || item.ip6Addresses.length">
{{ (item.ip4Addresses || []).concat(item.ip6Addresses || []).join(', ') }}
</span>
<span v-else>-</span>
</NeTableCell>
</NeTableRow>
</NeTableBody>
<template #paginator>
Expand Down
81 changes: 81 additions & 0 deletions src/composables/useNetworkDevices.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright (C) 2024 Nethesis S.r.l.
// SPDX-License-Identifier: GPL-3.0-or-later

import { computed, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { ubusCall } from '@/lib/standalone/ubus'
import { getAxiosErrorMessage } from '@nethesis/vue-components'
import {
getName,
type DeviceOrIface,
type ZoneWithDeviceNames,
type ZoneWithDevices
} from '@/lib/standalone/network'
import { zonesSorting } from '@/stores/standalone/firewall'

/**
* Composable that handles the network devices retrieved from the ns.devices list-devices API
*/
export function useNetworkDevices() {
const { t } = useI18n()

const allDevices = ref<DeviceOrIface[]>([])
const devicesByZone = ref<ZoneWithDeviceNames[]>([])
const loadingListDevices = ref(false)
const errorListDevices = ref('')
const errorListDevicesDetails = ref('')

const sortedZonesAndDevices = computed(() => {
const zones: ZoneWithDevices[] = []
devicesByZone.value.forEach((z: ZoneWithDeviceNames) => {
const deviceList: DeviceOrIface[] = []
z.devices.forEach((devName: string) => {
const devFound = allDevices.value.find((dev) => getName(dev) === devName)

if (devFound) {
deviceList.push(devFound)
}
})
const zone = { name: z.name, devices: deviceList }
zones.push(zone)
})

return zones.sort(zonesSorting)
})

async function listDevices() {
loadingListDevices.value = true
allDevices.value = []
devicesByZone.value = []
errorListDevices.value = ''
errorListDevicesDetails.value = ''

try {
const res = await ubusCall('ns.devices', 'list-devices')
const bond_devices = res.data.all_devices
.filter((device: DeviceOrIface) => device.name?.startsWith('bond-'))
.map((device: DeviceOrIface) => device.name?.slice(5))

allDevices.value = res.data.all_devices.filter(
(device: DeviceOrIface) => !bond_devices.includes(device.name ?? device['.name'])
)
devicesByZone.value = res.data.devices_by_zone
} catch (err: any) {
console.error(err)
errorListDevices.value = t(getAxiosErrorMessage(err))
errorListDevicesDetails.value = err.toString()
} finally {
loadingListDevices.value = false
}
}

return {
allDevices,
devicesByZone,
sortedZonesAndDevices,
listDevices,
loadingListDevices,
errorListDevices,
errorListDevicesDetails
}
}
Loading

0 comments on commit 6bc76cb

Please sign in to comment.