Skip to content

Commit 47f2f0f

Browse files
committed
ECOPROJECT-3706 | feat: Introduce Cluster Sizer feature for OpenShift cluster recommendations
- Added a new `ClusterSizingWizard` component to guide users through selecting migration preferences and reviewing cluster sizing recommendations. - Implemented a two-step wizard that collects user inputs for worker node configurations and displays calculated requirements based on VMware inventory data. - Created supporting components including `SizingInputForm`, `SizingResult`, and their respective footers for navigation. - Integrated mock data for development and testing purposes, with a plan for future API integration. This feature enhances the user experience by providing tailored cluster sizing recommendations, facilitating smoother migrations to OpenShift. Signed-off-by: Jonathan Kilzi <[email protected]> ECOPROJECT-3706 | feat: Update Cluster Sizer: Plan, API integration and dependencies - Updated the `.gitignore` to include specific cursor plans while excluding others. - Bumped the version of `@migration-planner-ui/api-client` from `0.0.36` to `0.0.37` in both `package.json` and `package-lock.json`. - Introduced a new plan document for the Cluster Sizer API integration detailing implementation steps and key files. - Refactored `ClusterSizingWizard` to utilize the new API client for fetching cluster requirements, removing mock data dependencies. - Enhanced `SizingResult` and `SizingInputForm` components to handle optional API response fields and improve user feedback. - Cleaned up code formatting and comments for better readability. This update improves the integration of the Cluster Sizer feature with the backend API, enhancing the overall user experience in the migration planning process. Signed-off-by: Jonathan Kilzi <[email protected]>
1 parent 4fe0d54 commit 47f2f0f

20 files changed

+1756
-12
lines changed
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
---
2+
name: Cluster Sizer API Integration
3+
overview: Integrate the ClusterSizingWizard with the real AssessmentApi by replacing mock data with the calculateAssessmentClusterRequirements endpoint, using the existing DI container pattern.
4+
todos:
5+
- id: update-types
6+
content: Update types.ts to re-export API types from api-client and keep only UI-specific types
7+
status: completed
8+
- id: update-wizard
9+
content: Update ClusterSizingWizard to use AssessmentApi via DI container instead of mocks
10+
status: completed
11+
- id: update-result
12+
content: Add null checks in SizingResult for optional resourceConsumption fields
13+
status: completed
14+
- id: cleanup-report
15+
content: Remove unused onCalculate prop from ClusterSizingWizard usage in Report.tsx
16+
status: completed
17+
- id: remove-ha-text
18+
content: "Remove \"High Availability: Yes\" text from SizingResult component"
19+
status: completed
20+
- id: update-default-overcommit
21+
content: Change default overcommit ratio from 1:6 to 1:4 in constants.ts
22+
status: completed
23+
---
24+
25+
# Cluster Sizer API Integration Plan
26+
27+
## Overview
28+
29+
The `@migration-planner-ui/api-client` package now includes the `calculateAssessmentClusterRequirements` method in `AssessmentApi` along with the `ClusterRequirementsRequest` and `ClusterRequirementsResponse` types. The wizard will use the existing DI pattern (`useInjection` hook) to access the API.
30+
31+
## Key Files
32+
33+
- [`src/pages/report/cluster-sizer/ClusterSizingWizard.tsx`](src/pages/report/cluster-sizer/ClusterSizingWizard.tsx) - Main component to update
34+
- [`src/pages/report/cluster-sizer/types.ts`](src/pages/report/cluster-sizer/types.ts) - Replace API types with api-client imports
35+
- [`src/pages/report/cluster-sizer/SizingResult.tsx`](src/pages/report/cluster-sizer/SizingResult.tsx) - Add null checks for optional fields
36+
- [`src/main/Symbols.ts`](src/main/Symbols.ts) - Already has `AssessmentApi` symbol registered
37+
38+
## API Details
39+
40+
The api-client provides:
41+
42+
- `AssessmentApi.calculateAssessmentClusterRequirements({ id, clusterRequirementsRequest })`
43+
- Returns `ClusterRequirementsResponse` with `clusterSizing`, `resourceConsumption`, and `inventoryTotals`
44+
45+
Note: `resourceConsumption.limits` and `resourceConsumption.overCommitRatio` are optional in the api-client types.
46+
47+
## Implementation Steps
48+
49+
### 1. Update types.ts
50+
51+
Remove duplicate API types and re-export from api-client:
52+
53+
```typescript
54+
// Re-export API types from api-client
55+
export type {
56+
ClusterRequirementsRequest,
57+
ClusterRequirementsResponse,
58+
ClusterSizing,
59+
InventoryTotals,
60+
SizingResourceConsumption,
61+
} from '@migration-planner-ui/api-client/models';
62+
63+
// Keep UI-specific types (SizingFormValues, WorkerNodePreset, etc.)
64+
```
65+
66+
### 2. Update ClusterSizingWizard.tsx
67+
68+
- Import `useInjection` from `@migration-planner-ui/ioc`
69+
- Import `AssessmentApi` from `@migration-planner-ui/api-client/apis`
70+
- Import `Symbols` from `@/main/Symbols`
71+
- Remove `fetchMockClusterRequirements` import
72+
- Remove `onCalculate` prop (no longer needed)
73+
- Use `assessmentApi.calculateAssessmentClusterRequirements()` directly
74+
```typescript
75+
const assessmentApi = useInjection<AssessmentApi>(Symbols.AssessmentApi);
76+
77+
const result = await assessmentApi.calculateAssessmentClusterRequirements({
78+
id: assessmentId,
79+
clusterRequirementsRequest: request,
80+
});
81+
```
82+
83+
84+
### 3. Update SizingResult.tsx
85+
86+
Add null checks for optional API response fields:
87+
88+
```typescript
89+
sizerOutput.resourceConsumption.overCommitRatio?.cpu ?? 0
90+
sizerOutput.resourceConsumption.limits?.cpu ?? 0
91+
```
92+
93+
### 4. Clean up Report.tsx
94+
95+
Remove the unused `onCalculate` prop from `ClusterSizingWizard` usage.
96+
97+
### 5. Remove "High Availability: Yes" from SizingResult
98+
99+
Remove the hardcoded "High Availability: Yes" text from both:
100+
101+
- The `generatePlainTextRecommendation` function (clipboard copy text)
102+
- The JSX render section in `SizingResult.tsx`
103+
104+
### 6. Change default overcommit ratio
105+
106+
In [`src/pages/report/cluster-sizer/constants.ts`](src/pages/report/cluster-sizer/constants.ts), update `DEFAULT_FORM_VALUES.overcommitRatio` from `6` (High Density 1:6) to `4` (Standard 1:4).
107+
108+
### 7. Mock files decision
109+
110+
Keep `src/pages/report/cluster-sizer/mocks/` folder for unit testing purposes, but remove the runtime dependency on `data.mock.ts`.

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
# vscode config
44
# .vscode/*
5-
.cursor/**/*
5+
.cursor/
6+
!.cursor/plans/*
67

78

89
# ide config

package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
},
3333
"dependencies": {
3434
"@emotion/css": "^11.13.0",
35-
"@migration-planner-ui/api-client": "^0.0.36",
35+
"@migration-planner-ui/api-client": "^0.0.37",
3636
"@migration-planner-ui/ioc": "^0.0.36",
3737
"@patternfly/react-charts": "7.4.9",
3838
"@patternfly/react-core": "^6.2.2",

specs/cluster-sizer.md

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# Cluster Sizer – Target Cluster Recommendations
2+
3+
## Overview
4+
The Cluster Sizer feature helps users generate OpenShift cluster sizing recommendations based on their VMware inventory data and migration preferences. It presents a two-step wizard that collects user preferences and displays calculated requirements for the target OpenShift cluster.
5+
6+
## Related Jira Tickets
7+
- **ECOPROJECT-3637**: Implement "Recommend me an OpenShift cluster" wizard
8+
- **ECOPROJECT-3631**: Cluster requirements API integration (backend sizer library)
9+
10+
## Figma Mockups
11+
- Configuration step: `node-id=6896-15678`
12+
- Results step: `node-id=7181-9318`
13+
- File: `Migration-assessment` design file
14+
15+
## Wizard Steps
16+
17+
### Step 1: Migration Preferences
18+
Users configure their target cluster parameters:
19+
- **Run workloads on control plane nodes** (checkbox): Whether to schedule VM workloads on control plane nodes
20+
- **Worker node CPU cores** (dropdown, required): CPU cores per worker node (8, 16, 32, 64, 96, 128)
21+
- **Worker node memory** (dropdown, required): Memory in GB per worker node (16, 32, 64, 128, 256, 512)
22+
- **Over-commit ratio** (dropdown, required): Resource sharing factor (1:1, 1:2, 1:4, 1:6)
23+
- 1:1 = No over-commit (dedicated)
24+
- 1:2 = Low density
25+
- 1:4 = Standard density
26+
- 1:6 = High density
27+
28+
### Step 2: Review Cluster Recommendations
29+
Displays calculated sizing based on inventory data and user preferences:
30+
- **Inventory Summary**: Total VMs, CPU cores, and memory from source VMware cluster
31+
- **Cluster Sizing**: Recommended worker nodes, control plane nodes, total nodes, total CPU, total memory
32+
- **Resource Utilization**: CPU consumption %, memory consumption %, resource limits, over-commit ratios
33+
34+
## API Integration
35+
36+
### Endpoint
37+
```
38+
POST /api/v1/assessments/{id}/cluster-requirements
39+
```
40+
41+
### Request Payload
42+
```typescript
43+
interface ClusterRequirementsRequest {
44+
clusterId: string; // VMware cluster ID
45+
overCommitRatio: "1:1" | "1:2" | "1:4" | "1:6";
46+
workerNodeCPU: number; // CPU cores per worker
47+
workerNodeMemory: number; // Memory in GB per worker
48+
controlPlaneSchedulable: boolean;
49+
}
50+
```
51+
52+
### Response Payload
53+
```typescript
54+
interface ClusterRequirementsResponse {
55+
clusterSizing: {
56+
controlPlaneNodes: number;
57+
totalCPU: number;
58+
totalMemory: number;
59+
totalNodes: number;
60+
workerNodes: number;
61+
};
62+
inventoryTotals: {
63+
totalCPU: number;
64+
totalMemory: number;
65+
totalVMs: number;
66+
};
67+
resourceConsumption: {
68+
cpu: number; // percentage
69+
memory: number; // percentage
70+
limits: { cpu: number; memory: number };
71+
overCommitRatio: { cpu: number; memory: number };
72+
};
73+
}
74+
```
75+
76+
## File Structure
77+
```
78+
src/pages/report/cluster-sizer/
79+
├── index.ts # Re-exports
80+
├── types.ts # TypeScript interfaces and type definitions
81+
├── constants.ts # Form options (CPU, memory, over-commit dropdowns)
82+
├── ClusterSizingWizard.tsx # Main wizard modal component
83+
├── SizingInputForm.tsx # Step 1: Migration preferences form
84+
├── SizingResult.tsx # Step 2: Results display
85+
└── mockData.ts # Mock API responses for development
86+
```
87+
88+
## UX Behavior Notes
89+
- Modal title: "Target cluster recommendations"
90+
- Default values: Control plane schedulable = false, CPU = 32, Memory = 32GB, Over-commit = 1:6
91+
- Footer buttons:
92+
- Step 1: "Next" (primary) + "Cancel" (link)
93+
- Step 2: "Close" (primary) + "Back" (link)
94+
- Copy to clipboard: Results can be copied as plain text for sharing
95+
- Loading state: Shows spinner while calculating recommendations
96+
- Error handling: Displays error message if API call fails
97+
98+
## Integration Point
99+
The wizard is triggered from the Report page (`src/pages/report/Report.tsx`) via a "Get cluster sizing recommendation" button. It receives the `assessmentId` as a prop to identify which VMware cluster inventory to use.
100+
File renamed without changes.

src/pages/report/Report.tsx

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ import { useDiscoverySources } from '../../migration-wizard/contexts/discovery-s
3232
import { Provider as DiscoverySourcesProvider } from '../../migration-wizard/contexts/discovery-sources/Provider';
3333
import { EnhancedDownloadButton } from '../../migration-wizard/steps/discovery/EnhancedDownloadButton';
3434
import { ExportError, SnapshotLike } from '../../services/report-export/types';
35-
import { openAssistedInstaller } from '../assessment/utils/functions';
3635
import { parseLatestSnapshot } from '../assessment/utils/snapshotParser';
3736
import { AgentStatusView } from '../environment/sources-table/AgentStatusView';
3837

@@ -41,6 +40,7 @@ import {
4140
ClusterOption,
4241
} from './assessment-report/clusterView';
4342
import { Dashboard } from './assessment-report/Dashboard';
43+
import { ClusterSizingWizard } from './cluster-sizer/ClusterSizingWizard';
4444

4545
type AssessmentLike = {
4646
id: string | number;
@@ -56,6 +56,7 @@ const Inner: React.FC = () => {
5656
const [exportError, setExportError] = useState<ExportError | null>(null);
5757
const [selectedClusterId, setSelectedClusterId] = useState<string>('all');
5858
const [isClusterSelectOpen, setIsClusterSelectOpen] = useState(false);
59+
const [isSizingWizardOpen, setIsSizingWizardOpen] = useState(false);
5960

6061
useMount(async () => {
6162
if (
@@ -340,11 +341,17 @@ const Inner: React.FC = () => {
340341
}`}
341342
/>
342343
</SplitItem>
343-
<SplitItem>
344-
<Button variant="primary" onClick={openAssistedInstaller}>
345-
Create a target cluster
346-
</Button>
347-
</SplitItem>
344+
345+
{selectedClusterId !== 'all' ? (
346+
<SplitItem>
347+
<Button
348+
variant="primary"
349+
onClick={() => setIsSizingWizardOpen(true)}
350+
>
351+
View target cluster recommendations
352+
</Button>
353+
</SplitItem>
354+
) : null}
348355
</Split>
349356
) : undefined
350357
}
@@ -370,6 +377,14 @@ const Inner: React.FC = () => {
370377
</Content>
371378
</Bullseye>
372379
)}
380+
381+
<ClusterSizingWizard
382+
isOpen={isSizingWizardOpen}
383+
onClose={() => setIsSizingWizardOpen(false)}
384+
clusterName={clusterView.selectionLabel}
385+
clusterId={selectedClusterId}
386+
assessmentId={id || ''}
387+
/>
373388
</AppPage>
374389
);
375390
};

0 commit comments

Comments
 (0)