feat(dedicated): add PCIe NVMe Add-in group (Sabrent adapter combos)
New separate-category config group for stacking high-performance
M.2 NVMe storage in any free PCIe slot, on top of the bay-attached
drives. Doesn't compete with bay drives — uses PCIe slots
independently.
Two adapter paths:
- Sabrent EC-PCIE (\$17.98) — single M.2 NVMe in any PCIe x4+ slot
- Sabrent EC-P4BF (\$99.99) — 4× M.2 NVMe in one PCIe 4.0 x16 slot,
requires PCIe bifurcation support (R440/R540/R640/R740 verified;
R740xd LFF rear PCIe may need check at build time)
10 curated combos + "None" default. Pricing: 12-month payback ×
1.5x markup on (Sabrent retail drive cost + adapter cost).
Single-drive (EC-PCIE):
1× 1 TB Rocket 4 Plus +\$35
1× 2 TB Rocket 4 Plus +\$40
1× 4 TB Rocket 4 Plus +\$115
1× 8 TB Rocket 4 Plus +\$465
1× 2 TB Rocket 5 Gen5 +\$65
1× 4 TB Rocket 5 Gen5 +\$180
4-drive bifurcation (EC-P4BF):
4× 1 TB Rocket 4 Plus +\$140
4× 2 TB Rocket 4 Plus +\$160
4× 4 TB Rocket 4 Plus +\$455
4× 8 TB Rocket 4 Plus +\$1,865 (extreme density, ~32 TB raw)
Drive prices scraped from Sabrent's Shopify product JSON
(/products/{slug}.json) on 2026-04-26.
Attached to LFF + SFF chassis (R440 / R540 / R640 / R740 /
R740xd SFF / R740xd LFF). Skip on R640 NVMe / R740xd NVMe — those
chassis route all PCIe lanes to the U.2 backplane, so no slots
are free for add-in cards.
Tests: bumped group count to 11; added a test verifying chassis
attachment correctness. 15/15 pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -533,6 +533,52 @@ class ConfigOptionSeeder extends Seeder
|
|||||||
|
|
||||||
$gen14PrivateNet->plans()->syncWithoutDetaching($gen14AllPlans);
|
$gen14PrivateNet->plans()->syncWithoutDetaching($gen14AllPlans);
|
||||||
|
|
||||||
|
// ─── Dedicated 14th Gen — PCIe NVMe Add-in ──────────────────────
|
||||||
|
// Separate category from bay-attached drives — uses empty PCIe slots
|
||||||
|
// via Sabrent EC-PCIE (single-drive) or EC-P4BF (4-drive bifurcation
|
||||||
|
// x16) adapter cards. Customers can stack this on top of bay drives
|
||||||
|
// for fast scratch / cache / metadata storage.
|
||||||
|
// Pricing: 12-month payback × 1.5x markup on (Sabrent retail drive
|
||||||
|
// cost + adapter cost). Drive prices scraped from sabrent.com on
|
||||||
|
// 2026-04-26: Rocket 4 Plus 1TB \$249.99 / 2TB \$289.99 / 4TB \$879.99
|
||||||
|
// / 8TB \$3,699.99; Rocket 5 Gen5 1TB \$400 / 2TB \$499.99 / 4TB \$1,400.
|
||||||
|
// Adapters: EC-PCIE \$17.98, EC-P4BF \$99.99.
|
||||||
|
$gen14PciNvme = PlanConfigGroup::updateOrCreate(
|
||||||
|
['name' => 'Dedicated 14th Gen — PCIe NVMe Add-in'],
|
||||||
|
[
|
||||||
|
'description' => 'Add high-performance M.2 NVMe storage in any free PCIe slot via Sabrent adapter cards. Single-drive (EC-PCIE) fits any x4+ slot; 4-drive (EC-P4BF) needs an x16 slot with bifurcation support.',
|
||||||
|
'mode' => 'preset',
|
||||||
|
'service_type' => 'dedicated',
|
||||||
|
'is_active' => true,
|
||||||
|
'sort_order' => 47,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
$gen14PciNvmeOption = $this->seedRadioOption($gen14PciNvme, 'PCIe NVMe Add-in', false, 1);
|
||||||
|
$this->seedValues($gen14PciNvmeOption, [
|
||||||
|
['label' => 'No PCIe NVMe add-in', 'value' => 'none', 'monthly' => 0, 'is_default' => true],
|
||||||
|
// Single-drive via EC-PCIE adapter (Gen4)
|
||||||
|
['label' => '1× 1 TB Rocket 4 Plus M.2 NVMe (EC-PCIE)', 'value' => '1x1tb-r4p-pcie', 'monthly' => 35.00],
|
||||||
|
['label' => '1× 2 TB Rocket 4 Plus M.2 NVMe (EC-PCIE)', 'value' => '1x2tb-r4p-pcie', 'monthly' => 40.00],
|
||||||
|
['label' => '1× 4 TB Rocket 4 Plus M.2 NVMe (EC-PCIE)', 'value' => '1x4tb-r4p-pcie', 'monthly' => 115.00],
|
||||||
|
['label' => '1× 8 TB Rocket 4 Plus M.2 NVMe (EC-PCIE)', 'value' => '1x8tb-r4p-pcie', 'monthly' => 465.00],
|
||||||
|
// Single-drive Gen5 via EC-PCIE
|
||||||
|
['label' => '1× 2 TB Rocket 5 Gen5 M.2 NVMe (EC-PCIE)', 'value' => '1x2tb-r5-pcie', 'monthly' => 65.00],
|
||||||
|
['label' => '1× 4 TB Rocket 5 Gen5 M.2 NVMe (EC-PCIE)', 'value' => '1x4tb-r5-pcie', 'monthly' => 180.00],
|
||||||
|
// 4-drive bifurcation via EC-P4BF (requires PCIe x16 + bifurcation)
|
||||||
|
['label' => '4× 1 TB Rocket 4 Plus M.2 NVMe (EC-P4BF)', 'value' => '4x1tb-r4p-p4bf', 'monthly' => 140.00],
|
||||||
|
['label' => '4× 2 TB Rocket 4 Plus M.2 NVMe (EC-P4BF)', 'value' => '4x2tb-r4p-p4bf', 'monthly' => 160.00],
|
||||||
|
['label' => '4× 4 TB Rocket 4 Plus M.2 NVMe (EC-P4BF)', 'value' => '4x4tb-r4p-p4bf', 'monthly' => 455.00],
|
||||||
|
['label' => '4× 8 TB Rocket 4 Plus M.2 NVMe (EC-P4BF)', 'value' => '4x8tb-r4p-p4bf', 'monthly' => 1865.00],
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Attach to LFF + SFF chassis (have free PCIe slots). Skip on the
|
||||||
|
// U.2 NVMe-native chassis since they don't have PCIe slots free
|
||||||
|
// for add-in cards (lanes are routed to the U.2 backplane).
|
||||||
|
$gen14PciNvmeSlugs = ['r440-4lff', 'r540-8lff', 'r640-8sff', 'r740-16sff', 'r740xd-24sff', 'r740xd-12lff'];
|
||||||
|
$gen14PciNvmePlans = Plan::query()->whereIn('slug', $gen14PciNvmeSlugs)->pluck('id');
|
||||||
|
$gen14PciNvme->plans()->syncWithoutDetaching($gen14PciNvmePlans);
|
||||||
|
|
||||||
// ─── Dedicated 14th Gen — LFF Drive Bays ────────────────────────
|
// ─── Dedicated 14th Gen — LFF Drive Bays ────────────────────────
|
||||||
$gen14Lff = PlanConfigGroup::updateOrCreate(
|
$gen14Lff = PlanConfigGroup::updateOrCreate(
|
||||||
['name' => 'Dedicated 14th Gen — LFF Drive Bays'],
|
['name' => 'Dedicated 14th Gen — LFF Drive Bays'],
|
||||||
|
|||||||
@@ -148,13 +148,13 @@ test('1.5 TB RAM is filtered out for chassis with 16 DIMM slots', function (): v
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test('seeder creates the 10 dedicated 14th-gen config groups', function (): void {
|
test('seeder creates the 11 dedicated 14th-gen config groups', function (): void {
|
||||||
$names = PlanConfigGroup::query()
|
$names = PlanConfigGroup::query()
|
||||||
->where('name', 'like', 'Dedicated 14th Gen%')
|
->where('name', 'like', 'Dedicated 14th Gen%')
|
||||||
->pluck('name')
|
->pluck('name')
|
||||||
->all();
|
->all();
|
||||||
|
|
||||||
expect(count($names))->toBe(10);
|
expect(count($names))->toBe(11);
|
||||||
expect($names)->toContain('Dedicated 14th Gen — CPU Upgrade');
|
expect($names)->toContain('Dedicated 14th Gen — CPU Upgrade');
|
||||||
expect($names)->toContain('Dedicated 14th Gen — CPU Upgrade (R740xd)');
|
expect($names)->toContain('Dedicated 14th Gen — CPU Upgrade (R740xd)');
|
||||||
expect($names)->toContain('Dedicated 14th Gen — RAM Upgrade');
|
expect($names)->toContain('Dedicated 14th Gen — RAM Upgrade');
|
||||||
@@ -162,11 +162,26 @@ test('seeder creates the 10 dedicated 14th-gen config groups', function (): void
|
|||||||
expect($names)->toContain('Dedicated 14th Gen — Bandwidth');
|
expect($names)->toContain('Dedicated 14th Gen — Bandwidth');
|
||||||
expect($names)->toContain('Dedicated 14th Gen — IPv4 Block');
|
expect($names)->toContain('Dedicated 14th Gen — IPv4 Block');
|
||||||
expect($names)->toContain('Dedicated 14th Gen — Private Networking');
|
expect($names)->toContain('Dedicated 14th Gen — Private Networking');
|
||||||
|
expect($names)->toContain('Dedicated 14th Gen — PCIe NVMe Add-in');
|
||||||
expect($names)->toContain('Dedicated 14th Gen — LFF Drive Bays');
|
expect($names)->toContain('Dedicated 14th Gen — LFF Drive Bays');
|
||||||
expect($names)->toContain('Dedicated 14th Gen — SFF Drive Bays');
|
expect($names)->toContain('Dedicated 14th Gen — SFF Drive Bays');
|
||||||
expect($names)->toContain('Dedicated 14th Gen — NVMe Drive Bays');
|
expect($names)->toContain('Dedicated 14th Gen — NVMe Drive Bays');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('PCIe NVMe Add-in attaches only to LFF + SFF chassis, not pure-NVMe', function (): void {
|
||||||
|
foreach (['r440-4lff', 'r540-8lff', 'r640-8sff', 'r740-16sff', 'r740xd-24sff', 'r740xd-12lff'] as $slug) {
|
||||||
|
$response = $this->get("/dedicated-servers/{$slug}");
|
||||||
|
$names = collect($response->viewData('page')['props']['configGroups'])->pluck('name');
|
||||||
|
expect($names)->toContain('Dedicated 14th Gen — PCIe NVMe Add-in');
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (['r640-10nvme', 'r740xd-24nvme'] as $slug) {
|
||||||
|
$response = $this->get("/dedicated-servers/{$slug}");
|
||||||
|
$names = collect($response->viewData('page')['props']['configGroups'])->pluck('name');
|
||||||
|
expect($names)->not->toContain('Dedicated 14th Gen — PCIe NVMe Add-in');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
test('drive bay options are filtered to those that fit chassis bay count', function (): void {
|
test('drive bay options are filtered to those that fit chassis bay count', function (): void {
|
||||||
// R440 has 4 LFF bays — combos requiring more bays should be hidden.
|
// R440 has 4 LFF bays — combos requiring more bays should be hidden.
|
||||||
$r440Response = $this->get('/dedicated-servers/r440-4lff');
|
$r440Response = $this->get('/dedicated-servers/r440-4lff');
|
||||||
|
|||||||
Reference in New Issue
Block a user