@@ -25,7 +25,7 @@ use omicron_common::api::external;
2525use omicron_common:: api:: external:: http_pagination:: PaginatedBy ;
2626use omicron_common:: api:: external:: {
2727 CreateResult , DeleteResult , Error , ListResultVec , LookupResult , NameOrId ,
28- ResourceType ,
28+ ResourceType , UpdateResult ,
2929} ;
3030use ref_cast:: RefCast ;
3131use sled_agent_types:: early_networking:: RouterPeerType ;
@@ -225,6 +225,181 @@ impl DataStore {
225225 } )
226226 }
227227
228+ pub async fn bgp_config_update (
229+ & self ,
230+ opctx : & OpContext ,
231+ sel : & networking:: BgpConfigSelector ,
232+ update : & networking:: BgpConfigUpdate ,
233+ ) -> UpdateResult < BgpConfig > {
234+ use nexus_db_schema:: schema:: bgp_config;
235+ use nexus_db_schema:: schema:: bgp_config:: dsl as bgp_config_dsl;
236+ use nexus_db_schema:: schema:: {
237+ bgp_announce_set, bgp_announce_set:: dsl as announce_set_dsl,
238+ } ;
239+
240+ let err = OptionalError :: new ( ) ;
241+ let conn = self . pool_connection_authorized ( opctx) . await ?;
242+ self . transaction_retry_wrapper ( "bgp_config_update" )
243+ . transaction ( & conn, |conn| {
244+ let err = err. clone ( ) ;
245+ async move {
246+ let name_or_id = sel. name_or_id . clone ( ) ;
247+
248+ // Look up the existing config
249+ let existing: BgpConfig = match name_or_id {
250+ NameOrId :: Id ( id) => bgp_config_dsl:: bgp_config
251+ . filter ( bgp_config:: id. eq ( id) )
252+ . filter ( bgp_config:: time_deleted. is_null ( ) )
253+ . select ( BgpConfig :: as_select ( ) )
254+ . limit ( 1 )
255+ . first_async :: < BgpConfig > ( & conn)
256+ . await
257+ . map_err ( |e| {
258+ let msg = "failed to lookup bgp config by id" ;
259+ error ! ( opctx. log, "{msg}" ; "error" => ?e) ;
260+ match e {
261+ diesel:: result:: Error :: NotFound => err
262+ . bail ( Error :: not_found_by_id (
263+ ResourceType :: BgpConfig ,
264+ & id,
265+ ) ) ,
266+ _ => err. bail ( Error :: internal_error ( msg) ) ,
267+ }
268+ } ) ?,
269+ NameOrId :: Name ( name) => bgp_config_dsl:: bgp_config
270+ . filter ( bgp_config:: name. eq ( name. to_string ( ) ) )
271+ . filter ( bgp_config:: time_deleted. is_null ( ) )
272+ . select ( BgpConfig :: as_select ( ) )
273+ . limit ( 1 )
274+ . first_async :: < BgpConfig > ( & conn)
275+ . await
276+ . map_err ( |e| {
277+ let msg = "failed to lookup bgp config by name" ;
278+ error ! ( opctx. log, "{msg}" ; "error" => ?e) ;
279+ match e {
280+ diesel:: result:: Error :: NotFound => err
281+ . bail ( Error :: not_found_by_name (
282+ ResourceType :: BgpConfig ,
283+ & name,
284+ ) ) ,
285+ _ => err. bail ( Error :: internal_error ( msg) ) ,
286+ }
287+ } ) ?,
288+ } ;
289+
290+ // Resolve bgp_announce_set_id if an update was requested
291+ let new_bgp_announce_set_id = match update
292+ . bgp_announce_set_id
293+ . clone ( )
294+ {
295+ None => existing. bgp_announce_set_id ,
296+ Some ( NameOrId :: Name ( name) ) => {
297+ announce_set_dsl:: bgp_announce_set
298+ . filter (
299+ bgp_announce_set:: time_deleted. is_null ( ) ,
300+ )
301+ . filter (
302+ bgp_announce_set:: name. eq ( name. to_string ( ) ) ,
303+ )
304+ . select ( bgp_announce_set:: id)
305+ . limit ( 1 )
306+ . first_async :: < Uuid > ( & conn)
307+ . await
308+ . map_err ( |e| {
309+ let msg =
310+ "failed to lookup announce set by name" ;
311+ error ! ( opctx. log, "{msg}" ; "error" => ?e) ;
312+ match e {
313+ diesel:: result:: Error :: NotFound => err
314+ . bail ( Error :: not_found_by_name (
315+ ResourceType :: BgpAnnounceSet ,
316+ & name,
317+ ) ) ,
318+ _ => {
319+ err. bail ( Error :: internal_error ( msg) )
320+ }
321+ }
322+ } ) ?
323+ }
324+ Some ( NameOrId :: Id ( id) ) => {
325+ announce_set_dsl:: bgp_announce_set
326+ . filter (
327+ bgp_announce_set:: time_deleted. is_null ( ) ,
328+ )
329+ . filter ( bgp_announce_set:: id. eq ( id) )
330+ . select ( bgp_announce_set:: id)
331+ . limit ( 1 )
332+ . first_async :: < Uuid > ( & conn)
333+ . await
334+ . map_err ( |e| {
335+ let msg =
336+ "failed to lookup announce set by id" ;
337+ error ! ( opctx. log, "{msg}" ; "error" => ?e) ;
338+ match e {
339+ diesel:: result:: Error :: NotFound => err
340+ . bail ( Error :: not_found_by_id (
341+ ResourceType :: BgpAnnounceSet ,
342+ & id,
343+ ) ) ,
344+ _ => {
345+ err. bail ( Error :: internal_error ( msg) )
346+ }
347+ }
348+ } ) ?
349+ }
350+ } ;
351+
352+ let new_name = update
353+ . name
354+ . as_ref ( )
355+ . map ( |n| n. to_string ( ) )
356+ . unwrap_or_else ( || existing. name ( ) . to_string ( ) ) ;
357+ let new_description = update
358+ . description
359+ . clone ( )
360+ . unwrap_or_else ( || existing. description ( ) . to_string ( ) ) ;
361+ let new_max_paths = update
362+ . max_paths
363+ . map ( |m| m. as_u8 ( ) )
364+ . unwrap_or ( * existing. max_paths ) ;
365+
366+ diesel:: update ( bgp_config_dsl:: bgp_config)
367+ . filter ( bgp_config_dsl:: id. eq ( existing. id ( ) ) )
368+ . set ( (
369+ bgp_config_dsl:: time_modified. eq ( Utc :: now ( ) ) ,
370+ bgp_config_dsl:: name. eq ( new_name) ,
371+ bgp_config_dsl:: description. eq ( new_description) ,
372+ bgp_config_dsl:: bgp_announce_set_id
373+ . eq ( new_bgp_announce_set_id) ,
374+ bgp_config_dsl:: max_paths
375+ . eq ( i16:: from ( new_max_paths) ) ,
376+ ) )
377+ . returning ( BgpConfig :: as_returning ( ) )
378+ . get_result_async ( & conn)
379+ . await
380+ . map_err ( |e| {
381+ let msg = "bgp_config_update failed" ;
382+ error ! ( opctx. log, "{msg}" ; "error" => ?e) ;
383+ err. bail ( public_error_from_diesel (
384+ e,
385+ ErrorHandler :: Server ,
386+ ) )
387+ } )
388+ }
389+ } )
390+ . await
391+ . map_err ( |e| {
392+ let msg = "bgp_config_update failed" ;
393+ if let Some ( err) = err. take ( ) {
394+ error ! ( opctx. log, "{msg}" ; "error" => ?err) ;
395+ err
396+ } else {
397+ error ! ( opctx. log, "{msg}" ; "error" => ?e) ;
398+ public_error_from_diesel ( e, ErrorHandler :: Server )
399+ }
400+ } )
401+ }
402+
228403 pub async fn bgp_config_delete (
229404 & self ,
230405 opctx : & OpContext ,
0 commit comments