Skip to content

Commit d5d874b

Browse files
committed
Trying to collect timesync status in inventory
1 parent bf49154 commit d5d874b

File tree

11 files changed

+243
-2
lines changed

11 files changed

+243
-2
lines changed

Cargo.lock

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

nexus/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ nexus-mgs-updates.workspace = true
6464
nexus-networking.workspace = true
6565
nexus-saga-recovery.workspace = true
6666
nexus-test-interface.workspace = true
67+
ntp-admin-client.workspace = true
6768
num-integer.workspace = true
6869
omicron-cockroach-metrics.workspace = true
6970
openssl.workspace = true

nexus/db-model/src/inventory.rs

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ use nexus_db_schema::schema::{
3434
inv_last_reconciliation_disk_result,
3535
inv_last_reconciliation_orphaned_dataset,
3636
inv_last_reconciliation_zone_result, inv_mupdate_override_non_boot,
37-
inv_nvme_disk_firmware, inv_omicron_sled_config,
37+
inv_ntp_timesync, inv_nvme_disk_firmware, inv_omicron_sled_config,
3838
inv_omicron_sled_config_dataset, inv_omicron_sled_config_disk,
3939
inv_omicron_sled_config_zone, inv_omicron_sled_config_zone_nic,
4040
inv_physical_disk, inv_root_of_trust, inv_root_of_trust_page,
@@ -61,7 +61,7 @@ use nexus_sled_agent_shared::inventory::{
6161
};
6262
use nexus_types::inventory::{
6363
BaseboardId, Caboose, CockroachStatus, Collection, NvmeFirmware,
64-
PowerState, RotPage, RotSlot,
64+
PowerState, RotPage, RotSlot, TimeSync,
6565
};
6666
use omicron_common::api::external;
6767
use omicron_common::api::internal::shared::NetworkInterface;
@@ -2788,6 +2788,38 @@ impl TryFrom<InvCockroachStatus> for CockroachStatus {
27882788
}
27892789
}
27902790

2791+
#[derive(Queryable, Clone, Debug, Selectable, Insertable)]
2792+
#[diesel(table_name = inv_ntp_timesync)]
2793+
pub struct InvNtpTimesync {
2794+
pub inv_collection_id: DbTypedUuid<CollectionKind>,
2795+
pub ip: ipv6::Ipv6Addr,
2796+
pub port: SqlU16,
2797+
pub synced: bool,
2798+
}
2799+
2800+
impl InvNtpTimesync {
2801+
pub fn new(
2802+
inv_collection_id: CollectionUuid,
2803+
timesync: &TimeSync,
2804+
) -> Result<Self, anyhow::Error> {
2805+
Ok(Self {
2806+
inv_collection_id: inv_collection_id.into(),
2807+
ip: timesync.addr.ip().into(),
2808+
port: timesync.addr.port().into(),
2809+
synced: timesync.synced,
2810+
})
2811+
}
2812+
}
2813+
2814+
impl From<InvNtpTimesync> for nexus_types::inventory::TimeSync {
2815+
fn from(value: InvNtpTimesync) -> Self {
2816+
Self {
2817+
addr: SocketAddrV6::new(*value.ip, *value.port, 0, 0),
2818+
synced: value.synced,
2819+
}
2820+
}
2821+
}
2822+
27912823
#[cfg(test)]
27922824
mod test {
27932825
use nexus_types::inventory::NvmeFirmware;

nexus/db-queries/src/db/datastore/inventory.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ use nexus_db_model::InvLastReconciliationDatasetResult;
4242
use nexus_db_model::InvLastReconciliationDiskResult;
4343
use nexus_db_model::InvLastReconciliationOrphanedDataset;
4444
use nexus_db_model::InvLastReconciliationZoneResult;
45+
use nexus_db_model::InvNtpTimesync;
4546
use nexus_db_model::InvNvmeDiskFirmware;
4647
use nexus_db_model::InvOmicronSledConfig;
4748
use nexus_db_model::InvOmicronSledConfigDataset;
@@ -93,6 +94,7 @@ use nexus_types::inventory::CockroachStatus;
9394
use nexus_types::inventory::Collection;
9495
use nexus_types::inventory::PhysicalDiskFirmware;
9596
use nexus_types::inventory::SledAgent;
97+
use nexus_types::inventory::TimeSync;
9698
use omicron_cockroach_metrics::NodeId as CockroachNodeId;
9799
use omicron_common::api::external::Error;
98100
use omicron_common::api::external::InternalContext;
@@ -385,6 +387,13 @@ impl DataStore {
385387
.collect::<Result<Vec<_>, _>>()
386388
.map_err(|e| Error::internal_error(&e.to_string()))?;
387389

390+
let inv_ntp_timesync_records: Vec<InvNtpTimesync> = collection
391+
.ntp_timesync
392+
.iter()
393+
.map(|timesync| InvNtpTimesync::new(collection_id, timesync))
394+
.collect::<Result<Vec<_>, _>>()
395+
.map_err(|e| Error::internal_error(&e.to_string()))?;
396+
388397
// This implementation inserts all records associated with the
389398
// collection in one transaction. This is primarily for simplicity. It
390399
// means we don't have to worry about other readers seeing a
@@ -1424,6 +1433,14 @@ impl DataStore {
14241433
.await?;
14251434
}
14261435

1436+
// Insert the NTP info we've observed
1437+
if !inv_ntp_timesync_records.is_empty() {
1438+
use nexus_db_schema::schema::inv_ntp_timesync::dsl;
1439+
diesel::insert_into(dsl::inv_ntp_timesync)
1440+
.values(inv_ntp_timesync_records)
1441+
.execute_async(&conn)
1442+
.await?;
1443+
}
14271444

14281445
// Finally, insert the list of errors.
14291446
{
@@ -1714,6 +1731,7 @@ impl DataStore {
17141731
nerrors: usize,
17151732
nclickhouse_keeper_membership: usize,
17161733
ncockroach_status: usize,
1734+
nntp_timesync: usize,
17171735
}
17181736

17191737
let NumRowsDeleted {
@@ -1744,6 +1762,7 @@ impl DataStore {
17441762
nerrors,
17451763
nclickhouse_keeper_membership,
17461764
ncockroach_status,
1765+
nntp_timesync,
17471766
} =
17481767
self.transaction_retry_wrapper("inventory_delete_collection")
17491768
.transaction(&conn, |conn| async move {
@@ -2000,6 +2019,17 @@ impl DataStore {
20002019
.execute_async(&conn)
20012020
.await?
20022021
};
2022+
// Remove rows for NTP timesync
2023+
let nntp_timesync = {
2024+
use nexus_db_schema::schema::inv_ntp_timesync::dsl;
2025+
diesel::delete(
2026+
dsl::inv_ntp_timesync.filter(
2027+
dsl::inv_collection_id.eq(db_collection_id),
2028+
),
2029+
)
2030+
.execute_async(&conn)
2031+
.await?
2032+
};
20032033

20042034
Ok(NumRowsDeleted {
20052035
ncollections,
@@ -2029,6 +2059,7 @@ impl DataStore {
20292059
nerrors,
20302060
nclickhouse_keeper_membership,
20312061
ncockroach_status,
2062+
nntp_timesync,
20322063
})
20332064
})
20342065
.await
@@ -2069,6 +2100,7 @@ impl DataStore {
20692100
"nerrors" => nerrors,
20702101
"nclickhouse_keeper_membership" => nclickhouse_keeper_membership,
20712102
"ncockroach_status" => ncockroach_status,
2103+
"nntp_timesync" => nntp_timesync,
20722104
);
20732105

20742106
Ok(())
@@ -3435,6 +3467,25 @@ impl DataStore {
34353467
.collect::<Result<BTreeMap<_, _>, Error>>()?
34363468
};
34373469

3470+
// Load TimeSync statuses
3471+
let ntp_timesync: IdOrdMap<TimeSync> = {
3472+
use nexus_db_schema::schema::inv_ntp_timesync::dsl;
3473+
3474+
let records: Vec<InvNtpTimesync> = dsl::inv_ntp_timesync
3475+
.filter(dsl::inv_collection_id.eq(db_id))
3476+
.select(InvNtpTimesync::as_select())
3477+
.load_async(&*conn)
3478+
.await
3479+
.map_err(|e| {
3480+
public_error_from_diesel(e, ErrorHandler::Server)
3481+
})?;
3482+
3483+
records
3484+
.into_iter()
3485+
.map(|record| TimeSync::from(record))
3486+
.collect::<IdOrdMap<_>>()
3487+
};
3488+
34383489
// Finally, build up the sled-agent map using the sled agent and
34393490
// omicron zone rows. A for loop is easier to understand than into_iter
34403491
// + filter_map + return Result + collect.
@@ -3681,6 +3732,7 @@ impl DataStore {
36813732
sled_agents,
36823733
clickhouse_keeper_cluster_membership,
36833734
cockroach_status,
3735+
ntp_timesync,
36843736
})
36853737
}
36863738
}

nexus/db-schema/src/schema.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1899,6 +1899,15 @@ table! {
18991899
}
19001900
}
19011901

1902+
table! {
1903+
inv_ntp_timesync (inv_collection_id, ip, port) {
1904+
inv_collection_id -> Uuid,
1905+
ip -> Inet,
1906+
port -> Int4,
1907+
synced -> Bool,
1908+
}
1909+
}
1910+
19021911
/* blueprints */
19031912

19041913
table! {

nexus/inventory/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ id-map.workspace = true
2323
iddqd.workspace = true
2424
nexus-sled-agent-shared.workspace = true
2525
nexus-types.workspace = true
26+
ntp-admin-client.workspace = true
2627
omicron-common.workspace = true
2728
omicron-uuid-kinds.workspace = true
2829
reqwest.workspace = true
@@ -31,6 +32,7 @@ sled-agent-client.workspace = true
3132
sled-agent-types.workspace = true
3233
sled-agent-zone-images-examples.workspace = true
3334
slog.workspace = true
35+
slog-error-chain.workspace = true
3436
strum.workspace = true
3537
swrite.workspace = true
3638
thiserror.workspace = true

nexus/inventory/src/builder.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ use nexus_types::inventory::RotPageWhich;
3131
use nexus_types::inventory::RotState;
3232
use nexus_types::inventory::ServiceProcessor;
3333
use nexus_types::inventory::SledAgent;
34+
use nexus_types::inventory::TimeSync;
3435
use nexus_types::inventory::Zpool;
3536
use omicron_cockroach_metrics::CockroachMetric;
3637
use omicron_cockroach_metrics::NodeId;
@@ -39,6 +40,7 @@ use omicron_uuid_kinds::CollectionKind;
3940
use std::collections::BTreeMap;
4041
use std::collections::BTreeSet;
4142
use std::hash::Hash;
43+
use std::net::SocketAddrV6;
4244
use std::sync::Arc;
4345
use thiserror::Error;
4446
use typed_rng::TypedUuidRng;
@@ -120,6 +122,7 @@ pub struct CollectionBuilder {
120122
clickhouse_keeper_cluster_membership:
121123
BTreeSet<ClickhouseKeeperClusterMembership>,
122124
cockroach_status: BTreeMap<NodeId, CockroachStatus>,
125+
ntp_timesync: IdOrdMap<TimeSync>,
123126
// CollectionBuilderRng is taken by value, rather than passed in as a
124127
// mutable ref, to encourage a tree-like structure where each RNG is
125128
// generally independent.
@@ -150,6 +153,7 @@ impl CollectionBuilder {
150153
sleds: IdOrdMap::new(),
151154
clickhouse_keeper_cluster_membership: BTreeSet::new(),
152155
cockroach_status: BTreeMap::new(),
156+
ntp_timesync: IdOrdMap::new(),
153157
rng: CollectionBuilderRng::from_entropy(),
154158
}
155159
}
@@ -173,6 +177,7 @@ impl CollectionBuilder {
173177
clickhouse_keeper_cluster_membership: self
174178
.clickhouse_keeper_cluster_membership,
175179
cockroach_status: self.cockroach_status,
180+
ntp_timesync: self.ntp_timesync,
176181
}
177182
}
178183

@@ -550,6 +555,20 @@ impl CollectionBuilder {
550555
self.clickhouse_keeper_cluster_membership.insert(membership);
551556
}
552557

558+
/// Record information about timesync
559+
pub fn found_ntp_timesync(
560+
&mut self,
561+
addr: SocketAddrV6,
562+
timesync: ntp_admin_client::types::TimeSync,
563+
) -> Result<(), anyhow::Error> {
564+
self.ntp_timesync
565+
.insert_unique(TimeSync { addr, synced: timesync.sync })
566+
.map_err(|err| err.into_owned())
567+
.with_context(|| {
568+
format!("NTP service {addr} reported time multiple times")
569+
})
570+
}
571+
553572
/// Record metrics from a CockroachDB node
554573
pub fn found_cockroach_metrics(
555574
&mut self,
@@ -621,6 +640,8 @@ mod test {
621640
assert!(collection.cabooses_found.is_empty());
622641
assert!(collection.rot_pages_found.is_empty());
623642
assert!(collection.clickhouse_keeper_cluster_membership.is_empty());
643+
assert!(collection.cockroach_status.is_empty());
644+
assert!(collection.ntp_timesync.is_empty());
624645
}
625646

626647
// Simple test of a single, fairly typical collection that contains just

0 commit comments

Comments
 (0)