cloud-init-dev team mailing list archive
-
cloud-init-dev team
-
Mailing list archive
-
Message #03136
Re: [Merge] ~rjschwei/cloud-init:suseIntegrate into cloud-init:master
Some comments inline.
Diff comments:
> diff --git a/cloudinit/distros/opensuse.py b/cloudinit/distros/opensuse.py
> new file mode 100644
> index 0000000..0386e10
> --- /dev/null
> +++ b/cloudinit/distros/opensuse.py
> @@ -0,0 +1,233 @@
> +# vi: ts=4 expandtab
for header information.
if you want to put copyright, you can.
see cloudinit/distros/parsers/hosts.py (example with author and copyrihgt)
or cloudinit/net/eni.py (just the single line ehader).
# This file is part of cloud-init. See LICENSE file for license information.
then, put the 'vi' line as last line in file.
> +#
> +# Copyright (C) 2017 SUSE LLC
> +# Copyright (C) 2013 Hewlett-Packard Development Company, L.P.
> +#
> +# Author: Robert Schweikert <rjschwei@xxxxxxxx>
> +# Author: Juerg Haefliger <juerg.haefliger@xxxxxx>
> +#
> +# Leaning very heavily on the RHEL and Debian implementation
> +#
> +# This program is free software: you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License version 3, as
> +# published by the Free Software Foundation.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program. If not, see <http://www.gnu.org/licenses/>.
> +
> +from cloudinit import distros
> +
> +from cloudinit.distros.parsers.hostname import HostnameConf
> +
> +from cloudinit import helpers
> +from cloudinit import log as logging
> +from cloudinit import util
> +
> +from cloudinit.distros import net_util
> +from cloudinit.distros import rhel_util as rhutil
> +#from cloudinit.net import sysconfig
remove these comment lines.
> +#from cloudinit.net.network_state import parse_net_config_data
> +from cloudinit.settings import PER_INSTANCE
> +
> +LOG = logging.getLogger(__name__)
> +
> +class Distro(distros.Distro):
> + clock_conf_fn = '/etc/sysconfig/clock'
> + hostname_conf_fn = '/etc/HOSTNAME'
> + init_cmd = ['service']
> + locale_conf_fn = '/etc/sysconfig/language'
> + network_conf_fn = '/etc/sysconfig/network'
> + network_script_tpl = '/etc/sysconfig/network/ifcfg-%s'
> + resolve_conf_fn = '/etc/resolv.conf'
> + route_conf_tpl = '/etc/sysconfig/network/ifroute-%s'
> + systemd_hostname_conf_fn = '/etc/hostname'
> + systemd_locale_conf_fn = '/etc/locale.conf'
> + tz_local_fn = '/etc/localtime'
> +
> + def __init__(self, name, cfg, paths):
> + distros.Distro.__init__(self, name, cfg, paths)
> +# self._net_renderer = sysconfig.Renderer()
> + # This will be used to restrict certain
these comments must have been copied. i'm not sure what they mean here.
> + # calls from repeatly happening (when they
> + # should only happen say once per instance...)
> + self._runner = helpers.Runners(paths)
> + self.osfamily = 'suse'
> + cfg['ssh_svcname'] = 'sshd'
> + self.systemdDist = util.which('systemctl')
> + if self.systemdDist:
can we use the 'uses_systemd()' facility for consistency?
> + self.init_cmd = ['systemctl']
> + cfg['ssh_svcname'] = 'sshd.service'
> +
> + def apply_locale(self, locale, out_fn=None):
> + if self.systemdDist:
> + if not out_fn:
> + out_fn = self.systemd_locale_conf_fn
> + locale_cfg = {'LANG': locale}
> + else:
> + if not out_fn:
> + out_fn = self.locale_conf_fn
> + locale_cfg = {'RC_LANG': locale}
> + rhutil.update_sysconfig_file(out_fn, locale_cfg)
> +
> + def install_packages(self, pkglist):
> + self.package_command('install', args='-l', pkgs=pkglist)
can you change -l to
--auto-agree-with-licenses
unless some zypper version didn't have the long format, this is more self documenting.
> +
> + def package_command(self, command, args=None, pkgs=None):
> + if pkgs is None:
> + pkgs = []
> +
> + cmd = ['zypper']
> + # No user interaction possible, enable non-interactive mode
> + cmd.append('--non-interactive')
> +
> + # Comand is the operation, such as install
> + if command == 'upgrade':
> + command = 'update'
> + cmd.append(command)
> +
> + # args are the arguments to the command, not global options
> + if args and isinstance(args, str):
> + cmd.append(args)
> + elif args and isinstance(args, list):
> + cmd.extend(args)
> +
> + pkglist = util.expand_package_list('%s-%s', pkgs)
> + cmd.extend(pkglist)
> +
> + # Allow the output of this to flow outwards (ie not be captured)
> + util.subp(cmd, capture=False)
> +
> + def set_timezone(self, tz):
> + tz_file = self._find_tz_file(tz)
> + if self.systemdDist:
> + # Currently, timedatectl complains if invoked during startup
> + # so for compatibility, create the link manually.
> + util.del_file(self.tz_local_fn)
> + util.sym_link(tz_file, self.tz_local_fn)
> + else:
> + # Adjust the sysconfig clock zone setting
> + clock_cfg = {
> + 'TIMEZONE': str(tz),
> + }
> + rhutil.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 update_package_sources(self):
> + self._runner.run("update-sources", self.package_command,
> + ['refresh'], freq=PER_INSTANCE)
> +
> +
> + def _bring_up_interfaces(self, device_names):
> + if device_names and 'all' in device_names:
> + raise RuntimeError(('Distro %s can not translate '
> + 'the device name "all"') % (self.name))
> + return distros.Distro._bring_up_interfaces(self, device_names)
> +
> +
> + def _read_hostname(self, filename, default=None):
> + if self.systemdDist and filename.endswith('/previous-hostname'):
> + return util.load_file(filename).strip()
> + elif self.systemdDist:
> + (out, _err) = util.subp(['hostname'])
> + if len(out):
> + return out
> + else:
> + return default
> + else:
> + try:
> + conf = self._read_hostname_conf(filename)
> + hostname = conf.hostname
> + except IOError:
> + pass
> + if not hostname:
> + return default
> + return hostname
> +
> + def _read_hostname_conf(self, filename):
> + conf = HostnameConf(util.load_file(filename))
> + conf.parse()
> + return conf
> +
> + def _read_system_hostname(self):
> + if self.systemdDist:
> + host_fn = self.systemd_hostname_conf_fn
> + else:
> + host_fn = self.hostname_conf_fn
> + return (host_fn, self._read_hostname(host_fn))
> +
> + def _write_hostname(self, hostname, out_fn):
> + if self.systemdDist and out_fn.endswith('/previous-hostname'):
> + util.write_file(out_fn, hostname)
> + elif self.systemdDist:
> + util.subp(['hostnamectl', 'set-hostname', str(hostname)])
> + else:
> + conf = None
> + try:
> + # Try to update the previous one
> + # so lets see if we can read it first.
> + conf = self._read_hostname_conf(out_fn)
> + except IOError:
> + pass
> + if not conf:
> + conf = HostnameConf('')
> + conf.set_hostname(hostname)
> + util.write_file(out_fn, str(conf), 0o644)
> +
> + def _write_network(self, settings):
> + # Convert debian settings to ifcfg format
> + entries = net_util.translate_network(settings)
> + LOG.debug("Translated ubuntu style network settings %s into %s",
> + settings, entries)
> + # Make the intermediate format as the suse format...
> + nameservers = []
> + searchservers = []
> + dev_names = entries.keys()
> + for (dev, info) in entries.items():
> + net_fn = self.network_script_tpl % (dev)
> + route_fn = self.route_conf_tpl % (dev)
> + mode = None
> + if info.get('auto', None):
> + mode = 'auto'
> + else:
> + mode = 'manual'
> + bootproto = info.get('bootproto', None)
> + gateway = info.get('gateway', None)
> + net_cfg = {
> + 'BOOTPROTO': bootproto,
> + 'BROADCAST': info.get('broadcast'),
> + 'GATEWAY': gateway,
> + 'IPADDR': info.get('address'),
> + 'LLADDR': info.get('hwaddress'),
> + 'NETMASK': info.get('netmask'),
> + 'STARTMODE': mode,
> + 'USERCONTROL': 'no'
> + }
> + if dev != 'lo':
> + net_cfg['ETHTOOL_OPTIONS'] = ''
> + else:
> + net_cfg['FIREWALL'] = 'no'
> + rhutil.update_sysconfig_file(net_fn, net_cfg, True)
> + if gateway and bootproto == 'static':
> + default_route = 'default %s' %gateway
> + util.write_file(route_fn, default_route, 0o644)
> + if 'dns-nameservers' in info:
> + nameservers.extend(info['dns-nameservers'])
> + if 'dns-search' in info:
> + searchservers.extend(info['dns-search'])
> + if nameservers or searchservers:
> + rhutil.update_resolve_conf_file(self.resolve_conf_fn,
> + nameservers, searchservers)
> + return dev_names
> +
> +# New interface cannot yet be implemented/used as we have to figure out
for now just drop this comments.
> +# how to have a distro specific renderer
> +# def _write_network_config(self, netconfig):
> +# ns = parse_net_config_data(netconfig)
> +# self._net_renderer.render_network_state("/", ns)
> +# return []
> diff --git a/cloudinit/distros/sles.py b/cloudinit/distros/sles.py
> index dbec2ed..f58db99 100644
> --- a/cloudinit/distros/sles.py
> +++ b/cloudinit/distros/sles.py
> @@ -1,167 +1,28 @@
> -# Copyright (C) 2013 Hewlett-Packard Development Company, L.P.
> +# vi: ts=4 expandtab
> #
> -# Author: Juerg Haefliger <juerg.haefliger@xxxxxx>
> +# Copyright (C) 2017 SUSE LLC
> #
> -# This file is part of cloud-init. See LICENSE file for license information.
> -
> -from cloudinit import distros
> +# Leaning very heavily on the RHEL and Debian implementation
headers and such cleanup.
> +# Author: Robert Schweikert <rjschwei@xxxxxxxx>
> +#
> +# This program is free software: you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License version 3, as
> +# published by the Free Software Foundation.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program. If not, see <http://www.gnu.org/licenses/>.
>
> -from cloudinit.distros.parsers.hostname import HostnameConf
> +from cloudinit.distros import opensuse
>
> -from cloudinit import helpers
> from cloudinit import log as logging
> -from cloudinit import util
> -
> -from cloudinit.distros import net_util
> -from cloudinit.distros import rhel_util
> -from cloudinit.settings import PER_INSTANCE
>
> LOG = logging.getLogger(__name__)
>
> +class Distro(opensuse.Distro):
> + pass
>
> -class Distro(distros.Distro):
> - clock_conf_fn = '/etc/sysconfig/clock'
> - locale_conf_fn = '/etc/sysconfig/language'
> - network_conf_fn = '/etc/sysconfig/network'
> - hostname_conf_fn = '/etc/HOSTNAME'
> - network_script_tpl = '/etc/sysconfig/network/ifcfg-%s'
> - resolve_conf_fn = '/etc/resolv.conf'
> - tz_local_fn = '/etc/localtime'
> -
> - def __init__(self, name, cfg, paths):
> - distros.Distro.__init__(self, name, cfg, paths)
> - # This will be used to restrict certain
> - # calls from repeatly happening (when they
> - # should only happen say once per instance...)
> - self._runner = helpers.Runners(paths)
> - self.osfamily = 'suse'
> -
> - def install_packages(self, pkglist):
> - self.package_command('install', args='-l', pkgs=pkglist)
> -
> - def _write_network(self, settings):
> - # Convert debian settings to ifcfg format
> - entries = net_util.translate_network(settings)
> - LOG.debug("Translated ubuntu style network settings %s into %s",
> - settings, entries)
> - # Make the intermediate format as the suse format...
> - nameservers = []
> - searchservers = []
> - dev_names = entries.keys()
> - for (dev, info) in entries.items():
> - net_fn = self.network_script_tpl % (dev)
> - mode = info.get('auto')
> - if mode and mode.lower() == 'true':
> - mode = 'auto'
> - else:
> - mode = 'manual'
> - net_cfg = {
> - 'BOOTPROTO': info.get('bootproto'),
> - 'BROADCAST': info.get('broadcast'),
> - 'GATEWAY': info.get('gateway'),
> - 'IPADDR': info.get('address'),
> - 'LLADDR': info.get('hwaddress'),
> - 'NETMASK': info.get('netmask'),
> - 'STARTMODE': mode,
> - 'USERCONTROL': 'no'
> - }
> - if dev != 'lo':
> - net_cfg['ETHERDEVICE'] = dev
> - net_cfg['ETHTOOL_OPTIONS'] = ''
> - else:
> - net_cfg['FIREWALL'] = 'no'
> - rhel_util.update_sysconfig_file(net_fn, net_cfg, True)
> - if 'dns-nameservers' in info:
> - nameservers.extend(info['dns-nameservers'])
> - if 'dns-search' in info:
> - searchservers.extend(info['dns-search'])
> - if nameservers or searchservers:
> - rhel_util.update_resolve_conf_file(self.resolve_conf_fn,
> - nameservers, searchservers)
> - return dev_names
> -
> - def apply_locale(self, locale, out_fn=None):
> - if not out_fn:
> - out_fn = self.locale_conf_fn
> - locale_cfg = {
> - 'RC_LANG': locale,
> - }
> - rhel_util.update_sysconfig_file(out_fn, locale_cfg)
> -
> - def _write_hostname(self, hostname, out_fn):
> - conf = None
> - try:
> - # Try to update the previous one
> - # so lets see if we can read it first.
> - conf = self._read_hostname_conf(out_fn)
> - except IOError:
> - pass
> - if not conf:
> - conf = HostnameConf('')
> - conf.set_hostname(hostname)
> - util.write_file(out_fn, str(conf), 0o644)
> -
> - def _read_system_hostname(self):
> - host_fn = self.hostname_conf_fn
> - return (host_fn, self._read_hostname(host_fn))
> -
> - def _read_hostname_conf(self, filename):
> - conf = HostnameConf(util.load_file(filename))
> - conf.parse()
> - return conf
> -
> - def _read_hostname(self, filename, default=None):
> - hostname = None
> - try:
> - conf = self._read_hostname_conf(filename)
> - hostname = conf.hostname
> - except IOError:
> - pass
> - if not hostname:
> - return default
> - return hostname
> -
> - def _bring_up_interfaces(self, device_names):
> - if device_names and 'all' in device_names:
> - raise RuntimeError(('Distro %s can not translate '
> - 'the device name "all"') % (self.name))
> - return distros.Distro._bring_up_interfaces(self, device_names)
> -
> - def set_timezone(self, tz):
> - tz_file = self._find_tz_file(tz)
> - # Adjust the sysconfig clock zone setting
> - clock_cfg = {
> - 'TIMEZONE': str(tz),
> - }
> - rhel_util.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):
> - if pkgs is None:
> - pkgs = []
> -
> - cmd = ['zypper']
> - # No user interaction possible, enable non-interactive mode
> - cmd.append('--non-interactive')
> -
> - # Comand is the operation, such as install
> - cmd.append(command)
> -
> - # args are the arguments to the command, not global options
> - if args and isinstance(args, str):
> - cmd.append(args)
> - elif args and isinstance(args, list):
> - cmd.extend(args)
> -
> - pkglist = util.expand_package_list('%s-%s', pkgs)
> - cmd.extend(pkglist)
> -
> - # Allow the output of this to flow outwards (ie not be captured)
> - util.subp(cmd, capture=False)
> -
> - def update_package_sources(self):
> - self._runner.run("update-sources", self.package_command,
> - ['refresh'], freq=PER_INSTANCE)
> -
> -# vi: ts=4 expandtab
> diff --git a/tests/unittests/test_handler/test_handler_set_hostname.py b/tests/unittests/test_handler/test_handler_set_hostname.py
> index 4b18de7..8678859 100644
> --- a/tests/unittests/test_handler/test_handler_set_hostname.py
> +++ b/tests/unittests/test_handler/test_handler_set_hostname.py
> @@ -70,7 +70,8 @@ class TestHostname(t_help.FilesystemMockingTestCase):
> cc = cloud.Cloud(ds, paths, {}, distro, None)
> self.patchUtils(self.tmp)
> cc_set_hostname.handle('cc_set_hostname', cfg, cc, LOG, [])
> - contents = util.load_file("/etc/HOSTNAME")
> - self.assertEqual('blah', contents.strip())
these tests are bad.
the set_hostname tests really should just ensure that the distro's set_hostname gets called, and we should have tests for those classes that they do the right thing rather than testing the distro classes' behavior here. I think i'm' ok with this change here, but its really a pain.
the check for 'distro.systemdDist' ends up actually checking the test environment, as it doesn't get mocked at all.
> + if not distro.systemdDist:
> + contents = util.load_file(distro.hostname_conf_fn)
> + self.assertEqual('blah', contents.strip())
>
> # vi: ts=4 expandtab
> diff --git a/tox.ini b/tox.ini
> index 1e7ca2d..a17156c 100644
> --- a/tox.ini
> +++ b/tox.ini
> @@ -92,6 +92,22 @@ deps =
> six==1.9.0
> -r{toxinidir}/test-requirements.txt
>
> +[testenv:opensusel42]
> +basepython = python2.7
> +commands = nosetests {posargs:tests/unittests}
> +deps =
> + # requirements
> + argparse==1.3.0
> + jinja2==2.8
> + PyYAML==3.11
> + PrettyTable==0.7.2
> + oauthlib==0.7.2
> + configobj==5.0.6
> + requests==2.11.1
> + jsonpatch==1.11
> + six==1.9.0
> + -r{toxinidir}/test-requirements.txt
> +
this is good thank you.
> [testenv:tip-pycodestyle]
> commands = {envpython} -m pycodestyle {posargs:cloudinit/ tests/ tools/}
> deps = pycodestyle
--
https://code.launchpad.net/~rjschwei/cloud-init/+git/cloud-init/+merge/329616
Your team cloud-init commiters is requested to review the proposed merge of ~rjschwei/cloud-init:suseIntegrate into cloud-init:master.
References