summaryrefslogtreecommitdiffstats
path: root/ansible_collections/community/hrobot/plugins/modules/boot.py
diff options
context:
space:
mode:
Diffstat (limited to 'ansible_collections/community/hrobot/plugins/modules/boot.py')
-rw-r--r--ansible_collections/community/hrobot/plugins/modules/boot.py444
1 files changed, 444 insertions, 0 deletions
diff --git a/ansible_collections/community/hrobot/plugins/modules/boot.py b/ansible_collections/community/hrobot/plugins/modules/boot.py
new file mode 100644
index 000000000..64917d9b8
--- /dev/null
+++ b/ansible_collections/community/hrobot/plugins/modules/boot.py
@@ -0,0 +1,444 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2019 Felix Fontein <felix@fontein.de>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+DOCUMENTATION = r'''
+---
+module: boot
+short_description: Set boot configuration
+version_added: 1.2.0
+author:
+ - Felix Fontein (@felixfontein)
+description:
+ - Set the boot configuration for a dedicated server.
+seealso:
+ - module: community.hrobot.ssh_key
+ description: Add, remove or update SSH key
+ - module: community.hrobot.ssh_key_info
+ description: Query information on SSH keys
+extends_documentation_fragment:
+ - community.hrobot.robot
+ - community.hrobot.attributes
+ - community.hrobot.attributes.actiongroup_robot
+
+attributes:
+ action_group:
+ version_added: 1.6.0
+ check_mode:
+ support: full
+ diff_mode:
+ support: none
+
+options:
+ server_number:
+ description:
+ - The server number of the server whose boot configuration to adjust.
+ type: int
+ required: true
+ regular_boot:
+ description:
+ - If this option is provided, all special boot configurations are removed and
+ the installed operating system will be booted up next (assuming it is bootable).
+ - Precisely one of I(regular_boot), I(rescue), I(install_linux), I(install_vnc),
+ I(install_windows), I(install_plesk), and I(install_cpanel) must be provided.
+ type: bool
+ choices:
+ - true
+ rescue:
+ description:
+ - If this option is provided, the rescue system will be activated for the next boot.
+ - Precisely one of I(regular_boot), I(rescue), I(install_linux), I(install_vnc),
+ I(install_windows), I(install_plesk), and I(install_cpanel) must be provided.
+ type: dict
+ suboptions:
+ os:
+ description:
+ - The operating system to use for the rescue system. Possible choices can
+ change over time.
+ - Currently, C(linux), C(linuxold), C(freebsd), C(freebsdold), C(freebsdax),
+ C(freebsdbetaax), C(vkvm), and C(vkvmold) seem to be available.
+ type: str
+ required: true
+ arch:
+ description:
+ - The architecture to use for the rescue system.
+ - Not all architectures are available for all operating systems.
+ - Defaults to C(64).
+ type: int
+ choices:
+ - 32
+ - 64
+ authorized_keys:
+ description:
+ - One or more SSH key fingerprints to equip the rescue system with.
+ - Only fingerprints for SSH keys deposited in the Robot API can be used.
+ - You can use the M(community.hrobot.ssh_key_info) module to query the
+ SSH keys you can use, and the M(community.hrobot.ssh_key) module to
+ add or update SSH keys.
+ type: list
+ elements: str
+ install_linux:
+ description:
+ - If this option is provided, a Linux system install will be activated for the next boot.
+ - Precisely one of I(regular_boot), I(rescue), I(install_linux), I(install_vnc),
+ I(install_windows), I(install_plesk), and I(install_cpanel) must be provided.
+ type: dict
+ suboptions:
+ dist:
+ description:
+ - The distribution to install.
+ type: str
+ required: true
+ arch:
+ description:
+ - The architecture to use for the install.
+ - Not all architectures are available for all distributions.
+ - Defaults to C(64).
+ type: int
+ choices:
+ - 32
+ - 64
+ lang:
+ description:
+ - The language to use for the operating system.
+ type: str
+ required: true
+ authorized_keys:
+ description:
+ - One or more SSH key fingerprints to equip the rescue system with.
+ - Only fingerprints for SSH keys deposited in the Robot API can be used.
+ - You can use the M(community.hrobot.ssh_key_info) module to query the
+ SSH keys you can use, and the M(community.hrobot.ssh_key) module to
+ add or update SSH keys.
+ type: list
+ elements: str
+ install_vnc:
+ description:
+ - If this option is provided, a VNC installation will be activated for the next boot.
+ - Precisely one of I(regular_boot), I(rescue), I(install_linux), I(install_vnc),
+ I(install_windows), I(install_plesk), and I(install_cpanel) must be provided.
+ type: dict
+ suboptions:
+ dist:
+ description:
+ - The distribution to install.
+ type: str
+ required: true
+ arch:
+ description:
+ - The architecture to use for the install.
+ - Not all architectures are available for all distributions.
+ - Defaults to C(64).
+ type: int
+ choices:
+ - 32
+ - 64
+ lang:
+ description:
+ - The language to use for the operating system.
+ type: str
+ required: true
+ install_windows:
+ description:
+ - If this option is provided, a Windows installation will be activated for the next boot.
+ - Precisely one of I(regular_boot), I(rescue), I(install_linux), I(install_vnc),
+ I(install_windows), I(install_plesk), and I(install_cpanel) must be provided.
+ type: dict
+ suboptions:
+ lang:
+ description:
+ - The language to use for Windows.
+ type: str
+ required: true
+ install_plesk:
+ description:
+ - If this option is provided, a Plesk installation will be activated for the next boot.
+ - Precisely one of I(regular_boot), I(rescue), I(install_linux), I(install_vnc),
+ I(install_windows), I(install_plesk), and I(install_cpanel) must be provided.
+ type: dict
+ suboptions:
+ dist:
+ description:
+ - The distribution to install.
+ type: str
+ required: true
+ arch:
+ description:
+ - The architecture to use for the install.
+ - Not all architectures are available for all distributions.
+ - Defaults to C(64).
+ type: int
+ choices:
+ - 32
+ - 64
+ lang:
+ description:
+ - The language to use for the operating system.
+ type: str
+ required: true
+ hostname:
+ description:
+ - The hostname.
+ type: str
+ required: true
+ install_cpanel:
+ description:
+ - If this option is provided, a cPanel installation will be activated for the next boot.
+ - Precisely one of I(regular_boot), I(rescue), I(install_linux), I(install_vnc),
+ I(install_windows), I(install_plesk), and I(install_cpanel) must be provided.
+ type: dict
+ suboptions:
+ dist:
+ description:
+ - The distribution to install.
+ type: str
+ required: true
+ arch:
+ description:
+ - The architecture to use for the install.
+ - Not all architectures are available for all distributions.
+ - Defaults to C(64).
+ type: int
+ choices:
+ - 32
+ - 64
+ lang:
+ description:
+ - The language to use for the operating system.
+ type: str
+ required: true
+ hostname:
+ description:
+ - The hostname.
+ type: str
+ required: true
+'''
+
+EXAMPLES = r'''
+- name: Disable all special boot configurations
+ community.hrobot.boot:
+ hetzner_user: foo
+ hetzner_password: bar
+ regular_boot: true
+
+- name: Enable a rescue system (64bit Linux) for the next boot
+ community.hrobot.boot:
+ hetzner_user: foo
+ hetzner_password: bar
+ rescue:
+ os: linux
+
+- name: Enable a Linux install for the next boot
+ community.hrobot.boot:
+ hetzner_user: foo
+ hetzner_password: bar
+ install_linux:
+ dist: CentOS 5.5 minimal
+ lang: en
+ authorized_keys:
+ - 56:29:99:a4:5d:ed:ac:95:c1:f5:88:82:90:5d:dd:10
+ - 15:28:b0:03:95:f0:77:b3:10:56:15:6b:77:22:a5:bb
+'''
+
+RETURN = r'''
+configuration_type:
+ description:
+ - Describes the active boot configuration.
+ returned: success
+ type: str
+ choices:
+ - regular_boot
+ - rescue
+ - install_linux
+ - install_vnc
+ - install_windows
+ - install_plesk
+ - install_cpanel
+password:
+ description:
+ - The root password for the active boot configuration, if available.
+ - For non-rescue boot configurations, it is avised to change the root password
+ as soon as possible.
+ returned: success and if a boot configuration other than C(regular_boot) is active
+ type: str
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible.module_utils.six.moves.urllib.parse import urlencode
+
+from ansible_collections.community.hrobot.plugins.module_utils.robot import (
+ BASE_URL,
+ ROBOT_DEFAULT_ARGUMENT_SPEC,
+ fetch_url_json,
+)
+
+
+BOOT_CONFIGURATION_DATA = [
+ ('rescue', 'rescue', {
+ 'os': ('os', 'os'),
+ 'arch': ('arch', 'arch'),
+ 'authorized_keys': ('authorized_key', 'authorized_key'),
+ }),
+ ('install_linux', 'linux', {
+ 'dist': ('dist', 'dist'),
+ 'arch': ('arch', 'arch'),
+ 'lang': ('lang', 'lang'),
+ 'authorized_keys': ('authorized_key', 'authorized_key'),
+ }),
+ ('install_vnc', 'vnc', {
+ 'dist': ('dist', 'dist'),
+ 'arch': ('arch', 'arch'),
+ 'lang': ('lang', 'lang'),
+ }),
+ ('install_windows', 'windows', {
+ 'lang': ('lang', 'lang'),
+ }),
+ ('install_plesk', 'plesk', {
+ 'dist': ('dist', 'dist'),
+ 'arch': ('arch', 'arch'),
+ 'lang': ('lang', 'lang'),
+ 'hostname': ('hostname', 'hostname'),
+ }),
+ ('install_cpanel', 'cpanel', {
+ 'dist': ('dist', 'dist'),
+ 'arch': ('arch', 'arch'),
+ 'lang': ('lang', 'lang'),
+ 'hostname': ('hostname', 'hostname'),
+ }),
+]
+
+
+def main():
+ argument_spec = dict(
+ server_number=dict(type='int', required=True),
+ regular_boot=dict(type='bool', choices=[True]),
+ rescue=dict(type='dict', options=dict(
+ os=dict(type='str', required=True),
+ arch=dict(type='int', choices=[32, 64]),
+ authorized_keys=dict(type='list', elements='str', no_log=False),
+ )),
+ install_linux=dict(type='dict', options=dict(
+ dist=dict(type='str', required=True),
+ arch=dict(type='int', choices=[32, 64]),
+ lang=dict(type='str', required=True),
+ authorized_keys=dict(type='list', elements='str', no_log=False),
+ )),
+ install_vnc=dict(type='dict', options=dict(
+ dist=dict(type='str', required=True),
+ arch=dict(type='int', choices=[32, 64]),
+ lang=dict(type='str', required=True),
+ )),
+ install_windows=dict(type='dict', options=dict(
+ lang=dict(type='str', required=True),
+ )),
+ install_plesk=dict(type='dict', options=dict(
+ dist=dict(type='str', required=True),
+ arch=dict(type='int', choices=[32, 64]),
+ lang=dict(type='str', required=True),
+ hostname=dict(type='str', required=True),
+ )),
+ install_cpanel=dict(type='dict', options=dict(
+ dist=dict(type='str', required=True),
+ arch=dict(type='int', choices=[32, 64]),
+ lang=dict(type='str', required=True),
+ hostname=dict(type='str', required=True),
+ )),
+ )
+ argument_spec.update(ROBOT_DEFAULT_ARGUMENT_SPEC)
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ mutually_exclusive=[('regular_boot', 'rescue', 'install_linux', 'install_vnc', 'install_windows', 'install_plesk', 'install_cpanel')],
+ required_one_of=[('regular_boot', 'rescue', 'install_linux', 'install_vnc', 'install_windows', 'install_plesk', 'install_cpanel')],
+ )
+
+ server_number = module.params['server_number']
+ changed = False
+
+ # Retrieve current boot config
+ url = "{0}/boot/{1}".format(BASE_URL, server_number)
+ result, error = fetch_url_json(module, url, accept_errors=['SERVER_NOT_FOUND', 'BOOT_NOT_AVAILABLE'])
+ if error is not None:
+ if error == 'SERVER_NOT_FOUND':
+ module.fail_json(msg='This server does not exist, or you do not have access rights for it')
+ if error == 'BOOT_NOT_AVAILABLE':
+ module.fail_json(msg='There is no boot configuration available for this server')
+ raise AssertionError('Unexpected error {0}'.format(error)) # pragma: no cover
+
+ # Deactivate current boot configurations that are not requested
+ for option_name, other_name, dummy in BOOT_CONFIGURATION_DATA:
+ if (result['boot'].get(other_name) or {}).get('active') and not module.params[option_name]:
+ changed = True
+ if not module.check_mode:
+ url = "{0}/boot/{1}/{2}".format(BASE_URL, server_number, other_name)
+ fetch_url_json(module, url, method='DELETE', allow_empty_result=True)
+
+ # Enable/compare boot configuration
+ return_values = {
+ 'configuration_type': 'regular_boot',
+ 'password': None,
+ }
+ for option_name, other_name, options in BOOT_CONFIGURATION_DATA:
+ if module.params[option_name]:
+ return_values['configuration_type'] = option_name
+ existing = result['boot'].get(other_name) or {}
+ return_values['password'] = existing.get('password')
+ data = {}
+ for option_key, (result_key, data_key) in options.items():
+ option = module.params[option_name][option_key]
+ if option is None or option == []:
+ continue
+ data[data_key] = option
+ if existing.get('active'):
+ # Idempotence check
+ needs_change = False
+ for option_key, (result_key, data_key) in options.items():
+ should = module.params[option_name][option_key]
+ if should is None:
+ continue
+ # unfold the return object for the idempotence check to work correctly
+ has = existing.get(data_key)
+ if has and option_key == 'authorized_keys':
+ has = [x['key']['fingerprint'] for x in has]
+ if isinstance(has, list):
+ has = sorted(has)
+ if not isinstance(should, list):
+ should = [should]
+ should = sorted(should)
+ if should != has:
+ needs_change = True
+ else:
+ needs_change = True
+
+ if needs_change:
+ changed = True
+ if not module.check_mode:
+ url = "{0}/boot/{1}/{2}".format(BASE_URL, server_number, other_name)
+ if existing.get('active'):
+ # Deactivate existing boot configuration
+ fetch_url_json(module, url, method='DELETE', allow_empty_result=True)
+ # Enable new boot configuration
+ headers = {"Content-type": "application/x-www-form-urlencoded"}
+ result, dummy = fetch_url_json(
+ module,
+ url,
+ data=urlencode(data, True),
+ headers=headers,
+ method='POST',
+ )
+ return_values['password'] = (result.get(other_name) or {}).get('password')
+ else:
+ return_values['password'] = None
+
+ module.exit_json(changed=changed, **return_values)
+
+
+if __name__ == '__main__': # pragma: no cover
+ main() # pragma: no cover