@@ -24,9 +24,9 @@ use nexus_sled_agent_shared::inventory::ConfigReconcilerInventoryResult;
24
24
use nexus_sled_agent_shared:: inventory:: OmicronZoneConfig ;
25
25
use nexus_sled_agent_shared:: inventory:: OmicronZoneImageSource ;
26
26
use nexus_sled_agent_shared:: inventory:: OmicronZoneType ;
27
+ use ntp_admin_client:: types:: TimeSync ;
27
28
use omicron_common:: address:: Ipv6Subnet ;
28
29
use omicron_uuid_kinds:: OmicronZoneUuid ;
29
- use sled_agent_types:: time_sync:: TimeSync ;
30
30
use sled_agent_types:: zone_bundle:: ZoneBundleCause ;
31
31
use sled_agent_types:: zone_images:: ResolverStatus ;
32
32
use sled_storage:: config:: MountConfig ;
@@ -36,10 +36,7 @@ use slog::warn;
36
36
use slog_error_chain:: InlineErrorChain ;
37
37
use std:: borrow:: Cow ;
38
38
use std:: collections:: BTreeMap ;
39
- use std:: net:: IpAddr ;
40
- use std:: net:: Ipv6Addr ;
41
39
use std:: num:: NonZeroUsize ;
42
- use std:: str:: FromStr as _;
43
40
use std:: sync:: Arc ;
44
41
45
42
use super :: OmicronDatasets ;
@@ -70,6 +67,8 @@ pub enum TimeSyncError {
70
67
NoRunningNtpZone ,
71
68
#[ error( "multiple running NTP zones - this should never happen!" ) ]
72
69
MultipleRunningNtpZones ,
70
+ #[ error( "failed to communicate with NTP admin server" ) ]
71
+ NtpAdmin ( #[ from] ntp_admin_client:: Error < ntp_admin_client:: types:: Error > ) ,
73
72
#[ error( "failed to execute chronyc within NTP zone" ) ]
74
73
ExecuteChronyc ( #[ source] RunCommandError ) ,
75
74
#[ error(
@@ -465,10 +464,10 @@ impl OmicronZones {
465
464
}
466
465
467
466
/// Check the timesync status from a running NTP zone (if it exists)
468
- pub ( super ) async fn check_timesync ( & self ) -> TimeSyncStatus {
467
+ pub ( super ) async fn check_timesync ( & self , log : & Logger ) -> TimeSyncStatus {
469
468
match & self . timesync_config {
470
469
TimeSyncConfig :: Normal => {
471
- match self . timesync_status_from_ntp_zone ( ) . await {
470
+ match self . timesync_status_from_ntp_zone ( log ) . await {
472
471
Ok ( timesync) => TimeSyncStatus :: TimeSync ( timesync) ,
473
472
Err ( err) => {
474
473
TimeSyncStatus :: FailedToGetSyncStatus ( Arc :: new ( err) )
@@ -481,83 +480,45 @@ impl OmicronZones {
481
480
482
481
async fn timesync_status_from_ntp_zone (
483
482
& self ,
483
+ log : & Logger ,
484
484
) -> Result < TimeSync , TimeSyncError > {
485
485
// Get the one and only running NTP zone, or return an error.
486
- let mut running_ntp_zones = self . zones . iter ( ) . filter_map ( |z| {
486
+ let mut ntp_admin_addresses = self . zones . iter ( ) . filter_map ( |z| {
487
487
if !z. config . zone_type . is_ntp ( ) {
488
488
return None ;
489
489
}
490
+ // TODO(https://github.com/oxidecomputer/omicron/issues/6796):
491
+ //
492
+ // We could avoid hard-coding the port here if the zone was fully
493
+ // specified to include both NTP and Admin server addresses.
494
+ let mut addr = match z. config . zone_type {
495
+ OmicronZoneType :: BoundaryNtp { address, .. } => address,
496
+ OmicronZoneType :: InternalNtp { address, .. } => address,
497
+ _ => return None ,
498
+ } ;
499
+ addr. set_port ( omicron_common:: address:: NTP_ADMIN_PORT ) ;
490
500
491
501
match & z. state {
492
- ZoneState :: Running ( running_zone ) => Some ( running_zone ) ,
502
+ ZoneState :: Running ( _ ) => Some ( addr ) ,
493
503
ZoneState :: PartiallyShutDown { .. }
494
504
| ZoneState :: FailedToStart ( _) => None ,
495
505
}
496
506
} ) ;
497
- let running_ntp_zone =
498
- running_ntp_zones. next ( ) . ok_or ( TimeSyncError :: NoRunningNtpZone ) ?;
499
- if running_ntp_zones. next ( ) . is_some ( ) {
507
+ let ntp_admin_address = ntp_admin_addresses
508
+ . next ( )
509
+ . ok_or ( TimeSyncError :: NoRunningNtpZone ) ?;
510
+ if ntp_admin_addresses. next ( ) . is_some ( ) {
500
511
return Err ( TimeSyncError :: MultipleRunningNtpZones ) ;
501
512
}
502
513
503
- // XXXNTP - This could be replaced with a direct connection to the
504
- // daemon using a patched version of the chrony_candm crate to allow
505
- // a custom server socket path. From the GZ, it should be possible to
506
- // connect to the UNIX socket at
507
- // format!("{}/var/run/chrony/chronyd.sock", ntp_zone.root())
508
-
509
- let stdout = running_ntp_zone
510
- . run_cmd ( & [ "/usr/bin/chronyc" , "-c" , "tracking" ] )
511
- . map_err ( TimeSyncError :: ExecuteChronyc ) ?;
512
-
513
- let v: Vec < & str > = stdout. split ( ',' ) . collect ( ) ;
514
-
515
- if v. len ( ) < 10 {
516
- return Err ( TimeSyncError :: FailedToParse {
517
- reason : "too few fields" ,
518
- stdout,
519
- } ) ;
520
- }
521
-
522
- let Ok ( ref_id) = u32:: from_str_radix ( v[ 0 ] , 16 ) else {
523
- return Err ( TimeSyncError :: FailedToParse {
524
- reason : "bad ref_id" ,
525
- stdout,
526
- } ) ;
527
- } ;
528
- let ip_addr =
529
- IpAddr :: from_str ( v[ 1 ] ) . unwrap_or ( Ipv6Addr :: UNSPECIFIED . into ( ) ) ;
530
- let Ok ( stratum) = u8:: from_str ( v[ 2 ] ) else {
531
- return Err ( TimeSyncError :: FailedToParse {
532
- reason : "bad stratum" ,
533
- stdout,
534
- } ) ;
535
- } ;
536
- let Ok ( ref_time) = f64:: from_str ( v[ 3 ] ) else {
537
- return Err ( TimeSyncError :: FailedToParse {
538
- reason : "bad ref_time" ,
539
- stdout,
540
- } ) ;
541
- } ;
542
- let Ok ( correction) = f64:: from_str ( v[ 4 ] ) else {
543
- return Err ( TimeSyncError :: FailedToParse {
544
- reason : "bad correction" ,
545
- stdout,
546
- } ) ;
547
- } ;
548
-
549
- // Per `chronyc waitsync`'s implementation, if either the
550
- // reference IP address is not unspecified or the reference
551
- // ID is not 0 or 0x7f7f0101, we are synchronized to a peer.
552
- let peer_sync =
553
- !ip_addr. is_unspecified ( ) || ( ref_id != 0 && ref_id != 0x7f7f0101 ) ;
514
+ let client = ntp_admin_client:: Client :: new (
515
+ & format ! ( "http://{ntp_admin_address}" ) ,
516
+ log. clone ( ) ,
517
+ ) ;
554
518
555
- let sync = stratum < 10
556
- && ref_time > 1234567890.0
557
- && peer_sync
558
- && correction. abs ( ) <= 0.05 ;
519
+ let timesync = client. timesync ( ) . await ?. into_inner ( ) ;
559
520
560
- Ok ( TimeSync { sync , ref_id , ip_addr , stratum , ref_time , correction } )
521
+ Ok ( timesync )
561
522
}
562
523
}
563
524
@@ -1111,6 +1072,7 @@ mod tests {
1111
1072
use sled_agent_types:: zone_images:: ZoneManifestStatus ;
1112
1073
use std:: collections:: BTreeSet ;
1113
1074
use std:: collections:: VecDeque ;
1075
+ use std:: net:: Ipv6Addr ;
1114
1076
use std:: sync:: Mutex ;
1115
1077
use tufaceous_artifact:: ArtifactHash ;
1116
1078
0 commit comments