diff --git a/firstboot/userdata_timesync.yaml b/firstboot/userdata_timesync.yaml new file mode 100644 index 0000000000..1802b436e2 --- /dev/null +++ b/firstboot/userdata_timesync.yaml @@ -0,0 +1,97 @@ +heat_template_version: rocky + +parameters: + NtpServer: + default: ['0.pool.ntp.org', '1.pool.ntp.org', '2.pool.ntp.org', '3.pool.ntp.org'] + description: NTP servers list. Defaulted to a set of pool.ntp.org servers + in order to have a sane default for Pacemaker deployments when + not configuring this parameter by default. + type: comma_delimited_list + NtpPool: + default: [] + description: NTP pool list. Defaults to [], so only NtpServer is used by + default. + type: comma_delimited_list + NtpService: + default: chrony + description: NTP Service to use for the timesync bootstrap. + type: string + + +description: > + Uses cloud-init to bootstrap timesync configuration to ensure it is done + as soon as possible. We do additional and more complex configurations as + part of the deployment itself. + +conditions: + use_chrony: {equals: [{get_param: NtpService}, 'chrony']} + +resources: + userdata: + type: OS::Heat::MultipartMime + properties: + parts: + - config: {get_resource: timesync_chrony} + - config: {get_resource: timesync_sync} + + # chrony sync needs chrony to be configured, if not chrony just exit + timesync_chrony: + type: OS::Heat::SoftwareConfig + properties: + config: + str_replace: + template: | + #!/bin/bash + if [ "$service" != "chrony" ]; then + exit 0 + fi + set -x + SERVERS="$ntp_servers" + POOLS="$ntp_pools" + systemctl is-active --quiet chronyd || systemctl start chronyd + for server in $SERVERS; do + chronyc add server "${server}" iburst + done + for pool in $POOLS; do + chronyc add server "${pool}" iburst + done + chronyc sources + params: + $ntp_servers: + list_join: [' ', {get_param: NtpServer}] + $ntp_pools: + list_join: [' ', {get_param: NtpPool}] + $service: {get_param: NtpService} + + # attempt a timesync on boot to ensure the time has been synced + timesync_sync: + type: OS::Heat::SoftwareConfig + properties: + config: + str_replace: + template: | + #!/bin/bash + set -x + if [ "$service" = "chrony" ]; then + if command -v chronyc >/dev/null; then + chronyc waitsync 20 + else + echo "No chronyc available, skipping sync" + fi + elif [ "$service" = "ntp" ]; then + if command -v ntpdate >/dev/null; then + ntpdate -u $ntp_servers + else + echo "No ntpdate available, skipping sync" + fi + fi + hwclock --systohc --utc + params: + $service: {get_param: NtpService} + $ntp_servers: + list_join: [' ', {get_param: NtpServer}] + + +outputs: + OS::stack_id: + value: {get_resource: userdata} diff --git a/overcloud-resource-registry-puppet.j2.yaml b/overcloud-resource-registry-puppet.j2.yaml index 16607ab363..156b0cfca7 100644 --- a/overcloud-resource-registry-puppet.j2.yaml +++ b/overcloud-resource-registry-puppet.j2.yaml @@ -44,6 +44,10 @@ resource_registry: # To disable, replace with firstboot/userdata_default.yaml OS::TripleO::NodeAdminUserData: firstboot/userdata_heat_admin.yaml + # This bootstraps the timesync configuration for any subsequent deployment + # operations. To disable, replace with firstboot/userdata_default.yaml + OS::TripleO::NodeTimesyncUserData: firstboot/userdata_timesync.yaml + # Hooks for operator extra config # NodeUserData == Cloud-init additional user-data, e.g cloud-config # role::NodeUserData == Role specific cloud-init additional user-data diff --git a/overcloud.j2.yaml b/overcloud.j2.yaml index 4837f87163..e8c3f63274 100644 --- a/overcloud.j2.yaml +++ b/overcloud.j2.yaml @@ -458,6 +458,12 @@ resources: NodeAdminUserData: type: OS::TripleO::NodeAdminUserData + # Bootstraps an ntp configuration and includes a hardware clock sync to + # for containers. + # Should return a OS::Heat::MultipartMime reference via OS::stack_id + NodeTimesyncUserData: + type: OS::TripleO::NodeTimesyncUserData + # For optional operator additional userdata # Should return a OS::Heat::MultipartMime reference via OS::stack_id NodeUserData: @@ -636,6 +642,8 @@ resources: parts: - config: {get_resource: NodeAdminUserData} type: multipart + - config: {get_resource: NodeTimesyncUserData} + type: multipart - config: {get_resource: NodeUserData} type: multipart - config: {get_resource: {{role.name}}RoleUserData}