コンテンツにスキップ

NTP テーブル群 — コード由来デフォルト (Phase A) + 失敗挙動 (Phase D)

このページは NTP / NTP_SERVER / NTP_KEY 3 テーブルを横断して、YANG 定義・init_cfg.json.j2chrony.conf.j2 テンプレート・hostcfgd ハンドラの全行精読から得た暗黙デフォルト乖離dead fieldsilent drop を記録する。各テーブルの詳細は NTP (global)NTP_SERVERNTP_KEY を参照。

コード由来デフォルト分析

NTP|global フィールド

フィールド YANG default init_cfg.json.j2 有効デフォルト 分類
authentication disabled "disabled" disabled 一致
dhcp enabled "enabled" enabled 一致
server_role enabled "disabled" disabled YANG-実装乖離
src_intf なし (任意) "eth0" "eth0" build-time ハードコード
vrf なし (任意) "default" "default" build-time ハードコード
admin_state enabled "enabled" enabled 一致

server_role — YANG default=enabled vs init_cfg.json.j2="disabled"

sonic-ntp.yang L155 は default enabled を宣言するが、init_cfg.json.j2 L214 は "server_role": "disabled" を明示的に書き込む1

さらに chrony.conf.j2 L57-63 は SmartSwitch (DEVICE_METADATA.localhost.subtype == 'SmartSwitch' かつ type != 'SmartSwitchDPU') のときのみ server_role の値を参照する:

{% if device_metadata.subtype == 'SmartSwitch' and device_metadata.type != 'SmartSwitchDPU' -%}
{% if global.server_role == 'enabled' or global.dhcp == 'enabled' -%}
allow
binddevice bridge-midplane
{% endif -%}
{% endif -%}

非 SmartSwitch では server_role は dead field — フィールド値にかかわらず chrony.conf への影響はない。SmartSwitch では dhcp == 'enabled' (default) でも allow が追加されるため、server_role=disabled でも SmartSwitch は NTP server として動作する。

src_intf — YANG は任意、init_cfg が "eth0" を注入

YANG 上は任意の leaf-list だが、init_cfg.json.j2"eth0" を常に設定する。chrony.conf.j2 L87-107 は global.src_intf が存在する場合に bindacqaddress <ip> を生成し、インタフェース名の prefix でテーブルを振り分ける:

  • eth0MGMT_INTERFACE
  • Ethernet*INTERFACE
  • Loopback*LOOPBACK_INTERFACE
  • PortChannel*PORTCHANNEL_INTERFACE
  • Vlan*VLAN_INTERFACE

src_intf が leaf-list (複数値) でも global.src_intf が文字列として取り出される点に注意。hostcfgd handle_ntp_source_intf_chgsrc_intf; 区切りで split して比較する2

vrf — YANG は任意、init_cfg が "default" を注入

init_cfg.json.j2"vrf": "default" を設定。chronyd-starter.sh はランタイムに:

  1. MGMT_VRF_CONFIG|vrf_global.mgmtVrfEnabled"true" かを確認
  2. true なら NTP|global.vrf を読み、"default" なら default VRF で起動、それ以外 (mgmt) なら ip vrf exec mgmt chronyd
  3. false なら常に default VRF で起動

YANG must 制約は DB 書き込み時のみ評価されるが、chronyd-starter.sh はランタイムに MGMT_VRF_CONFIG を再確認する。MGMT VRF を無効化したまま vrf=mgmt が DB に残ると chronyd は mgmt VRF で起動しようとして失敗する可能性がある(経路依存乖離)。


NTP_SERVER フィールド

フィールド YANG default template fallback minigraph 有効デフォルト 分類
association_type server \| d('server') - server 一致 (二重保護)
iburst on 条件付き "on" 固定 on 一致。ただし潜在バグあり
resolve_as なし (任意) \| d(server) - server_address キー template fallback
admin_state enabled filter による - enabled 一致
trusted no - - no 一致。resolve_as 要件あり
version 4 - - 4 一致
key なし (任意) - - - authentication=disabled 時 silent drop

iburst の潜在バグ

chrony.conf.j2 L37 は {% if config.iburst %} で判定する。Jinja2/Python では文字列 'off' は truthy であるため、iburst = 'off' が DB に入っていても iburst オプションが chrony.conf に追加されてしまう3

正しくは config.iburst == 'on' と比較すべきところを truthy 判定しているため、明示的に iburst=off を設定しても効かない可能性がある。YANG が on/off enum を強制するので on/off 以外の値は入らないが、iburst = 'off' の場合の動作が意図と異なる。

NTP_SERVER.key — authentication=disabled 時 silent drop

chrony.conf.j2 L30-34:

{% if global.authentication == 'enabled' -%}
    {% if config.key -%}
        {% set soptions = soptions ~ ' key ' ~ config.key -%}
    {% endif -%}
{% endif -%}

NTP.authentication = 'disabled' (デフォルト) のとき、NTP_SERVER.key に値が設定されていても chrony.confkey オプションは生成されない。YANG バリデーションで leafref は通るが、認証なしでは鍵が使われない。

NTP_SERVER.trusted — resolve_as が必須条件

chrony.keys.j2 L8-10:

{% for server in NTP_SERVER if NTP_SERVER[server].trusted == 'yes' and
                               NTP_SERVER[server].resolve_as -%}
    {% set _ = trusted_arr.append(NTP_SERVER[server].resolve_as) -%}

trusted = 'yes' でも resolve_as が空の場合は trusted_str に追加されない。YANG で resolve_as は任意 leaf のため、CLI や minigraph.py が resolve_as を設定しない場合は trusted 指定が silent drop される。


NTP_KEY フィールド

フィールド YANG default template参照 有効デフォルト 分類
type md5 NTP_KEY[keyid].type (必須チェック) md5 一致。RFC 8573 非推奨
trusted no 未参照 no dead field
value なし (任意) b64decode 必須 - Base64 エンコード前提

NTP_KEY.trusted — dead field

chrony.keys.j2NTP_KEY[keyid].trusted を一切参照しない。trustedkey の制御は NTP_SERVER[server].trusted フィールドで行う。NTP_KEY.trusted = 'yes' を設定しても chrony.keys ファイルへの影響はない。

NTP_KEY.value — Base64 エンコード必須

chrony.keys.j2 L16 は NTP_KEY[keyid].value | b64decode でデコードする。DB に平文を格納すると Base64 として誤ってデコードされ、chrony が誤った鍵値を使用する。CLI config ntp authentication-key add が Base64 エンコードを行う前提。


乖離・特殊挙動サマリ

分類 フィールド 詳細
YANG-実装乖離 NTP.server_role YANG default=enabled、init_cfg="disabled" — 有効デフォルトは disabled
build-time ハードコード NTP.src_intf YANG 任意だが init_cfg が "eth0" を常時注入
build-time ハードコード NTP.vrf YANG 任意だが init_cfg が "default" を常時注入
dead field (非SmartSwitch) NTP.server_role 非 SmartSwitch では chrony.conf.j2 が参照しない
dead field NTP_KEY.trusted chrony.keys.j2 は NTP_KEY.trusted を未参照
silent drop NTP_SERVER.key authentication=disabled 時は key が chrony.conf に反映されない
silent drop NTP_SERVER.trusted=yes resolve_as 未設定なら trustedkey に含まれない
潜在バグ NTP_SERVER.iburst if config.iburst が truthy 判定 → iburst='off' でも有効になる可能性
経路依存乖離 NTP.vrf YANG must はDB書込時のみ評価。chronyd-starter.sh はランタイムに MGMT_VRF_CONFIG を再確認
platform依存 NTP.server_role / NTP.dhcp SmartSwitch のみ allow+binddevice を追加
書き込み順依存 NTP_SERVER.key / NTP_KEY NTP_KEY 未登録時に NTP_SERVER.key を設定すると YANG leafref 拒否
Base64前提 NTP_KEY.value b64decode 必須。平文格納は誤動作
template fallback NTP_SERVER.association_type \| d('server') で YANG と一致するフォールバックあり
template fallback NTP_SERVER.resolve_as \| d(server) でアドレスキーにフォールバック

失敗挙動 (Phase D)

詳細証跡は meta/_intermediate/cdb-flow/ntp-failure.md を参照。

hostcfgd NtpCfg ハンドラの失敗経路

失敗条件 検出箇所 結果 evidence
systemctl restart chrony 失敗 (handle_ntp_source_intf_chg) hostcfgd:1324-1328 LOG_ERRreturn(キャッシュ更新なし・再試行なし) hostcfgd:1326-1329
systemctl restart chrony 失敗 (ntp_global_update) hostcfgd:1356-1361 LOG_ERRreturn(キャッシュ更新なし — CONFIG_DB 変更は適用済みだがキャッシュが旧値のまま残存) hostcfgd:1358-1361
systemctl restart chrony 失敗 (ntp_srv_key_update) hostcfgd:1397-1402 LOG_ERRreturn(キャッシュ更新なし → 次イベントで再処理保証) hostcfgd:1399-1402
src_intf に対応するサーバが未設定 hostcfgd:1315-1316 return(no-op、サーバ登録後に反映) hostcfgd:1315-1316
systemctl stop/start chrony 失敗(MGMT_VRF_CONFIG 変更時) hostcfgd:1659-1665 CalledProcessErrorLOG_ERRreturn(mgmt_vrf_enabled キャッシュ未更新) hostcfgd:1663-1666

キャッシュ不整合リスク(ntp_global_update)

ntp_global_updatesystemctl restart chrony 失敗時にキャッシュを更新しない(L1364 の self.cache[key] = datareturn で到達しない)。CONFIG_DB の値は既に変更済みのため、次回同フィールドに同一値が書かれた場合にキャッシュ差分なしと誤判定し no-op になる可能性がある(経路依存不整合)。

テンプレート失敗経路(サイレント動作)

失敗条件 結果 evidence
NTP_SERVER.admin_state == 'disabled' そのサーバを chrony.conf から除外(サイレント除去) chrony.conf.j2:20
NTP_KEY.type または NTP_KEY.value が空 そのキーをキーファイルからスキップ(サイレントスキップ) chrony.keys.j2:15
NTP_KEY.value が不正 Base64 b64decode が誤ってデコード → 誤った鍵値を書き込む(サイレント誤動作) chrony.keys.j2:16
NTP_SERVER.trusted == 'yes' かつ resolve_as 未設定 trusted_str に追加されない(サイレントドロップ) chrony.keys.j2:8-10
NTP.authentication != 'enabled' かつ NTP_SERVER.key 設定済み key オプションが生成されない(サイレントドロップ) chrony.conf.j2:30-34
NTP.authentication == 'enabled' かつ NTP_KEY が空 keyfile ディレクティブ追加されるが chrony.keys が空 → 認証エラーで chrony が起動失敗する可能性 chrony.conf.j2:124-128
config.iburst == 'off'(Jinja2 truthy 判定) iburst オプションが生成される(意図に反する) chrony.conf.j2:37

chronyd-starter.sh の失敗経路

失敗条件 結果 evidence
sonic-db-cliMGMT_VRF_CONFIG|vrf_global.mgmtVrfEnabled 読み取り失敗 VRF_ENABLED が空 → default VRF で起動(安全フォールバック) chronyd-starter.sh:3-16
sonic-db-cliNTP|global.vrf 読み取り失敗(mgmtVrfEnabled=true のとき) VRF_CONFIGURED が空 → mgmt VRF で起動(意図しないフォールバック) chronyd-starter.sh:5-11
ip vrf exec mgmt chronyd 失敗(mgmt VRF 未設定) exec 失敗 → chrony サービス起動不可(サービス障害) chronyd-starter.sh:11

失敗の可観測性

NTP 処理系は STATE_DB への NTP ステータス書き込みを持たない。失敗検知は以下のみで行う:

  • journalctl -u chrony — chrony サービスの起動失敗
  • grep 'NtpCfg.*Failed' /var/log/syslog — hostcfgd の LOG_ERR 出力
  • chronyc tracking / chronyc sources — 実際の同期状態確認

書込み順依存 (Phase B)

NTP_KEY 先行必須 — NTP_SERVER.key leafref

sonic-ntp.yang L199-203 は NTP_SERVER.<server>.keyNTP_KEY.id への leafref として定義する。YANG バリデーション層は SET 時に参照先の存在を検証するため、NTP_KEY|<id> が未登録の状態で NTP_SERVER|<server>.key=<id> を書き込むと SET が拒否される。

正しい順序: NTP_KEY|<id> SET → NTP_SERVER|<server>.key=<id> SET。

DEL の逆順序: NTP_SERVERkey フィールドをクリアまたは NTP_SERVER エントリを DEL → NTP_KEY|<id> DEL。参照を残したまま NTP_KEY を先に DEL すると leafref が dangling になり DEL が失敗する。

NTP_KEY 先行推奨 — authentication=enabled 設定時

chrony.conf.j2 L125-131 は NTP.authentication == 'enabled' のときのみ NTP_SERVER.key フィールドを chrony.conf に出力する。NTP|global.authenticationenabled に切り替えるタイミングで NTP_KEY が未登録だと、chrony.keys ファイルが空のまま chrony が再起動し認証が機能しない。

正しい順序: NTP_KEY|<id> SET → NTP|global.authentication=enabled SET。

MGMT_VRF_CONFIG 先行必須 — vrf=mgmt 設定時

sonic-ntp.yang L127-129 の must 制約により、NTP|global.vrf = 'mgmt' を書き込む際に MGMT_VRF_CONFIG|vrf_global.mgmtVrfEnabled = 'true' が未設定だと YANG must 違反として SET が拒否される。

逆方向の依存: MGMT_VRF_CONFIG.mgmtVrfEnabledfalse に戻す前に NTP|global.vrfdefault に戻さないと、chronyd-starter.sh がランタイムに ip vrf exec mgmt chronyd で起動しようとして失敗する(YANG must はブート時以降の整合性を保証しない経路依存乖離)。

参照インタフェース先行必須 — src_intf(eth0 以外)

src_intfeth0 以外のインタフェース名(EthernetXLoopbackXPortChannelX 等)を設定する場合、対応するインタフェーステーブル(PORT|EthernetXLOOPBACK_INTERFACE|LoopbackX 等)が先行して存在しなければ leafref バリデーションが失敗する。eth0pattern 'eth0' の string 型で leafref を迂回しており常に書き込み可能。

hostcfgd: NTP_SERVER / NTP_KEY の合算処理タイミング

ntp_srv_key_handlerNTP_SERVER または NTP_KEY のいずれかが変更されると、両テーブルを同時に全件読み取って chrony を再起動する。NTP_KEY SET イベントと NTP_SERVER.key SET イベントが別々に届く場合、1 回目の再起動では NTP_SERVER.key がまだ未設定の状態で chrony が起動するが、2 回目のイベントで正しく反映される。YANG leafref バリデーションが NTP_KEY 未存在時の NTP_SERVER.key SET を拒否するため、実運用上この race は防がれる。

ブート時の書込みシーケンス

sonic-cfggeninit_cfg.json.j2 を展開して CONFIG_DB に NTP|global を書き込んだ後、hostcfgdload() でスナップショットを一括取得する。hostcfgdload() は chrony を再起動しない(ブート時の NTP 設定は chrony の起動設定ファイルから直接読み込まれる)。ブート後の最初の CONFIG_DB 変更イベントで初めて chrony restart が発火する。

順序依存サマリ

# 依存関係 違反時の挙動
1 NTP_KEY\|<id> 先行 → NTP_SERVER\|<server>.key=<id> SET YANG leafref 拒否(SET 失敗)
2 NTP_SERVER.key クリア 先行 → NTP_KEY\|<id> DEL YANG leafref dangling(DEL 失敗)
3 NTP_KEY 登録 先行 → NTP\|global.authentication=enabled chrony 認証失敗(鍵なし起動)
4 MGMT_VRF_CONFIG.mgmtVrfEnabled=true 先行 → NTP\|global.vrf=mgmt YANG must 違反(SET 失敗)
5 NTP\|global.vrf=default 先行 → MGMT_VRF_CONFIG.mgmtVrfEnabled=false chronyd 起動失敗(mgmt VRF 不存在)
6 対応インタフェーステーブル 先行 → NTP\|global.src_intf=<intf> (eth0 以外) YANG leafref 拒否(SET 失敗)

中間調査詳細: meta/_intermediate/cdb-flow/ntp-ordering.md

関連ページ

引用元


  1. init_cfg.json.j2 L210-219: "NTP": {"global": {"authentication": "disabled", "dhcp": "enabled", "server_role": "disabled", "src_intf": "eth0", "admin_state": "enabled", "vrf": "default"}}. https://github.com/sonic-net/sonic-buildimage/blob/9ea932ec2e18f35e58268ec2e4456b1d4afd65cd/files/build_templates/init_cfg.json.j2#L210-L219 

  2. hostcfgd L1319: ifs = self.cache.get('global', {}).get('src_intf', '').split(';') — leaf-list が ; 区切り文字列として格納される CONFIG_DB の実装依存。https://github.com/sonic-net/sonic-host-services/blob/master/scripts/hostcfgd 

  3. chrony.conf.j2 L37: {% if config.iburst %} — Jinja2 で文字列 'off' は truthy。iburst = 'off' のサーバも iburst オプションが生成される潜在的挙動。https://github.com/sonic-net/sonic-buildimage/blob/9ea932ec2e18f35e58268ec2e4456b1d4afd65cd/files/image_config/chrony/chrony.conf.j2#L37