Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Upcoming Features
---

Integrate Firewall-nodebalancer support for ACLP-Alerting ([#13089](https://github.com/linode/manager/pull/13089))
95 changes: 84 additions & 11 deletions packages/manager/src/factories/cloudpulse/alerts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ export const alertFactory = Factory.Sync.makeFactory<Alert>({
updated_by: 'system',
});

const firewallDimensions: Dimension[] = [
const firewallLinodeDimensions: Dimension[] = [
{ label: 'VPC-Subnet', dimension_label: 'vpc_subnet_id', values: [] },
{
label: 'Interface Type',
Expand All @@ -257,29 +257,84 @@ const firewallDimensions: Dimension[] = [
{ label: 'Linode Region', dimension_label: 'region_id', values: [] },
];

const firewallNodebalancerDimensions: Dimension[] = [
{ label: 'Protocol', dimension_label: 'protocol', values: ['TCP', 'UDP'] },
{ label: 'IP Version', dimension_label: 'ip_version', values: ['v4', 'v6'] },
{ label: 'NodeBalancer', dimension_label: 'nodebalancer_id', values: [] },
];

export const firewallMetricDefinitionFactory =
Factory.Sync.makeFactory<MetricDefinition>({
label: 'Firewall Metric',
metric: 'firewall_metric',
unit: 'metric_unit',
label: 'Current connections (Linode)',
metric: 'fw_active_connections',
unit: 'Count',
metric_type: 'gauge',
scrape_interval: '300s',
scrape_interval: '60s',
is_alertable: true,
available_aggregate_functions: ['avg', 'sum', 'max', 'min', 'count'],
dimensions: firewallDimensions,
available_aggregate_functions: ['avg', 'max', 'min'],
dimensions: firewallLinodeDimensions,
});

export const firewallMetricDefinitionsResponse: MetricDefinition[] = [
firewallMetricDefinitionFactory.build({
label: 'Current connections',
label: 'Current connections (Linode)',
metric: 'fw_active_connections',
unit: 'count',
unit: 'Count',
available_aggregate_functions: ['avg', 'max', 'min'],
dimensions: firewallLinodeDimensions,
}),
firewallMetricDefinitionFactory.build({
label: 'Ingress packets accepted',
label: 'Ingress Packets Accepted (Linode)',
metric: 'fw_ingress_packets_accepted',
unit: 'packets_per_second',
unit: 'packets/s',
available_aggregate_functions: ['sum'],
dimensions: firewallLinodeDimensions,
}),
firewallMetricDefinitionFactory.build({
label: 'Available Connections (Linode)',
metric: 'fw_available_connections',
unit: 'Count',
available_aggregate_functions: ['avg', 'max', 'min'],
dimensions: firewallLinodeDimensions,
}),
firewallMetricDefinitionFactory.build({
label: 'Ingress Bytes Accepted (Linode)',
metric: 'fw_ingress_bytes_accepted',
unit: 'Bps',
available_aggregate_functions: ['sum'],
dimensions: firewallLinodeDimensions,
}),
firewallMetricDefinitionFactory.build({
label: 'Ingress Bytes Accepted (Node Balancer)',
metric: 'nb_ingress_bytes_accepted',
unit: 'Bps',
scrape_interval: '300s',
available_aggregate_functions: ['sum'],
dimensions: firewallNodebalancerDimensions,
}),
firewallMetricDefinitionFactory.build({
label: 'Ingress Bytes Dropped (Node Balancer)',
metric: 'nb_ingress_bytes_dropped',
unit: 'Bps',
scrape_interval: '300s',
available_aggregate_functions: ['sum'],
dimensions: firewallNodebalancerDimensions,
}),
firewallMetricDefinitionFactory.build({
label: 'Ingress Packets Accepted (Node Balancer)',
metric: 'nb_ingress_packets_accepted',
unit: 'packets/s',
scrape_interval: '300s',
available_aggregate_functions: ['sum'],
dimensions: firewallNodebalancerDimensions,
}),
firewallMetricDefinitionFactory.build({
label: 'Ingress Packets Dropped (Node Balancer)',
metric: 'nb_ingress_packets_dropped',
unit: 'packets/s',
scrape_interval: '300s',
available_aggregate_functions: ['sum'],
dimensions: firewallNodebalancerDimensions,
}),
];

Expand Down Expand Up @@ -579,3 +634,21 @@ export const blockStorageMetricCriteria =
},
],
});

export const firewallNodebalancerMetricCriteria =
Factory.Sync.makeFactory<AlertDefinitionMetricCriteria>({
label: 'Ingress Packets Dropped (Node Balancer)',
metric: 'nb_ingress_packets_dropped',
unit: 'packets/s',
aggregate_function: 'sum',
operator: 'gt',
threshold: 1000,
dimension_filters: [
{
label: 'NodeBalancer',
dimension_label: 'nodebalancer_id',
operator: 'in',
value: '333',
},
],
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ import {
type AlertDefinitionMetricCriteria,
type CloudPulseServiceType,
} from '@linode/api-v4';
import { useAllLinodesQuery, useAllVPCsQuery } from '@linode/queries';
import {
useAllLinodesQuery,
useAllNodeBalancersQuery,
useAllVPCsQuery,
} from '@linode/queries';
import { Divider } from '@linode/ui';
import { GridLegacy } from '@mui/material';
import React, { useMemo } from 'react';
Expand All @@ -17,6 +21,7 @@ import {
import { getVPCSubnets } from '../CreateAlert/Criteria/DimensionFilterValue/utils';
import {
LINODE_DIMENSION_LABEL,
NODEBALANCER_DIMENSION_LABEL,
VPC_SUBNET_DIMENSION_LABEL,
} from './constants';
import { DisplayAlertDetailChips } from './DisplayAlertDetailChips';
Expand Down Expand Up @@ -47,9 +52,18 @@ export const RenderAlertMetricsAndDimensions = React.memo(
ruleCriteria,
VPC_SUBNET_DIMENSION_LABEL
);
const isNodebalancersRequired = isCheckRequired(
ruleCriteria,
NODEBALANCER_DIMENSION_LABEL
);
// Initialize the query, but only run when needed
const { data: linodes } = useAllLinodesQuery({}, {}, isLinodeRequired);
const { data: vpcs } = useAllVPCsQuery({ enabled: isVPCRequired });
const { data: nodebalancers } = useAllNodeBalancersQuery(
isNodebalancersRequired,
{},
{}
);

// create a map of id to labels for lookup
const linodeMap = useMemo(
Expand All @@ -73,6 +87,17 @@ export const RenderAlertMetricsAndDimensions = React.memo(
}, {});
}, [vpcs]);

const nodebalancersMap = useMemo(() => {
return (
nodebalancers?.reduce<Record<string, string>>((acc, nodebalancer) => {
return {
...acc,
[String(nodebalancer.id)]: nodebalancer.label,
};
}, {}) ?? {}
);
}, [nodebalancers]);

if (!ruleCriteria.rules?.length) {
return <NullComponent />;
}
Expand Down Expand Up @@ -125,6 +150,7 @@ export const RenderAlertMetricsAndDimensions = React.memo(
serviceType,
value,
vpcSubnetMap,
nodebalancersMap,
}),
]
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ export const transformationAllowedOperators: DimensionFilterOperatorType[] = [

export const LINODE_DIMENSION_LABEL = 'linode_id';
export const VPC_SUBNET_DIMENSION_LABEL = 'vpc_subnet_id';
export const NODEBALANCER_DIMENSION_LABEL = 'nodebalancer_id';
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,10 @@ describe('getResolvedDimensionValue', () => {
'subnet-1': 'VPC-1_subnet-1',
'subnet-2': 'VPC-1_subnet-2',
};

const nodebalancersMap = {
'33': 'nodebalancer-a',
'44': 'nodebalancer-b',
};
it('should return correct transformed value', () => {
const linodeResult = getResolvedDimensionValue({
dimensionFilterKey: 'linode_id',
Expand All @@ -91,6 +94,7 @@ describe('getResolvedDimensionValue', () => {
serviceType: 'firewall',
linodeMap,
vpcSubnetMap,
nodebalancersMap,
});
expect(linodeResult).toBe('linode-a, linode-b');

Expand All @@ -101,10 +105,23 @@ describe('getResolvedDimensionValue', () => {
serviceType: 'firewall',
linodeMap,
vpcSubnetMap,
nodebalancersMap,
});
expect(vpcResult).toBe('VPC-1_subnet-1');
});

it('should return correct transformed value for nodebalancer_id', () => {
const nodebalancerResult = getResolvedDimensionValue({
dimensionFilterKey: 'nodebalancer_id',
dimensionOperator: 'in',
value: '33,44',
serviceType: 'firewall',
linodeMap,
vpcSubnetMap,
nodebalancersMap,
});
expect(nodebalancerResult).toBe('nodebalancer-a, nodebalancer-b');
});
it('should not transform value if operator is not in allowed list', () => {
const result = getResolvedDimensionValue({
dimensionFilterKey: 'linode_id',
Expand All @@ -113,6 +130,7 @@ describe('getResolvedDimensionValue', () => {
serviceType: 'firewall',
linodeMap,
vpcSubnetMap,
nodebalancersMap,
});
expect(result).toBe('linode-c, linode-d');
});
Expand All @@ -125,6 +143,7 @@ describe('getResolvedDimensionValue', () => {
serviceType: 'firewall',
linodeMap,
vpcSubnetMap,
nodebalancersMap,
});
expect(nullResult).toBe('');
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { transformDimensionValue } from '../Utils/utils';
import {
LINODE_DIMENSION_LABEL,
NODEBALANCER_DIMENSION_LABEL,
transformationAllowedOperators,
VPC_SUBNET_DIMENSION_LABEL,
} from './constants';
Expand Down Expand Up @@ -82,6 +83,10 @@ export interface ResolvedDimensionValueProps {
* linode id to label map
*/
linodeMap: Record<string, string>;
/**
* nodebalancer id to label map.
*/
nodebalancersMap: Record<string, string>;
/**
* Service type of the alert.
*/
Expand All @@ -108,6 +113,7 @@ export const getResolvedDimensionValue = (
serviceType,
value,
vpcSubnetMap,
nodebalancersMap,
} = props;
if (!value) return '';

Expand All @@ -127,6 +133,13 @@ export const getResolvedDimensionValue = (
resolvedValue = resolveIds(value, vpcSubnetMap);
}

if (
dimensionFilterKey === NODEBALANCER_DIMENSION_LABEL &&
transformationAllowedOperators.includes(dimensionOperator)
) {
resolvedValue = resolveIds(value, nodebalancersMap);
}

return transformationAllowedOperators.includes(dimensionOperator)
? transformCommaSeperatedDimensionValues(
resolvedValue,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import EntityIcon from 'src/assets/icons/entityIcons/alertsresources.svg';
import { DebouncedSearchTextField } from 'src/components/DebouncedSearchTextField';
import { useResourcesQuery } from 'src/queries/cloudpulse/resources';

import { filterFirewallResources } from '../../Utils/utils';
import { StyledPlaceholder } from '../AlertsDetail/AlertDetail';
import { MULTILINE_ERROR_SEPARATOR } from '../constants';
import { AlertListNoticeMessages } from '../Utils/AlertListNoticeMessages';
Expand Down Expand Up @@ -41,6 +42,7 @@ import type {
AlertDefinitionType,
CloudPulseServiceType,
Filter,
Firewall,
Region,
} from '@linode/api-v4';

Expand All @@ -64,6 +66,11 @@ export interface AlertResourcesProp {
*/
alertType: AlertDefinitionType;

/**
* The entity type for firewall filtering (linode or nodebalancer)
*/
entityType?: 'linode' | 'nodebalancer';

/**
* The error text that needs to displayed incase needed
*/
Expand Down Expand Up @@ -106,6 +113,7 @@ export const AlertResources = React.memo((props: AlertResourcesProp) => {
alertLabel,
alertResourceIds = [],
alertType,
entityType,
errorText,
handleResourcesSelection,
hideLabel,
Expand Down Expand Up @@ -194,7 +202,12 @@ export const AlertResources = React.memo((props: AlertResourcesProp) => {
), // Enable query only if serviceType and supportedRegionIds are available, in case of firewall only serviceType is needed
serviceType,
{},
xFilterToBeApplied
xFilterToBeApplied,
serviceType === 'firewall' && entityType ? entityType : undefined,
serviceType === 'firewall' && entityType
? (resources: Firewall[]) =>
filterFirewallResources(resources, entityType)
: undefined
);

const regionFilteredResources = React.useMemo(() => {
Expand Down
Loading