1#!/usr/bin/python
2# -*- coding: utf-8 -*-
3
4# Copyright: (c) 2013, Hiroaki Nakamura <hnakamur@gmail.com>
5# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
6
7from __future__ import absolute_import, division, print_function
8__metaclass__ = type
9
10
11DOCUMENTATION = '''
12---
13module: hostname
14author:
15    - Adrian Likins (@alikins)
16    - Hideki Saito (@saito-hideki)
17version_added: "1.4"
18short_description: Manage hostname
19requirements: [ hostname ]
20description:
21    - Set system's hostname, supports most OSs/Distributions, including those using systemd.
22    - Note, this module does *NOT* modify C(/etc/hosts). You need to modify it yourself using other modules like template or replace.
23    - Windows, HP-UX and AIX are not currently supported.
24options:
25    name:
26        description:
27            - Name of the host
28        required: true
29    use:
30        description:
31            - Which strategy to use to update the hostname.
32            - If not set we try to autodetect, but this can be problematic, specially with containers as they can present misleading information.
33        choices: ['generic', 'debian','sles', 'redhat', 'alpine', 'systemd', 'openrc', 'openbsd', 'solaris', 'freebsd']
34        version_added: '2.9'
35'''
36
37EXAMPLES = '''
38- name: Set a hostname
39  ansible.builtin.hostname:
40    name: web01
41'''
42
43import os
44import platform
45import socket
46import traceback
47
48from ansible.module_utils.basic import (
49    AnsibleModule,
50    get_distribution,
51    get_distribution_version,
52)
53from ansible.module_utils.common.sys_info import get_platform_subclass
54from ansible.module_utils.facts.system.service_mgr import ServiceMgrFactCollector
55from ansible.module_utils._text import to_native
56
57STRATS = {'generic': 'Generic', 'debian': 'Debian', 'sles': 'SLES', 'redhat': 'RedHat', 'alpine': 'Alpine',
58          'systemd': 'Systemd', 'openrc': 'OpenRC', 'openbsd': 'OpenBSD', 'solaris': 'Solaris', 'freebsd': 'FreeBSD'}
59
60
61class UnimplementedStrategy(object):
62    def __init__(self, module):
63        self.module = module
64
65    def update_current_and_permanent_hostname(self):
66        self.unimplemented_error()
67
68    def update_current_hostname(self):
69        self.unimplemented_error()
70
71    def update_permanent_hostname(self):
72        self.unimplemented_error()
73
74    def get_current_hostname(self):
75        self.unimplemented_error()
76
77    def set_current_hostname(self, name):
78        self.unimplemented_error()
79
80    def get_permanent_hostname(self):
81        self.unimplemented_error()
82
83    def set_permanent_hostname(self, name):
84        self.unimplemented_error()
85
86    def unimplemented_error(self):
87        system = platform.system()
88        distribution = get_distribution()
89        if distribution is not None:
90            msg_platform = '%s (%s)' % (system, distribution)
91        else:
92            msg_platform = system
93        self.module.fail_json(
94            msg='hostname module cannot be used on platform %s' % msg_platform)
95
96
97class Hostname(object):
98    """
99    This is a generic Hostname manipulation class that is subclassed
100    based on platform.
101
102    A subclass may wish to set different strategy instance to self.strategy.
103
104    All subclasses MUST define platform and distribution (which may be None).
105    """
106
107    platform = 'Generic'
108    distribution = None
109    strategy_class = UnimplementedStrategy
110
111    def __new__(cls, *args, **kwargs):
112        new_cls = get_platform_subclass(Hostname)
113        return super(cls, new_cls).__new__(new_cls)
114
115    def __init__(self, module):
116        self.module = module
117        self.name = module.params['name']
118        self.use = module.params['use']
119
120        if self.use is not None:
121            strat = globals()['%sStrategy' % STRATS[self.use]]
122            self.strategy = strat(module)
123        elif self.platform == 'Linux' and ServiceMgrFactCollector.is_systemd_managed(module):
124            self.strategy = SystemdStrategy(module)
125        else:
126            self.strategy = self.strategy_class(module)
127
128    def update_current_and_permanent_hostname(self):
129        return self.strategy.update_current_and_permanent_hostname()
130
131    def get_current_hostname(self):
132        return self.strategy.get_current_hostname()
133
134    def set_current_hostname(self, name):
135        self.strategy.set_current_hostname(name)
136
137    def get_permanent_hostname(self):
138        return self.strategy.get_permanent_hostname()
139
140    def set_permanent_hostname(self, name):
141        self.strategy.set_permanent_hostname(name)
142
143
144class GenericStrategy(object):
145    """
146    This is a generic Hostname manipulation strategy class.
147
148    A subclass may wish to override some or all of these methods.
149      - get_current_hostname()
150      - get_permanent_hostname()
151      - set_current_hostname(name)
152      - set_permanent_hostname(name)
153    """
154
155    def __init__(self, module):
156        self.module = module
157        self.changed = False
158        self.hostname_cmd = self.module.get_bin_path('hostnamectl', False)
159        if not self.hostname_cmd:
160            self.hostname_cmd = self.module.get_bin_path('hostname', True)
161
162    def update_current_and_permanent_hostname(self):
163        self.update_current_hostname()
164        self.update_permanent_hostname()
165        return self.changed
166
167    def update_current_hostname(self):
168        name = self.module.params['name']
169        current_name = self.get_current_hostname()
170        if current_name != name:
171            if not self.module.check_mode:
172                self.set_current_hostname(name)
173            self.changed = True
174
175    def update_permanent_hostname(self):
176        name = self.module.params['name']
177        permanent_name = self.get_permanent_hostname()
178        if permanent_name != name:
179            if not self.module.check_mode:
180                self.set_permanent_hostname(name)
181            self.changed = True
182
183    def get_current_hostname(self):
184        cmd = [self.hostname_cmd]
185        rc, out, err = self.module.run_command(cmd)
186        if rc != 0:
187            self.module.fail_json(msg="Command failed rc=%d, out=%s, err=%s" % (rc, out, err))
188        return to_native(out).strip()
189
190    def set_current_hostname(self, name):
191        cmd = [self.hostname_cmd, name]
192        rc, out, err = self.module.run_command(cmd)
193        if rc != 0:
194            self.module.fail_json(msg="Command failed rc=%d, out=%s, err=%s" % (rc, out, err))
195
196    def get_permanent_hostname(self):
197        return 'UNKNOWN'
198
199    def set_permanent_hostname(self, name):
200        pass
201
202
203class DebianStrategy(GenericStrategy):
204    """
205    This is a Debian family Hostname manipulation strategy class - it edits
206    the /etc/hostname file.
207    """
208
209    HOSTNAME_FILE = '/etc/hostname'
210
211    def get_permanent_hostname(self):
212        if not os.path.isfile(self.HOSTNAME_FILE):
213            try:
214                open(self.HOSTNAME_FILE, "a").write("")
215            except IOError as e:
216                self.module.fail_json(msg="failed to write file: %s" %
217                                          to_native(e), exception=traceback.format_exc())
218        try:
219            f = open(self.HOSTNAME_FILE)
220            try:
221                return f.read().strip()
222            finally:
223                f.close()
224        except Exception as e:
225            self.module.fail_json(msg="failed to read hostname: %s" %
226                                      to_native(e), exception=traceback.format_exc())
227
228    def set_permanent_hostname(self, name):
229        try:
230            f = open(self.HOSTNAME_FILE, 'w+')
231            try:
232                f.write("%s\n" % name)
233            finally:
234                f.close()
235        except Exception as e:
236            self.module.fail_json(msg="failed to update hostname: %s" %
237                                      to_native(e), exception=traceback.format_exc())
238
239
240class SLESStrategy(GenericStrategy):
241    """
242    This is a SLES Hostname strategy class - it edits the
243    /etc/HOSTNAME file.
244    """
245    HOSTNAME_FILE = '/etc/HOSTNAME'
246
247    def get_permanent_hostname(self):
248        if not os.path.isfile(self.HOSTNAME_FILE):
249            try:
250                open(self.HOSTNAME_FILE, "a").write("")
251            except IOError as e:
252                self.module.fail_json(msg="failed to write file: %s" %
253                                          to_native(e), exception=traceback.format_exc())
254        try:
255            f = open(self.HOSTNAME_FILE)
256            try:
257                return f.read().strip()
258            finally:
259                f.close()
260        except Exception as e:
261            self.module.fail_json(msg="failed to read hostname: %s" %
262                                      to_native(e), exception=traceback.format_exc())
263
264    def set_permanent_hostname(self, name):
265        try:
266            f = open(self.HOSTNAME_FILE, 'w+')
267            try:
268                f.write("%s\n" % name)
269            finally:
270                f.close()
271        except Exception as e:
272            self.module.fail_json(msg="failed to update hostname: %s" %
273                                      to_native(e), exception=traceback.format_exc())
274
275
276class RedHatStrategy(GenericStrategy):
277    """
278    This is a Redhat Hostname strategy class - it edits the
279    /etc/sysconfig/network file.
280    """
281    NETWORK_FILE = '/etc/sysconfig/network'
282
283    def get_permanent_hostname(self):
284        try:
285            f = open(self.NETWORK_FILE, 'rb')
286            try:
287                for line in f.readlines():
288                    if line.startswith('HOSTNAME'):
289                        k, v = line.split('=')
290                        return v.strip()
291            finally:
292                f.close()
293        except Exception as e:
294            self.module.fail_json(msg="failed to read hostname: %s" %
295                                      to_native(e), exception=traceback.format_exc())
296
297    def set_permanent_hostname(self, name):
298        try:
299            lines = []
300            found = False
301            f = open(self.NETWORK_FILE, 'rb')
302            try:
303                for line in f.readlines():
304                    if line.startswith('HOSTNAME'):
305                        lines.append("HOSTNAME=%s\n" % name)
306                        found = True
307                    else:
308                        lines.append(line)
309            finally:
310                f.close()
311            if not found:
312                lines.append("HOSTNAME=%s\n" % name)
313            f = open(self.NETWORK_FILE, 'w+')
314            try:
315                f.writelines(lines)
316            finally:
317                f.close()
318        except Exception as e:
319            self.module.fail_json(msg="failed to update hostname: %s" %
320                                      to_native(e), exception=traceback.format_exc())
321
322
323class AlpineStrategy(GenericStrategy):
324    """
325    This is a Alpine Linux Hostname manipulation strategy class - it edits
326    the /etc/hostname file then run hostname -F /etc/hostname.
327    """
328
329    HOSTNAME_FILE = '/etc/hostname'
330
331    def update_current_and_permanent_hostname(self):
332        self.update_permanent_hostname()
333        self.update_current_hostname()
334        return self.changed
335
336    def get_permanent_hostname(self):
337        if not os.path.isfile(self.HOSTNAME_FILE):
338            try:
339                open(self.HOSTNAME_FILE, "a").write("")
340            except IOError as e:
341                self.module.fail_json(msg="failed to write file: %s" %
342                                          to_native(e), exception=traceback.format_exc())
343        try:
344            f = open(self.HOSTNAME_FILE)
345            try:
346                return f.read().strip()
347            finally:
348                f.close()
349        except Exception as e:
350            self.module.fail_json(msg="failed to read hostname: %s" %
351                                      to_native(e), exception=traceback.format_exc())
352
353    def set_permanent_hostname(self, name):
354        try:
355            f = open(self.HOSTNAME_FILE, 'w+')
356            try:
357                f.write("%s\n" % name)
358            finally:
359                f.close()
360        except Exception as e:
361            self.module.fail_json(msg="failed to update hostname: %s" %
362                                      to_native(e), exception=traceback.format_exc())
363
364    def set_current_hostname(self, name):
365        cmd = [self.hostname_cmd, '-F', self.HOSTNAME_FILE]
366        rc, out, err = self.module.run_command(cmd)
367        if rc != 0:
368            self.module.fail_json(msg="Command failed rc=%d, out=%s, err=%s" % (rc, out, err))
369
370
371class SystemdStrategy(GenericStrategy):
372    """
373    This is a Systemd hostname manipulation strategy class - it uses
374    the hostnamectl command.
375    """
376
377    def get_current_hostname(self):
378        cmd = [self.hostname_cmd, '--transient', 'status']
379        rc, out, err = self.module.run_command(cmd)
380        if rc != 0:
381            self.module.fail_json(msg="Command failed rc=%d, out=%s, err=%s" % (rc, out, err))
382        return to_native(out).strip()
383
384    def set_current_hostname(self, name):
385        if len(name) > 64:
386            self.module.fail_json(msg="name cannot be longer than 64 characters on systemd servers, try a shorter name")
387        cmd = [self.hostname_cmd, '--transient', 'set-hostname', name]
388        rc, out, err = self.module.run_command(cmd)
389        if rc != 0:
390            self.module.fail_json(msg="Command failed rc=%d, out=%s, err=%s" % (rc, out, err))
391
392    def get_permanent_hostname(self):
393        cmd = [self.hostname_cmd, '--static', 'status']
394        rc, out, err = self.module.run_command(cmd)
395        if rc != 0:
396            self.module.fail_json(msg="Command failed rc=%d, out=%s, err=%s" % (rc, out, err))
397        return to_native(out).strip()
398
399    def set_permanent_hostname(self, name):
400        if len(name) > 64:
401            self.module.fail_json(msg="name cannot be longer than 64 characters on systemd servers, try a shorter name")
402        cmd = [self.hostname_cmd, '--pretty', 'set-hostname', name]
403        rc, out, err = self.module.run_command(cmd)
404        if rc != 0:
405            self.module.fail_json(msg="Command failed rc=%d, out=%s, err=%s" % (rc, out, err))
406        cmd = [self.hostname_cmd, '--static', 'set-hostname', name]
407        rc, out, err = self.module.run_command(cmd)
408        if rc != 0:
409            self.module.fail_json(msg="Command failed rc=%d, out=%s, err=%s" % (rc, out, err))
410
411
412class OpenRCStrategy(GenericStrategy):
413    """
414    This is a Gentoo (OpenRC) Hostname manipulation strategy class - it edits
415    the /etc/conf.d/hostname file.
416    """
417
418    HOSTNAME_FILE = '/etc/conf.d/hostname'
419
420    def get_permanent_hostname(self):
421        name = 'UNKNOWN'
422        try:
423            try:
424                f = open(self.HOSTNAME_FILE, 'r')
425                for line in f:
426                    line = line.strip()
427                    if line.startswith('hostname='):
428                        name = line[10:].strip('"')
429                        break
430            except Exception as e:
431                self.module.fail_json(msg="failed to read hostname: %s" %
432                                          to_native(e), exception=traceback.format_exc())
433        finally:
434            f.close()
435
436        return name
437
438    def set_permanent_hostname(self, name):
439        try:
440            try:
441                f = open(self.HOSTNAME_FILE, 'r')
442                lines = [x.strip() for x in f]
443
444                for i, line in enumerate(lines):
445                    if line.startswith('hostname='):
446                        lines[i] = 'hostname="%s"' % name
447                        break
448                f.close()
449
450                f = open(self.HOSTNAME_FILE, 'w')
451                f.write('\n'.join(lines) + '\n')
452            except Exception as e:
453                self.module.fail_json(msg="failed to update hostname: %s" %
454                                          to_native(e), exception=traceback.format_exc())
455        finally:
456            f.close()
457
458
459class OpenBSDStrategy(GenericStrategy):
460    """
461    This is a OpenBSD family Hostname manipulation strategy class - it edits
462    the /etc/myname file.
463    """
464
465    HOSTNAME_FILE = '/etc/myname'
466
467    def get_permanent_hostname(self):
468        if not os.path.isfile(self.HOSTNAME_FILE):
469            try:
470                open(self.HOSTNAME_FILE, "a").write("")
471            except IOError as e:
472                self.module.fail_json(msg="failed to write file: %s" %
473                                          to_native(e), exception=traceback.format_exc())
474        try:
475            f = open(self.HOSTNAME_FILE)
476            try:
477                return f.read().strip()
478            finally:
479                f.close()
480        except Exception as e:
481            self.module.fail_json(msg="failed to read hostname: %s" %
482                                      to_native(e), exception=traceback.format_exc())
483
484    def set_permanent_hostname(self, name):
485        try:
486            f = open(self.HOSTNAME_FILE, 'w+')
487            try:
488                f.write("%s\n" % name)
489            finally:
490                f.close()
491        except Exception as e:
492            self.module.fail_json(msg="failed to update hostname: %s" %
493                                      to_native(e), exception=traceback.format_exc())
494
495
496class SolarisStrategy(GenericStrategy):
497    """
498    This is a Solaris11 or later Hostname manipulation strategy class - it
499    execute hostname command.
500    """
501
502    def set_current_hostname(self, name):
503        cmd_option = '-t'
504        cmd = [self.hostname_cmd, cmd_option, name]
505        rc, out, err = self.module.run_command(cmd)
506        if rc != 0:
507            self.module.fail_json(msg="Command failed rc=%d, out=%s, err=%s" % (rc, out, err))
508
509    def get_permanent_hostname(self):
510        fmri = 'svc:/system/identity:node'
511        pattern = 'config/nodename'
512        cmd = '/usr/sbin/svccfg -s %s listprop -o value %s' % (fmri, pattern)
513        rc, out, err = self.module.run_command(cmd, use_unsafe_shell=True)
514        if rc != 0:
515            self.module.fail_json(msg="Command failed rc=%d, out=%s, err=%s" % (rc, out, err))
516        return to_native(out).strip()
517
518    def set_permanent_hostname(self, name):
519        cmd = [self.hostname_cmd, name]
520        rc, out, err = self.module.run_command(cmd)
521        if rc != 0:
522            self.module.fail_json(msg="Command failed rc=%d, out=%s, err=%s" % (rc, out, err))
523
524
525class FreeBSDStrategy(GenericStrategy):
526    """
527    This is a FreeBSD hostname manipulation strategy class - it edits
528    the /etc/rc.conf.d/hostname file.
529    """
530
531    HOSTNAME_FILE = '/etc/rc.conf.d/hostname'
532
533    def get_permanent_hostname(self):
534
535        name = 'UNKNOWN'
536        if not os.path.isfile(self.HOSTNAME_FILE):
537            try:
538                open(self.HOSTNAME_FILE, "a").write("hostname=temporarystub\n")
539            except IOError as e:
540                self.module.fail_json(msg="failed to write file: %s" %
541                                          to_native(e), exception=traceback.format_exc())
542        try:
543            try:
544                f = open(self.HOSTNAME_FILE, 'r')
545                for line in f:
546                    line = line.strip()
547                    if line.startswith('hostname='):
548                        name = line[10:].strip('"')
549                        break
550            except Exception as e:
551                self.module.fail_json(msg="failed to read hostname: %s" %
552                                          to_native(e), exception=traceback.format_exc())
553        finally:
554            f.close()
555
556        return name
557
558    def set_permanent_hostname(self, name):
559        try:
560            try:
561                f = open(self.HOSTNAME_FILE, 'r')
562                lines = [x.strip() for x in f]
563
564                for i, line in enumerate(lines):
565                    if line.startswith('hostname='):
566                        lines[i] = 'hostname="%s"' % name
567                        break
568                f.close()
569
570                f = open(self.HOSTNAME_FILE, 'w')
571                f.write('\n'.join(lines) + '\n')
572            except Exception as e:
573                self.module.fail_json(msg="failed to update hostname: %s" %
574                                          to_native(e), exception=traceback.format_exc())
575        finally:
576            f.close()
577
578
579class FedoraHostname(Hostname):
580    platform = 'Linux'
581    distribution = 'Fedora'
582    strategy_class = SystemdStrategy
583
584
585class SLESHostname(Hostname):
586    platform = 'Linux'
587    distribution = 'Sles'
588    try:
589        distribution_version = get_distribution_version()
590        # cast to float may raise ValueError on non SLES, we use float for a little more safety over int
591        if distribution_version and 10 <= float(distribution_version) <= 12:
592            strategy_class = SLESStrategy
593        else:
594            raise ValueError()
595    except ValueError:
596        strategy_class = UnimplementedStrategy
597
598
599class OpenSUSEHostname(Hostname):
600    platform = 'Linux'
601    distribution = 'Opensuse'
602    strategy_class = SystemdStrategy
603
604
605class OpenSUSELeapHostname(Hostname):
606    platform = 'Linux'
607    distribution = 'Opensuse-leap'
608    strategy_class = SystemdStrategy
609
610
611class OpenSUSETumbleweedHostname(Hostname):
612    platform = 'Linux'
613    distribution = 'Opensuse-tumbleweed'
614    strategy_class = SystemdStrategy
615
616
617class AsteraHostname(Hostname):
618    platform = 'Linux'
619    distribution = '"astralinuxce"'
620    strategy_class = SystemdStrategy
621
622
623class ArchHostname(Hostname):
624    platform = 'Linux'
625    distribution = 'Arch'
626    strategy_class = SystemdStrategy
627
628
629class ArchARMHostname(Hostname):
630    platform = 'Linux'
631    distribution = 'Archarm'
632    strategy_class = SystemdStrategy
633
634
635class AlmaLinuxHostname(Hostname):
636    platform = 'Linux'
637    distribution = 'Almalinux'
638    strategy_class = SystemdStrategy
639
640
641class ManjaroHostname(Hostname):
642    platform = 'Linux'
643    distribution = 'Manjaro'
644    strategy_class = SystemdStrategy
645
646
647class RHELHostname(Hostname):
648    platform = 'Linux'
649    distribution = 'Redhat'
650    strategy_class = RedHatStrategy
651
652
653class CentOSHostname(Hostname):
654    platform = 'Linux'
655    distribution = 'Centos'
656    strategy_class = RedHatStrategy
657
658
659class ClearLinuxHostname(Hostname):
660    platform = 'Linux'
661    distribution = 'Clear-linux-os'
662    strategy_class = SystemdStrategy
663
664
665class CloudlinuxserverHostname(Hostname):
666    platform = 'Linux'
667    distribution = 'Cloudlinuxserver'
668    strategy_class = RedHatStrategy
669
670
671class CloudlinuxHostname(Hostname):
672    platform = 'Linux'
673    distribution = 'Cloudlinux'
674    strategy_class = RedHatStrategy
675
676
677class CoreosHostname(Hostname):
678    platform = 'Linux'
679    distribution = 'Coreos'
680    strategy_class = SystemdStrategy
681
682
683class FlatcarHostname(Hostname):
684    platform = 'Linux'
685    distribution = 'Flatcar'
686    strategy_class = SystemdStrategy
687
688
689class ScientificHostname(Hostname):
690    platform = 'Linux'
691    distribution = 'Scientific'
692    strategy_class = RedHatStrategy
693
694
695class OracleLinuxHostname(Hostname):
696    platform = 'Linux'
697    distribution = 'Oracle'
698    strategy_class = RedHatStrategy
699
700
701class VirtuozzoLinuxHostname(Hostname):
702    platform = 'Linux'
703    distribution = 'Virtuozzo'
704    strategy_class = RedHatStrategy
705
706
707class AmazonLinuxHostname(Hostname):
708    platform = 'Linux'
709    distribution = 'Amazon'
710    strategy_class = RedHatStrategy
711
712
713class DebianHostname(Hostname):
714    platform = 'Linux'
715    distribution = 'Debian'
716    strategy_class = DebianStrategy
717
718
719class KylinHostname(Hostname):
720    platform = 'Linux'
721    distribution = 'Kylin'
722    strategy_class = DebianStrategy
723
724
725class CumulusHostname(Hostname):
726    platform = 'Linux'
727    distribution = 'Cumulus-linux'
728    strategy_class = DebianStrategy
729
730
731class KaliHostname(Hostname):
732    platform = 'Linux'
733    distribution = 'Kali'
734    strategy_class = DebianStrategy
735
736
737class UbuntuHostname(Hostname):
738    platform = 'Linux'
739    distribution = 'Ubuntu'
740    strategy_class = DebianStrategy
741
742
743class LinuxmintHostname(Hostname):
744    platform = 'Linux'
745    distribution = 'Linuxmint'
746    strategy_class = DebianStrategy
747
748
749class LinaroHostname(Hostname):
750    platform = 'Linux'
751    distribution = 'Linaro'
752    strategy_class = DebianStrategy
753
754
755class DevuanHostname(Hostname):
756    platform = 'Linux'
757    distribution = 'Devuan'
758    strategy_class = DebianStrategy
759
760
761class RaspbianHostname(Hostname):
762    platform = 'Linux'
763    distribution = 'Raspbian'
764    strategy_class = DebianStrategy
765
766
767class GentooHostname(Hostname):
768    platform = 'Linux'
769    distribution = 'Gentoo'
770    strategy_class = OpenRCStrategy
771
772
773class ALTLinuxHostname(Hostname):
774    platform = 'Linux'
775    distribution = 'Altlinux'
776    strategy_class = RedHatStrategy
777
778
779class AlpineLinuxHostname(Hostname):
780    platform = 'Linux'
781    distribution = 'Alpine'
782    strategy_class = AlpineStrategy
783
784
785class OpenBSDHostname(Hostname):
786    platform = 'OpenBSD'
787    distribution = None
788    strategy_class = OpenBSDStrategy
789
790
791class SolarisHostname(Hostname):
792    platform = 'SunOS'
793    distribution = None
794    strategy_class = SolarisStrategy
795
796
797class FreeBSDHostname(Hostname):
798    platform = 'FreeBSD'
799    distribution = None
800    strategy_class = FreeBSDStrategy
801
802
803class NetBSDHostname(Hostname):
804    platform = 'NetBSD'
805    distribution = None
806    strategy_class = FreeBSDStrategy
807
808
809class NeonHostname(Hostname):
810    platform = 'Linux'
811    distribution = 'Neon'
812    strategy_class = DebianStrategy
813
814
815class OsmcHostname(Hostname):
816    platform = 'Linux'
817    distribution = 'Osmc'
818    strategy_class = SystemdStrategy
819
820
821class VoidLinuxHostname(Hostname):
822    platform = 'Linux'
823    distribution = 'Void'
824    strategy_class = DebianStrategy
825
826
827class PopHostname(Hostname):
828    platform = 'Linux'
829    distribution = 'Pop'
830    strategy_class = DebianStrategy
831
832
833def main():
834    module = AnsibleModule(
835        argument_spec=dict(
836            name=dict(type='str', required=True),
837            use=dict(type='str', choices=STRATS.keys())
838        ),
839        supports_check_mode=True,
840    )
841
842    hostname = Hostname(module)
843    name = module.params['name']
844
845    current_hostname = hostname.get_current_hostname()
846    permanent_hostname = hostname.get_permanent_hostname()
847
848    changed = hostname.update_current_and_permanent_hostname()
849
850    if name != current_hostname:
851        name_before = current_hostname
852    elif name != permanent_hostname:
853        name_before = permanent_hostname
854
855    kw = dict(changed=changed, name=name,
856              ansible_facts=dict(ansible_hostname=name.split('.')[0],
857                                 ansible_nodename=name,
858                                 ansible_fqdn=socket.getfqdn(),
859                                 ansible_domain='.'.join(socket.getfqdn().split('.')[1:])))
860
861    if changed:
862        kw['diff'] = {'after': 'hostname = ' + name + '\n',
863                      'before': 'hostname = ' + name_before + '\n'}
864
865    module.exit_json(**kw)
866
867
868if __name__ == '__main__':
869    main()
870