@@ -188,6 +188,12 @@ pub fn saga_instance_create() -> SagaTemplate<SagaInstanceCreate> {
188188 new_action_noop_undo ( sic_create_network_interfaces) ,
189189 ) ;
190190
191+ template_builder. append (
192+ "disk_requests" ,
193+ "CreateDiskRequests" ,
194+ new_action_noop_undo ( sic_create_disk_requests) ,
195+ ) ;
196+
191197 template_builder. append (
192198 "initial_runtime" ,
193199 "CreateInstanceRecord" ,
@@ -455,6 +461,55 @@ async fn sic_create_network_interfaces_undo(
455461 Ok ( ( ) )
456462}
457463
464+ async fn sic_create_disk_requests (
465+ sagactx : ActionContext < SagaInstanceCreate > ,
466+ ) -> Result < Vec < sled_agent_client:: types:: DiskRequest > , ActionError > {
467+ let osagactx = sagactx. user_data ( ) ;
468+ let datastore = osagactx. datastore ( ) ;
469+ let saga_params = sagactx. saga_params ( ) ;
470+ let opctx = OpContext :: for_saga_action ( & sagactx, & saga_params. serialized_authn ) ;
471+
472+ let authz_project = datastore
473+ . project_lookup_by_id ( saga_params. project_id )
474+ . await
475+ . map_err ( ActionError :: action_failed) ?;
476+
477+ let saga_disks = & sagactx. saga_params ( ) . create_params . disks ;
478+
479+ let mut reqs = Vec :: with_capacity ( saga_disks. len ( ) ) ;
480+
481+ for ( i, disk) in saga_disks. iter ( ) . enumerate ( ) {
482+ match disk {
483+ params:: InstanceDiskAttachment :: Create ( _) => {
484+ // TODO attach disk created in a previous step
485+ todo ! ( ) ;
486+ } ,
487+ params:: InstanceDiskAttachment :: Attach ( instance_disk_attach) => {
488+ let disk_name = & instance_disk_attach. disk ;
489+ let ( _, disk) = datastore. disk_fetch ( & opctx, & authz_project, & db:: model:: Name :: from ( disk_name. clone ( ) ) )
490+ . await
491+ . map_err ( ActionError :: action_failed) ?;
492+
493+ let volume = datastore. volume_get ( disk. id ( ) ) . await . map_err ( ActionError :: action_failed) ?;
494+
495+ reqs. push ( sled_agent_client:: types:: DiskRequest {
496+ name : disk. name ( ) . to_string ( ) ,
497+ slot : sled_agent_client:: types:: Slot ( i as u8 ) ,
498+ // TODO offer ability to attach read-only?
499+ read_only : false ,
500+ device : "nvme" . to_string ( ) ,
501+ // TODO Nexus has to send the highest gen of the volume + 1
502+ gen : 0 ,
503+ volume_construction_request : serde_json:: from_str ( & volume. data ( ) )
504+ . map_err ( |e| e. to_string ( ) ) . map_err ( ActionError :: action_failed) ?,
505+ } ) ;
506+ }
507+ }
508+ }
509+
510+ Ok ( reqs)
511+ }
512+
458513async fn sic_create_instance_record (
459514 sagactx : ActionContext < SagaInstanceCreate > ,
460515) -> Result < InstanceHardware , ActionError > {
@@ -466,6 +521,8 @@ async fn sic_create_instance_record(
466521 let nics = sagactx
467522 . lookup :: < Option < Vec < NetworkInterface > > > ( "network_interfaces" ) ?
468523 . unwrap_or_default ( ) ;
524+ let disks = sagactx
525+ . lookup :: < Vec < sled_agent_client:: types:: DiskRequest > > ( "disk_requests" ) ?;
469526
470527 let runtime = InstanceRuntimeState {
471528 run_state : InstanceState :: Creating ,
@@ -498,9 +555,14 @@ async fn sic_create_instance_record(
498555 Ok ( InstanceHardware {
499556 runtime : instance. runtime ( ) . clone ( ) . into ( ) ,
500557 nics,
501- // TODO disks are currently only attached in instance_set_runtime, but
502- // InstanceCreate could grow to request disks too.
503- disks : vec ! [ ] ,
558+ disks : disks. iter ( ) . map ( |x| propolis_client:: api:: DiskRequest {
559+ name : x. name . clone ( ) ,
560+ slot : propolis_client:: api:: Slot ( x. slot . 0 ) ,
561+ read_only : x. read_only ,
562+ device : x. device . clone ( ) ,
563+ gen : x. gen ,
564+ volume_construction_request : serde_json:: from_str ( & serde_json:: to_string ( & x. volume_construction_request ) . unwrap ( ) ) . unwrap ( ) ,
565+ } ) . collect ( ) ,
504566 } )
505567}
506568
0 commit comments