compatibility fixes for Fedora and RHEL
This patch fixes issues in Fedora 18 (and upcoming RHEL 7) which are
present due to their use of systemd:
- store locale configuration in /etc/locale.conf
- store hostname in /etc/hostname
- use a symlink for /etc/localtime (prior code would set the timezone
but corrupt data in /usr/share/zoneinfo due to presence of symlink)
It also contains fixes for issues unrelated to systemd adoption:
- explicitly scan /dev/sr0 with blkid in order to get the optical drive
in the blkid cache. This prevents an issue on systems running 2.6
kernels (such as RHEL 6) in which config disks on some devices won't
be detected unless the device has previously been queried.
(For reference, see https://patchwork.kernel.org/patch/1770241/)
- append a newline when rewriting sysconfig files, as this is customary
text configuration file formatting and is expected by some parsers
(such as the ifcfg-rh plugin for NetworkManager)
This commit is contained in:
@@ -47,8 +47,10 @@ class Distro(distros.Distro):
|
|||||||
# See: http://tiny.cc/6r99fw
|
# See: http://tiny.cc/6r99fw
|
||||||
clock_conf_fn = "/etc/sysconfig/clock"
|
clock_conf_fn = "/etc/sysconfig/clock"
|
||||||
locale_conf_fn = '/etc/sysconfig/i18n'
|
locale_conf_fn = '/etc/sysconfig/i18n'
|
||||||
|
systemd_locale_conf_fn = '/etc/locale.conf'
|
||||||
network_conf_fn = "/etc/sysconfig/network"
|
network_conf_fn = "/etc/sysconfig/network"
|
||||||
hostname_conf_fn = "/etc/sysconfig/network"
|
hostname_conf_fn = "/etc/sysconfig/network"
|
||||||
|
systemd_hostname_conf_fn = "/etc/hostname"
|
||||||
network_script_tpl = '/etc/sysconfig/network-scripts/ifcfg-%s'
|
network_script_tpl = '/etc/sysconfig/network-scripts/ifcfg-%s'
|
||||||
resolve_conf_fn = "/etc/resolv.conf"
|
resolve_conf_fn = "/etc/resolv.conf"
|
||||||
tz_local_fn = "/etc/localtime"
|
tz_local_fn = "/etc/localtime"
|
||||||
@@ -143,21 +145,36 @@ class Distro(distros.Distro):
|
|||||||
]
|
]
|
||||||
if not exists:
|
if not exists:
|
||||||
lines.insert(0, util.make_header())
|
lines.insert(0, util.make_header())
|
||||||
util.write_file(fn, "\n".join(lines), 0644)
|
util.write_file(fn, "\n".join(lines) + "\n", 0644)
|
||||||
|
|
||||||
|
def _dist_uses_systemd(self):
|
||||||
|
# Fedora 18 and RHEL 7 were the first adopters in their series
|
||||||
|
(dist, vers) = util.system_info()['dist'][:2]
|
||||||
|
major = (int)(vers.split('.')[0])
|
||||||
|
return ((dist.startswith('Red Hat Enterprise Linux') and major >= 7)
|
||||||
|
or (dist.startswith('Fedora') and major >= 18))
|
||||||
|
|
||||||
def apply_locale(self, locale, out_fn=None):
|
def apply_locale(self, locale, out_fn=None):
|
||||||
if not out_fn:
|
if self._dist_uses_systemd():
|
||||||
out_fn = self.locale_conf_fn
|
if not out_fn:
|
||||||
|
out_fn = self.systemd_locale_conf_fn
|
||||||
|
out_fn = self.systemd_locale_conf_fn
|
||||||
|
else:
|
||||||
|
if not out_fn:
|
||||||
|
out_fn = self.locale_conf_fn
|
||||||
locale_cfg = {
|
locale_cfg = {
|
||||||
'LANG': locale,
|
'LANG': locale,
|
||||||
}
|
}
|
||||||
self._update_sysconfig_file(out_fn, locale_cfg)
|
self._update_sysconfig_file(out_fn, locale_cfg)
|
||||||
|
|
||||||
def _write_hostname(self, hostname, out_fn):
|
def _write_hostname(self, hostname, out_fn):
|
||||||
host_cfg = {
|
if self._dist_uses_systemd():
|
||||||
'HOSTNAME': hostname,
|
util.subp(['hostnamectl', 'set-hostname', str(hostname)])
|
||||||
}
|
else:
|
||||||
self._update_sysconfig_file(out_fn, host_cfg)
|
host_cfg = {
|
||||||
|
'HOSTNAME': hostname,
|
||||||
|
}
|
||||||
|
self._update_sysconfig_file(out_fn, host_cfg)
|
||||||
|
|
||||||
def _select_hostname(self, hostname, fqdn):
|
def _select_hostname(self, hostname, fqdn):
|
||||||
# See: http://bit.ly/TwitgL
|
# See: http://bit.ly/TwitgL
|
||||||
@@ -167,15 +184,25 @@ class Distro(distros.Distro):
|
|||||||
return hostname
|
return hostname
|
||||||
|
|
||||||
def _read_system_hostname(self):
|
def _read_system_hostname(self):
|
||||||
return (self.network_conf_fn,
|
if self._dist_uses_systemd():
|
||||||
self._read_hostname(self.network_conf_fn))
|
host_fn = self.systemd_hostname_conf_fn
|
||||||
|
else:
|
||||||
|
host_fn = self.hostname_conf_fn
|
||||||
|
return (host_fn, self._read_hostname(host_fn))
|
||||||
|
|
||||||
def _read_hostname(self, filename, default=None):
|
def _read_hostname(self, filename, default=None):
|
||||||
(_exists, contents) = self._read_conf(filename)
|
if self._dist_uses_systemd():
|
||||||
if 'HOSTNAME' in contents:
|
(out, _err) = util.subp(['hostname'])
|
||||||
return contents['HOSTNAME']
|
if len(out):
|
||||||
|
return out
|
||||||
|
else:
|
||||||
|
return default
|
||||||
else:
|
else:
|
||||||
return default
|
(_exists, contents) = self._read_conf(filename)
|
||||||
|
if 'HOSTNAME' in contents:
|
||||||
|
return contents['HOSTNAME']
|
||||||
|
else:
|
||||||
|
return default
|
||||||
|
|
||||||
def _read_conf(self, fn):
|
def _read_conf(self, fn):
|
||||||
exists = False
|
exists = False
|
||||||
@@ -200,13 +227,19 @@ class Distro(distros.Distro):
|
|||||||
if not os.path.isfile(tz_file):
|
if not os.path.isfile(tz_file):
|
||||||
raise RuntimeError(("Invalid timezone %s,"
|
raise RuntimeError(("Invalid timezone %s,"
|
||||||
" no file found at %s") % (tz, tz_file))
|
" no file found at %s") % (tz, tz_file))
|
||||||
# Adjust the sysconfig clock zone setting
|
if self._dist_uses_systemd():
|
||||||
clock_cfg = {
|
# Currently, timedatectl complains if invoked during startup
|
||||||
'ZONE': str(tz),
|
# so for compatibility, create the link manually.
|
||||||
}
|
util.del_file(self.tz_local_fn)
|
||||||
self._update_sysconfig_file(self.clock_conf_fn, clock_cfg)
|
util.sym_link(tz_file, self.tz_local_fn)
|
||||||
# This ensures that the correct tz will be used for the system
|
else:
|
||||||
util.copy(tz_file, self.tz_local_fn)
|
# Adjust the sysconfig clock zone setting
|
||||||
|
clock_cfg = {
|
||||||
|
'ZONE': str(tz),
|
||||||
|
}
|
||||||
|
self._update_sysconfig_file(self.clock_conf_fn, clock_cfg)
|
||||||
|
# This ensures that the correct tz will be used for the system
|
||||||
|
util.copy(tz_file, self.tz_local_fn)
|
||||||
|
|
||||||
def package_command(self, command, args=None, pkgs=None):
|
def package_command(self, command, args=None, pkgs=None):
|
||||||
if pkgs is None:
|
if pkgs is None:
|
||||||
|
|||||||
@@ -258,6 +258,9 @@ def find_candidate_devs():
|
|||||||
* labeled with 'config-2'
|
* labeled with 'config-2'
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Query optical drive to get it in blkid cache for 2.6 kernels
|
||||||
|
util.find_devs_with(path="/dev/sr0")
|
||||||
|
|
||||||
by_fstype = (util.find_devs_with("TYPE=vfat") +
|
by_fstype = (util.find_devs_with("TYPE=vfat") +
|
||||||
util.find_devs_with("TYPE=iso9660"))
|
util.find_devs_with("TYPE=iso9660"))
|
||||||
by_label = util.find_devs_with("LABEL=config-2")
|
by_label = util.find_devs_with("LABEL=config-2")
|
||||||
|
|||||||
@@ -87,6 +87,9 @@ class DataSourceNoCloud(sources.DataSource):
|
|||||||
|
|
||||||
label = self.ds_cfg.get('fs_label', "cidata")
|
label = self.ds_cfg.get('fs_label', "cidata")
|
||||||
if label is not None:
|
if label is not None:
|
||||||
|
# Query optical drive to get it in blkid cache for 2.6 kernels
|
||||||
|
util.find_devs_with(path="/dev/sr0")
|
||||||
|
|
||||||
fslist = util.find_devs_with("TYPE=vfat")
|
fslist = util.find_devs_with("TYPE=vfat")
|
||||||
fslist.extend(util.find_devs_with("TYPE=iso9660"))
|
fslist.extend(util.find_devs_with("TYPE=iso9660"))
|
||||||
|
|
||||||
|
|||||||
@@ -408,6 +408,7 @@ def system_info():
|
|||||||
'release': platform.release(),
|
'release': platform.release(),
|
||||||
'python': platform.python_version(),
|
'python': platform.python_version(),
|
||||||
'uname': platform.uname(),
|
'uname': platform.uname(),
|
||||||
|
'dist': platform.linux_distribution(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -259,8 +259,9 @@ class TestConfigDriveDataSource(MockerTestCase):
|
|||||||
def test_find_candidates(self):
|
def test_find_candidates(self):
|
||||||
devs_with_answers = {}
|
devs_with_answers = {}
|
||||||
|
|
||||||
def my_devs_with(criteria):
|
def my_devs_with(*args, **kwargs):
|
||||||
return devs_with_answers[criteria]
|
criteria = args[0] if len(args) else kwargs.pop('criteria', None)
|
||||||
|
return devs_with_answers.get(criteria, [])
|
||||||
|
|
||||||
def my_is_partition(dev):
|
def my_is_partition(dev):
|
||||||
return dev[-1] in "0123456789" and not dev.startswith("sr")
|
return dev[-1] in "0123456789" and not dev.startswith("sr")
|
||||||
|
|||||||
Reference in New Issue
Block a user