@@ -43,7 +43,9 @@ use nexus_db_model::BpTarget;
43
43
use nexus_types:: deployment:: Blueprint ;
44
44
use nexus_types:: deployment:: BlueprintMetadata ;
45
45
use nexus_types:: deployment:: BlueprintTarget ;
46
- use nexus_types:: deployment:: OmicronZonesConfig ;
46
+ use nexus_types:: deployment:: BlueprintZoneDisposition ;
47
+ use nexus_types:: deployment:: BlueprintZoneFilter ;
48
+ use nexus_types:: deployment:: BlueprintZonesConfig ;
47
49
use omicron_common:: api:: external:: DataPageParams ;
48
50
use omicron_common:: api:: external:: Error ;
49
51
use omicron_common:: api:: external:: ListResultVec ;
@@ -105,52 +107,67 @@ impl DataStore {
105
107
// so that we can produce the `Error` type that we want here.
106
108
let row_blueprint = DbBlueprint :: from ( blueprint) ;
107
109
let blueprint_id = row_blueprint. id ;
110
+
111
+ // `Blueprint` stores the policy for each zone next to the zone itself.
112
+ // This would ideally be represented as a simple column in
113
+ // bp_omicron_zone.
114
+ //
115
+ // But historically, `Blueprint` used to store the set of zones in
116
+ // service in a BTreeSet. Since most zones are expected to be in
117
+ // service, we store the set of zones NOT in service (which we expect
118
+ // to be much smaller, often empty). Build that inverted set here.
119
+ //
120
+ // This will soon be replaced with an extra column in the
121
+ // `bp_omicron_zone` table, coupled with other data migrations.
122
+ let omicron_zones_not_in_service = blueprint
123
+ . all_blueprint_zones ( BlueprintZoneFilter :: All )
124
+ . filter_map ( |( _, zone) | {
125
+ // This is going to go away soon when we change the database
126
+ // representation to store the zone disposition enum next to
127
+ // each zone. For now, do an exhaustive match so that this
128
+ // fails if we add a new variant.
129
+ match zone. disposition {
130
+ BlueprintZoneDisposition :: InService => None ,
131
+ BlueprintZoneDisposition :: Quiesced => {
132
+ Some ( BpOmicronZoneNotInService {
133
+ blueprint_id,
134
+ bp_omicron_zone_id : zone. config . id ,
135
+ } )
136
+ }
137
+ }
138
+ } )
139
+ . collect :: < Vec < _ > > ( ) ;
140
+
108
141
let sled_omicron_zones = blueprint
109
- . omicron_zones
142
+ . blueprint_zones
110
143
. iter ( )
111
144
. map ( |( sled_id, zones_config) | {
112
145
BpSledOmicronZones :: new ( blueprint_id, * sled_id, zones_config)
113
146
} )
114
147
. collect :: < Vec < _ > > ( ) ;
115
148
let omicron_zones = blueprint
116
- . omicron_zones
149
+ . blueprint_zones
117
150
. iter ( )
118
151
. flat_map ( |( sled_id, zones_config) | {
119
- zones_config. zones . iter ( ) . map ( |zone| {
152
+ zones_config. zones . iter ( ) . map ( move |zone| {
120
153
BpOmicronZone :: new ( blueprint_id, * sled_id, zone)
121
154
. map_err ( |e| Error :: internal_error ( & format ! ( "{:#}" , e) ) )
122
155
} )
123
156
} )
124
157
. collect :: < Result < Vec < _ > , Error > > ( ) ?;
125
158
let omicron_zone_nics = blueprint
126
- . omicron_zones
159
+ . blueprint_zones
127
160
. values ( )
128
161
. flat_map ( |zones_config| {
129
162
zones_config. zones . iter ( ) . filter_map ( |zone| {
130
163
BpOmicronZoneNic :: new ( blueprint_id, zone)
131
- . with_context ( || format ! ( "zone {:?}" , zone. id) )
164
+ . with_context ( || format ! ( "zone {:?}" , zone. config . id) )
132
165
. map_err ( |e| Error :: internal_error ( & format ! ( "{:#}" , e) ) )
133
166
. transpose ( )
134
167
} )
135
168
} )
136
169
. collect :: < Result < Vec < BpOmicronZoneNic > , _ > > ( ) ?;
137
170
138
- // `Blueprint` stores a set of zones in service, but in the database we
139
- // store the set of zones NOT in service (which we expect to be much
140
- // smaller, often empty). Build that inverted set here.
141
- let omicron_zones_not_in_service = {
142
- let mut zones_not_in_service = Vec :: new ( ) ;
143
- for zone in & omicron_zones {
144
- if !blueprint. zones_in_service . contains ( & zone. id ) {
145
- zones_not_in_service. push ( BpOmicronZoneNotInService {
146
- blueprint_id,
147
- bp_omicron_zone_id : zone. id ,
148
- } ) ;
149
- }
150
- }
151
- zones_not_in_service
152
- } ;
153
-
154
171
// This implementation inserts all records associated with the
155
172
// blueprint in one transaction. This is required: we don't want
156
173
// any planner or executor to see a half-inserted blueprint, nor do we
@@ -273,10 +290,10 @@ impl DataStore {
273
290
// the `OmicronZonesConfig` generation number for each sled that is a
274
291
// part of this blueprint. Construct the BTreeMap we ultimately need,
275
292
// but all the `zones` vecs will be empty until our next query below.
276
- let mut omicron_zones : BTreeMap < Uuid , OmicronZonesConfig > = {
293
+ let mut blueprint_zones : BTreeMap < Uuid , BlueprintZonesConfig > = {
277
294
use db:: schema:: bp_sled_omicron_zones:: dsl;
278
295
279
- let mut omicron_zones = BTreeMap :: new ( ) ;
296
+ let mut blueprint_zones = BTreeMap :: new ( ) ;
280
297
let mut paginator = Paginator :: new ( SQL_BATCH_SIZE ) ;
281
298
while let Some ( p) = paginator. next ( ) {
282
299
let batch = paginated (
@@ -295,9 +312,9 @@ impl DataStore {
295
312
paginator = p. found_batch ( & batch, & |s| s. sled_id ) ;
296
313
297
314
for s in batch {
298
- let old = omicron_zones . insert (
315
+ let old = blueprint_zones . insert (
299
316
s. sled_id ,
300
- OmicronZonesConfig {
317
+ BlueprintZonesConfig {
301
318
generation : * s. generation ,
302
319
zones : Vec :: new ( ) ,
303
320
} ,
@@ -310,7 +327,7 @@ impl DataStore {
310
327
}
311
328
}
312
329
313
- omicron_zones
330
+ blueprint_zones
314
331
} ;
315
332
316
333
// Assemble a mutable map of all the NICs found, by NIC id. As we
@@ -391,11 +408,6 @@ impl DataStore {
391
408
omicron_zones_not_in_service
392
409
} ;
393
410
394
- // Create the in-memory list of zones _in_ service, which we'll
395
- // calculate below as we load zones. (Any zone that isn't present in
396
- // `omicron_zones_not_in_service` is considered in service.)
397
- let mut zones_in_service = BTreeSet :: new ( ) ;
398
-
399
411
// Load all the zones for each sled.
400
412
{
401
413
use db:: schema:: bp_omicron_zone:: dsl;
@@ -438,8 +450,9 @@ impl DataStore {
438
450
} )
439
451
} )
440
452
. transpose ( ) ?;
441
- let sled_zones =
442
- omicron_zones. get_mut ( & z. sled_id ) . ok_or_else ( || {
453
+ let sled_zones = blueprint_zones
454
+ . get_mut ( & z. sled_id )
455
+ . ok_or_else ( || {
443
456
// This error means that we found a row in
444
457
// bp_omicron_zone with no associated record in
445
458
// bp_sled_omicron_zones. This should be
@@ -451,8 +464,14 @@ impl DataStore {
451
464
) )
452
465
} ) ?;
453
466
let zone_id = z. id ;
467
+ let disposition =
468
+ if omicron_zones_not_in_service. remove ( & zone_id) {
469
+ BlueprintZoneDisposition :: Quiesced
470
+ } else {
471
+ BlueprintZoneDisposition :: InService
472
+ } ;
454
473
let zone = z
455
- . into_omicron_zone_config ( nic_row)
474
+ . into_blueprint_zone_config ( nic_row, disposition )
456
475
. with_context ( || {
457
476
format ! ( "zone {:?}: parse from database" , zone_id)
458
477
} )
@@ -463,14 +482,6 @@ impl DataStore {
463
482
) )
464
483
} ) ?;
465
484
sled_zones. zones . push ( zone) ;
466
-
467
- // If we can remove `zone_id` from
468
- // `omicron_zones_not_in_service`, then the zone is not in
469
- // service. Otherwise, add it to the list of in-service
470
- // zones.
471
- if !omicron_zones_not_in_service. remove ( & zone_id) {
472
- zones_in_service. insert ( zone_id) ;
473
- }
474
485
}
475
486
}
476
487
}
@@ -488,8 +499,7 @@ impl DataStore {
488
499
489
500
Ok ( Blueprint {
490
501
id : blueprint_id,
491
- omicron_zones,
492
- zones_in_service,
502
+ blueprint_zones,
493
503
parent_blueprint_id,
494
504
internal_dns_version,
495
505
external_dns_version,
@@ -1359,10 +1369,8 @@ mod tests {
1359
1369
[ blueprint1. id]
1360
1370
) ;
1361
1371
1362
- // There ought to be no sleds or zones in service, and no parent
1363
- // blueprint.
1364
- assert_eq ! ( blueprint1. omicron_zones. len( ) , 0 ) ;
1365
- assert_eq ! ( blueprint1. zones_in_service. len( ) , 0 ) ;
1372
+ // There ought to be no sleds or zones, and no parent blueprint.
1373
+ assert_eq ! ( blueprint1. blueprint_zones. len( ) , 0 ) ;
1366
1374
assert_eq ! ( blueprint1. parent_blueprint_id, None ) ;
1367
1375
1368
1376
// Trying to insert the same blueprint again should fail.
@@ -1407,20 +1415,17 @@ mod tests {
1407
1415
) ;
1408
1416
1409
1417
// Check the number of blueprint elements against our collection.
1410
- assert_eq ! ( blueprint1. omicron_zones . len( ) , policy. sleds. len( ) ) ;
1418
+ assert_eq ! ( blueprint1. blueprint_zones . len( ) , policy. sleds. len( ) ) ;
1411
1419
assert_eq ! (
1412
- blueprint1. omicron_zones . len( ) ,
1420
+ blueprint1. blueprint_zones . len( ) ,
1413
1421
collection. omicron_zones. len( )
1414
1422
) ;
1415
1423
assert_eq ! (
1416
1424
blueprint1. all_omicron_zones( ) . count( ) ,
1417
1425
collection. all_omicron_zones( ) . count( )
1418
1426
) ;
1419
1427
// All zones should be in service.
1420
- assert_eq ! (
1421
- blueprint1. zones_in_service. len( ) ,
1422
- blueprint1. all_omicron_zones( ) . count( )
1423
- ) ;
1428
+ assert_all_zones_in_service ( & blueprint1) ;
1424
1429
assert_eq ! ( blueprint1. parent_blueprint_id, None ) ;
1425
1430
1426
1431
// Set blueprint1 as the current target, and ensure that we cannot
@@ -1489,19 +1494,16 @@ mod tests {
1489
1494
1490
1495
// Check that we added the new sled and its zones.
1491
1496
assert_eq ! (
1492
- blueprint1. omicron_zones . len( ) + 1 ,
1493
- blueprint2. omicron_zones . len( )
1497
+ blueprint1. blueprint_zones . len( ) + 1 ,
1498
+ blueprint2. blueprint_zones . len( )
1494
1499
) ;
1495
1500
assert_eq ! (
1496
1501
blueprint1. all_omicron_zones( ) . count( ) + num_new_sled_zones,
1497
1502
blueprint2. all_omicron_zones( ) . count( )
1498
1503
) ;
1499
1504
1500
1505
// All zones should be in service.
1501
- assert_eq ! (
1502
- blueprint2. zones_in_service. len( ) ,
1503
- blueprint2. all_omicron_zones( ) . count( )
1504
- ) ;
1506
+ assert_all_zones_in_service ( & blueprint2) ;
1505
1507
assert_eq ! ( blueprint2. parent_blueprint_id, Some ( blueprint1. id) ) ;
1506
1508
1507
1509
// Check that we can write it to the DB and read it back.
@@ -1869,4 +1871,18 @@ mod tests {
1869
1871
db. cleanup ( ) . await . unwrap ( ) ;
1870
1872
logctx. cleanup_successful ( ) ;
1871
1873
}
1874
+
1875
+ fn assert_all_zones_in_service ( blueprint : & Blueprint ) {
1876
+ let not_in_service = blueprint
1877
+ . all_blueprint_zones ( BlueprintZoneFilter :: All )
1878
+ . filter ( |( _, z) | {
1879
+ z. disposition != BlueprintZoneDisposition :: InService
1880
+ } )
1881
+ . collect :: < Vec < _ > > ( ) ;
1882
+ assert ! (
1883
+ not_in_service. is_empty( ) ,
1884
+ "expected all zones to be in service, \
1885
+ found these zones not in service: {not_in_service:?}"
1886
+ ) ;
1887
+ }
1872
1888
}
0 commit comments