-
Notifications
You must be signed in to change notification settings - Fork 17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
CA-369899: Add NTP options DHCP|Default|Manual|None #35
Changes from 7 commits
c202279
a992e30
003ece8
af66f76
5830764
27d076b
ee123b2
f8a6181
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
# Default NTP domains are <int>.[centos|xenserver].pool.ntp.org | ||
DEFAULT_NTP_DOMAINS = [".centos.pool.ntp.org", ".xenserver.pool.ntp.org"] | ||
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
|
@@ -15,7 +15,8 @@ | |||||||
|
||||||||
import XenAPI | ||||||||
import datetime | ||||||||
import subprocess, re, shutil, sys, tempfile, socket, os | ||||||||
import time | ||||||||
import subprocess, re, shutil, sys, tempfile, socket, os, stat | ||||||||
from pprint import pprint | ||||||||
from simpleconfig import SimpleConfigFile | ||||||||
|
||||||||
|
@@ -486,9 +487,32 @@ def UpdateFromHostname(self): | |||||||
self.ScanHostname(output.split("\n")) | ||||||||
|
||||||||
def UpdateFromNTPConf(self): | ||||||||
(status, output) = getstatusoutput("/bin/cat /etc/chrony.conf") | ||||||||
if status == 0: | ||||||||
self.ScanNTPConf(output.split("\n")) | ||||||||
if not 'ntp' in self.data: | ||||||||
self.data['ntp'] = {} | ||||||||
|
||||||||
if os.path.isfile("/etc/chrony.conf"): | ||||||||
with open("/etc/chrony.conf", "r") as confFile: | ||||||||
self.ScanNTPConf(confFile.read().splitlines()) | ||||||||
|
||||||||
self.data['ntp']['method'] = "" | ||||||||
chronyPerm = os.stat("/etc/dhcp/dhclient.d/chrony.sh").st_mode | ||||||||
if ( | ||||||||
chronyPerm & stat.S_IXUSR | ||||||||
and chronyPerm & stat.S_IXGRP | ||||||||
and chronyPerm & stat.S_IXOTH | ||||||||
): | ||||||||
self.data['ntp']['method'] = "DHCP" | ||||||||
elif self.data['ntp']['servers']: | ||||||||
self.data['ntp']['method'] = "Manual" | ||||||||
|
||||||||
servers = self.data['ntp']['servers'] | ||||||||
if len(servers) == 4 and all( | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Some coding standards consider the use of magic numbers a defect, I tried to get some references, because I don't understand the meaning of 4 in this context:
Initially, skipped adding a review comment about it, but now I recognize that the meaning 4 in this case is not obvious to me. Hence, it may deserve an explanatory comment or (and?) the use of a symbolic constant (if that is sufficient) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
inDefaultNTPDomains(server) | ||||||||
for server in self.data['ntp']['servers'] | ||||||||
): | ||||||||
self.data['ntp']['method'] = "Default" | ||||||||
else: | ||||||||
self.data['ntp']['method'] = "Disabled" | ||||||||
|
||||||||
def StringToBool(self, inString): | ||||||||
return inString.lower().startswith('true') | ||||||||
|
@@ -528,17 +552,107 @@ def SaveToNTPConf(self): | |||||||
# Double-check authentication | ||||||||
Auth.Inst().AssertAuthenticated() | ||||||||
|
||||||||
file = None | ||||||||
try: | ||||||||
file = open("/etc/chrony.conf", "w") | ||||||||
for other in self.ntp.othercontents([]): | ||||||||
file.write(other+"\n") | ||||||||
for server in self.ntp.servers([]): | ||||||||
file.write("server "+server+" iburst\n") | ||||||||
with open("/etc/chrony.conf", "w") as confFile: | ||||||||
for other in self.ntp.othercontents([]): | ||||||||
confFile.write(other + "\n") | ||||||||
for server in self.ntp.servers([]): | ||||||||
confFile.write("server " + server + " iburst\n") | ||||||||
finally: | ||||||||
if file is not None: file.close() | ||||||||
self.UpdateFromNTPConf() | ||||||||
|
||||||||
# Force chronyd to update the time | ||||||||
if self.data['ntp']['method'] != "Disabled": | ||||||||
# Prompt chrony to update the time immediately | ||||||||
getoutput("chronyc makestep") | ||||||||
# Write the system time (set by the single shot NTP) to the HW clock | ||||||||
getoutput("hwclock -w") | ||||||||
|
||||||||
def AddDHCPNTP(self): | ||||||||
# Double-check authentication | ||||||||
Auth.Inst().AssertAuthenticated() | ||||||||
|
||||||||
oldPermissions = os.stat("/etc/dhcp/dhclient.d/chrony.sh").st_mode | ||||||||
newPermissions = oldPermissions | (stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH) | ||||||||
os.chmod("/etc/dhcp/dhclient.d/chrony.sh", newPermissions) | ||||||||
|
||||||||
interfaces = self.GetDHClientInterfaces() | ||||||||
for interface in interfaces: | ||||||||
ntpServer = self.GetDHCPNTPServer(interface) | ||||||||
|
||||||||
with open("/var/lib/dhclient/chrony.servers.%s" % interface, "w") as chronyFile: | ||||||||
chronyFile.write("%s iburst prefer\n" % ntpServer) | ||||||||
GeraldEV marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||
|
||||||||
# Ensure chrony is enabled | ||||||||
self.EnableService("chronyd") | ||||||||
self.EnableService("chrony-wait") | ||||||||
|
||||||||
def ResetDefaultNTPServers(self): | ||||||||
# Double-check authentication | ||||||||
Auth.Inst().AssertAuthenticated() | ||||||||
|
||||||||
Data.Inst().NTPServersSet( | ||||||||
["%d%s" % (i, DEFAULT_NTP_DOMAINS[0]) for i in range(4)] | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here, I see the magic number 4 again, it is more clear here. Checking above, I see it means ".centos.pool.ntp.org", so the code would be more obvious to read this way: CENTOS_NTP_POOL_DOMAIN = ".centos.pool.ntp.org"
DEFAULT_NTP_DOMAINS = [CENTOS_NTP_POOL_DOMAIN, ".xenserver.pool.ntp.org"] ["%d%s" % (ntp_host, CENTOS_NTP_POOL_DOMAIN) for ntp_host in range(4)] Or it could be nice to move the "." to the format string of the host: CENTOS_NTP_POOL_DOMAIN = "centos.pool.ntp.org"
DEFAULT_NTP_DOMAINS = [CENTOS_NTP_POOL_DOMAIN, "xenserver.pool.ntp.org"] ["%d.%s" % (ntp_host, CENTOS_NTP_POOL_DOMAIN) for ntp_host in range(4)] I know this also affects inDefaultNTPDomains(), but not significantly. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And then use the new constant:
Suggested change
|
||||||||
) | ||||||||
GeraldEV marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||
|
||||||||
def GetDHClientInterfaces(self): | ||||||||
try: | ||||||||
leases = [filename for filename in os.listdir("/var/lib/xcp") if "leases" in filename] | ||||||||
except OSError: | ||||||||
return [] | ||||||||
|
||||||||
pattern = "dhclient-(.*).leases" | ||||||||
interfaces = [] | ||||||||
for dhclientFile in leases: | ||||||||
match = re.match(pattern, dhclientFile) | ||||||||
if match: | ||||||||
interfaces.append(match.group(1)) | ||||||||
|
||||||||
return interfaces | ||||||||
|
||||||||
def GetDHCPNTPServer(self, interface): | ||||||||
expectedLeaseFile = "/var/lib/xcp/dhclient-%s.leases" % interface | ||||||||
if not os.path.isfile(expectedLeaseFile): | ||||||||
return None | ||||||||
|
||||||||
with open(expectedLeaseFile, "r") as leaseFile: | ||||||||
data = leaseFile.read().splitlines() | ||||||||
|
||||||||
ntpServerLines = [line for line in data if "ntp-servers" in line] | ||||||||
|
||||||||
# Extract <ip-addr> from " option ntp-servers <ip-addr>;" | ||||||||
try: | ||||||||
ntpServer = ntpServerLines[-1].split()[-1][:-1] | ||||||||
except: | ||||||||
ntpServer = None | ||||||||
|
||||||||
return ntpServer | ||||||||
|
||||||||
def RemoveDHCPNTP(self): | ||||||||
# Double-check authentication | ||||||||
Auth.Inst().AssertAuthenticated() | ||||||||
|
||||||||
oldPermissions = os.stat("/etc/dhcp/dhclient.d/chrony.sh").st_mode | ||||||||
newPermissions = oldPermissions & ~(stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH) | ||||||||
os.chmod("/etc/dhcp/dhclient.d/chrony.sh", newPermissions) | ||||||||
|
||||||||
getstatusoutput("rm -f /var/lib/dhclient/chrony.servers.*") | ||||||||
|
||||||||
def SetTimeManually(self, date): | ||||||||
# Double-check authentication | ||||||||
Auth.Inst().AssertAuthenticated() | ||||||||
|
||||||||
self.NTPServersSet([]) | ||||||||
self.RemoveDHCPNTP() | ||||||||
self.SaveToNTPConf() | ||||||||
|
||||||||
self.DisableService("chrony-wait") | ||||||||
GeraldEV marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||
self.DisableService("chronyd") | ||||||||
|
||||||||
timestring = date.strftime("%Y-%m-%d %H:%M:00") | ||||||||
getstatusoutput("date --set='%s'" % timestring) | ||||||||
getstatusoutput("hwclock --utc --systohc") | ||||||||
|
||||||||
def SaveToResolveConf(self): | ||||||||
# Double-check authentication | ||||||||
Auth.Inst().AssertAuthenticated() | ||||||||
|
@@ -724,9 +838,6 @@ def ScanHostname(self, inLines): | |||||||
self.data['sysconfig']['network']['hostname'] = inLines[0] | ||||||||
|
||||||||
def ScanNTPConf(self, inLines): | ||||||||
if not 'ntp' in self.data: | ||||||||
self.data['ntp'] = {} | ||||||||
|
||||||||
self.data['ntp']['servers'] = [] | ||||||||
self.data['ntp']['othercontents'] = [] | ||||||||
|
||||||||
|
@@ -953,6 +1064,12 @@ def DisableManagement(self): | |||||||
# Network reconfigured so this link is potentially no longer valid | ||||||||
self.session = Auth.Inst().CloseSession(self.session) | ||||||||
|
||||||||
def AdjustNTPForStaticNetwork(self): | ||||||||
self.RemoveDHCPNTP() | ||||||||
if not self.data['ntp']['servers']: # No NTP servers after removing DHCP | ||||||||
self.ResetDefaultNTPServers() | ||||||||
self.SaveToNTPConf() | ||||||||
|
||||||||
def LocalHostEnable(self): | ||||||||
Auth.Inst().AssertAuthenticatedOrPasswordUnset() | ||||||||
self.RequireSession() | ||||||||
|
@@ -1131,10 +1248,6 @@ def StopService(self, service): | |||||||
if status != 0: | ||||||||
raise Exception(output) | ||||||||
|
||||||||
def NTPStatus(self): | ||||||||
status, output = getstatusoutput("/usr/bin/ntpstat") | ||||||||
return output | ||||||||
|
||||||||
def SetVerboseBoot(self, inVerbose): | ||||||||
if inVerbose: | ||||||||
name = 'noisy' | ||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion to move the static definition of the default NTP server domain to the constants: