Repository: ansible/ansible
Pull request: Added 'use' option to hostname
  fixes #25543

##### SUMMARY
##### ISSUE TYPE
<!--- Pick one below and delete the rest -->
- Feature Pull Request

##### COMPONENT NAME
<!--- Write the short name of the module, plugin, task or feature below -->
modules/hostname

Problem statement:
hostname fails when systemd is installed but dbus is not running
##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
hostname module

##### ANSIBLE VERSION
```
ansible 2.2.1.0
```

##### OS / ENVIRONMENT
Debian Jessie

##### SUMMARY

On systems without dbus, ansible's hostname module fails with error:

```
Command failed rc=1, out=, err=Failed to create bus connection
```


With ansible 1.7 and the same system and playbook it works. Installing dbus workarounds this issue.

##### STEPS TO REPRODUCE
Run hostname task agains a machine without dbus installed.

<!--- Paste example playbooks or commands between quotes below -->
```
hosts: all
tasks:
  - hostname: name=myhost
```

##### EXPECTED RESULTS
Hostname should be set

##### ACTUAL RESULTS
```
Command failed rc=1, out=, err=Failed to create bus connection
```

Hints from issue discussion:
cc @alikins @saito-hideki
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
This error happens when `systemd` is installed but `dbus` isn't. I guess it could occur when remote user isn't `root` and `libpam-systemd` isn't installed.

When systemd is available, I am not sure if the module should try to detect if dbus is available (and fails when dbus isn't available) or if a fallback method should be used in such case.
**deleted**
sorry, mixed repos there.
Just for the record this bites us with default installations of Ubuntu 16.04 via the minimal VM option and probably also with the cloud images (untested). In difference to 14.04 and 18.04 or RHEL7 / CentOS you won't have the dbus package preinstalled which breaks hostnamectl.

So if you end up here because you dealing with minimal installations of xenial you can workaround installing the slick dependency chain with a task ahead requiring gather_facts. Not optimal and maybe the requirement of hostnamectl on systemd systems should be better documented.

```
  - name: xenial | ensure dbus for working hostnamectl
    apt:
      name: dbus
      state: present
    when: ansible_distribution_release == 'xenial'
```

```
The following additional packages will be installed:
  libcap-ng0 libdbus-1-3
Suggested packages:
  dbus-user-session | dbus-x11
The following NEW packages will be installed:
  dbus libcap-ng0 libdbus-1-3
0 upgraded, 3 newly installed, 0 to remove and 0 not upgraded.
```
Here's a efficient PR which fixes the issue by adding is-dbus-running sanity check: https://github.com/ansible/ansible/pull/55516

Probably couple more :+1: or comments would help.
@xtaran 
I guess #55516 would help since [it forces to fallback](https://github.com/agrrh/ansible/blob/6846546d96c5930d2ad1014bc126e23076d3ba43/lib/ansible/modules/system/hostname.py#L110-L115) to simple `hostname`-based mechanism which should work fine without systemd and dbus.

Commit message: Added 'use' option to hostname

  fixes #25543

Patch to review:
diff --git a/changelogs/fragments/hostname_use.yml b/changelogs/fragments/hostname_use.yml
new file mode 100644
index 00000000000000..bcf4e83625513b
--- /dev/null
+++ b/changelogs/fragments/hostname_use.yml
@@ -0,0 +1,2 @@
+minor_changes:
+    - added ``use`` option to ``hostname`` module to allow user to override autodetection.
diff --git a/lib/ansible/modules/system/hostname.py b/lib/ansible/modules/system/hostname.py
index 9a92bfd73020ca..5db14f0b61f1f8 100644
--- a/lib/ansible/modules/system/hostname.py
+++ b/lib/ansible/modules/system/hostname.py
@@ -29,6 +29,12 @@
         description:
             - Name of the host
         required: true
+    use:
+        description:
+            - Which strategy to use to update the hostname.
+            - If not set we try to autodetect, but this can be problematic, specially with containers as they can present misleading information.
+        choices: ['generic', 'debian','sles', 'redhat', 'alpine', 'systemd', 'openrc', 'openbsd', 'solaris', 'freebsd']
+        version_added: '2.9'
 '''
 
 EXAMPLES = '''
@@ -50,6 +56,9 @@
 from ansible.module_utils.facts.system.service_mgr import ServiceMgrFactCollector
 from ansible.module_utils._text import to_native
 
+STRATS = {'generic': 'Generic', 'debian': 'Debian', 'sles': 'SLES', 'redhat': 'RedHat', 'alpine': 'Alpine',
+          'systemd': 'Systemd', 'openrc': 'OpenRC', 'openbsd': 'OpenBSD', 'solaris': 'Solaris', 'freebsd': 'FreeBSD'}
+
 
 class UnimplementedStrategy(object):
     def __init__(self, module):
@@ -107,7 +116,12 @@ def __new__(cls, *args, **kwargs):
     def __init__(self, module):
         self.module = module
         self.name = module.params['name']
-        if self.platform == 'Linux' and ServiceMgrFactCollector.is_systemd_managed(module):
+        self.use = module.params['use']
+
+        if self.use is not None:
+            strat = globals()['%sStrategy' % STRATS[self.use]]
+            self.strategy = strat(module)
+        elif self.platform == 'Linux' and ServiceMgrFactCollector.is_systemd_managed(module):
             self.strategy = SystemdStrategy(module)
         else:
             self.strategy = self.strategy_class(module)
@@ -724,7 +738,8 @@ class NeonHostname(Hostname):
 def main():
     module = AnsibleModule(
         argument_spec=dict(
-            name=dict(required=True)
+            name=dict(type='str', required=True),
+            use=dict(type='str', choices=['generic', 'debian', 'sles', 'redhat', 'alpine', 'systemd', 'openrc', 'openbsd', 'solaris', 'freebsd'])
         ),
         supports_check_mode=True,
     )


Code review comments:
diff_hunk: @@ -724,7 +738,8 @@ class NeonHostname(Hostname):
 def main():
     module = AnsibleModule(
         argument_spec=dict(
-            name=dict(required=True)
+            name=dict(type='str', required=True),
+            use=dict(type='str', choices=['generic', 'debian', 'sles', 'redhat', 'alpine', 'systemd', 'openrc', 'openbsd', 'solaris', 'freebsd'])
path: lib/ansible/modules/system/hostname.py
text: @user1:
Could you do something like `choices=STRATS.keys()` here to avoid duplicating the list?