Skip to content

Commit cb68613

Browse files
anooprajendrabsanders
authored andcommitted
FEATURE: Allow non-frontend parents to be time servers
Also allow for non management of time
1 parent d7e8142 commit cb68613

File tree

4 files changed

+67
-26
lines changed

4 files changed

+67
-26
lines changed

common/nodes/time-server.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ https://github.com/Teradata/stacki/blob/master/LICENSE.txt
1212
<stack:script stack:stage="install-post">
1313
/opt/stack/bin/stack set attr attr=time.protocol value=chrony
1414
/opt/stack/bin/stack set appliance attr frontend attr=time.protocol value=chrony
15+
/opt/stack/bin/stack set appliance attr frontend attr=time.orphantype value=parent
1516
/opt/stack/bin/stack report host time &hostname; | /opt/stack/bin/stack report script | sh
1617
</stack:script>
1718
</stack:stack>

common/src/stack/command/stack/commands/report/host/time/__init__.py

Lines changed: 62 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,51 +12,91 @@
1212
from stack.exception import *
1313

1414
## DESIGN CONSIDERATIONS
15-
# The NTP system set up by default in Stacki has the
15+
# The timekeeping system set up by default in Stacki has the
1616
# following design considerations.
1717
#
18-
# 1. The Frontend syncs its own time from external servers. - stored in time.servers attribute
19-
# 2. The Frontend serves NTP to all backends on all networks that are PXE enabled.
20-
# 3. By default, the Backends sync time from the frontend.
18+
# 1. All servers in the cluster, can sync times from 2 sources. Any external server
19+
# specified in the time.servers attribute, and any servers with time.orphantype value
20+
# set to "parent"
21+
# 2. By default, the frontend is configured as a parent, and can serve time to the rest
22+
# of the cluster.
23+
# 3. time.servers attribute should ideally be set for the frontend, so that it can pick
24+
# up
2125
# 4. If the time.servers attribute is set for a backend host,
2226
# then those servers are appended to the list of servers to
2327
# sync from.
2428
# 5. If certain nodes are set as parent servers, they can form a time island, from which
2529
# all other servers can sync
30+
# 6. If Stacki shouldn't be managing time at all, then the time.protocol attribute can
31+
# be unset, and the admin can manage the time by themselves
2632

2733
class Command(stack.commands.HostArgumentProcessor,
2834
stack.commands.NetworkArgumentProcessor,
2935
stack.commands.report.command):
3036
"""
3137
Create a time configuration report (NTP or chrony).
38+
At a minimum at least one server in the cluster has to be designated as a "parent"
39+
time server. This ensures that there's atleast one server in the cluster that can
40+
serve time. By default, the stacki frontend is a parent timekeeper.
41+
Ideally, a "parent" time server will also have the "time.servers" attribute set
42+
to talk to an external time server, so that the cluster is in sync with the external
43+
time-keeping entity.
3244
3345
Relevant Attributes:
3446
3547
time.servers - Optional - Comma-separated list of servers (IP addresses
3648
or resolvable names) that dictate the servers to use for time-keeping.
3749
This list is in *addition* to the Stacki frontend.
3850
39-
time.orphantype - Optional - Only the value of "parent" affects the behaviour
40-
if the NTP service. If a host is designated as a parent Orphan-type,
41-
that means this host can co-ordinate time with other peers to maintain
51+
time.orphantype - Required for at-least one host in the cluster.
52+
Only the value of "parent" affects the behaviour
53+
of the NTP service. If a host is designated as a
54+
parent Orphan-type, that means this host can
55+
co-ordinate time with other peers to maintain
4256
time on the time island.
4357
4458
time.protocol - Optional - Can take the value of "chrony" or "ntp". Default
45-
Stacki behaviour is to use chrony for the hosts.
59+
Stacki behaviour is to use chrony for all hosts, except SLES 11 hosts.
60+
SLES 11 hosts use NTP by default
4661
4762
<arg optional='0' type='string' name='host'>
4863
Host name of machine
4964
</arg>
5065
5166
<example cmd='report host time backend-0-0'>
5267
Create a time configuration file for backend-0-0.
68+
69+
*Configuration*
70+
Scenario 1: Use Frontend for time keeping
71+
If the desired behavior is to use the frontend as the primary, and only time server,
72+
no configuration changes have to be made in Stacki. This is the default behavior.
73+
74+
Scenario 2: Use frontend for time keeping, in sync with an external time server.
75+
Set time.servers attribute on the frontend to the IP Address, or Hostname of an external time server.
76+
This will set the frontend to sync time against an external time server, and all other hosts to
77+
sync time from the frontend.
78+
79+
Scenario 3: Sync time on all hosts using an external time server only.
80+
Unset the frontend appliance attribute of time.orphantype. This will disable the frontend
81+
from serving time.
82+
Set the time.servers attribute for all hosts to the IP address, or Hostname of external
83+
time server. This will require that all hosts can contact, and connect to the external time
84+
server.
85+
86+
Scenario 4: Sync time on some hosts from external time servers, and create a time island.
87+
Set some of the hosts' time.orphantype attribute to parent. Then set those hosts' time.servers
88+
attribute to the IP address, or Hostname of external time server. The list of parent time servers
89+
can include the frontend or not.
90+
91+
Scenario 5: Don't use Stacki to sync time.
92+
To do this, make sure that the time.protocol attribute is not set for a host or a set of hosts.
5393
</example>
5494
"""
5595

5696
def getNTPPeers(self, host):
5797

5898
peerlist = []
59-
parents = [ h for h in self.attrs if ( self.attrs[h].get('time.orphantype') == 'parent' and h != host and h != self.frontend ) ]
99+
parents = [ h for h in self.attrs if ( self.attrs[h].get('time.orphantype') == 'parent' and h != host) ]
60100
if parents:
61101
op = self.call('list.host.interface', parents + ['expanded=true'])
62102
networks = self.getNTPNetworkNames(host)
@@ -65,6 +105,7 @@ def getNTPPeers(self, host):
65105
for i in n:
66106
if i not in peerlist:
67107
peerlist.append(i)
108+
68109
return peerlist
69110

70111
def getNTPNetworkNames(self, host):
@@ -82,7 +123,8 @@ def getNTPNetworkNames(self, host):
82123
if ntp_net_extra:
83124
for net in ntp_net_extra.split(','):
84125
if net not in stacki_networks:
85-
raise CommandError(self, f"Network {net} is unknown to Stacki. Fix 'time.networks' attribute for host {host}")
126+
raise CommandError(self, f"Network {net} is unknown to Stacki\n" + \
127+
"Fix 'time.networks' attribute for host {host}")
86128
networks.append(net)
87129
return networks
88130

@@ -103,20 +145,11 @@ def getNTPServers(self, host):
103145
if timeservers:
104146
timeservers = timeservers.split(",")
105147

106-
if self.appliance == 'frontend':
107-
if not timeservers:
108-
timeservers = [self.attrs[host].get('Kickstart_PublicNTPHost')]
109-
else:
110-
n = []
111-
output = self.call('list.host.interface', [ host , 'expanded=true'])
112-
for o in output:
113-
if o['network'] in self.frontend_ntp_addrs:
114-
n.append(self.frontend_ntp_addrs[o['network']])
115-
if not timeservers:
116-
timeservers = n
117-
else:
118-
timeservers = n + timeservers
148+
if not timeservers:
149+
timeservers = []
119150

151+
for peer in self.getNTPPeers(host):
152+
timeservers.append(peer)
120153
return timeservers
121154

122155
def set_timezone(self, host):
@@ -163,12 +196,17 @@ def run(self, params, args):
163196
self.appliance = self.attrs[host].get('appliance')
164197
self.osversion = self.attrs[host].get('os.version')
165198

199+
if protocol == None:
200+
continue
201+
166202
if self.osversion == '11.x':
167203
protocol = 'ntp'
168204

169205
self.timeservers = self.getNTPServers(host)
170206
if len(self.timeservers) == 0:
171-
raise CommandError(self, f'No time servers specified for host {host}. Check network interface assignments')
207+
if not 'time.orphantype' in self.attrs[host] or not self.attrs[host]['time.orphantype'] == 'parent':
208+
raise CommandError(self, f'No time servers specified for host {host}\n' +
209+
'Check time.* attributes, and network interface assignments')
172210

173211
self.runImplementation('time_%s' % protocol, (host))
174212

common/src/stack/command/stack/commands/report/host/time/imp_time_chrony.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,8 @@ def run(self, host):
9393
else:
9494
self.client(host)
9595

96-
self.owner.addOutput(host, "/usr/sbin/chronyd -q 'server %s iburst'" % self.owner.timeservers[0])
96+
if self.owner.timeservers:
97+
self.owner.addOutput(host, "/usr/sbin/chronyd -q 'server %s iburst'" % self.owner.timeservers[0])
9798

9899
self.owner.addOutput(host, "systemctl enable chronyd")
99100
self.owner.addOutput(host, 'systemctl start chronyd')

common/src/stack/command/stack/commands/report/host/time/imp_time_ntp.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,8 @@ def run(self, host):
125125
#
126126
# set the clock right now
127127
#
128-
self.owner.addOutput(host, '/usr/sbin/ntpdate %s' % self.owner.timeservers[0])
128+
if self.owner.timeservers:
129+
self.owner.addOutput(host, '/usr/sbin/ntpdate %s' % self.owner.timeservers[0])
129130
# Restart the NTPD service
130131
if self.owner.osversion == '11.x':
131132
self.owner.addOutput(host, 'service ntp start')

0 commit comments

Comments
 (0)