77use crate :: bootstrap:: params:: StartSledAgentRequest ;
88use crate :: ledger:: { Ledger , Ledgerable } ;
99use crate :: params:: {
10- DatasetRequest , ServiceType , ServiceZoneRequest , ServiceZoneService ,
11- ZoneType ,
10+ DatasetKind , DatasetRequest , ServiceType , ServiceZoneRequest ,
11+ ServiceZoneService , ZoneType ,
1212} ;
1313use crate :: rack_setup:: config:: SetupServiceConfig as Config ;
1414use crate :: storage:: dataset:: DatasetName ;
@@ -89,6 +89,9 @@ pub enum PlanError {
8989
9090 #[ error( "Failed to construct an HTTP client: {0}" ) ]
9191 HttpClient ( reqwest:: Error ) ,
92+
93+ #[ error( "Ran out of sleds / U2 storage pools" ) ]
94+ NotEnoughSleds ,
9295}
9396
9497#[ derive( Clone , Debug , Default , Deserialize , Serialize , PartialEq ) ]
@@ -250,15 +253,13 @@ impl Plan {
250253 . await ?;
251254 let is_scrimlet =
252255 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 ,
255258 subnet,
256259 sled_address,
257260 u2_zpools,
258261 is_scrimlet,
259- addr_alloc : AddressBumpAllocator :: new ( subnet) ,
260- request : Default :: default ( ) ,
261- } )
262+ ) )
262263 } ,
263264 ) )
264265 . await ;
@@ -316,10 +317,8 @@ impl Plan {
316317 DNS_HTTP_PORT ,
317318 )
318319 . 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 ) ?;
323322
324323 sled. request . services . push ( ServiceZoneRequest {
325324 id,
@@ -350,17 +349,13 @@ impl Plan {
350349 dns_builder
351350 . service_backend_zone ( ServiceName :: Cockroach , & zone, port)
352351 . unwrap ( ) ;
352+ let dataset_name =
353+ sled. alloc_from_u2_zpool ( DatasetKind :: CockroachDb ) ?;
353354 sled. request . services . push ( ServiceZoneRequest {
354355 id,
355356 zone_type : ZoneType :: CockroachDb ,
356357 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 } ) ,
364359 gz_addresses : vec ! [ ] ,
365360 services : vec ! [ ServiceZoneService {
366361 id,
@@ -434,9 +429,8 @@ impl Plan {
434429 svc_port_builder. next_dns ( id, & mut services_ip_pool) ?;
435430 let dns_port = omicron_common:: address:: DNS_PORT ;
436431 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) ?;
440434
441435 sled. request . services . push ( ServiceZoneRequest {
442436 id,
@@ -499,17 +493,13 @@ impl Plan {
499493 dns_builder
500494 . service_backend_zone ( ServiceName :: Clickhouse , & zone, port)
501495 . unwrap ( ) ;
496+ let dataset_name =
497+ sled. alloc_from_u2_zpool ( DatasetKind :: Clickhouse ) ?;
502498 sled. request . services . push ( ServiceZoneRequest {
503499 id,
504500 zone_type : ZoneType :: Clickhouse ,
505501 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 } ) ,
513503 gz_addresses : vec ! [ ] ,
514504 services : vec ! [ ServiceZoneService {
515505 id,
@@ -569,7 +559,7 @@ impl Plan {
569559 id,
570560 name : DatasetName :: new (
571561 pool. clone ( ) ,
572- crate :: params :: DatasetKind :: Crucible ,
562+ DatasetKind :: Crucible ,
573563 ) ,
574564 } ) ,
575565 gz_addresses : vec ! [ ] ,
@@ -685,6 +675,9 @@ struct SledInfo {
685675 sled_address : SocketAddrV6 ,
686676 /// the list of zpools on the Sled
687677 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 > > ,
688681 /// whether this Sled is a scrimlet
689682 is_scrimlet : bool ,
690683 /// allocator for addresses in this Sled's subnet
@@ -693,6 +686,58 @@ struct SledInfo {
693686 request : SledRequest ,
694687}
695688
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+
696741struct ServicePortBuilder {
697742 next_snat_ip : Option < IpAddr > ,
698743 next_snat_port : Wrapping < u16 > ,
0 commit comments