diff --git a/packages/api-v4/.changeset/pr-11400-upcoming-features-1733998451458.md b/packages/api-v4/.changeset/pr-11400-upcoming-features-1733998451458.md
new file mode 100644
index 00000000000..e41614586a6
--- /dev/null
+++ b/packages/api-v4/.changeset/pr-11400-upcoming-features-1733998451458.md
@@ -0,0 +1,5 @@
+---
+"@linode/api-v4": Upcoming Features
+---
+
+New `Block Storage Performance B1` linode capability ([#11400](https://github.com/linode/manager/pull/11400))
diff --git a/packages/api-v4/src/linodes/types.ts b/packages/api-v4/src/linodes/types.ts
index 6f3d94caad3..5689c24ba34 100644
--- a/packages/api-v4/src/linodes/types.ts
+++ b/packages/api-v4/src/linodes/types.ts
@@ -20,7 +20,7 @@ export interface Linode {
id: number;
alerts: LinodeAlerts;
backups: LinodeBackups;
- capabilities?: LinodeCapabilities[]; // @TODO BSE: Remove optionality once BSE is fully rolled out
+ capabilities: LinodeCapabilities[];
created: string;
disk_encryption?: EncryptionStatus; // @TODO LDE: Remove optionality once LDE is fully rolled out
region: string;
@@ -55,7 +55,10 @@ export interface LinodeBackups {
last_successful: string | null;
}
-export type LinodeCapabilities = 'Block Storage Encryption' | 'SMTP Enabled';
+export type LinodeCapabilities =
+ | 'Block Storage Encryption'
+ | 'SMTP Enabled'
+ | 'Block Storage Performance B1';
export type Window =
| 'Scheduling'
diff --git a/packages/manager/.changeset/pr-11400-upcoming-features-1733927873131.md b/packages/manager/.changeset/pr-11400-upcoming-features-1733927873131.md
new file mode 100644
index 00000000000..6db16c6f7d1
--- /dev/null
+++ b/packages/manager/.changeset/pr-11400-upcoming-features-1733927873131.md
@@ -0,0 +1,5 @@
+---
+"@linode/manager": Upcoming Features
+---
+
+High performance volume indicator ([#11400](https://github.com/linode/manager/pull/11400))
diff --git a/packages/manager/src/__data__/linodes.ts b/packages/manager/src/__data__/linodes.ts
index cf156461fa9..8cf0cac9de2 100644
--- a/packages/manager/src/__data__/linodes.ts
+++ b/packages/manager/src/__data__/linodes.ts
@@ -16,6 +16,7 @@ export const linode1: Linode = {
window: 'W2',
},
},
+ capabilities: [],
created: '2017-12-07T19:12:58',
group: 'active',
hypervisor: 'kvm',
@@ -65,6 +66,7 @@ export const linode2: Linode = {
window: 'Scheduling',
},
},
+ capabilities: [],
created: '2018-02-22T16:11:07',
group: 'inactive',
hypervisor: 'kvm',
@@ -114,6 +116,7 @@ export const linode3: Linode = {
window: 'Scheduling',
},
},
+ capabilities: [],
created: '2018-02-22T16:11:07',
group: 'inactive',
hypervisor: 'kvm',
@@ -163,6 +166,7 @@ export const linode4: Linode = {
window: 'Scheduling',
},
},
+ capabilities: [],
created: '2018-02-22T16:11:07',
group: 'inactive',
hypervisor: 'kvm',
diff --git a/packages/manager/src/features/Linodes/HighPerformanceVolumeIcon.tsx b/packages/manager/src/features/Linodes/HighPerformanceVolumeIcon.tsx
new file mode 100644
index 00000000000..792e5d78462
--- /dev/null
+++ b/packages/manager/src/features/Linodes/HighPerformanceVolumeIcon.tsx
@@ -0,0 +1,33 @@
+import { IconButton, Tooltip } from '@linode/ui';
+import BoltIcon from '@mui/icons-material/Bolt';
+import React from 'react';
+
+import type { LinodeCapabilities } from '@linode/api-v4';
+
+interface Props {
+ linodeCapabilities: LinodeCapabilities[];
+}
+
+export function HighPerformanceVolumeIcon({ linodeCapabilities }: Props) {
+ const isHighPerformanceVolume = !!linodeCapabilities?.includes(
+ 'Block Storage Performance B1'
+ );
+
+ if (!isHighPerformanceVolume) {
+ return null;
+ }
+
+ return (
+
+
+
+
+
+ );
+}
diff --git a/packages/manager/src/features/Linodes/LinodeEntityDetail.tsx b/packages/manager/src/features/Linodes/LinodeEntityDetail.tsx
index 8a698b468d5..4a9a9d19f65 100644
--- a/packages/manager/src/features/Linodes/LinodeEntityDetail.tsx
+++ b/packages/manager/src/features/Linodes/LinodeEntityDetail.tsx
@@ -117,6 +117,7 @@ export const LinodeEntityDetail = (props: Props) => {
ipv6={trimmedIPv6}
isLKELinode={Boolean(linode.lke_cluster_id)}
isVPCOnlyLinode={isVPCOnlyLinode}
+ linodeCapabilities={linode.capabilities}
linodeId={linode.id}
linodeIsInDistributedRegion={linodeIsInDistributedRegion}
linodeLabel={linode.label}
diff --git a/packages/manager/src/features/Linodes/LinodeEntityDetailBody.tsx b/packages/manager/src/features/Linodes/LinodeEntityDetailBody.tsx
index 3a305a8c6d0..262899eedb3 100644
--- a/packages/manager/src/features/Linodes/LinodeEntityDetailBody.tsx
+++ b/packages/manager/src/features/Linodes/LinodeEntityDetailBody.tsx
@@ -17,8 +17,9 @@ import { usePreferences } from 'src/queries/profile/preferences';
import { useProfile } from 'src/queries/profile/profile';
import { pluralize } from 'src/utilities/pluralize';
-import { encryptionStatusTestId } from '../Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodeTable';
import { EncryptedStatus } from '../Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodeTable';
+import { encryptionStatusTestId } from '../Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodeTable';
+import { HighPerformanceVolumeIcon } from './HighPerformanceVolumeIcon';
import {
StyledBodyGrid,
StyledColumnLabelGrid,
@@ -41,6 +42,7 @@ import type {
EncryptionStatus,
Interface,
Linode,
+ LinodeCapabilities,
} from '@linode/api-v4/lib/linodes/types';
import type { Subnet } from '@linode/api-v4/lib/vpcs';
import type { TypographyProps } from '@linode/ui';
@@ -66,6 +68,7 @@ export interface BodyProps {
ipv6: Linode['ipv6'];
isLKELinode: boolean; // indicates whether linode belongs to an LKE cluster
isVPCOnlyLinode: boolean;
+ linodeCapabilities: LinodeCapabilities[];
linodeId: number;
linodeIsInDistributedRegion: boolean;
linodeLabel: string;
@@ -85,6 +88,7 @@ export const LinodeEntityDetailBody = React.memo((props: BodyProps) => {
ipv6,
isLKELinode,
isVPCOnlyLinode,
+ linodeCapabilities,
linodeId,
linodeIsInDistributedRegion,
linodeLabel,
@@ -151,9 +155,23 @@ export const LinodeEntityDetailBody = React.memo((props: BodyProps) => {
{gbRAM} GB RAM
-
- {pluralize('Volume', 'Volumes', numVolumes)}
-
+ ({
+ alignItems: 'center',
+ display: 'flex',
+ gap: theme.spacing(),
+ })}
+ >
+
+ {pluralize('Volume', 'Volumes', numVolumes)}
+
+
+ {numVolumes > 0 && (
+
+ )}
+
{isDiskEncryptionFeatureEnabled && encryptionStatus && (
diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeStorage/LinodeVolumes.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeStorage/LinodeVolumes.tsx
index d24cac6b32d..a645ef6c422 100644
--- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeStorage/LinodeVolumes.tsx
+++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeStorage/LinodeVolumes.tsx
@@ -164,6 +164,7 @@ export const LinodeVolumes = () => {
}
isDetailsPageRow
key={volume.id}
+ linodeCapabilities={linode?.capabilities}
volume={volume}
/>
);
diff --git a/packages/manager/src/features/Linodes/LinodesLanding/LinodeRow/LinodeRow.test.tsx b/packages/manager/src/features/Linodes/LinodesLanding/LinodeRow/LinodeRow.test.tsx
index 8d2a11edd56..d25057816fc 100644
--- a/packages/manager/src/features/Linodes/LinodesLanding/LinodeRow/LinodeRow.test.tsx
+++ b/packages/manager/src/features/Linodes/LinodesLanding/LinodeRow/LinodeRow.test.tsx
@@ -33,6 +33,7 @@ describe('LinodeRow', () => {
}}
alerts={linode.alerts}
backups={linode.backups}
+ capabilities={linode.capabilities}
created={linode.created}
group={linode.group}
hypervisor={linode.hypervisor}
diff --git a/packages/manager/src/features/Linodes/LinodesLanding/ListView.tsx b/packages/manager/src/features/Linodes/LinodesLanding/ListView.tsx
index e09cc3a0326..2ea351f5d50 100644
--- a/packages/manager/src/features/Linodes/LinodesLanding/ListView.tsx
+++ b/packages/manager/src/features/Linodes/LinodesLanding/ListView.tsx
@@ -38,6 +38,7 @@ export const ListView = (props: RenderLinodesProps) => {
}}
alerts={linode.alerts}
backups={linode.backups}
+ capabilities={linode.capabilities}
created={linode.created}
group={linode.group}
hypervisor={linode.hypervisor}
diff --git a/packages/manager/src/features/Volumes/VolumeTableRow.test.tsx b/packages/manager/src/features/Volumes/VolumeTableRow.test.tsx
index 2c745da6744..e101f8a40b0 100644
--- a/packages/manager/src/features/Volumes/VolumeTableRow.test.tsx
+++ b/packages/manager/src/features/Volumes/VolumeTableRow.test.tsx
@@ -1,3 +1,4 @@
+import { waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import * as React from 'react';
@@ -174,4 +175,23 @@ describe('Volume table row - for linodes detail page', () => {
// Make sure there is a detach button
expect(getByText('Detach'));
});
+
+ it('should show a high performance icon tooltip if Linode has the capability', async () => {
+ const { getByLabelText, getByText } = await renderWithThemeAndRouter(
+ wrapWithTableBody(
+
+ )
+ );
+
+ const highPerformanceIcon = getByLabelText('High Performance');
+
+ expect(highPerformanceIcon).toBeVisible();
+ await userEvent.click(highPerformanceIcon);
+ await waitFor(() => expect(getByText('High Performance')).toBeVisible());
+ });
});
diff --git a/packages/manager/src/features/Volumes/VolumeTableRow.tsx b/packages/manager/src/features/Volumes/VolumeTableRow.tsx
index 6b0a7f81124..c6609a6b34f 100644
--- a/packages/manager/src/features/Volumes/VolumeTableRow.tsx
+++ b/packages/manager/src/features/Volumes/VolumeTableRow.tsx
@@ -12,6 +12,7 @@ import { useNotificationsQuery } from 'src/queries/account/notifications';
import { useInProgressEvents } from 'src/queries/events/events';
import { useRegionsQuery } from 'src/queries/regions/regions';
+import { HighPerformanceVolumeIcon } from '../Linodes/HighPerformanceVolumeIcon';
import {
getDerivedVolumeStatusFromStatusAndEvent,
getEventProgress,
@@ -20,7 +21,7 @@ import {
import { VolumesActionMenu } from './VolumesActionMenu';
import type { ActionHandlers } from './VolumesActionMenu';
-import type { Volume } from '@linode/api-v4';
+import type { LinodeCapabilities, Volume } from '@linode/api-v4';
export const useStyles = makeStyles()({
volumePath: {
@@ -33,6 +34,7 @@ interface Props {
handlers: ActionHandlers;
isBlockStorageEncryptionFeatureEnabled?: boolean;
isDetailsPageRow?: boolean;
+ linodeCapabilities?: LinodeCapabilities[];
volume: Volume;
}
@@ -42,6 +44,7 @@ export const VolumeTableRow = React.memo((props: Props) => {
handlers,
isBlockStorageEncryptionFeatureEnabled,
isDetailsPageRow,
+ linodeCapabilities,
volume,
} = props;
@@ -115,7 +118,21 @@ export const VolumeTableRow = React.memo((props: Props) => {
wrap: 'nowrap',
}}
>
- {volume.label}
+ ({
+ alignItems: 'center',
+ display: 'flex',
+ gap: theme.spacing(),
+ })}
+ >
+ {volume.label}
+ {linodeCapabilities && (
+
+ )}
+
+
{isEligibleForUpgradeToNVMe && (
({
dbEntities: await mswDB.getAll('linodes'),
- seedEntities: linodeFactory.buildList(count),
+ seedEntities: linodeFactory.buildList(count, {
+ capabilities: ['Block Storage Performance B1'],
+ }),
});
const configs: [number, Config][] = linodeSeeds.map((linodeSeed) => {