7
7
use crate :: bootstrap:: params:: StartSledAgentRequest ;
8
8
use crate :: ledger:: { Ledger , Ledgerable } ;
9
9
use crate :: params:: {
10
- DatasetRequest , ServiceType , ServiceZoneRequest , ServiceZoneService ,
11
- ZoneType ,
10
+ DatasetKind , DatasetRequest , ServiceType , ServiceZoneRequest ,
11
+ ServiceZoneService , ZoneType ,
12
12
} ;
13
13
use crate :: rack_setup:: config:: SetupServiceConfig as Config ;
14
14
use crate :: storage:: dataset:: DatasetName ;
@@ -89,6 +89,9 @@ pub enum PlanError {
89
89
90
90
#[ error( "Failed to construct an HTTP client: {0}" ) ]
91
91
HttpClient ( reqwest:: Error ) ,
92
+
93
+ #[ error( "Ran out of sleds / U2 storage pools" ) ]
94
+ NotEnoughSleds ,
92
95
}
93
96
94
97
#[ derive( Clone , Debug , Default , Deserialize , Serialize , PartialEq ) ]
@@ -250,15 +253,13 @@ impl Plan {
250
253
. await ?;
251
254
let is_scrimlet =
252
255
Self :: is_sled_scrimlet ( log, sled_address) . await ?;
253
- Ok ( SledInfo {
254
- sled_id : sled_request. id ,
256
+ Ok ( SledInfo :: new (
257
+ sled_request. id ,
255
258
subnet,
256
259
sled_address,
257
260
u2_zpools,
258
261
is_scrimlet,
259
- addr_alloc : AddressBumpAllocator :: new ( subnet) ,
260
- request : Default :: default ( ) ,
261
- } )
262
+ ) )
262
263
} ,
263
264
) )
264
265
. await ;
@@ -316,10 +317,8 @@ impl Plan {
316
317
DNS_HTTP_PORT ,
317
318
)
318
319
. unwrap ( ) ;
319
- let dataset_name = DatasetName :: new (
320
- sled. u2_zpools [ 0 ] . clone ( ) ,
321
- crate :: params:: DatasetKind :: InternalDns ,
322
- ) ;
320
+ let dataset_name =
321
+ sled. alloc_from_u2_zpool ( DatasetKind :: InternalDns ) ?;
323
322
324
323
sled. request . services . push ( ServiceZoneRequest {
325
324
id,
@@ -350,17 +349,13 @@ impl Plan {
350
349
dns_builder
351
350
. service_backend_zone ( ServiceName :: Cockroach , & zone, port)
352
351
. unwrap ( ) ;
352
+ let dataset_name =
353
+ sled. alloc_from_u2_zpool ( DatasetKind :: CockroachDb ) ?;
353
354
sled. request . services . push ( ServiceZoneRequest {
354
355
id,
355
356
zone_type : ZoneType :: CockroachDb ,
356
357
addresses : vec ! [ ip] ,
357
- dataset : Some ( DatasetRequest {
358
- id,
359
- name : DatasetName :: new (
360
- sled. u2_zpools [ 0 ] . clone ( ) ,
361
- crate :: params:: DatasetKind :: CockroachDb ,
362
- ) ,
363
- } ) ,
358
+ dataset : Some ( DatasetRequest { id, name : dataset_name } ) ,
364
359
gz_addresses : vec ! [ ] ,
365
360
services : vec ! [ ServiceZoneService {
366
361
id,
@@ -434,9 +429,8 @@ impl Plan {
434
429
svc_port_builder. next_dns ( id, & mut services_ip_pool) ?;
435
430
let dns_port = omicron_common:: address:: DNS_PORT ;
436
431
let dns_address = SocketAddr :: new ( external_ip, dns_port) ;
437
- let dataset_kind = crate :: params:: DatasetKind :: ExternalDns ;
438
- let dataset_name =
439
- DatasetName :: new ( sled. u2_zpools [ 0 ] . clone ( ) , dataset_kind) ;
432
+ let dataset_kind = DatasetKind :: ExternalDns ;
433
+ let dataset_name = sled. alloc_from_u2_zpool ( dataset_kind) ?;
440
434
441
435
sled. request . services . push ( ServiceZoneRequest {
442
436
id,
@@ -499,17 +493,13 @@ impl Plan {
499
493
dns_builder
500
494
. service_backend_zone ( ServiceName :: Clickhouse , & zone, port)
501
495
. unwrap ( ) ;
496
+ let dataset_name =
497
+ sled. alloc_from_u2_zpool ( DatasetKind :: Clickhouse ) ?;
502
498
sled. request . services . push ( ServiceZoneRequest {
503
499
id,
504
500
zone_type : ZoneType :: Clickhouse ,
505
501
addresses : vec ! [ ip] ,
506
- dataset : Some ( DatasetRequest {
507
- id,
508
- name : DatasetName :: new (
509
- sled. u2_zpools [ 0 ] . clone ( ) ,
510
- crate :: params:: DatasetKind :: Clickhouse ,
511
- ) ,
512
- } ) ,
502
+ dataset : Some ( DatasetRequest { id, name : dataset_name } ) ,
513
503
gz_addresses : vec ! [ ] ,
514
504
services : vec ! [ ServiceZoneService {
515
505
id,
@@ -569,7 +559,7 @@ impl Plan {
569
559
id,
570
560
name : DatasetName :: new (
571
561
pool. clone ( ) ,
572
- crate :: params :: DatasetKind :: Crucible ,
562
+ DatasetKind :: Crucible ,
573
563
) ,
574
564
} ) ,
575
565
gz_addresses : vec ! [ ] ,
@@ -685,6 +675,9 @@ struct SledInfo {
685
675
sled_address : SocketAddrV6 ,
686
676
/// the list of zpools on the Sled
687
677
u2_zpools : Vec < ZpoolName > ,
678
+ /// spreads components across a Sled's zpools
679
+ u2_zpool_allocators :
680
+ HashMap < DatasetKind , Box < dyn Iterator < Item = usize > + Send + Sync > > ,
688
681
/// whether this Sled is a scrimlet
689
682
is_scrimlet : bool ,
690
683
/// allocator for addresses in this Sled's subnet
@@ -693,6 +686,58 @@ struct SledInfo {
693
686
request : SledRequest ,
694
687
}
695
688
689
+ impl SledInfo {
690
+ fn new (
691
+ sled_id : Uuid ,
692
+ subnet : Ipv6Subnet < SLED_PREFIX > ,
693
+ sled_address : SocketAddrV6 ,
694
+ u2_zpools : Vec < ZpoolName > ,
695
+ is_scrimlet : bool ,
696
+ ) -> SledInfo {
697
+ SledInfo {
698
+ sled_id,
699
+ subnet,
700
+ sled_address,
701
+ u2_zpools,
702
+ u2_zpool_allocators : HashMap :: new ( ) ,
703
+ is_scrimlet,
704
+ addr_alloc : AddressBumpAllocator :: new ( subnet) ,
705
+ request : Default :: default ( ) ,
706
+ }
707
+ }
708
+
709
+ /// Allocates a dataset of the specified type from one of the U.2 pools on
710
+ /// this Sled
711
+ fn alloc_from_u2_zpool (
712
+ & mut self ,
713
+ kind : DatasetKind ,
714
+ ) -> Result < DatasetName , PlanError > {
715
+ // We have two goals here:
716
+ //
717
+ // - For datasets of different types, they should be able to use the
718
+ // same pool.
719
+ //
720
+ // - For datasets of the same type, they must be on separate pools. We
721
+ // want to fail explicitly if we can't do that (which might happen if
722
+ // we've tried to allocate more datasets than we have pools). Sled
723
+ // Agent does not support having multiple datasets of some types
724
+ // (e.g., cockroachdb) on the same pool.
725
+ //
726
+ // To achieve this, we maintain one iterator per dataset kind that
727
+ // enumerates the valid zpool indexes.
728
+ let allocator = self
729
+ . u2_zpool_allocators
730
+ . entry ( kind. clone ( ) )
731
+ . or_insert_with ( || Box :: new ( 0 ..self . u2_zpools . len ( ) ) ) ;
732
+ match allocator. next ( ) {
733
+ None => Err ( PlanError :: NotEnoughSleds ) ,
734
+ Some ( which_zpool) => {
735
+ Ok ( DatasetName :: new ( self . u2_zpools [ which_zpool] . clone ( ) , kind) )
736
+ }
737
+ }
738
+ }
739
+ }
740
+
696
741
struct ServicePortBuilder {
697
742
next_snat_ip : Option < IpAddr > ,
698
743
next_snat_port : Wrapping < u16 > ,
0 commit comments