Merge ~rjschwei/cloud-init:suseIntegrate into cloud-init:master

Proposed by Robert Schweikert
Status: Merged
Approved by: Scott Moser
Approved revision: 03953443306bccd173c3f06c6cf124f907396eee
Merged at revision: cbda576a7bbf846710ad55940bf8ca1f2d2194b9
Proposed branch: ~rjschwei/cloud-init:suseIntegrate
Merge into: cloud-init:master
Diff against target: 643 lines (+369/-165)
11 files modified
cloudinit/config/cc_resolv_conf.py (+1/-1)
cloudinit/distros/__init__.py (+1/-1)
cloudinit/distros/opensuse.py (+210/-0)
cloudinit/distros/sles.py (+5/-155)
templates/hosts.suse.tmpl (+0/-3)
templates/ntp.conf.opensuse.tmpl (+100/-0)
tests/unittests/test_distros/test_opensuse.py (+12/-0)
tests/unittests/test_distros/test_sles.py (+12/-0)
tests/unittests/test_handler/test_handler_locale.py (+9/-3)
tests/unittests/test_handler/test_handler_set_hostname.py (+3/-2)
tox.ini (+16/-0)
Reviewer Review Type Date Requested Status
Server Team CI bot continuous-integration Approve
Review via email: mp+329616@code.launchpad.net

Description of the change

Integrate support for openSUSE, return SLES support to a working state.

To post a comment you must log in.
Revision history for this message
Scott Moser (smoser) wrote :

Some comments inline.

Revision history for this message
Server Team CI bot (server-team-bot) wrote :

FAILED: Continuous integration, rev:8fe1df5788a6f8d840dc0ac1505a5c1442a0b595
https://jenkins.ubuntu.com/server/job/cloud-init-ci/204/
Executed test runs:
    FAILED: Checkout

Click here to trigger a rebuild:
https://jenkins.ubuntu.com/server/job/cloud-init-ci/204/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Server Team CI bot (server-team-bot) wrote :

PASSED: Continuous integration, rev:8fe1df5788a6f8d840dc0ac1505a5c1442a0b595
https://jenkins.ubuntu.com/server/job/cloud-init-ci/205/
Executed test runs:
    SUCCESS: Checkout
    SUCCESS: Unit & Style Tests
    SUCCESS: Ubuntu LTS: Build
    SUCCESS: Ubuntu LTS: Integration
    SUCCESS: MAAS Compatability Testing
    IN_PROGRESS: Declarative: Post Actions

Click here to trigger a rebuild:
https://jenkins.ubuntu.com/server/job/cloud-init-ci/205/rebuild

review: Approve (continuous-integration)
~rjschwei/cloud-init:suseIntegrate updated
583e3b4... by Robert Schweikert

- flake8 clean up for SUSE support

4ece32a... by Robert Schweikert

- Style conformance changes

Revision history for this message
Robert Schweikert (rjschwei) wrote :

@smoser can you give this another look please?

Revision history for this message
Scott Moser (smoser) wrote :

A few very small nits.
The rest looks good.
Please fix those and then I approve.

~rjschwei/cloud-init:suseIntegrate updated
0395344... by Robert Schweikert

- SUSE integration
  + Remove openSUSE specific hosts template. The hosts file gets written
    based on OS family and both SLES and openSUSE have "suse" as family
  + Add ntp config file template for openSUSE. The ntp configuration is written
    based on distro name
  + Fix up comments in main integration implementation

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/cloudinit/config/cc_resolv_conf.py b/cloudinit/config/cc_resolv_conf.py
2index 2548d1f..9812562 100644
3--- a/cloudinit/config/cc_resolv_conf.py
4+++ b/cloudinit/config/cc_resolv_conf.py
5@@ -55,7 +55,7 @@ LOG = logging.getLogger(__name__)
6
7 frequency = PER_INSTANCE
8
9-distros = ['fedora', 'rhel', 'sles']
10+distros = ['fedora', 'opensuse', 'rhel', 'sles']
11
12
13 def generate_resolv_conf(template_fn, params, target_fname="/etc/resolv.conf"):
14diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py
15index 1fd48a7..807b3ea 100755
16--- a/cloudinit/distros/__init__.py
17+++ b/cloudinit/distros/__init__.py
18@@ -35,7 +35,7 @@ OSFAMILIES = {
19 'redhat': ['centos', 'fedora', 'rhel'],
20 'gentoo': ['gentoo'],
21 'freebsd': ['freebsd'],
22- 'suse': ['sles'],
23+ 'suse': ['opensuse', 'sles'],
24 'arch': ['arch'],
25 }
26
27diff --git a/cloudinit/distros/opensuse.py b/cloudinit/distros/opensuse.py
28new file mode 100644
29index 0000000..b46007d
30--- /dev/null
31+++ b/cloudinit/distros/opensuse.py
32@@ -0,0 +1,210 @@
33+# Copyright (C) 2017 SUSE LLC
34+# Copyright (C) 2013 Hewlett-Packard Development Company, L.P.
35+#
36+# Author: Robert Schweikert <rjschwei@suse.com>
37+# Author: Juerg Haefliger <juerg.haefliger@hp.com>
38+#
39+# This file is part of cloud-init. See LICENSE file for license information.
40+
41+from cloudinit import distros
42+
43+from cloudinit.distros.parsers.hostname import HostnameConf
44+
45+from cloudinit import helpers
46+from cloudinit import log as logging
47+from cloudinit import util
48+
49+from cloudinit.distros import net_util
50+from cloudinit.distros import rhel_util as rhutil
51+from cloudinit.settings import PER_INSTANCE
52+
53+LOG = logging.getLogger(__name__)
54+
55+
56+class Distro(distros.Distro):
57+ clock_conf_fn = '/etc/sysconfig/clock'
58+ hostname_conf_fn = '/etc/HOSTNAME'
59+ init_cmd = ['service']
60+ locale_conf_fn = '/etc/sysconfig/language'
61+ network_conf_fn = '/etc/sysconfig/network'
62+ network_script_tpl = '/etc/sysconfig/network/ifcfg-%s'
63+ resolve_conf_fn = '/etc/resolv.conf'
64+ route_conf_tpl = '/etc/sysconfig/network/ifroute-%s'
65+ systemd_hostname_conf_fn = '/etc/hostname'
66+ systemd_locale_conf_fn = '/etc/locale.conf'
67+ tz_local_fn = '/etc/localtime'
68+
69+ def __init__(self, name, cfg, paths):
70+ distros.Distro.__init__(self, name, cfg, paths)
71+ self._runner = helpers.Runners(paths)
72+ self.osfamily = 'suse'
73+ cfg['ssh_svcname'] = 'sshd'
74+ if self.uses_systemd():
75+ self.init_cmd = ['systemctl']
76+ cfg['ssh_svcname'] = 'sshd.service'
77+
78+ def apply_locale(self, locale, out_fn=None):
79+ if self.uses_systemd():
80+ if not out_fn:
81+ out_fn = self.systemd_locale_conf_fn
82+ locale_cfg = {'LANG': locale}
83+ else:
84+ if not out_fn:
85+ out_fn = self.locale_conf_fn
86+ locale_cfg = {'RC_LANG': locale}
87+ rhutil.update_sysconfig_file(out_fn, locale_cfg)
88+
89+ def install_packages(self, pkglist):
90+ self.package_command(
91+ 'install',
92+ args='--auto-agree-with-licenses',
93+ pkgs=pkglist
94+ )
95+
96+ def package_command(self, command, args=None, pkgs=None):
97+ if pkgs is None:
98+ pkgs = []
99+
100+ cmd = ['zypper']
101+ # No user interaction possible, enable non-interactive mode
102+ cmd.append('--non-interactive')
103+
104+ # Command is the operation, such as install
105+ if command == 'upgrade':
106+ command = 'update'
107+ cmd.append(command)
108+
109+ # args are the arguments to the command, not global options
110+ if args and isinstance(args, str):
111+ cmd.append(args)
112+ elif args and isinstance(args, list):
113+ cmd.extend(args)
114+
115+ pkglist = util.expand_package_list('%s-%s', pkgs)
116+ cmd.extend(pkglist)
117+
118+ # Allow the output of this to flow outwards (ie not be captured)
119+ util.subp(cmd, capture=False)
120+
121+ def set_timezone(self, tz):
122+ tz_file = self._find_tz_file(tz)
123+ if self.uses_systemd():
124+ # Currently, timedatectl complains if invoked during startup
125+ # so for compatibility, create the link manually.
126+ util.del_file(self.tz_local_fn)
127+ util.sym_link(tz_file, self.tz_local_fn)
128+ else:
129+ # Adjust the sysconfig clock zone setting
130+ clock_cfg = {
131+ 'TIMEZONE': str(tz),
132+ }
133+ rhutil.update_sysconfig_file(self.clock_conf_fn, clock_cfg)
134+ # This ensures that the correct tz will be used for the system
135+ util.copy(tz_file, self.tz_local_fn)
136+
137+ def update_package_sources(self):
138+ self._runner.run("update-sources", self.package_command,
139+ ['refresh'], freq=PER_INSTANCE)
140+
141+ def _bring_up_interfaces(self, device_names):
142+ if device_names and 'all' in device_names:
143+ raise RuntimeError(('Distro %s can not translate '
144+ 'the device name "all"') % (self.name))
145+ return distros.Distro._bring_up_interfaces(self, device_names)
146+
147+ def _read_hostname(self, filename, default=None):
148+ if self.uses_systemd() and filename.endswith('/previous-hostname'):
149+ return util.load_file(filename).strip()
150+ elif self.uses_systemd():
151+ (out, _err) = util.subp(['hostname'])
152+ if len(out):
153+ return out
154+ else:
155+ return default
156+ else:
157+ try:
158+ conf = self._read_hostname_conf(filename)
159+ hostname = conf.hostname
160+ except IOError:
161+ pass
162+ if not hostname:
163+ return default
164+ return hostname
165+
166+ def _read_hostname_conf(self, filename):
167+ conf = HostnameConf(util.load_file(filename))
168+ conf.parse()
169+ return conf
170+
171+ def _read_system_hostname(self):
172+ if self.uses_systemd():
173+ host_fn = self.systemd_hostname_conf_fn
174+ else:
175+ host_fn = self.hostname_conf_fn
176+ return (host_fn, self._read_hostname(host_fn))
177+
178+ def _write_hostname(self, hostname, out_fn):
179+ if self.uses_systemd() and out_fn.endswith('/previous-hostname'):
180+ util.write_file(out_fn, hostname)
181+ elif self.uses_systemd():
182+ util.subp(['hostnamectl', 'set-hostname', str(hostname)])
183+ else:
184+ conf = None
185+ try:
186+ # Try to update the previous one
187+ # so lets see if we can read it first.
188+ conf = self._read_hostname_conf(out_fn)
189+ except IOError:
190+ pass
191+ if not conf:
192+ conf = HostnameConf('')
193+ conf.set_hostname(hostname)
194+ util.write_file(out_fn, str(conf), 0o644)
195+
196+ def _write_network(self, settings):
197+ # Convert debian settings to ifcfg format
198+ entries = net_util.translate_network(settings)
199+ LOG.debug("Translated ubuntu style network settings %s into %s",
200+ settings, entries)
201+ # Make the intermediate format as the suse format...
202+ nameservers = []
203+ searchservers = []
204+ dev_names = entries.keys()
205+ for (dev, info) in entries.items():
206+ net_fn = self.network_script_tpl % (dev)
207+ route_fn = self.route_conf_tpl % (dev)
208+ mode = None
209+ if info.get('auto', None):
210+ mode = 'auto'
211+ else:
212+ mode = 'manual'
213+ bootproto = info.get('bootproto', None)
214+ gateway = info.get('gateway', None)
215+ net_cfg = {
216+ 'BOOTPROTO': bootproto,
217+ 'BROADCAST': info.get('broadcast'),
218+ 'GATEWAY': gateway,
219+ 'IPADDR': info.get('address'),
220+ 'LLADDR': info.get('hwaddress'),
221+ 'NETMASK': info.get('netmask'),
222+ 'STARTMODE': mode,
223+ 'USERCONTROL': 'no'
224+ }
225+ if dev != 'lo':
226+ net_cfg['ETHTOOL_OPTIONS'] = ''
227+ else:
228+ net_cfg['FIREWALL'] = 'no'
229+ rhutil.update_sysconfig_file(net_fn, net_cfg, True)
230+ if gateway and bootproto == 'static':
231+ default_route = 'default %s' % gateway
232+ util.write_file(route_fn, default_route, 0o644)
233+ if 'dns-nameservers' in info:
234+ nameservers.extend(info['dns-nameservers'])
235+ if 'dns-search' in info:
236+ searchservers.extend(info['dns-search'])
237+ if nameservers or searchservers:
238+ rhutil.update_resolve_conf_file(self.resolve_conf_fn,
239+ nameservers, searchservers)
240+ return dev_names
241+
242+# vi: ts=4 expandtab
243diff --git a/cloudinit/distros/sles.py b/cloudinit/distros/sles.py
244index dbec2ed..6e336cb 100644
245--- a/cloudinit/distros/sles.py
246+++ b/cloudinit/distros/sles.py
247@@ -1,167 +1,17 @@
248-# Copyright (C) 2013 Hewlett-Packard Development Company, L.P.
249+# Copyright (C) 2017 SUSE LLC
250 #
251-# Author: Juerg Haefliger <juerg.haefliger@hp.com>
252+# Author: Robert Schweikert <rjschwei@suse.com>
253 #
254 # This file is part of cloud-init. See LICENSE file for license information.
255
256-from cloudinit import distros
257+from cloudinit.distros import opensuse
258
259-from cloudinit.distros.parsers.hostname import HostnameConf
260-
261-from cloudinit import helpers
262 from cloudinit import log as logging
263-from cloudinit import util
264-
265-from cloudinit.distros import net_util
266-from cloudinit.distros import rhel_util
267-from cloudinit.settings import PER_INSTANCE
268
269 LOG = logging.getLogger(__name__)
270
271
272-class Distro(distros.Distro):
273- clock_conf_fn = '/etc/sysconfig/clock'
274- locale_conf_fn = '/etc/sysconfig/language'
275- network_conf_fn = '/etc/sysconfig/network'
276- hostname_conf_fn = '/etc/HOSTNAME'
277- network_script_tpl = '/etc/sysconfig/network/ifcfg-%s'
278- resolve_conf_fn = '/etc/resolv.conf'
279- tz_local_fn = '/etc/localtime'
280-
281- def __init__(self, name, cfg, paths):
282- distros.Distro.__init__(self, name, cfg, paths)
283- # This will be used to restrict certain
284- # calls from repeatly happening (when they
285- # should only happen say once per instance...)
286- self._runner = helpers.Runners(paths)
287- self.osfamily = 'suse'
288-
289- def install_packages(self, pkglist):
290- self.package_command('install', args='-l', pkgs=pkglist)
291-
292- def _write_network(self, settings):
293- # Convert debian settings to ifcfg format
294- entries = net_util.translate_network(settings)
295- LOG.debug("Translated ubuntu style network settings %s into %s",
296- settings, entries)
297- # Make the intermediate format as the suse format...
298- nameservers = []
299- searchservers = []
300- dev_names = entries.keys()
301- for (dev, info) in entries.items():
302- net_fn = self.network_script_tpl % (dev)
303- mode = info.get('auto')
304- if mode and mode.lower() == 'true':
305- mode = 'auto'
306- else:
307- mode = 'manual'
308- net_cfg = {
309- 'BOOTPROTO': info.get('bootproto'),
310- 'BROADCAST': info.get('broadcast'),
311- 'GATEWAY': info.get('gateway'),
312- 'IPADDR': info.get('address'),
313- 'LLADDR': info.get('hwaddress'),
314- 'NETMASK': info.get('netmask'),
315- 'STARTMODE': mode,
316- 'USERCONTROL': 'no'
317- }
318- if dev != 'lo':
319- net_cfg['ETHERDEVICE'] = dev
320- net_cfg['ETHTOOL_OPTIONS'] = ''
321- else:
322- net_cfg['FIREWALL'] = 'no'
323- rhel_util.update_sysconfig_file(net_fn, net_cfg, True)
324- if 'dns-nameservers' in info:
325- nameservers.extend(info['dns-nameservers'])
326- if 'dns-search' in info:
327- searchservers.extend(info['dns-search'])
328- if nameservers or searchservers:
329- rhel_util.update_resolve_conf_file(self.resolve_conf_fn,
330- nameservers, searchservers)
331- return dev_names
332-
333- def apply_locale(self, locale, out_fn=None):
334- if not out_fn:
335- out_fn = self.locale_conf_fn
336- locale_cfg = {
337- 'RC_LANG': locale,
338- }
339- rhel_util.update_sysconfig_file(out_fn, locale_cfg)
340-
341- def _write_hostname(self, hostname, out_fn):
342- conf = None
343- try:
344- # Try to update the previous one
345- # so lets see if we can read it first.
346- conf = self._read_hostname_conf(out_fn)
347- except IOError:
348- pass
349- if not conf:
350- conf = HostnameConf('')
351- conf.set_hostname(hostname)
352- util.write_file(out_fn, str(conf), 0o644)
353-
354- def _read_system_hostname(self):
355- host_fn = self.hostname_conf_fn
356- return (host_fn, self._read_hostname(host_fn))
357-
358- def _read_hostname_conf(self, filename):
359- conf = HostnameConf(util.load_file(filename))
360- conf.parse()
361- return conf
362-
363- def _read_hostname(self, filename, default=None):
364- hostname = None
365- try:
366- conf = self._read_hostname_conf(filename)
367- hostname = conf.hostname
368- except IOError:
369- pass
370- if not hostname:
371- return default
372- return hostname
373-
374- def _bring_up_interfaces(self, device_names):
375- if device_names and 'all' in device_names:
376- raise RuntimeError(('Distro %s can not translate '
377- 'the device name "all"') % (self.name))
378- return distros.Distro._bring_up_interfaces(self, device_names)
379-
380- def set_timezone(self, tz):
381- tz_file = self._find_tz_file(tz)
382- # Adjust the sysconfig clock zone setting
383- clock_cfg = {
384- 'TIMEZONE': str(tz),
385- }
386- rhel_util.update_sysconfig_file(self.clock_conf_fn, clock_cfg)
387- # This ensures that the correct tz will be used for the system
388- util.copy(tz_file, self.tz_local_fn)
389-
390- def package_command(self, command, args=None, pkgs=None):
391- if pkgs is None:
392- pkgs = []
393-
394- cmd = ['zypper']
395- # No user interaction possible, enable non-interactive mode
396- cmd.append('--non-interactive')
397-
398- # Comand is the operation, such as install
399- cmd.append(command)
400-
401- # args are the arguments to the command, not global options
402- if args and isinstance(args, str):
403- cmd.append(args)
404- elif args and isinstance(args, list):
405- cmd.extend(args)
406-
407- pkglist = util.expand_package_list('%s-%s', pkgs)
408- cmd.extend(pkglist)
409-
410- # Allow the output of this to flow outwards (ie not be captured)
411- util.subp(cmd, capture=False)
412-
413- def update_package_sources(self):
414- self._runner.run("update-sources", self.package_command,
415- ['refresh'], freq=PER_INSTANCE)
416+class Distro(opensuse.Distro):
417+ pass
418
419 # vi: ts=4 expandtab
420diff --git a/templates/hosts.suse.tmpl b/templates/hosts.suse.tmpl
421index 399ec9b..b608269 100644
422--- a/templates/hosts.suse.tmpl
423+++ b/templates/hosts.suse.tmpl
424@@ -14,12 +14,9 @@ you need to add the following to config:
425 #
426 # The following lines are desirable for IPv4 capable hosts
427 127.0.0.1 localhost
428-127.0.0.1 {{fqdn}} {{hostname}}
429-
430
431 # The following lines are desirable for IPv6 capable hosts
432 ::1 localhost ipv6-localhost ipv6-loopback
433-::1 {{fqdn}} {{hostname}}
434 fe00::0 ipv6-localnet
435
436 ff00::0 ipv6-mcastprefix
437diff --git a/templates/ntp.conf.opensuse.tmpl b/templates/ntp.conf.opensuse.tmpl
438new file mode 100644
439index 0000000..5c5fc4d
440--- /dev/null
441+++ b/templates/ntp.conf.opensuse.tmpl
442@@ -0,0 +1,100 @@
443+## template:jinja
444+
445+################################################################################
446+## /etc/ntp.conf
447+##
448+## Sample NTP configuration file.
449+## See package 'ntp-doc' for documentation, Mini-HOWTO and FAQ.
450+## Copyright (c) 1998 S.u.S.E. GmbH Fuerth, Germany.
451+##
452+## Author: Michael Andres, <ma@suse.de>
453+## Michael Skibbe, <mskibbe@suse.de>
454+##
455+################################################################################
456+
457+##
458+## Radio and modem clocks by convention have addresses in the
459+## form 127.127.t.u, where t is the clock type and u is a unit
460+## number in the range 0-3.
461+##
462+## Most of these clocks require support in the form of a
463+## serial port or special bus peripheral. The particular
464+## device is normally specified by adding a soft link
465+## /dev/device-u to the particular hardware device involved,
466+## where u correspond to the unit number above.
467+##
468+## Generic DCF77 clock on serial port (Conrad DCF77)
469+## Address: 127.127.8.u
470+## Serial Port: /dev/refclock-u
471+##
472+## (create soft link /dev/refclock-0 to the particular ttyS?)
473+##
474+# server 127.127.8.0 mode 5 prefer
475+
476+##
477+## Undisciplined Local Clock. This is a fake driver intended for backup
478+## and when no outside source of synchronized time is available.
479+##
480+# server 127.127.1.0 # local clock (LCL)
481+# fudge 127.127.1.0 stratum 10 # LCL is unsynchronized
482+
483+##
484+## Add external Servers using
485+## # rcntpd addserver <yourserver>
486+## The servers will only be added to the currently running instance, not
487+## to /etc/ntp.conf.
488+##
489+{% if pools %}# pools
490+{% endif %}
491+{% for pool in pools -%}
492+pool {{pool}} iburst
493+{% endfor %}
494+{%- if servers %}# servers
495+{% endif %}
496+{% for server in servers -%}
497+server {{server}} iburst
498+{% endfor %}
499+
500+# Access control configuration; see /usr/share/doc/packages/ntp/html/accopt.html for
501+# details. The web page <http://support.ntp.org/bin/view/Support/AccessRestrictions>
502+# might also be helpful.
503+#
504+# Note that "restrict" applies to both servers and clients, so a configuration
505+# that might be intended to block requests from certain clients could also end
506+# up blocking replies from your own upstream servers.
507+
508+# By default, exchange time with everybody, but don't allow configuration.
509+restrict -4 default notrap nomodify nopeer noquery
510+restrict -6 default notrap nomodify nopeer noquery
511+
512+# Local users may interrogate the ntp server more closely.
513+restrict 127.0.0.1
514+restrict ::1
515+
516+# Clients from this (example!) subnet have unlimited access, but only if
517+# cryptographically authenticated.
518+#restrict 192.168.123.0 mask 255.255.255.0 notrust
519+
520+##
521+## Miscellaneous stuff
522+##
523+
524+driftfile /var/lib/ntp/drift/ntp.drift # path for drift file
525+
526+logfile /var/log/ntp # alternate log file
527+# logconfig =syncstatus + sysevents
528+# logconfig =all
529+
530+# statsdir /tmp/ # directory for statistics files
531+# filegen peerstats file peerstats type day enable
532+# filegen loopstats file loopstats type day enable
533+# filegen clockstats file clockstats type day enable
534+
535+#
536+# Authentication stuff
537+#
538+keys /etc/ntp.keys # path for keys file
539+trustedkey 1 # define trusted keys
540+requestkey 1 # key (7) for accessing server variables
541+controlkey 1 # key (6) for accessing server variables
542+
543diff --git a/tests/unittests/test_distros/test_opensuse.py b/tests/unittests/test_distros/test_opensuse.py
544new file mode 100644
545index 0000000..bdb1d63
546--- /dev/null
547+++ b/tests/unittests/test_distros/test_opensuse.py
548@@ -0,0 +1,12 @@
549+# This file is part of cloud-init. See LICENSE file for license information.
550+
551+from ..helpers import CiTestCase
552+
553+from . import _get_distro
554+
555+
556+class TestopenSUSE(CiTestCase):
557+
558+ def test_get_distro(self):
559+ distro = _get_distro("opensuse")
560+ self.assertEqual(distro.osfamily, 'suse')
561diff --git a/tests/unittests/test_distros/test_sles.py b/tests/unittests/test_distros/test_sles.py
562new file mode 100644
563index 0000000..c656aac
564--- /dev/null
565+++ b/tests/unittests/test_distros/test_sles.py
566@@ -0,0 +1,12 @@
567+# This file is part of cloud-init. See LICENSE file for license information.
568+
569+from ..helpers import CiTestCase
570+
571+from . import _get_distro
572+
573+
574+class TestSLES(CiTestCase):
575+
576+ def test_get_distro(self):
577+ distro = _get_distro("sles")
578+ self.assertEqual(distro.osfamily, 'suse')
579diff --git a/tests/unittests/test_handler/test_handler_locale.py b/tests/unittests/test_handler/test_handler_locale.py
580index e9a810c..aaf6c76 100644
581--- a/tests/unittests/test_handler/test_handler_locale.py
582+++ b/tests/unittests/test_handler/test_handler_locale.py
583@@ -49,9 +49,15 @@ class TestLocale(t_help.FilesystemMockingTestCase):
584 }
585 cc = self._get_cloud('sles')
586 cc_locale.handle('cc_locale', cfg, cc, LOG, [])
587-
588- contents = util.load_file('/etc/sysconfig/language', decode=False)
589+ if cc.distro.uses_systemd:
590+ locale_conf = cc.distro.systemd_locale_conf_fn
591+ else:
592+ locale_conf = cc.distro.locale_conf_fn
593+ contents = util.load_file(locale_conf, decode=False)
594 n_cfg = ConfigObj(BytesIO(contents))
595- self.assertEqual({'RC_LANG': cfg['locale']}, dict(n_cfg))
596+ if cc.distro.uses_systemd():
597+ self.assertEqual({'LANG': cfg['locale']}, dict(n_cfg))
598+ else:
599+ self.assertEqual({'RC_LANG': cfg['locale']}, dict(n_cfg))
600
601 # vi: ts=4 expandtab
602diff --git a/tests/unittests/test_handler/test_handler_set_hostname.py b/tests/unittests/test_handler/test_handler_set_hostname.py
603index 4b18de7..8165bf9 100644
604--- a/tests/unittests/test_handler/test_handler_set_hostname.py
605+++ b/tests/unittests/test_handler/test_handler_set_hostname.py
606@@ -70,7 +70,8 @@ class TestHostname(t_help.FilesystemMockingTestCase):
607 cc = cloud.Cloud(ds, paths, {}, distro, None)
608 self.patchUtils(self.tmp)
609 cc_set_hostname.handle('cc_set_hostname', cfg, cc, LOG, [])
610- contents = util.load_file("/etc/HOSTNAME")
611- self.assertEqual('blah', contents.strip())
612+ if not distro.uses_systemd():
613+ contents = util.load_file(distro.hostname_conf_fn)
614+ self.assertEqual('blah', contents.strip())
615
616 # vi: ts=4 expandtab
617diff --git a/tox.ini b/tox.ini
618index 1e7ca2d..a17156c 100644
619--- a/tox.ini
620+++ b/tox.ini
621@@ -92,6 +92,22 @@ deps =
622 six==1.9.0
623 -r{toxinidir}/test-requirements.txt
624
625+[testenv:opensusel42]
626+basepython = python2.7
627+commands = nosetests {posargs:tests/unittests}
628+deps =
629+ # requirements
630+ argparse==1.3.0
631+ jinja2==2.8
632+ PyYAML==3.11
633+ PrettyTable==0.7.2
634+ oauthlib==0.7.2
635+ configobj==5.0.6
636+ requests==2.11.1
637+ jsonpatch==1.11
638+ six==1.9.0
639+ -r{toxinidir}/test-requirements.txt
640+
641 [testenv:tip-pycodestyle]
642 commands = {envpython} -m pycodestyle {posargs:cloudinit/ tests/ tools/}
643 deps = pycodestyle

Subscribers

People subscribed via source and target branches