1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2026-04-24 20:59:16 +00:00

Prepare main for 13.0.0 (#11834)

* Bump version to 13.0.0.

* Remove deprecated modules and plugins.

* Remove deprecated module utils.

* Remove leftovers.

* Remove mode=compatibility.

* Change default of is_pre740 from true to false.

* Change default of force_defaults from true to false.

* Remove support for ubuntu_legacy mechanism.

* Remove cpanm compatibility tests.
This commit is contained in:
Felix Fontein 2026-04-20 12:35:43 +02:00 committed by GitHub
parent 7ce198f0e7
commit 72c13c85ad
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
62 changed files with 104 additions and 13052 deletions

View file

@ -1,232 +0,0 @@
#!/usr/bin/python
# Copyright Ansible Project
# 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 annotations
DOCUMENTATION = r"""
module: atomic_container
short_description: Manage the containers on the atomic host platform
description:
- Manage the containers on the atomic host platform.
- Allows to manage the lifecycle of a container on the atomic host platform.
deprecated:
removed_in: 13.0.0
why: Project Atomic was sunset by the end of 2019.
alternative: There is none.
author: "Giuseppe Scrivano (@giuseppe)"
requirements:
- atomic
notes:
- According to U(https://projectatomic.io/) the project has been sunset around 2019/2020, in favor of C(podman) and Fedora CoreOS.
extends_documentation_fragment:
- community.general.attributes
attributes:
check_mode:
support: none
diff_mode:
support: none
options:
backend:
description:
- Define the backend to use for the container.
required: true
choices: ["docker", "ostree"]
type: str
name:
description:
- Name of the container.
required: true
type: str
image:
description:
- The image to use to install the container.
required: true
type: str
rootfs:
description:
- Define the rootfs of the image.
type: str
state:
description:
- State of the container.
choices: ["absent", "latest", "present", "rollback"]
default: "latest"
type: str
mode:
description:
- Define if it is an user or a system container.
choices: ["user", "system"]
type: str
values:
description:
- Values for the installation of the container.
- This option is permitted only with mode 'user' or 'system'.
- The values specified here will be used at installation time as --set arguments for atomic install.
type: list
elements: str
default: []
"""
EXAMPLES = r"""
- name: Install the etcd system container
community.general.atomic_container:
name: etcd
image: rhel/etcd
backend: ostree
state: latest
mode: system
values:
- ETCD_NAME=etcd.server
- name: Uninstall the etcd system container
community.general.atomic_container:
name: etcd
image: rhel/etcd
backend: ostree
state: absent
mode: system
"""
RETURN = r"""
msg:
description: The command standard output.
returned: always
type: str
sample: 'Using default tag: latest ...'
"""
# import module snippets
import traceback
from ansible.module_utils.basic import AnsibleModule
def do_install(module, mode, rootfs, container, image, values_list, backend):
system_list = ["--system"] if mode == "system" else []
user_list = ["--user"] if mode == "user" else []
rootfs_list = [f"--rootfs={rootfs}"] if rootfs else []
atomic_bin = module.get_bin_path("atomic")
args = (
[atomic_bin, "install", f"--storage={backend}", f"--name={container}"]
+ system_list
+ user_list
+ rootfs_list
+ values_list
+ [image]
)
rc, out, err = module.run_command(args, check_rc=False)
if rc != 0:
module.fail_json(rc=rc, msg=err)
else:
changed = "Extracting" in out or "Copying blob" in out
module.exit_json(msg=out, changed=changed)
def do_update(module, container, image, values_list):
atomic_bin = module.get_bin_path("atomic")
args = [atomic_bin, "containers", "update", f"--rebase={image}"] + values_list + [container]
rc, out, err = module.run_command(args, check_rc=False)
if rc != 0:
module.fail_json(rc=rc, msg=err)
else:
changed = "Extracting" in out or "Copying blob" in out
module.exit_json(msg=out, changed=changed)
def do_uninstall(module, name, backend):
atomic_bin = module.get_bin_path("atomic")
args = [atomic_bin, "uninstall", f"--storage={backend}", name]
rc, out, err = module.run_command(args, check_rc=False)
if rc != 0:
module.fail_json(rc=rc, msg=err)
module.exit_json(msg=out, changed=True)
def do_rollback(module, name):
atomic_bin = module.get_bin_path("atomic")
args = [atomic_bin, "containers", "rollback", name]
rc, out, err = module.run_command(args, check_rc=False)
if rc != 0:
module.fail_json(rc=rc, msg=err)
else:
changed = "Rolling back" in out
module.exit_json(msg=out, changed=changed)
def core(module):
mode = module.params["mode"]
name = module.params["name"]
image = module.params["image"]
rootfs = module.params["rootfs"]
values = module.params["values"]
backend = module.params["backend"]
state = module.params["state"]
atomic_bin = module.get_bin_path("atomic")
module.run_command_environ_update = dict(LANG="C", LC_ALL="C", LC_MESSAGES="C")
values_list = [f"--set={x}" for x in values] if values else []
args = [
atomic_bin,
"containers",
"list",
"--no-trunc",
"-n",
"--all",
"-f",
f"backend={backend}",
"-f",
f"container={name}",
]
rc, out, err = module.run_command(args, check_rc=False)
if rc != 0:
module.fail_json(rc=rc, msg=err)
return
present = name in out
if state == "present" and present:
module.exit_json(msg=out, changed=False)
elif (state in ["latest", "present"]) and not present:
do_install(module, mode, rootfs, name, image, values_list, backend)
elif state == "latest":
do_update(module, name, image, values_list)
elif state == "absent":
if not present:
module.exit_json(msg="The container is not present", changed=False)
else:
do_uninstall(module, name, backend)
elif state == "rollback":
do_rollback(module, name)
def main():
module = AnsibleModule(
argument_spec=dict(
mode=dict(choices=["user", "system"]),
name=dict(required=True),
image=dict(required=True),
rootfs=dict(),
state=dict(default="latest", choices=["present", "absent", "latest", "rollback"]),
backend=dict(required=True, choices=["docker", "ostree"]),
values=dict(type="list", default=[], elements="str"),
),
)
if module.params["values"] is not None and module.params["mode"] == "default":
module.fail_json(msg="values is supported only with user or system mode")
# Verify that the platform supports atomic command
dummy = module.get_bin_path("atomic", required=True)
try:
core(module)
except Exception as e:
module.fail_json(msg=f"Unanticipated error running atomic: {e}", exception=traceback.format_exc())
if __name__ == "__main__":
main()

View file

@ -1,105 +0,0 @@
#!/usr/bin/python
# Copyright Ansible Project
# 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 annotations
DOCUMENTATION = r"""
module: atomic_host
short_description: Manage the atomic host platform
description:
- Manage the atomic host platform.
- Rebooting of Atomic host platform should be done outside this module.
deprecated:
removed_in: 13.0.0
why: Project Atomic was sunset by the end of 2019.
alternative: There is none.
author:
- Saravanan KR (@krsacme)
notes:
- Host should be an atomic platform (verified by existence of '/run/ostree-booted' file).
- According to U(https://projectatomic.io/) the project has been sunset around 2019/2020, in favor of C(podman) and Fedora CoreOS.
requirements:
- atomic
extends_documentation_fragment:
- community.general.attributes
attributes:
check_mode:
support: none
diff_mode:
support: none
options:
revision:
description:
- The version number of the atomic host to be deployed.
- Providing V(latest) will upgrade to the latest available version.
default: 'latest'
aliases: [version]
type: str
"""
EXAMPLES = r"""
- name: Upgrade the atomic host platform to the latest version (atomic host upgrade)
community.general.atomic_host:
revision: latest
- name: Deploy a specific revision as the atomic host (atomic host deploy 23.130)
community.general.atomic_host:
revision: 23.130
"""
RETURN = r"""
msg:
description: The command standard output.
returned: always
type: str
sample: 'Already on latest'
"""
import os
import traceback
from ansible.module_utils.basic import AnsibleModule
def core(module):
revision = module.params["revision"]
atomic_bin = module.get_bin_path("atomic", required=True)
module.run_command_environ_update = dict(LANG="C", LC_ALL="C", LC_MESSAGES="C")
if revision == "latest":
args = [atomic_bin, "host", "upgrade"]
else:
args = [atomic_bin, "host", "deploy", revision]
rc, out, err = module.run_command(args, check_rc=False)
if rc == 77 and revision == "latest":
module.exit_json(msg="Already on latest", changed=False)
elif rc != 0:
module.fail_json(rc=rc, msg=err)
else:
module.exit_json(msg=out, changed=True)
def main():
module = AnsibleModule(
argument_spec=dict(
revision=dict(type="str", default="latest", aliases=["version"]),
),
)
# Verify that the platform is atomic host
if not os.path.exists("/run/ostree-booted"):
module.fail_json(msg="Module atomic_host is applicable for Atomic Host Platforms only")
try:
core(module)
except Exception as e:
module.fail_json(msg=f"{e}", exception=traceback.format_exc())
if __name__ == "__main__":
main()

View file

@ -1,177 +0,0 @@
#!/usr/bin/python
# Copyright Ansible Project
# 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 annotations
DOCUMENTATION = r"""
module: atomic_image
short_description: Manage the container images on the atomic host platform
description:
- Manage the container images on the atomic host platform.
- Allows to execute the commands specified by the RUN label in the container image when present.
deprecated:
removed_in: 13.0.0
why: Project Atomic was sunset by the end of 2019.
alternative: There is none.
author:
- Saravanan KR (@krsacme)
notes:
- According to U(https://projectatomic.io/) the project has been sunset around 2019/2020, in favor of C(podman) and Fedora CoreOS.
requirements:
- atomic
extends_documentation_fragment:
- community.general.attributes
attributes:
check_mode:
support: none
diff_mode:
support: none
options:
backend:
description:
- Define the backend where the image is pulled.
choices: ['docker', 'ostree']
type: str
name:
description:
- Name of the container image.
required: true
type: str
state:
description:
- The state of the container image.
- The state V(latest) will ensure container image is upgraded to the latest version and forcefully restart container,
if running.
choices: ['absent', 'latest', 'present']
default: 'latest'
type: str
started:
description:
- Start or stop the container.
type: bool
default: true
"""
EXAMPLES = r"""
- name: Execute the run command on rsyslog container image (atomic run rhel7/rsyslog)
community.general.atomic_image:
name: rhel7/rsyslog
state: latest
- name: Pull busybox to the OSTree backend
community.general.atomic_image:
name: busybox
state: latest
backend: ostree
"""
RETURN = r"""
msg:
description: The command standard output.
returned: always
type: str
sample: 'Using default tag: latest ...'
"""
import traceback
from ansible.module_utils.basic import AnsibleModule
def do_upgrade(module, image):
atomic_bin = module.get_bin_path("atomic")
args = [atomic_bin, "update", "--force", image]
rc, out, err = module.run_command(args, check_rc=False)
if rc != 0: # something went wrong emit the msg
module.fail_json(rc=rc, msg=err)
elif "Image is up to date" in out:
return False
return True
def core(module):
image = module.params["name"]
state = module.params["state"]
started = module.params["started"]
backend = module.params["backend"]
is_upgraded = False
module.run_command_environ_update = dict(LANG="C", LC_ALL="C", LC_MESSAGES="C")
atomic_bin = module.get_bin_path("atomic")
out = {}
err = {}
rc = 0
if backend:
if state == "present" or state == "latest":
args = [atomic_bin, "pull", f"--storage={backend}", image]
rc, out, err = module.run_command(args, check_rc=False)
if rc < 0:
module.fail_json(rc=rc, msg=err)
else:
out_run = ""
if started:
args = [atomic_bin, "run", f"--storage={backend}", image]
rc, out_run, err = module.run_command(args, check_rc=False)
if rc < 0:
module.fail_json(rc=rc, msg=err)
changed = "Extracting" in out or "Copying blob" in out
module.exit_json(msg=(out + out_run), changed=changed)
elif state == "absent":
args = [atomic_bin, "images", "delete", f"--storage={backend}", image]
rc, out, err = module.run_command(args, check_rc=False)
if rc < 0:
module.fail_json(rc=rc, msg=err)
else:
changed = "Unable to find" not in out
module.exit_json(msg=out, changed=changed)
return
if state == "present" or state == "latest":
if state == "latest":
is_upgraded = do_upgrade(module, image)
if started:
args = [atomic_bin, "run", image]
else:
args = [atomic_bin, "install", image]
elif state == "absent":
args = [atomic_bin, "uninstall", image]
rc, out, err = module.run_command(args, check_rc=False)
if rc < 0:
module.fail_json(rc=rc, msg=err)
elif rc == 1 and "already present" in err:
module.exit_json(restult=err, changed=is_upgraded)
elif started and "Container is running" in out:
module.exit_json(result=out, changed=is_upgraded)
else:
module.exit_json(msg=out, changed=True)
def main():
module = AnsibleModule(
argument_spec=dict(
backend=dict(type="str", choices=["docker", "ostree"]),
name=dict(type="str", required=True),
state=dict(type="str", default="latest", choices=["absent", "latest", "present"]),
started=dict(type="bool", default=True),
),
)
# Verify that the platform supports atomic command
dummy = module.get_bin_path("atomic", required=True)
try:
core(module)
except Exception as e:
module.fail_json(msg=f"{e}", exception=traceback.format_exc())
if __name__ == "__main__":
main()

View file

@ -1,154 +0,0 @@
#!/usr/bin/python
# Copyright (c) 2016, Jonathan Mainguy <jon@soh.re>
# 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
#
# basis of code taken from the ansible twillio and nexmo modules
from __future__ import annotations
DOCUMENTATION = r"""
module: catapult
short_description: Send a sms / mms using the catapult bandwidth API
description:
- Allows notifications to be sent using SMS / MMS using the catapult bandwidth API.
deprecated:
removed_in: 13.0.0
why: >-
DNS fails to resolve the API endpoint used by the module since Oct 2024.
See L(the associated issue, https://github.com/ansible-collections/community.general/issues/10318) for details.
alternative: There is none.
extends_documentation_fragment:
- community.general.attributes
attributes:
check_mode:
support: none
diff_mode:
support: none
options:
src:
type: str
description:
- One of your catapult telephone numbers the message should come from (must be in E.164 format, like V(+19195551212)).
required: true
dest:
type: list
elements: str
description:
- The phone number or numbers the message should be sent to (must be in E.164 format, like V(+19195551212)).
required: true
msg:
type: str
description:
- The contents of the text message (must be 2048 characters or less).
required: true
media:
type: str
description:
- For MMS messages, a media URL to the location of the media to be sent with the message.
user_id:
type: str
description:
- User ID from API account page.
required: true
api_token:
type: str
description:
- API Token from API account page.
required: true
api_secret:
type: str
description:
- API Secret from API account page.
required: true
author: "Jonathan Mainguy (@Jmainguy)"
notes:
- Will return changed even if the media URL is wrong.
- Will return changed if the destination number is invalid.
"""
EXAMPLES = r"""
- name: Send a mms to multiple users
community.general.catapult:
src: "+15035555555"
dest:
- "+12525089000"
- "+12018994225"
media: "http://example.com/foobar.jpg"
msg: "Task is complete"
user_id: "{{ user_id }}"
api_token: "{{ api_token }}"
api_secret: "{{ api_secret }}"
- name: Send a sms to a single user
community.general.catapult:
src: "+15035555555"
dest: "+12018994225"
msg: "Consider yourself notified"
user_id: "{{ user_id }}"
api_token: "{{ api_token }}"
api_secret: "{{ api_secret }}"
"""
import json
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.urls import fetch_url
def send(module, src, dest, msg, media, user_id, api_token, api_secret):
"""
Send the message
"""
AGENT = "Ansible"
URI = f"https://api.catapult.inetwork.com/v1/users/{user_id}/messages"
data = {"from": src, "to": dest, "text": msg}
if media:
data["media"] = media
headers = {"User-Agent": AGENT, "Content-type": "application/json"}
# Hack module params to have the Basic auth params that fetch_url expects
module.params["url_username"] = api_token.replace("\n", "")
module.params["url_password"] = api_secret.replace("\n", "")
return fetch_url(module, URI, data=json.dumps(data), headers=headers, method="post")
def main():
module = AnsibleModule(
argument_spec=dict(
src=dict(required=True),
dest=dict(required=True, type="list", elements="str"),
msg=dict(required=True),
user_id=dict(required=True),
api_token=dict(required=True, no_log=True),
api_secret=dict(required=True, no_log=True),
media=dict(),
),
)
src = module.params["src"]
dest = module.params["dest"]
msg = module.params["msg"]
media = module.params["media"]
user_id = module.params["user_id"]
api_token = module.params["api_token"]
api_secret = module.params["api_secret"]
for number in dest:
rc, info = send(module, src, number, msg, media, user_id, api_token, api_secret)
if info["status"] != 201:
body = json.loads(info["body"])
fail_msg = body["message"]
module.fail_json(msg=fail_msg)
changed = True
module.exit_json(changed=changed)
if __name__ == "__main__":
main()

View file

@ -83,16 +83,12 @@ options:
description:
- Controls the module behavior. See notes below for more details.
- The default changed from V(compatibility) to V(new) in community.general 9.0.0.
V(compatibility) was removed from community.general 13.0.0.
- 'O(mode=new): The O(name) parameter may refer to a module name, a distribution file, a HTTP URL or a git repository
URL as described in C(cpanminus) documentation. C(cpanm) version specifiers are recognized. This is the default mode
from community.general 9.0.0 onwards.'
- 'O(mode=compatibility): This was the default mode before community.general 9.0.0. O(name) must be either a module
name or a distribution file. If the perl module given by O(name) is installed (at the exact O(version) when specified),
then nothing happens. Otherwise, it is installed using the C(cpanm) executable. O(name) cannot be an URL, or a git
URL. C(cpanm) version specifiers do not work in this mode.'
- 'B(ATTENTION): V(compatibility) mode is deprecated and will be removed in community.general 13.0.0.'
type: str
choices: [compatibility, new]
choices: [new]
default: new
version_added: 3.0.0
name_check:
@ -184,7 +180,7 @@ class CPANMinus(ModuleHelper):
install_recommendations=dict(type="bool"),
install_suggestions=dict(type="bool"),
executable=dict(type="path"),
mode=dict(type="str", default="new", choices=["compatibility", "new"]),
mode=dict(type="str", default="new", choices=["new"]),
name_check=dict(type="str"),
),
required_one_of=[("name", "from_path")],
@ -204,17 +200,8 @@ class CPANMinus(ModuleHelper):
def __init_module__(self):
v = self.vars
if v.mode == "compatibility":
if v.name_check:
self.do_raise("Parameter name_check can only be used with mode=new")
self.deprecate(
"'mode=compatibility' is deprecated, use 'mode=new' instead",
version="13.0.0",
collection_name="community.general",
)
else:
if v.name and v.from_path:
self.do_raise("Parameters 'name' and 'from_path' are mutually exclusive when 'mode=new'")
if v.name and v.from_path:
self.do_raise("Parameters 'name' and 'from_path' are mutually exclusive when 'mode=new'")
self.command = v.executable if v.executable else self.command
self.runner = CmdRunner(self.module, self.command, self.command_args_formats, check_rc=True)
@ -260,22 +247,15 @@ class CPANMinus(ModuleHelper):
def __run__(self):
def process(rc, out, err):
if self.vars.mode == "compatibility" and rc != 0:
self.do_raise(msg=err, cmd=self.vars.cmd_args)
return "is up to date" not in err and "is up to date" not in out
v = self.vars
pkg_param = "from_path" if v.from_path else "name"
if v.mode == "compatibility":
if self._is_package_installed(v.name, v.locallib, v.version):
return
pkg_spec = v[pkg_param]
else:
installed = self._is_package_installed(v.name_check, v.locallib, v.version) if v.name_check else False
if installed:
return
pkg_spec = self.sanitize_pkg_spec_version(v[pkg_param], v.version)
installed = self._is_package_installed(v.name_check, v.locallib, v.version) if v.name_check else False
if installed:
return
pkg_spec = self.sanitize_pkg_spec_version(v[pkg_param], v.version)
with self.runner(
[

View file

@ -1,272 +0,0 @@
#!/usr/bin/python
#
# Copyright (c) 2016 Dimension Data
# Authors:
# - Aimon Bustardo <aimon.bustardo@dimensiondata.com>
# - Bert Diwa <Lamberto.Diwa@dimensiondata.com>
# - Adam Friedman <tintoy@tintoy.io>
#
# 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 annotations
DOCUMENTATION = r"""
module: dimensiondata_network
short_description: Create, update, and delete MCP 1.0 & 2.0 networks
extends_documentation_fragment:
- community.general.dimensiondata
- community.general.dimensiondata_wait
- community.general.attributes
description:
- Create, update, and delete MCP 1.0 & 2.0 networks.
deprecated:
removed_in: 13.0.0
why: Service and its endpoints are no longer available.
alternative: There is none.
author: 'Aimon Bustardo (@aimonb)'
attributes:
check_mode:
support: none
diff_mode:
support: none
options:
name:
description:
- The name of the network domain to create.
required: true
type: str
description:
description:
- Additional description of the network domain.
type: str
service_plan:
description:
- The service plan, either "ESSENTIALS" or "ADVANCED".
- MCP 2.0 Only.
choices: [ESSENTIALS, ADVANCED]
default: ESSENTIALS
type: str
state:
description:
- Should the resource be present or absent.
choices: [present, absent]
default: present
type: str
"""
EXAMPLES = r"""
- name: Create an MCP 1.0 network
community.general.dimensiondata_network:
region: na
location: NA5
name: mynet
- name: Create an MCP 2.0 network
community.general.dimensiondata_network:
region: na
mcp_user: my_user
mcp_password: my_password
location: NA9
name: mynet
service_plan: ADVANCED
- name: Delete a network
community.general.dimensiondata_network:
region: na
location: NA1
name: mynet
state: absent
"""
RETURN = r"""
network:
description: Dictionary describing the network.
returned: On success when O(state=present).
type: complex
contains:
id:
description: Network ID.
type: str
sample: "8c787000-a000-4050-a215-280893411a7d"
name:
description: Network name.
type: str
sample: "My network"
description:
description: Network description.
type: str
sample: "My network description"
location:
description: Datacenter location.
type: str
sample: NA3
status:
description: Network status. (MCP 2.0 only).
type: str
sample: NORMAL
private_net:
description: Private network subnet. (MCP 1.0 only).
type: str
sample: "10.2.3.0"
multicast:
description: Multicast enabled? (MCP 1.0 only).
type: bool
sample: false
"""
import traceback
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.dimensiondata import HAS_LIBCLOUD, DimensionDataModule
if HAS_LIBCLOUD:
from libcloud.common.dimensiondata import DimensionDataAPIException
from libcloud.compute.base import NodeLocation
class DimensionDataNetworkModule(DimensionDataModule):
"""
The dimensiondata_network module for Ansible.
"""
def __init__(self):
"""
Create a new Dimension Data network module.
"""
super().__init__(
module=AnsibleModule(
argument_spec=DimensionDataModule.argument_spec_with_wait(
name=dict(type="str", required=True),
description=dict(type="str"),
service_plan=dict(default="ESSENTIALS", choices=["ADVANCED", "ESSENTIALS"]),
state=dict(default="present", choices=["present", "absent"]),
),
required_together=DimensionDataModule.required_together(),
)
)
self.name = self.module.params["name"]
self.description = self.module.params["description"]
self.service_plan = self.module.params["service_plan"]
self.state = self.module.params["state"]
def state_present(self):
network = self._get_network()
if network:
self.module.exit_json(changed=False, msg="Network already exists", network=self._network_to_dict(network))
network = self._create_network()
self.module.exit_json(
changed=True,
msg=f'Created network "{self.name}" in datacenter "{self.location}".',
network=self._network_to_dict(network),
)
def state_absent(self):
network = self._get_network()
if not network:
self.module.exit_json(
changed=False, msg=f'Network "{self.name}" does not exist', network=self._network_to_dict(network)
)
self._delete_network(network)
def _get_network(self):
if self.mcp_version == "1.0":
networks = self.driver.list_networks(location=self.location)
else:
networks = self.driver.ex_list_network_domains(location=self.location)
matched_network = [network for network in networks if network.name == self.name]
if matched_network:
return matched_network[0]
return None
def _network_to_dict(self, network):
network_dict = dict(id=network.id, name=network.name, description=network.description)
if isinstance(network.location, NodeLocation):
network_dict["location"] = network.location.id
else:
network_dict["location"] = network.location
if self.mcp_version == "1.0":
network_dict["private_net"] = network.private_net
network_dict["multicast"] = network.multicast
network_dict["status"] = None
else:
network_dict["private_net"] = None
network_dict["multicast"] = None
network_dict["status"] = network.status
return network_dict
def _create_network(self):
# Make sure service_plan argument is defined
if self.mcp_version == "2.0" and "service_plan" not in self.module.params:
self.module.fail_json(msg="service_plan required when creating network and location is MCP 2.0")
# Create network
try:
if self.mcp_version == "1.0":
network = self.driver.ex_create_network(self.location, self.name, description=self.description)
else:
network = self.driver.ex_create_network_domain(
self.location, self.name, self.module.params["service_plan"], description=self.description
)
except DimensionDataAPIException as e:
self.module.fail_json(msg=f"Failed to create new network: {e}", exception=traceback.format_exc())
if self.module.params["wait"] is True:
network = self._wait_for_network_state(network.id, "NORMAL")
return network
def _delete_network(self, network):
try:
if self.mcp_version == "1.0":
deleted = self.driver.ex_delete_network(network)
else:
deleted = self.driver.ex_delete_network_domain(network)
if deleted:
self.module.exit_json(changed=True, msg=f"Deleted network with id {network.id}")
self.module.fail_json(f"Unexpected failure deleting network with id {network.id}")
except DimensionDataAPIException as e:
self.module.fail_json(msg=f"Failed to delete network: {e}", exception=traceback.format_exc())
def _wait_for_network_state(self, net_id, state_to_wait_for):
try:
return self.driver.connection.wait_for_state(
state_to_wait_for,
self.driver.ex_get_network_domain,
self.module.params["wait_poll_interval"],
self.module.params["wait_time"],
net_id,
)
except DimensionDataAPIException as e:
self.module.fail_json(
msg=f"Network did not reach {state_to_wait_for} state in time: {e}",
exception=traceback.format_exc(),
)
def main():
module = DimensionDataNetworkModule()
if module.state == "present":
module.state_present()
elif module.state == "absent":
module.state_absent()
if __name__ == "__main__":
main()

View file

@ -1,530 +0,0 @@
#!/usr/bin/python
# Copyright (c) 2016 Dimension Data
# 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
#
# Authors:
# - Adam Friedman <tintoy@tintoy.io>
from __future__ import annotations
DOCUMENTATION = r"""
module: dimensiondata_vlan
short_description: Manage a VLAN in a Cloud Control network domain
extends_documentation_fragment:
- community.general.dimensiondata
- community.general.dimensiondata_wait
- community.general.attributes
description:
- Manage VLANs in Cloud Control network domains.
deprecated:
removed_in: 13.0.0
why: Service and its endpoints are no longer available.
alternative: There is none.
author: 'Adam Friedman (@tintoy)'
attributes:
check_mode:
support: none
diff_mode:
support: none
options:
name:
description:
- The name of the target VLAN.
type: str
required: true
description:
description:
- A description of the VLAN.
type: str
default: ''
network_domain:
description:
- The ID or name of the target network domain.
required: true
type: str
private_ipv4_base_address:
description:
- The base address for the VLAN's IPv4 network (for example V(192.168.1.0)).
type: str
default: ''
private_ipv4_prefix_size:
description:
- The size of the IPv4 address space, for example V(24).
- Required, if O(private_ipv4_base_address) is specified.
type: int
default: 0
state:
description:
- The desired state for the target VLAN.
- V(readonly) ensures that the state is only ever read, not modified (the module fails if the resource does not exist).
choices: [present, absent, readonly]
default: present
type: str
allow_expand:
description:
- Permit expansion of the target VLAN's network if the module parameters specify a larger network than the VLAN currently
possesses.
- If V(false), the module fails under these conditions.
- This is intended to prevent accidental expansion of a VLAN's network (since this operation is not reversible).
type: bool
default: false
"""
EXAMPLES = r"""
- name: Add or update VLAN
community.general.dimensiondata_vlan:
region: na
location: NA5
network_domain: test_network
name: my_vlan1
description: A test VLAN
private_ipv4_base_address: 192.168.23.0
private_ipv4_prefix_size: 24
state: present
wait: true
- name: Read / get VLAN details
community.general.dimensiondata_vlan:
region: na
location: NA5
network_domain: test_network
name: my_vlan1
state: readonly
wait: true
- name: Delete a VLAN
community.general.dimensiondata_vlan:
region: na
location: NA5
network_domain: test_network
name: my_vlan_1
state: absent
wait: true
"""
RETURN = r"""
vlan:
description: Dictionary describing the VLAN.
returned: On success when O(state=present)
type: complex
contains:
id:
description: VLAN ID.
type: str
sample: "aaaaa000-a000-4050-a215-2808934ccccc"
name:
description: VLAN name.
type: str
sample: "My VLAN"
description:
description: VLAN description.
type: str
sample: "My VLAN description"
location:
description: Datacenter location.
type: str
sample: NA3
private_ipv4_base_address:
description: The base address for the VLAN's private IPV4 network.
type: str
sample: 192.168.23.0
private_ipv4_prefix_size:
description: The prefix size for the VLAN's private IPV4 network.
type: int
sample: 24
private_ipv4_gateway_address:
description: The gateway address for the VLAN's private IPV4 network.
type: str
sample: 192.168.23.1
private_ipv6_base_address:
description: The base address for the VLAN's IPV6 network.
type: str
sample: 2402:9900:111:1195:0:0:0:0
private_ipv6_prefix_size:
description: The prefix size for the VLAN's IPV6 network.
type: int
sample: 64
private_ipv6_gateway_address:
description: The gateway address for the VLAN's IPV6 network.
type: str
sample: 2402:9900:111:1195:0:0:0:1
status:
description: VLAN status.
type: str
sample: NORMAL
"""
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.dimensiondata import (
DimensionDataModule,
UnknownNetworkError,
)
try:
from libcloud.common.dimensiondata import DimensionDataAPIException, DimensionDataVlan
HAS_LIBCLOUD = True
except ImportError:
DimensionDataVlan = None
HAS_LIBCLOUD = False
class DimensionDataVlanModule(DimensionDataModule):
"""
The dimensiondata_vlan module for Ansible.
"""
def __init__(self):
"""
Create a new Dimension Data VLAN module.
"""
super().__init__(
module=AnsibleModule(
argument_spec=DimensionDataModule.argument_spec_with_wait(
name=dict(required=True, type="str"),
description=dict(default="", type="str"),
network_domain=dict(required=True, type="str"),
private_ipv4_base_address=dict(default="", type="str"),
private_ipv4_prefix_size=dict(default=0, type="int"),
allow_expand=dict(default=False, type="bool"),
state=dict(default="present", choices=["present", "absent", "readonly"]),
),
required_together=DimensionDataModule.required_together(),
)
)
self.name = self.module.params["name"]
self.description = self.module.params["description"]
self.network_domain_selector = self.module.params["network_domain"]
self.private_ipv4_base_address = self.module.params["private_ipv4_base_address"]
self.private_ipv4_prefix_size = self.module.params["private_ipv4_prefix_size"]
self.state = self.module.params["state"]
self.allow_expand = self.module.params["allow_expand"]
if self.wait and self.state != "present":
self.module.fail_json(msg='The wait parameter is only supported when state is "present".')
def state_present(self):
"""
Ensure that the target VLAN is present.
"""
network_domain = self._get_network_domain()
vlan = self._get_vlan(network_domain)
if not vlan:
if self.module.check_mode:
self.module.exit_json(
msg=f'VLAN "{self.name}" is absent from network domain "{self.network_domain_selector}" (should be present).',
changed=True,
)
vlan = self._create_vlan(network_domain)
self.module.exit_json(
msg=f'Created VLAN "{self.name}" in network domain "{self.network_domain_selector}".',
vlan=vlan_to_dict(vlan),
changed=True,
)
else:
diff = VlanDiff(vlan, self.module.params)
if not diff.has_changes():
self.module.exit_json(
msg=f'VLAN "{self.name}" is present in network domain "{self.network_domain_selector}" (no changes detected).',
vlan=vlan_to_dict(vlan),
changed=False,
)
return
try:
diff.ensure_legal_change()
except InvalidVlanChangeError as invalid_vlan_change:
self.module.fail_json(
msg=f'Unable to update VLAN "{self.name}" in network domain "{self.network_domain_selector}": {invalid_vlan_change}'
)
if diff.needs_expand() and not self.allow_expand:
self.module.fail_json(
msg=f"The configured private IPv4 network size ({self.private_ipv4_prefix_size}-bit prefix) for "
f"the VLAN differs from its current network size ({vlan.private_ipv4_range_size}-bit prefix) "
"and needs to be expanded. Use allow_expand=true if this is what you want."
)
if self.module.check_mode:
self.module.exit_json(
msg=f'VLAN "{self.name}" is present in network domain "{self.network_domain_selector}" (changes detected).',
vlan=vlan_to_dict(vlan),
changed=True,
)
if diff.needs_edit():
vlan.name = self.name
vlan.description = self.description
self.driver.ex_update_vlan(vlan)
if diff.needs_expand():
vlan.private_ipv4_range_size = self.private_ipv4_prefix_size
self.driver.ex_expand_vlan(vlan)
self.module.exit_json(
msg=f'Updated VLAN "{self.name}" in network domain "{self.network_domain_selector}".',
vlan=vlan_to_dict(vlan),
changed=True,
)
def state_readonly(self):
"""
Read the target VLAN's state.
"""
network_domain = self._get_network_domain()
vlan = self._get_vlan(network_domain)
if vlan:
self.module.exit_json(vlan=vlan_to_dict(vlan), changed=False)
else:
self.module.fail_json(
msg=f'VLAN "{self.name}" does not exist in network domain "{self.network_domain_selector}".'
)
def state_absent(self):
"""
Ensure that the target VLAN is not present.
"""
network_domain = self._get_network_domain()
vlan = self._get_vlan(network_domain)
if not vlan:
self.module.exit_json(
msg=f'VLAN "{self.name}" is absent from network domain "{self.network_domain_selector}".', changed=False
)
return
if self.module.check_mode:
self.module.exit_json(
msg=f'VLAN "{self.name}" is present in network domain "{self.network_domain_selector}" (should be absent).',
vlan=vlan_to_dict(vlan),
changed=True,
)
self._delete_vlan(vlan)
self.module.exit_json(
msg=f'Deleted VLAN "{self.name}" from network domain "{self.network_domain_selector}".', changed=True
)
def _get_vlan(self, network_domain):
"""
Retrieve the target VLAN details from CloudControl.
:param network_domain: The target network domain.
:return: The VLAN, or None if the target VLAN was not found.
:rtype: DimensionDataVlan
"""
vlans = self.driver.ex_list_vlans(location=self.location, network_domain=network_domain)
matching_vlans = [vlan for vlan in vlans if vlan.name == self.name]
if matching_vlans:
return matching_vlans[0]
return None
def _create_vlan(self, network_domain):
vlan = self.driver.ex_create_vlan(
network_domain, self.name, self.private_ipv4_base_address, self.description, self.private_ipv4_prefix_size
)
if self.wait:
vlan = self._wait_for_vlan_state(vlan.id, "NORMAL")
return vlan
def _delete_vlan(self, vlan):
try:
self.driver.ex_delete_vlan(vlan)
# Not currently supported for deletes due to a bug in libcloud (module will error out if "wait" is specified when "state" is not "present").
if self.wait:
self._wait_for_vlan_state(vlan, "NOT_FOUND")
except DimensionDataAPIException as api_exception:
self.module.fail_json(
msg=f'Failed to delete VLAN "{vlan.id}" due to unexpected error from the CloudControl API: {api_exception.msg}'
)
def _wait_for_vlan_state(self, vlan, state_to_wait_for):
network_domain = self._get_network_domain()
wait_poll_interval = self.module.params["wait_poll_interval"]
wait_time = self.module.params["wait_time"]
# Bizarre bug in libcloud when checking status after delete; socket.error is too generic to catch in this context so for now we don't even try.
try:
return self.driver.connection.wait_for_state(
state_to_wait_for, self.driver.ex_get_vlan, wait_poll_interval, wait_time, vlan
)
except DimensionDataAPIException as api_exception:
if api_exception.code != "RESOURCE_NOT_FOUND":
raise
return DimensionDataVlan(
id=vlan.id,
status="NOT_FOUND",
name="",
description="",
private_ipv4_range_address="",
private_ipv4_range_size=0,
ipv4_gateway="",
ipv6_range_address="",
ipv6_range_size=0,
ipv6_gateway="",
location=self.location,
network_domain=network_domain,
)
def _get_network_domain(self):
"""
Retrieve the target network domain from the Cloud Control API.
:return: The network domain.
"""
try:
return self.get_network_domain(self.network_domain_selector, self.location)
except UnknownNetworkError:
self.module.fail_json(
msg=f'Cannot find network domain "{self.network_domain_selector}" in datacenter "{self.location}".'
)
return None
class InvalidVlanChangeError(Exception):
"""
Error raised when an illegal change to VLAN state is attempted.
"""
pass
class VlanDiff:
"""
Represents differences between VLAN information (from CloudControl) and module parameters.
"""
def __init__(self, vlan, module_params):
"""
:param vlan: The VLAN information from CloudControl.
:type vlan: DimensionDataVlan
:param module_params: The module parameters.
:type module_params: dict
"""
self.vlan = vlan
self.module_params = module_params
self.name_changed = module_params["name"] != vlan.name
self.description_changed = module_params["description"] != vlan.description
self.private_ipv4_base_address_changed = (
module_params["private_ipv4_base_address"] != vlan.private_ipv4_range_address
)
self.private_ipv4_prefix_size_changed = (
module_params["private_ipv4_prefix_size"] != vlan.private_ipv4_range_size
)
# Is configured prefix size greater than or less than the actual prefix size?
private_ipv4_prefix_size_difference = module_params["private_ipv4_prefix_size"] - vlan.private_ipv4_range_size
self.private_ipv4_prefix_size_increased = private_ipv4_prefix_size_difference > 0
self.private_ipv4_prefix_size_decreased = private_ipv4_prefix_size_difference < 0
def has_changes(self):
"""
Does the VlanDiff represent any changes between the VLAN and module configuration?
:return: True, if there are change changes; otherwise, False.
"""
return self.needs_edit() or self.needs_expand()
def ensure_legal_change(self):
"""
Ensure the change (if any) represented by the VlanDiff represents a legal change to VLAN state.
- private_ipv4_base_address cannot be changed
- private_ipv4_prefix_size must be greater than or equal to the VLAN's existing private_ipv4_range_size
:raise InvalidVlanChangeError: The VlanDiff does not represent a legal change to VLAN state.
"""
# Cannot change base address for private IPv4 network.
if self.private_ipv4_base_address_changed:
raise InvalidVlanChangeError("Cannot change the private IPV4 base address for an existing VLAN.")
# Cannot shrink private IPv4 network (by increasing prefix size).
if self.private_ipv4_prefix_size_increased:
raise InvalidVlanChangeError(
"Cannot shrink the private IPV4 network for an existing VLAN (only expand is supported)."
)
def needs_edit(self):
"""
Is an Edit operation required to resolve the differences between the VLAN information and the module parameters?
:return: True, if an Edit operation is required; otherwise, False.
"""
return self.name_changed or self.description_changed
def needs_expand(self):
"""
Is an Expand operation required to resolve the differences between the VLAN information and the module parameters?
The VLAN's network is expanded by reducing the size of its network prefix.
:return: True, if an Expand operation is required; otherwise, False.
"""
return self.private_ipv4_prefix_size_decreased
def vlan_to_dict(vlan):
return {
"id": vlan.id,
"name": vlan.name,
"description": vlan.description,
"location": vlan.location.id,
"private_ipv4_base_address": vlan.private_ipv4_range_address,
"private_ipv4_prefix_size": vlan.private_ipv4_range_size,
"private_ipv4_gateway_address": vlan.ipv4_gateway,
"ipv6_base_address": vlan.ipv6_range_address,
"ipv6_prefix_size": vlan.ipv6_range_size,
"ipv6_gateway_address": vlan.ipv6_gateway,
"status": vlan.status,
}
def main():
module = DimensionDataVlanModule()
if module.state == "present":
module.state_present()
elif module.state == "readonly":
module.state_readonly()
elif module.state == "absent":
module.state_absent()
if __name__ == "__main__":
main()

View file

@ -75,8 +75,9 @@ options:
force_defaults:
description:
- If V(true), overwrite current O(description) and O(private) attributes with defaults.
- V(true) is deprecated for this option and will not be allowed starting in community.general 13.0.0. V(false) will be the default value then.
- The default value changed from V(true) to V(false) in community.general 13.0.0.
type: bool
default: false
version_added: 4.1.0
requirements:
- PyGithub>=1.54
@ -239,7 +240,7 @@ def main():
private=dict(type="bool"),
description=dict(type="str"),
api_url=dict(type="str", default="https://api.github.com"),
force_defaults=dict(type="bool"),
force_defaults=dict(type="bool", default=False),
)
module = AnsibleModule(
argument_spec=module_args,
@ -249,14 +250,6 @@ def main():
mutually_exclusive=[("username", "access_token")],
)
if module.params["force_defaults"] is None:
module.deprecate(
"'force_defaults=true' is deprecated and will not be allowed in community.general 13.0.0, use 'force_defaults=false' instead",
version="13.0.0",
collection_name="community.general",
)
module.params["force_defaults"] = True
if not HAS_GITHUB_PACKAGE:
module.fail_json(msg=missing_required_lib("PyGithub"), exception=GITHUB_IMP_ERR)

View file

@ -38,16 +38,11 @@ options:
notes:
- Currently the module is B(only supported for Debian, Ubuntu, and Arch Linux) systems.
- This module requires the package C(locales) installed in Debian and Ubuntu systems.
- If C(/etc/locale.gen) exists, the module assumes to be using the B(glibc) mechanism, else if C(/var/lib/locales/supported.d/)
exists it assumes to be using the B(ubuntu_legacy) mechanism, else it raises an error.
- If C(/etc/locale.gen) exists, the module assumes to be using the B(glibc) mechanism, else it raises an error.
Support for C(/var/lib/locales/supported.d/) (the V(ubuntu_legacy) mechanism) has been removed in community.general 13.0.0.
- When using V(glibc) mechanism, it manages locales by editing C(/etc/locale.gen) and running C(locale-gen).
- When using V(ubuntu_legacy) mechanism, it manages locales by editing C(/var/lib/locales/supported.d/local) and then running
C(locale-gen).
- Please note that the module asserts the availability of the locale by checking the files C(/usr/share/i18n/SUPPORTED) and
C(/usr/local/share/i18n/SUPPORTED), but the C(/usr/local) one is not supported by Archlinux.
- Please note that the code path that uses V(ubuntu_legacy) mechanism has not been tested for a while, because recent versions of
Ubuntu is already using the V(glibc) mechanism. There is no support for V(ubuntu_legacy), given our inability to test it.
Therefore, that mechanism is B(deprecated) and will be removed in community.general 13.0.0.
"""
EXAMPLES = r"""
@ -70,7 +65,6 @@ mechanism:
type: str
choices:
- glibc
- ubuntu_legacy
returned: success
sample: glibc
version_added: 10.2.0
@ -114,10 +108,6 @@ class LocaleGen(StateModuleHelper):
def __init_module__(self):
self.mechanisms = dict(
ubuntu_legacy=dict(
available=SUPPORTED_LOCALES,
apply_change=self.apply_change_ubuntu_legacy,
),
glibc=dict(
available=SUPPORTED_LOCALES,
apply_change=self.apply_change_glibc,
@ -127,18 +117,8 @@ class LocaleGen(StateModuleHelper):
if os.path.exists(ETC_LOCALE_GEN):
self.vars.ubuntu_mode = False
self.vars.mechanism = "glibc"
elif os.path.exists(VAR_LIB_LOCALES):
self.vars.ubuntu_mode = True
self.vars.mechanism = "ubuntu_legacy"
self.module.deprecate(
"On this machine mechanism=ubuntu_legacy is used. This mechanism is deprecated and will be removed from"
" in community.general 13.0.0. If you see this message on a modern Debian or Ubuntu version,"
" please create an issue in the community.general repository",
version="13.0.0",
collection_name="community.general",
)
else:
self.do_raise(f'{VAR_LIB_LOCALES} and {ETC_LOCALE_GEN} are missing. Is the package "locales" installed?')
self.do_raise(f'{ETC_LOCALE_GEN} is missing. Is the package "locales" installed?')
self.runner = locale_runner(self.module)
@ -269,34 +249,6 @@ class LocaleGen(StateModuleHelper):
with runner() as ctx:
ctx.run()
def apply_change_ubuntu_legacy(self, target_state, names):
"""Create or remove locale.
Keyword arguments:
target_state -- Desired state, either present or absent.
names -- Name list including encoding such as de_CH.UTF-8.
"""
runner = locale_gen_runner(self.module)
if target_state == "present":
# Create locale.
# Ubuntu's patched locale-gen automatically adds the new locale to /var/lib/locales/supported.d/local
with runner() as ctx:
ctx.run()
else:
# Delete locale involves discarding the locale from /var/lib/locales/supported.d/local and regenerating all locales.
with open(VAR_LIB_LOCALES_LOCAL) as fr:
content = fr.readlines()
with open(VAR_LIB_LOCALES_LOCAL, "w") as fw:
for line in content:
locale, charset = line.split(" ")
if locale not in names:
fw.write(line)
# Purge locales and regenerate.
# Please provide a patch if you know how to avoid regenerating the locales to keep!
with runner("purge") as ctx:
ctx.run()
@check_mode_skip
def __state_fallback__(self):
if self.vars.state_tracking == self.vars.state:

View file

@ -1,215 +0,0 @@
#!/usr/bin/python
# Copyright (c) 2017, 2018, Oracle and/or its affiliates.
# 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 annotations
DOCUMENTATION = r"""
module: oci_vcn
short_description: Manage Virtual Cloud Networks(VCN) in OCI
deprecated:
removed_in: 13.0.0
why: Superseded by official Oracle collection.
alternative: Use module C(oci_network_vcn) from the C(oracle.oci) collection.
description:
- This module allows the user to create, delete and update virtual cloud networks(VCNs) in OCI. The complete Oracle Cloud
Infrastructure Ansible Modules can be downloaded from U(https://github.com/oracle/oci-ansible-modules/releases).
attributes:
check_mode:
support: none
diff_mode:
support: none
options:
cidr_block:
description: The CIDR IP address block of the VCN. Required when creating a VCN with O(state=present).
type: str
compartment_id:
description: The OCID of the compartment to contain the VCN. Required when creating a VCN with O(state=present). This
option is mutually exclusive with O(vcn_id).
type: str
display_name:
description: A user-friendly name. Does not have to be unique, and it is changeable.
type: str
aliases: ['name']
dns_label:
description: A DNS label for the VCN, used in conjunction with the VNIC's hostname and subnet's DNS label to form a fully
qualified domain name (FQDN) for each VNIC within this subnet (for example, V(bminstance-1.subnet123.vcn1.oraclevcn.com)).
Not required to be unique, but it is a best practice to set unique DNS labels for VCNs in your tenancy. Must be an alphanumeric
string that begins with a letter. The value cannot be changed.
type: str
state:
description: Create or update a VCN with O(state=present). Use O(state=absent) to delete a VCN.
type: str
default: present
choices: ['present', 'absent']
vcn_id:
description: The OCID of the VCN. Required when deleting a VCN with O(state=absent) or updating a VCN with O(state=present).
This option is mutually exclusive with O(compartment_id).
type: str
aliases: ['id']
author: "Rohit Chaware (@rohitChaware)"
extends_documentation_fragment:
- community.general.oracle
- community.general.oracle_creatable_resource
- community.general.oracle_wait_options
- community.general.oracle_tags
- community.general.attributes
"""
EXAMPLES = r"""
- name: Create a VCN
community.general.oci_vcn:
cidr_block: '10.0.0.0/16'
compartment_id: 'ocid1.compartment.oc1..xxxxxEXAMPLExxxxx'
display_name: my_vcn
dns_label: ansiblevcn
- name: Updates the specified VCN's display name
community.general.oci_vcn:
vcn_id: ocid1.vcn.oc1.phx.xxxxxEXAMPLExxxxx
display_name: ansible_vcn
- name: Delete the specified VCN
community.general.oci_vcn:
vcn_id: ocid1.vcn.oc1.phx.xxxxxEXAMPLExxxxx
state: absent
"""
RETURN = r"""
vcn:
description: Information about the VCN.
returned: On successful create and update operation
type: dict
sample:
{
"cidr_block": "10.0.0.0/16",
"compartment_id\"": "ocid1.compartment.oc1..xxxxxEXAMPLExxxxx",
"default_dhcp_options_id": "ocid1.dhcpoptions.oc1.phx.xxxxxEXAMPLExxxxx",
"default_route_table_id": "ocid1.routetable.oc1.phx.xxxxxEXAMPLExxxxx",
"default_security_list_id": "ocid1.securitylist.oc1.phx.xxxxxEXAMPLExxxxx",
"display_name": "ansible_vcn",
"dns_label": "ansiblevcn",
"id": "ocid1.vcn.oc1.phx.xxxxxEXAMPLExxxxx",
"lifecycle_state": "AVAILABLE",
"time_created": "2017-11-13T20:22:40.626000+00:00",
"vcn_domain_name": "ansiblevcn.oraclevcn.com"
}
"""
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible_collections.community.general.plugins.module_utils.oracle import oci_utils
try:
from oci.core.models import CreateVcnDetails, UpdateVcnDetails
from oci.core.virtual_network_client import VirtualNetworkClient
HAS_OCI_PY_SDK = True
except ImportError:
HAS_OCI_PY_SDK = False
def delete_vcn(virtual_network_client, module):
result = oci_utils.delete_and_wait(
resource_type="vcn",
client=virtual_network_client,
get_fn=virtual_network_client.get_vcn,
kwargs_get={"vcn_id": module.params["vcn_id"]},
delete_fn=virtual_network_client.delete_vcn,
kwargs_delete={"vcn_id": module.params["vcn_id"]},
module=module,
)
return result
def update_vcn(virtual_network_client, module):
result = oci_utils.check_and_update_resource(
resource_type="vcn",
client=virtual_network_client,
get_fn=virtual_network_client.get_vcn,
kwargs_get={"vcn_id": module.params["vcn_id"]},
update_fn=virtual_network_client.update_vcn,
primitive_params_update=["vcn_id"],
kwargs_non_primitive_update={UpdateVcnDetails: "update_vcn_details"},
module=module,
update_attributes=list(UpdateVcnDetails().attribute_map.keys()),
)
return result
def create_vcn(virtual_network_client, module):
create_vcn_details = CreateVcnDetails()
for attribute in create_vcn_details.attribute_map.keys():
if attribute in module.params:
setattr(create_vcn_details, attribute, module.params[attribute])
result = oci_utils.create_and_wait(
resource_type="vcn",
create_fn=virtual_network_client.create_vcn,
kwargs_create={"create_vcn_details": create_vcn_details},
client=virtual_network_client,
get_fn=virtual_network_client.get_vcn,
get_param="vcn_id",
module=module,
)
return result
def main():
module_args = oci_utils.get_taggable_arg_spec(supports_create=True, supports_wait=True)
module_args.update(
dict(
cidr_block=dict(type="str"),
compartment_id=dict(type="str"),
display_name=dict(type="str", aliases=["name"]),
dns_label=dict(type="str"),
state=dict(type="str", default="present", choices=["absent", "present"]),
vcn_id=dict(type="str", aliases=["id"]),
)
)
module = AnsibleModule(
argument_spec=module_args,
supports_check_mode=False,
mutually_exclusive=[["compartment_id", "vcn_id"]],
)
if not HAS_OCI_PY_SDK:
module.fail_json(msg=missing_required_lib("oci"))
virtual_network_client = oci_utils.create_service_client(module, VirtualNetworkClient)
exclude_attributes = {"display_name": True, "dns_label": True}
state = module.params["state"]
vcn_id = module.params["vcn_id"]
if state == "absent":
if vcn_id is not None:
result = delete_vcn(virtual_network_client, module)
else:
module.fail_json(msg="Specify vcn_id with state as 'absent' to delete a VCN.")
else:
if vcn_id is not None:
result = update_vcn(virtual_network_client, module)
else:
result = oci_utils.check_and_create_resource(
resource_type="vcn",
create_fn=create_vcn,
kwargs_create={
"virtual_network_client": virtual_network_client,
"module": module,
},
list_fn=virtual_network_client.list_vcns,
kwargs_list={"compartment_id": module.params["compartment_id"]},
module=module,
model=CreateVcnDetails(),
exclude_attributes=exclude_attributes,
)
module.exit_json(**result)
if __name__ == "__main__":
main()

View file

@ -1,503 +0,0 @@
#!/usr/bin/python
# Copyright (c) Ansible Project
# 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 annotations
DOCUMENTATION = r"""
module: oneandone_firewall_policy
short_description: Configure 1&1 firewall policy
description:
- Create, remove, reconfigure, update firewall policies. This module has a dependency on 1and1 >= 1.0.
deprecated:
removed_in: 13.0.0
why: DNS fails to resolve the API endpoint used by the module.
alternative: There is none.
extends_documentation_fragment:
- community.general.attributes
attributes:
check_mode:
support: full
diff_mode:
support: none
options:
state:
description:
- Define a firewall policy state to create, remove, or update.
type: str
default: 'present'
choices: ["present", "absent", "update"]
auth_token:
description:
- Authenticating API token provided by 1&1.
type: str
api_url:
description:
- Custom API URL. Overrides the E(ONEANDONE_API_URL) environment variable.
type: str
name:
description:
- Firewall policy name used with present state. Used as identifier (id or name) when used with absent state. maxLength=128.
type: str
firewall_policy:
description:
- The identifier (id or name) of the firewall policy used with update state.
type: str
rules:
description:
- List of rules that are set for the firewall policy. Each rule must contain protocol parameter, in addition to three
optional parameters (port_from, port_to, and source).
type: list
elements: dict
default: []
add_server_ips:
description:
- A list of server identifiers (ID or name) to be assigned to a firewall policy. Used in combination with update state.
type: list
elements: str
default: []
remove_server_ips:
description:
- A list of server IP IDs to be unassigned from a firewall policy. Used in combination with update state.
type: list
elements: str
default: []
add_rules:
description:
- List of rules that are added to an existing firewall policy. It is syntax is the same as the one used for rules parameter.
Used in combination with update state.
type: list
elements: dict
default: []
remove_rules:
description:
- List of rule IDs that are removed from an existing firewall policy. Used in combination with update state.
type: list
elements: str
default: []
description:
description:
- Firewall policy description. maxLength=256.
type: str
wait:
description:
- Wait for the instance to be in state 'running' before returning.
default: true
type: bool
wait_timeout:
description:
- How long before wait gives up, in seconds.
type: int
default: 600
wait_interval:
description:
- Defines the number of seconds to wait when using the _wait_for methods.
type: int
default: 5
requirements:
- "1and1"
author:
- "Amel Ajdinovic (@aajdinov)"
- "Ethan Devenport (@edevenport)"
"""
EXAMPLES = r"""
- name: Create a firewall policy
community.general.oneandone_firewall_policy:
auth_token: oneandone_private_api_key
name: ansible-firewall-policy
description: Testing creation of firewall policies with ansible
rules:
- protocol: TCP
port_from: 80
port_to: 80
source: 0.0.0.0
wait: true
wait_timeout: 500
- name: Destroy a firewall policy
community.general.oneandone_firewall_policy:
auth_token: oneandone_private_api_key
state: absent
name: ansible-firewall-policy
- name: Update a firewall policy
community.general.oneandone_firewall_policy:
auth_token: oneandone_private_api_key
state: update
firewall_policy: ansible-firewall-policy
name: ansible-firewall-policy-updated
description: Testing creation of firewall policies with ansible - updated
- name: Add server to a firewall policy
community.general.oneandone_firewall_policy:
auth_token: oneandone_private_api_key
firewall_policy: ansible-firewall-policy-updated
add_server_ips:
- server_identifier (id or name)
- "server_identifier #2 (id or name)"
wait: true
wait_timeout: 500
state: update
- name: Remove server from a firewall policy
community.general.oneandone_firewall_policy:
auth_token: oneandone_private_api_key
firewall_policy: ansible-firewall-policy-updated
remove_server_ips:
- B2504878540DBC5F7634EB00A07C1EBD (server's IP id)
wait: true
wait_timeout: 500
state: update
- name: Add rules to a firewall policy
community.general.oneandone_firewall_policy:
auth_token: oneandone_private_api_key
firewall_policy: ansible-firewall-policy-updated
description: Adding rules to an existing firewall policy
add_rules:
- protocol: TCP
port_from: 70
port_to: 70
source: 0.0.0.0
- protocol: TCP
port_from: 60
port_to: 60
source: 0.0.0.0
wait: true
wait_timeout: 500
state: update
- name: Remove rules from a firewall policy
community.general.oneandone_firewall_policy:
auth_token: oneandone_private_api_key
firewall_policy: ansible-firewall-policy-updated
remove_rules:
- "rule_id #1"
- "rule_id #2"
- '...'
wait: true
wait_timeout: 500
state: update
"""
RETURN = r"""
firewall_policy:
description: Information about the firewall policy that was processed.
type: dict
sample: {"id": "92B74394A397ECC3359825C1656D67A6", "name": "Default Policy"}
returned: always
"""
import os
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.oneandone import (
OneAndOneResources,
get_firewall_policy,
get_server,
wait_for_resource_creation_completion,
)
HAS_ONEANDONE_SDK = True
try:
import oneandone.client
except ImportError:
HAS_ONEANDONE_SDK = False
def _check_mode(module, result):
if module.check_mode:
module.exit_json(changed=result)
def _add_server_ips(module, oneandone_conn, firewall_id, server_ids):
"""
Assigns servers to a firewall policy.
"""
try:
attach_servers = []
for _server_id in server_ids:
server = get_server(oneandone_conn, _server_id, True)
attach_server = oneandone.client.AttachServer(
server_id=server["id"], server_ip_id=next(iter(server["ips"] or []), None)["id"]
)
attach_servers.append(attach_server)
if module.check_mode:
return bool(attach_servers)
firewall_policy = oneandone_conn.attach_server_firewall_policy(
firewall_id=firewall_id, server_ips=attach_servers
)
return firewall_policy
except Exception as e:
module.fail_json(msg=str(e))
def _remove_firewall_server(module, oneandone_conn, firewall_id, server_ip_id):
"""
Unassigns a server/IP from a firewall policy.
"""
try:
if module.check_mode:
firewall_server = oneandone_conn.get_firewall_server(firewall_id=firewall_id, server_ip_id=server_ip_id)
return bool(firewall_server)
firewall_policy = oneandone_conn.remove_firewall_server(firewall_id=firewall_id, server_ip_id=server_ip_id)
return firewall_policy
except Exception as e:
module.fail_json(msg=str(e))
def _add_firewall_rules(module, oneandone_conn, firewall_id, rules):
"""
Adds new rules to a firewall policy.
"""
try:
firewall_rules = []
for rule in rules:
firewall_rule = oneandone.client.FirewallPolicyRule(
protocol=rule["protocol"], port_from=rule["port_from"], port_to=rule["port_to"], source=rule["source"]
)
firewall_rules.append(firewall_rule)
if module.check_mode:
firewall_policy_id = get_firewall_policy(oneandone_conn, firewall_id)
return bool(firewall_rules and firewall_policy_id)
firewall_policy = oneandone_conn.add_firewall_policy_rule(
firewall_id=firewall_id, firewall_policy_rules=firewall_rules
)
return firewall_policy
except Exception as e:
module.fail_json(msg=str(e))
def _remove_firewall_rule(module, oneandone_conn, firewall_id, rule_id):
"""
Removes a rule from a firewall policy.
"""
try:
if module.check_mode:
rule = oneandone_conn.get_firewall_policy_rule(firewall_id=firewall_id, rule_id=rule_id)
return bool(rule)
firewall_policy = oneandone_conn.remove_firewall_rule(firewall_id=firewall_id, rule_id=rule_id)
return firewall_policy
except Exception as e:
module.fail_json(msg=str(e))
def update_firewall_policy(module, oneandone_conn):
"""
Updates a firewall policy based on input arguments.
Firewall rules and server ips can be added/removed to/from
firewall policy. Firewall policy name and description can be
updated as well.
module : AnsibleModule object
oneandone_conn: authenticated oneandone object
"""
try:
firewall_policy_id = module.params.get("firewall_policy")
name = module.params.get("name")
description = module.params.get("description")
add_server_ips = module.params.get("add_server_ips")
remove_server_ips = module.params.get("remove_server_ips")
add_rules = module.params.get("add_rules")
remove_rules = module.params.get("remove_rules")
changed = False
firewall_policy = get_firewall_policy(oneandone_conn, firewall_policy_id, True)
if firewall_policy is None:
_check_mode(module, False)
if name or description:
_check_mode(module, True)
firewall_policy = oneandone_conn.modify_firewall(
firewall_id=firewall_policy["id"], name=name, description=description
)
changed = True
if add_server_ips:
if module.check_mode:
_check_mode(module, _add_server_ips(module, oneandone_conn, firewall_policy["id"], add_server_ips))
firewall_policy = _add_server_ips(module, oneandone_conn, firewall_policy["id"], add_server_ips)
changed = True
if remove_server_ips:
chk_changed = False
for server_ip_id in remove_server_ips:
if module.check_mode:
chk_changed |= _remove_firewall_server(module, oneandone_conn, firewall_policy["id"], server_ip_id)
_remove_firewall_server(module, oneandone_conn, firewall_policy["id"], server_ip_id)
_check_mode(module, chk_changed)
firewall_policy = get_firewall_policy(oneandone_conn, firewall_policy["id"], True)
changed = True
if add_rules:
firewall_policy = _add_firewall_rules(module, oneandone_conn, firewall_policy["id"], add_rules)
_check_mode(module, firewall_policy)
changed = True
if remove_rules:
chk_changed = False
for rule_id in remove_rules:
if module.check_mode:
chk_changed |= _remove_firewall_rule(module, oneandone_conn, firewall_policy["id"], rule_id)
_remove_firewall_rule(module, oneandone_conn, firewall_policy["id"], rule_id)
_check_mode(module, chk_changed)
firewall_policy = get_firewall_policy(oneandone_conn, firewall_policy["id"], True)
changed = True
return (changed, firewall_policy)
except Exception as e:
module.fail_json(msg=str(e))
def create_firewall_policy(module, oneandone_conn):
"""
Create a new firewall policy.
module : AnsibleModule object
oneandone_conn: authenticated oneandone object
"""
try:
name = module.params.get("name")
description = module.params.get("description")
rules = module.params.get("rules")
wait = module.params.get("wait")
wait_timeout = module.params.get("wait_timeout")
wait_interval = module.params.get("wait_interval")
firewall_rules = []
for rule in rules:
firewall_rule = oneandone.client.FirewallPolicyRule(
protocol=rule["protocol"], port_from=rule["port_from"], port_to=rule["port_to"], source=rule["source"]
)
firewall_rules.append(firewall_rule)
firewall_policy_obj = oneandone.client.FirewallPolicy(name=name, description=description)
_check_mode(module, True)
firewall_policy = oneandone_conn.create_firewall_policy(
firewall_policy=firewall_policy_obj, firewall_policy_rules=firewall_rules
)
if wait:
wait_for_resource_creation_completion(
oneandone_conn, OneAndOneResources.firewall_policy, firewall_policy["id"], wait_timeout, wait_interval
)
firewall_policy = get_firewall_policy(oneandone_conn, firewall_policy["id"], True) # refresh
changed = True if firewall_policy else False
_check_mode(module, False)
return (changed, firewall_policy)
except Exception as e:
module.fail_json(msg=str(e))
def remove_firewall_policy(module, oneandone_conn):
"""
Removes a firewall policy.
module : AnsibleModule object
oneandone_conn: authenticated oneandone object
"""
try:
fp_id = module.params.get("name")
firewall_policy_id = get_firewall_policy(oneandone_conn, fp_id)
if module.check_mode:
if firewall_policy_id is None:
_check_mode(module, False)
_check_mode(module, True)
firewall_policy = oneandone_conn.delete_firewall(firewall_policy_id)
changed = True if firewall_policy else False
return (changed, {"id": firewall_policy["id"], "name": firewall_policy["name"]})
except Exception as e:
module.fail_json(msg=str(e))
def main():
module = AnsibleModule(
argument_spec=dict(
auth_token=dict(type="str", no_log=True, default=os.environ.get("ONEANDONE_AUTH_TOKEN")),
api_url=dict(type="str", default=os.environ.get("ONEANDONE_API_URL")),
name=dict(type="str"),
firewall_policy=dict(type="str"),
description=dict(type="str"),
rules=dict(type="list", elements="dict", default=[]),
add_server_ips=dict(type="list", elements="str", default=[]),
remove_server_ips=dict(type="list", elements="str", default=[]),
add_rules=dict(type="list", elements="dict", default=[]),
remove_rules=dict(type="list", elements="str", default=[]),
wait=dict(type="bool", default=True),
wait_timeout=dict(type="int", default=600),
wait_interval=dict(type="int", default=5),
state=dict(type="str", default="present", choices=["present", "absent", "update"]),
),
supports_check_mode=True,
)
if not HAS_ONEANDONE_SDK:
module.fail_json(msg="1and1 required for this module")
if not module.params.get("auth_token"):
module.fail_json(msg='The "auth_token" parameter or ONEANDONE_AUTH_TOKEN environment variable is required.')
if not module.params.get("api_url"):
oneandone_conn = oneandone.client.OneAndOneService(api_token=module.params.get("auth_token"))
else:
oneandone_conn = oneandone.client.OneAndOneService(
api_token=module.params.get("auth_token"), api_url=module.params.get("api_url")
)
state = module.params.get("state")
if state == "absent":
if not module.params.get("name"):
module.fail_json(msg="'name' parameter is required to delete a firewall policy.")
try:
(changed, firewall_policy) = remove_firewall_policy(module, oneandone_conn)
except Exception as e:
module.fail_json(msg=str(e))
elif state == "update":
if not module.params.get("firewall_policy"):
module.fail_json(msg="'firewall_policy' parameter is required to update a firewall policy.")
try:
(changed, firewall_policy) = update_firewall_policy(module, oneandone_conn)
except Exception as e:
module.fail_json(msg=str(e))
elif state == "present":
for param in ("name", "rules"):
if not module.params.get(param):
module.fail_json(msg=f"{param} parameter is required for new firewall policies.")
try:
(changed, firewall_policy) = create_firewall_policy(module, oneandone_conn)
except Exception as e:
module.fail_json(msg=str(e))
module.exit_json(changed=changed, firewall_policy=firewall_policy)
if __name__ == "__main__":
main()

View file

@ -1,634 +0,0 @@
#!/usr/bin/python
# Copyright (c) Ansible Project
# 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 annotations
DOCUMENTATION = r"""
module: oneandone_load_balancer
short_description: Configure 1&1 load balancer
description:
- Create, remove, update load balancers. This module has a dependency on 1and1 >= 1.0.
deprecated:
removed_in: 13.0.0
why: DNS fails to resolve the API endpoint used by the module.
alternative: There is none.
extends_documentation_fragment:
- community.general.attributes
attributes:
check_mode:
support: full
diff_mode:
support: none
options:
state:
description:
- Define a load balancer state to create, remove, or update.
type: str
default: 'present'
choices: ["present", "absent", "update"]
auth_token:
description:
- Authenticating API token provided by 1&1.
type: str
load_balancer:
description:
- The identifier (id or name) of the load balancer used with update state.
type: str
api_url:
description:
- Custom API URL. Overrides the E(ONEANDONE_API_URL) environment variable.
type: str
name:
description:
- Load balancer name used with present state. Used as identifier (ID or name) when used with absent state. maxLength=128.
type: str
health_check_test:
description:
- Type of the health check. At the moment, HTTP is not allowed.
type: str
choices: ["NONE", "TCP", "HTTP", "ICMP"]
health_check_interval:
description:
- Health check period in seconds. minimum=5, maximum=300, multipleOf=1.
type: str
health_check_path:
description:
- URL to call for checking. Required for HTTP health check. maxLength=1000.
type: str
health_check_parse:
description:
- Regular expression to check. Required for HTTP health check. maxLength=64.
type: str
persistence:
description:
- Persistence.
type: bool
persistence_time:
description:
- Persistence time in seconds. Required if persistence is enabled. minimum=30, maximum=1200, multipleOf=1.
type: str
method:
description:
- Balancing procedure.
type: str
choices: ["ROUND_ROBIN", "LEAST_CONNECTIONS"]
datacenter:
description:
- ID or country code of the datacenter where the load balancer is created.
- If not specified, it defaults to V(US).
type: str
choices: ["US", "ES", "DE", "GB"]
rules:
description:
- A list of rule objects that are set for the load balancer. Each rule must contain protocol, port_balancer, and port_server
parameters, in addition to source parameter, which is optional.
type: list
elements: dict
default: []
description:
description:
- Description of the load balancer. maxLength=256.
type: str
add_server_ips:
description:
- A list of server identifiers (id or name) to be assigned to a load balancer. Used in combination with O(state=update).
type: list
elements: str
default: []
remove_server_ips:
description:
- A list of server IP IDs to be unassigned from a load balancer. Used in combination with O(state=update).
type: list
elements: str
default: []
add_rules:
description:
- A list of rules that are added to an existing load balancer. It is syntax is the same as the one used for rules parameter.
Used in combination with O(state=update).
type: list
elements: dict
default: []
remove_rules:
description:
- A list of rule IDs that are removed from an existing load balancer. Used in combination with O(state=update).
type: list
elements: str
default: []
wait:
description:
- Wait for the instance to be in state 'running' before returning.
default: true
type: bool
wait_timeout:
description:
- How long before wait gives up, in seconds.
type: int
default: 600
wait_interval:
description:
- Defines the number of seconds to wait when using the _wait_for methods.
type: int
default: 5
requirements:
- "1and1"
author:
- Amel Ajdinovic (@aajdinov)
- Ethan Devenport (@edevenport)
"""
EXAMPLES = r"""
- name: Create a load balancer
community.general.oneandone_load_balancer:
auth_token: oneandone_private_api_key
name: ansible load balancer
description: Testing creation of load balancer with ansible
health_check_test: TCP
health_check_interval: 40
persistence: true
persistence_time: 1200
method: ROUND_ROBIN
datacenter: US
rules:
- protocol: TCP
port_balancer: 80
port_server: 80
source: 0.0.0.0
wait: true
wait_timeout: 500
- name: Destroy a load balancer
community.general.oneandone_load_balancer:
auth_token: oneandone_private_api_key
name: ansible load balancer
wait: true
wait_timeout: 500
state: absent
- name: Update a load balancer
community.general.oneandone_load_balancer:
auth_token: oneandone_private_api_key
load_balancer: ansible load balancer
name: ansible load balancer updated
description: Testing the update of a load balancer with ansible
wait: true
wait_timeout: 500
state: update
- name: Add server to a load balancer
community.general.oneandone_load_balancer:
auth_token: oneandone_private_api_key
load_balancer: ansible load balancer updated
description: Adding server to a load balancer with ansible
add_server_ips:
- server identifier (id or name)
wait: true
wait_timeout: 500
state: update
- name: Remove server from a load balancer
community.general.oneandone_load_balancer:
auth_token: oneandone_private_api_key
load_balancer: ansible load balancer updated
description: Removing server from a load balancer with ansible
remove_server_ips:
- B2504878540DBC5F7634EB00A07C1EBD (server's ip id)
wait: true
wait_timeout: 500
state: update
- name: Add rules to a load balancer
community.general.oneandone_load_balancer:
auth_token: oneandone_private_api_key
load_balancer: ansible load balancer updated
description: Adding rules to a load balancer with ansible
add_rules:
- protocol: TCP
port_balancer: 70
port_server: 70
source: 0.0.0.0
- protocol: TCP
port_balancer: 60
port_server: 60
source: 0.0.0.0
wait: true
wait_timeout: 500
state: update
- name: Remove rules from a load balancer
community.general.oneandone_load_balancer:
auth_token: oneandone_private_api_key
load_balancer: ansible load balancer updated
description: Adding rules to a load balancer with ansible
remove_rules:
- "rule_id #1"
- "rule_id #2"
- '...'
wait: true
wait_timeout: 500
state: update
"""
RETURN = r"""
load_balancer:
description: Information about the load balancer that was processed.
type: dict
sample: {"id": "92B74394A397ECC3359825C1656D67A6", "name": "Default Balancer"}
returned: always
"""
import os
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.oneandone import (
OneAndOneResources,
get_datacenter,
get_load_balancer,
get_server,
wait_for_resource_creation_completion,
)
HAS_ONEANDONE_SDK = True
try:
import oneandone.client
except ImportError:
HAS_ONEANDONE_SDK = False
DATACENTERS = ["US", "ES", "DE", "GB"]
HEALTH_CHECK_TESTS = ["NONE", "TCP", "HTTP", "ICMP"]
METHODS = ["ROUND_ROBIN", "LEAST_CONNECTIONS"]
def _check_mode(module, result):
if module.check_mode:
module.exit_json(changed=result)
def _add_server_ips(module, oneandone_conn, load_balancer_id, server_ids):
"""
Assigns servers to a load balancer.
"""
try:
attach_servers = []
for server_id in server_ids:
server = get_server(oneandone_conn, server_id, True)
attach_server = oneandone.client.AttachServer(
server_id=server["id"], server_ip_id=next(iter(server["ips"] or []), None)["id"]
)
attach_servers.append(attach_server)
if module.check_mode:
return bool(attach_servers)
load_balancer = oneandone_conn.attach_load_balancer_server(
load_balancer_id=load_balancer_id, server_ips=attach_servers
)
return load_balancer
except Exception as ex:
module.fail_json(msg=str(ex))
def _remove_load_balancer_server(module, oneandone_conn, load_balancer_id, server_ip_id):
"""
Unassigns a server/IP from a load balancer.
"""
try:
if module.check_mode:
lb_server = oneandone_conn.get_load_balancer_server(
load_balancer_id=load_balancer_id, server_ip_id=server_ip_id
)
return bool(lb_server)
load_balancer = oneandone_conn.remove_load_balancer_server(
load_balancer_id=load_balancer_id, server_ip_id=server_ip_id
)
return load_balancer
except Exception as ex:
module.fail_json(msg=str(ex))
def _add_load_balancer_rules(module, oneandone_conn, load_balancer_id, rules):
"""
Adds new rules to a load_balancer.
"""
try:
load_balancer_rules = []
for rule in rules:
load_balancer_rule = oneandone.client.LoadBalancerRule(
protocol=rule["protocol"],
port_balancer=rule["port_balancer"],
port_server=rule["port_server"],
source=rule["source"],
)
load_balancer_rules.append(load_balancer_rule)
if module.check_mode:
lb_id = get_load_balancer(oneandone_conn, load_balancer_id)
return bool(load_balancer_rules and lb_id)
load_balancer = oneandone_conn.add_load_balancer_rule(
load_balancer_id=load_balancer_id, load_balancer_rules=load_balancer_rules
)
return load_balancer
except Exception as ex:
module.fail_json(msg=str(ex))
def _remove_load_balancer_rule(module, oneandone_conn, load_balancer_id, rule_id):
"""
Removes a rule from a load_balancer.
"""
try:
if module.check_mode:
rule = oneandone_conn.get_load_balancer_rule(load_balancer_id=load_balancer_id, rule_id=rule_id)
return bool(rule)
load_balancer = oneandone_conn.remove_load_balancer_rule(load_balancer_id=load_balancer_id, rule_id=rule_id)
return load_balancer
except Exception as ex:
module.fail_json(msg=str(ex))
def update_load_balancer(module, oneandone_conn):
"""
Updates a load_balancer based on input arguments.
Load balancer rules and server ips can be added/removed to/from
load balancer. Load balancer name, description, health_check_test,
health_check_interval, persistence, persistence_time, and method
can be updated as well.
module : AnsibleModule object
oneandone_conn: authenticated oneandone object
"""
load_balancer_id = module.params.get("load_balancer")
name = module.params.get("name")
description = module.params.get("description")
health_check_test = module.params.get("health_check_test")
health_check_interval = module.params.get("health_check_interval")
health_check_path = module.params.get("health_check_path")
health_check_parse = module.params.get("health_check_parse")
persistence = module.params.get("persistence")
persistence_time = module.params.get("persistence_time")
method = module.params.get("method")
add_server_ips = module.params.get("add_server_ips")
remove_server_ips = module.params.get("remove_server_ips")
add_rules = module.params.get("add_rules")
remove_rules = module.params.get("remove_rules")
changed = False
load_balancer = get_load_balancer(oneandone_conn, load_balancer_id, True)
if load_balancer is None:
_check_mode(module, False)
if (
name
or description
or health_check_test
or health_check_interval
or health_check_path
or health_check_parse
or persistence
or persistence_time
or method
):
_check_mode(module, True)
load_balancer = oneandone_conn.modify_load_balancer(
load_balancer_id=load_balancer["id"],
name=name,
description=description,
health_check_test=health_check_test,
health_check_interval=health_check_interval,
health_check_path=health_check_path,
health_check_parse=health_check_parse,
persistence=persistence,
persistence_time=persistence_time,
method=method,
)
changed = True
if add_server_ips:
if module.check_mode:
_check_mode(module, _add_server_ips(module, oneandone_conn, load_balancer["id"], add_server_ips))
load_balancer = _add_server_ips(module, oneandone_conn, load_balancer["id"], add_server_ips)
changed = True
if remove_server_ips:
chk_changed = False
for server_ip_id in remove_server_ips:
if module.check_mode:
chk_changed |= _remove_load_balancer_server(module, oneandone_conn, load_balancer["id"], server_ip_id)
_remove_load_balancer_server(module, oneandone_conn, load_balancer["id"], server_ip_id)
_check_mode(module, chk_changed)
load_balancer = get_load_balancer(oneandone_conn, load_balancer["id"], True)
changed = True
if add_rules:
load_balancer = _add_load_balancer_rules(module, oneandone_conn, load_balancer["id"], add_rules)
_check_mode(module, load_balancer)
changed = True
if remove_rules:
chk_changed = False
for rule_id in remove_rules:
if module.check_mode:
chk_changed |= _remove_load_balancer_rule(module, oneandone_conn, load_balancer["id"], rule_id)
_remove_load_balancer_rule(module, oneandone_conn, load_balancer["id"], rule_id)
_check_mode(module, chk_changed)
load_balancer = get_load_balancer(oneandone_conn, load_balancer["id"], True)
changed = True
try:
return (changed, load_balancer)
except Exception as ex:
module.fail_json(msg=str(ex))
def create_load_balancer(module, oneandone_conn):
"""
Create a new load_balancer.
module : AnsibleModule object
oneandone_conn: authenticated oneandone object
"""
try:
name = module.params.get("name")
description = module.params.get("description")
health_check_test = module.params.get("health_check_test")
health_check_interval = module.params.get("health_check_interval")
health_check_path = module.params.get("health_check_path")
health_check_parse = module.params.get("health_check_parse")
persistence = module.params.get("persistence")
persistence_time = module.params.get("persistence_time")
method = module.params.get("method")
datacenter = module.params.get("datacenter")
rules = module.params.get("rules")
wait = module.params.get("wait")
wait_timeout = module.params.get("wait_timeout")
wait_interval = module.params.get("wait_interval")
load_balancer_rules = []
datacenter_id = None
if datacenter is not None:
datacenter_id = get_datacenter(oneandone_conn, datacenter)
if datacenter_id is None:
module.fail_json(msg=f"datacenter {datacenter} not found.")
for rule in rules:
load_balancer_rule = oneandone.client.LoadBalancerRule(
protocol=rule["protocol"],
port_balancer=rule["port_balancer"],
port_server=rule["port_server"],
source=rule["source"],
)
load_balancer_rules.append(load_balancer_rule)
_check_mode(module, True)
load_balancer_obj = oneandone.client.LoadBalancer(
health_check_path=health_check_path,
health_check_parse=health_check_parse,
name=name,
description=description,
health_check_test=health_check_test,
health_check_interval=health_check_interval,
persistence=persistence,
persistence_time=persistence_time,
method=method,
datacenter_id=datacenter_id,
)
load_balancer = oneandone_conn.create_load_balancer(
load_balancer=load_balancer_obj, load_balancer_rules=load_balancer_rules
)
if wait:
wait_for_resource_creation_completion(
oneandone_conn, OneAndOneResources.load_balancer, load_balancer["id"], wait_timeout, wait_interval
)
load_balancer = get_load_balancer(oneandone_conn, load_balancer["id"], True) # refresh
changed = True if load_balancer else False
_check_mode(module, False)
return (changed, load_balancer)
except Exception as ex:
module.fail_json(msg=str(ex))
def remove_load_balancer(module, oneandone_conn):
"""
Removes a load_balancer.
module : AnsibleModule object
oneandone_conn: authenticated oneandone object
"""
try:
lb_id = module.params.get("name")
load_balancer_id = get_load_balancer(oneandone_conn, lb_id)
if module.check_mode:
if load_balancer_id is None:
_check_mode(module, False)
_check_mode(module, True)
load_balancer = oneandone_conn.delete_load_balancer(load_balancer_id)
changed = True if load_balancer else False
return (changed, {"id": load_balancer["id"], "name": load_balancer["name"]})
except Exception as ex:
module.fail_json(msg=str(ex))
def main():
module = AnsibleModule(
argument_spec=dict(
auth_token=dict(type="str", no_log=True, default=os.environ.get("ONEANDONE_AUTH_TOKEN")),
api_url=dict(type="str", default=os.environ.get("ONEANDONE_API_URL")),
load_balancer=dict(type="str"),
name=dict(type="str"),
description=dict(type="str"),
health_check_test=dict(choices=HEALTH_CHECK_TESTS),
health_check_interval=dict(type="str"),
health_check_path=dict(type="str"),
health_check_parse=dict(type="str"),
persistence=dict(type="bool"),
persistence_time=dict(type="str"),
method=dict(choices=METHODS),
datacenter=dict(choices=DATACENTERS),
rules=dict(type="list", elements="dict", default=[]),
add_server_ips=dict(type="list", elements="str", default=[]),
remove_server_ips=dict(type="list", elements="str", default=[]),
add_rules=dict(type="list", elements="dict", default=[]),
remove_rules=dict(type="list", elements="str", default=[]),
wait=dict(type="bool", default=True),
wait_timeout=dict(type="int", default=600),
wait_interval=dict(type="int", default=5),
state=dict(type="str", default="present", choices=["present", "absent", "update"]),
),
supports_check_mode=True,
)
if not HAS_ONEANDONE_SDK:
module.fail_json(msg="1and1 required for this module")
if not module.params.get("auth_token"):
module.fail_json(msg="auth_token parameter is required.")
if not module.params.get("api_url"):
oneandone_conn = oneandone.client.OneAndOneService(api_token=module.params.get("auth_token"))
else:
oneandone_conn = oneandone.client.OneAndOneService(
api_token=module.params.get("auth_token"), api_url=module.params.get("api_url")
)
state = module.params.get("state")
if state == "absent":
if not module.params.get("name"):
module.fail_json(msg="'name' parameter is required for deleting a load balancer.")
try:
(changed, load_balancer) = remove_load_balancer(module, oneandone_conn)
except Exception as ex:
module.fail_json(msg=str(ex))
elif state == "update":
if not module.params.get("load_balancer"):
module.fail_json(msg="'load_balancer' parameter is required for updating a load balancer.")
try:
(changed, load_balancer) = update_load_balancer(module, oneandone_conn)
except Exception as ex:
module.fail_json(msg=str(ex))
elif state == "present":
for param in (
"name",
"health_check_test",
"health_check_interval",
"persistence",
"persistence_time",
"method",
"rules",
):
if not module.params.get(param):
module.fail_json(msg=f"{param} parameter is required for new load balancers.")
try:
(changed, load_balancer) = create_load_balancer(module, oneandone_conn)
except Exception as ex:
module.fail_json(msg=str(ex))
module.exit_json(changed=changed, load_balancer=load_balancer)
if __name__ == "__main__":
main()

View file

@ -1,948 +0,0 @@
#!/usr/bin/python
# Copyright (c) Ansible Project
# 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 annotations
DOCUMENTATION = r"""
module: oneandone_monitoring_policy
short_description: Configure 1&1 monitoring policy
description:
- Create, remove, update monitoring policies (and add/remove ports, processes, and servers). This module has a dependency
on 1and1 >= 1.0.
deprecated:
removed_in: 13.0.0
why: DNS fails to resolve the API endpoint used by the module.
alternative: There is none.
extends_documentation_fragment:
- community.general.attributes
attributes:
check_mode:
support: full
diff_mode:
support: none
options:
state:
description:
- Define a monitoring policy's state to create, remove, update.
type: str
default: present
choices: ["present", "absent", "update"]
auth_token:
description:
- Authenticating API token provided by 1&1.
type: str
api_url:
description:
- Custom API URL. Overrides the E(ONEANDONE_API_URL) environment variable.
type: str
name:
description:
- Monitoring policy name used with present state. Used as identifier (id or name) when used with absent state. maxLength=128.
type: str
monitoring_policy:
description:
- The identifier (id or name) of the monitoring policy used with update state.
type: str
agent:
description:
- Set true for using agent.
type: str
email:
description:
- User's email. maxLength=128.
type: str
description:
description:
- Monitoring policy description. maxLength=256.
type: str
thresholds:
description:
- Monitoring policy thresholds. Each of the suboptions have warning and critical, which both have alert and value suboptions.
Warning is used to set limits for warning alerts, critical is used to set critical alerts. alert enables alert, and
value is used to advise when the value is exceeded.
type: list
elements: dict
default: []
suboptions:
cpu:
description:
- Consumption limits of CPU.
required: true
ram:
description:
- Consumption limits of RAM.
required: true
disk:
description:
- Consumption limits of hard disk.
required: true
internal_ping:
description:
- Response limits of internal ping.
required: true
transfer:
description:
- Consumption limits for transfer.
required: true
ports:
description:
- Array of ports that are to be monitored.
type: list
elements: dict
default: []
suboptions:
protocol:
description:
- Internet protocol.
choices: ["TCP", "UDP"]
required: true
port:
description:
- Port number. minimum=1, maximum=65535.
required: true
alert_if:
description:
- Case of alert.
choices: ["RESPONDING", "NOT_RESPONDING"]
required: true
email_notification:
description:
- Set true for sending e-mail notifications.
required: true
processes:
description:
- Array of processes that are to be monitored.
type: list
elements: dict
default: []
suboptions:
process:
description:
- Name of the process. maxLength=50.
required: true
alert_if:
description:
- Case of alert.
choices: ["RUNNING", "NOT_RUNNING"]
required: true
add_ports:
description:
- Ports to add to the monitoring policy.
type: list
elements: dict
default: []
add_processes:
description:
- Processes to add to the monitoring policy.
type: list
elements: dict
default: []
add_servers:
description:
- Servers to add to the monitoring policy.
type: list
elements: str
default: []
remove_ports:
description:
- Ports to remove from the monitoring policy.
type: list
elements: str
default: []
remove_processes:
description:
- Processes to remove from the monitoring policy.
type: list
elements: str
default: []
remove_servers:
description:
- Servers to remove from the monitoring policy.
type: list
elements: str
default: []
update_ports:
description:
- Ports to be updated on the monitoring policy.
type: list
elements: dict
default: []
update_processes:
description:
- Processes to be updated on the monitoring policy.
type: list
elements: dict
default: []
wait:
description:
- Wait for the instance to be in state 'running' before returning.
default: true
type: bool
wait_timeout:
description:
- How long before wait gives up, in seconds.
type: int
default: 600
wait_interval:
description:
- Defines the number of seconds to wait when using the _wait_for methods.
type: int
default: 5
requirements:
- "1and1"
author:
- "Amel Ajdinovic (@aajdinov)"
- "Ethan Devenport (@edevenport)"
"""
EXAMPLES = r"""
- name: Create a monitoring policy
community.general.oneandone_monitoring_policy:
auth_token: oneandone_private_api_key
name: ansible monitoring policy
description: Testing creation of a monitoring policy with ansible
email: your@emailaddress.com
agent: true
thresholds:
- cpu:
warning:
value: 80
alert: false
critical:
value: 92
alert: false
- ram:
warning:
value: 80
alert: false
critical:
value: 90
alert: false
- disk:
warning:
value: 80
alert: false
critical:
value: 90
alert: false
- internal_ping:
warning:
value: 50
alert: false
critical:
value: 100
alert: false
- transfer:
warning:
value: 1000
alert: false
critical:
value: 2000
alert: false
ports:
- protocol: TCP
port: 22
alert_if: RESPONDING
email_notification: false
processes:
- process: test
alert_if: NOT_RUNNING
email_notification: false
wait: true
- name: Destroy a monitoring policy
community.general.oneandone_monitoring_policy:
auth_token: oneandone_private_api_key
state: absent
name: ansible monitoring policy
- name: Update a monitoring policy
community.general.oneandone_monitoring_policy:
auth_token: oneandone_private_api_key
monitoring_policy: ansible monitoring policy
name: ansible monitoring policy updated
description: Testing creation of a monitoring policy with ansible updated
email: another@emailaddress.com
thresholds:
- cpu:
warning:
value: 70
alert: false
critical:
value: 90
alert: false
- ram:
warning:
value: 70
alert: false
critical:
value: 80
alert: false
- disk:
warning:
value: 70
alert: false
critical:
value: 80
alert: false
- internal_ping:
warning:
value: 60
alert: false
critical:
value: 90
alert: false
- transfer:
warning:
value: 900
alert: false
critical:
value: 1900
alert: false
wait: true
state: update
- name: Add a port to a monitoring policy
community.general.oneandone_monitoring_policy:
auth_token: oneandone_private_api_key
monitoring_policy: ansible monitoring policy updated
add_ports:
- protocol: TCP
port: 33
alert_if: RESPONDING
email_notification: false
wait: true
state: update
- name: Update existing ports of a monitoring policy
community.general.oneandone_monitoring_policy:
auth_token: oneandone_private_api_key
monitoring_policy: ansible monitoring policy updated
update_ports:
- id: existing_port_id
protocol: TCP
port: 34
alert_if: RESPONDING
email_notification: false
- id: existing_port_id
protocol: TCP
port: 23
alert_if: RESPONDING
email_notification: false
wait: true
state: update
- name: Remove a port from a monitoring policy
community.general.oneandone_monitoring_policy:
auth_token: oneandone_private_api_key
monitoring_policy: ansible monitoring policy updated
remove_ports:
- port_id
state: update
- name: Add a process to a monitoring policy
community.general.oneandone_monitoring_policy:
auth_token: oneandone_private_api_key
monitoring_policy: ansible monitoring policy updated
add_processes:
- process: test_2
alert_if: NOT_RUNNING
email_notification: false
wait: true
state: update
- name: Update existing processes of a monitoring policy
community.general.oneandone_monitoring_policy:
auth_token: oneandone_private_api_key
monitoring_policy: ansible monitoring policy updated
update_processes:
- id: process_id
process: test_1
alert_if: NOT_RUNNING
email_notification: false
- id: process_id
process: test_3
alert_if: NOT_RUNNING
email_notification: false
wait: true
state: update
- name: Remove a process from a monitoring policy
community.general.oneandone_monitoring_policy:
auth_token: oneandone_private_api_key
monitoring_policy: ansible monitoring policy updated
remove_processes:
- process_id
wait: true
state: update
- name: Add server to a monitoring policy
community.general.oneandone_monitoring_policy:
auth_token: oneandone_private_api_key
monitoring_policy: ansible monitoring policy updated
add_servers:
- server id or name
wait: true
state: update
- name: Remove server from a monitoring policy
community.general.oneandone_monitoring_policy:
auth_token: oneandone_private_api_key
monitoring_policy: ansible monitoring policy updated
remove_servers:
- server01
wait: true
state: update
"""
RETURN = r"""
monitoring_policy:
description: Information about the monitoring policy that was processed.
type: dict
sample: {"id": "92B74394A397ECC3359825C1656D67A6", "name": "Default Policy"}
returned: always
"""
import os
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.oneandone import (
OneAndOneResources,
get_monitoring_policy,
get_server,
wait_for_resource_creation_completion,
)
HAS_ONEANDONE_SDK = True
try:
import oneandone.client
except ImportError:
HAS_ONEANDONE_SDK = False
def _check_mode(module, result):
if module.check_mode:
module.exit_json(changed=result)
def _add_ports(module, oneandone_conn, monitoring_policy_id, ports):
"""
Adds new ports to a monitoring policy.
"""
try:
monitoring_policy_ports = []
for _port in ports:
monitoring_policy_port = oneandone.client.Port(
protocol=_port["protocol"],
port=_port["port"],
alert_if=_port["alert_if"],
email_notification=_port["email_notification"],
)
monitoring_policy_ports.append(monitoring_policy_port)
if module.check_mode:
return bool(monitoring_policy_ports)
monitoring_policy = oneandone_conn.add_port(
monitoring_policy_id=monitoring_policy_id, ports=monitoring_policy_ports
)
return monitoring_policy
except Exception as ex:
module.fail_json(msg=str(ex))
def _delete_monitoring_policy_port(module, oneandone_conn, monitoring_policy_id, port_id):
"""
Removes a port from a monitoring policy.
"""
try:
if module.check_mode:
monitoring_policy = oneandone_conn.delete_monitoring_policy_port(
monitoring_policy_id=monitoring_policy_id, port_id=port_id
)
return bool(monitoring_policy)
monitoring_policy = oneandone_conn.delete_monitoring_policy_port(
monitoring_policy_id=monitoring_policy_id, port_id=port_id
)
return monitoring_policy
except Exception as ex:
module.fail_json(msg=str(ex))
def _modify_port(module, oneandone_conn, monitoring_policy_id, port_id, port):
"""
Modifies a monitoring policy port.
"""
try:
if module.check_mode:
cm_port = oneandone_conn.get_monitoring_policy_port(
monitoring_policy_id=monitoring_policy_id, port_id=port_id
)
return bool(cm_port)
monitoring_policy_port = oneandone.client.Port(
protocol=port["protocol"],
port=port["port"],
alert_if=port["alert_if"],
email_notification=port["email_notification"],
)
monitoring_policy = oneandone_conn.modify_port(
monitoring_policy_id=monitoring_policy_id, port_id=port_id, port=monitoring_policy_port
)
return monitoring_policy
except Exception as ex:
module.fail_json(msg=str(ex))
def _add_processes(module, oneandone_conn, monitoring_policy_id, processes):
"""
Adds new processes to a monitoring policy.
"""
try:
monitoring_policy_processes = []
for _process in processes:
monitoring_policy_process = oneandone.client.Process(
process=_process["process"],
alert_if=_process["alert_if"],
email_notification=_process["email_notification"],
)
monitoring_policy_processes.append(monitoring_policy_process)
if module.check_mode:
mp_id = get_monitoring_policy(oneandone_conn, monitoring_policy_id)
return bool(monitoring_policy_processes and mp_id)
monitoring_policy = oneandone_conn.add_process(
monitoring_policy_id=monitoring_policy_id, processes=monitoring_policy_processes
)
return monitoring_policy
except Exception as ex:
module.fail_json(msg=str(ex))
def _delete_monitoring_policy_process(module, oneandone_conn, monitoring_policy_id, process_id):
"""
Removes a process from a monitoring policy.
"""
try:
if module.check_mode:
process = oneandone_conn.get_monitoring_policy_process(
monitoring_policy_id=monitoring_policy_id, process_id=process_id
)
return bool(process)
monitoring_policy = oneandone_conn.delete_monitoring_policy_process(
monitoring_policy_id=monitoring_policy_id, process_id=process_id
)
return monitoring_policy
except Exception as ex:
module.fail_json(msg=str(ex))
def _modify_process(module, oneandone_conn, monitoring_policy_id, process_id, process):
"""
Modifies a monitoring policy process.
"""
try:
if module.check_mode:
cm_process = oneandone_conn.get_monitoring_policy_process(
monitoring_policy_id=monitoring_policy_id, process_id=process_id
)
return bool(cm_process)
monitoring_policy_process = oneandone.client.Process(
process=process["process"], alert_if=process["alert_if"], email_notification=process["email_notification"]
)
monitoring_policy = oneandone_conn.modify_process(
monitoring_policy_id=monitoring_policy_id, process_id=process_id, process=monitoring_policy_process
)
return monitoring_policy
except Exception as ex:
module.fail_json(msg=str(ex))
def _attach_monitoring_policy_server(module, oneandone_conn, monitoring_policy_id, servers):
"""
Attaches servers to a monitoring policy.
"""
try:
attach_servers = []
for _server_id in servers:
server_id = get_server(oneandone_conn, _server_id)
attach_server = oneandone.client.AttachServer(server_id=server_id)
attach_servers.append(attach_server)
if module.check_mode:
return bool(attach_servers)
monitoring_policy = oneandone_conn.attach_monitoring_policy_server(
monitoring_policy_id=monitoring_policy_id, servers=attach_servers
)
return monitoring_policy
except Exception as ex:
module.fail_json(msg=str(ex))
def _detach_monitoring_policy_server(module, oneandone_conn, monitoring_policy_id, server_id):
"""
Detaches a server from a monitoring policy.
"""
try:
if module.check_mode:
mp_server = oneandone_conn.get_monitoring_policy_server(
monitoring_policy_id=monitoring_policy_id, server_id=server_id
)
return bool(mp_server)
monitoring_policy = oneandone_conn.detach_monitoring_policy_server(
monitoring_policy_id=monitoring_policy_id, server_id=server_id
)
return monitoring_policy
except Exception as ex:
module.fail_json(msg=str(ex))
def update_monitoring_policy(module, oneandone_conn):
"""
Updates a monitoring_policy based on input arguments.
Monitoring policy ports, processes and servers can be added/removed to/from
a monitoring policy. Monitoring policy name, description, email,
thresholds for cpu, ram, disk, transfer and internal_ping
can be updated as well.
module : AnsibleModule object
oneandone_conn: authenticated oneandone object
"""
try:
monitoring_policy_id = module.params.get("monitoring_policy")
name = module.params.get("name")
description = module.params.get("description")
email = module.params.get("email")
thresholds = module.params.get("thresholds")
add_ports = module.params.get("add_ports")
update_ports = module.params.get("update_ports")
remove_ports = module.params.get("remove_ports")
add_processes = module.params.get("add_processes")
update_processes = module.params.get("update_processes")
remove_processes = module.params.get("remove_processes")
add_servers = module.params.get("add_servers")
remove_servers = module.params.get("remove_servers")
changed = False
monitoring_policy = get_monitoring_policy(oneandone_conn, monitoring_policy_id, True)
if monitoring_policy is None:
_check_mode(module, False)
_monitoring_policy = oneandone.client.MonitoringPolicy(name=name, description=description, email=email)
_thresholds = None
if thresholds:
threshold_entities = ["cpu", "ram", "disk", "internal_ping", "transfer"]
_thresholds = []
for threshold in thresholds:
key = list(threshold.keys())[0]
if key in threshold_entities:
_threshold = oneandone.client.Threshold(
entity=key,
warning_value=threshold[key]["warning"]["value"],
warning_alert=str(threshold[key]["warning"]["alert"]).lower(),
critical_value=threshold[key]["critical"]["value"],
critical_alert=str(threshold[key]["critical"]["alert"]).lower(),
)
_thresholds.append(_threshold)
if name or description or email or thresholds:
_check_mode(module, True)
monitoring_policy = oneandone_conn.modify_monitoring_policy(
monitoring_policy_id=monitoring_policy["id"],
monitoring_policy=_monitoring_policy,
thresholds=_thresholds,
)
changed = True
if add_ports:
if module.check_mode:
_check_mode(module, _add_ports(module, oneandone_conn, monitoring_policy["id"], add_ports))
monitoring_policy = _add_ports(module, oneandone_conn, monitoring_policy["id"], add_ports)
changed = True
if update_ports:
chk_changed = False
for update_port in update_ports:
if module.check_mode:
chk_changed |= _modify_port(
module, oneandone_conn, monitoring_policy["id"], update_port["id"], update_port
)
_modify_port(module, oneandone_conn, monitoring_policy["id"], update_port["id"], update_port)
monitoring_policy = get_monitoring_policy(oneandone_conn, monitoring_policy["id"], True)
changed = True
if remove_ports:
chk_changed = False
for port_id in remove_ports:
if module.check_mode:
chk_changed |= _delete_monitoring_policy_port(
module, oneandone_conn, monitoring_policy["id"], port_id
)
_delete_monitoring_policy_port(module, oneandone_conn, monitoring_policy["id"], port_id)
_check_mode(module, chk_changed)
monitoring_policy = get_monitoring_policy(oneandone_conn, monitoring_policy["id"], True)
changed = True
if add_processes:
monitoring_policy = _add_processes(module, oneandone_conn, monitoring_policy["id"], add_processes)
_check_mode(module, monitoring_policy)
changed = True
if update_processes:
chk_changed = False
for update_process in update_processes:
if module.check_mode:
chk_changed |= _modify_process(
module, oneandone_conn, monitoring_policy["id"], update_process["id"], update_process
)
_modify_process(module, oneandone_conn, monitoring_policy["id"], update_process["id"], update_process)
_check_mode(module, chk_changed)
monitoring_policy = get_monitoring_policy(oneandone_conn, monitoring_policy["id"], True)
changed = True
if remove_processes:
chk_changed = False
for process_id in remove_processes:
if module.check_mode:
chk_changed |= _delete_monitoring_policy_process(
module, oneandone_conn, monitoring_policy["id"], process_id
)
_delete_monitoring_policy_process(module, oneandone_conn, monitoring_policy["id"], process_id)
_check_mode(module, chk_changed)
monitoring_policy = get_monitoring_policy(oneandone_conn, monitoring_policy["id"], True)
changed = True
if add_servers:
monitoring_policy = _attach_monitoring_policy_server(
module, oneandone_conn, monitoring_policy["id"], add_servers
)
_check_mode(module, monitoring_policy)
changed = True
if remove_servers:
chk_changed = False
for _server_id in remove_servers:
server_id = get_server(oneandone_conn, _server_id)
if module.check_mode:
chk_changed |= _detach_monitoring_policy_server(
module, oneandone_conn, monitoring_policy["id"], server_id
)
_detach_monitoring_policy_server(module, oneandone_conn, monitoring_policy["id"], server_id)
_check_mode(module, chk_changed)
monitoring_policy = get_monitoring_policy(oneandone_conn, monitoring_policy["id"], True)
changed = True
return (changed, monitoring_policy)
except Exception as ex:
module.fail_json(msg=str(ex))
def create_monitoring_policy(module, oneandone_conn):
"""
Creates a new monitoring policy.
module : AnsibleModule object
oneandone_conn: authenticated oneandone object
"""
try:
name = module.params.get("name")
description = module.params.get("description")
email = module.params.get("email")
agent = module.params.get("agent")
thresholds = module.params.get("thresholds")
ports = module.params.get("ports")
processes = module.params.get("processes")
wait = module.params.get("wait")
wait_timeout = module.params.get("wait_timeout")
wait_interval = module.params.get("wait_interval")
_monitoring_policy = oneandone.client.MonitoringPolicy(
name,
description,
email,
agent,
)
_monitoring_policy.specs["agent"] = str(_monitoring_policy.specs["agent"]).lower()
threshold_entities = ["cpu", "ram", "disk", "internal_ping", "transfer"]
_thresholds = []
for threshold in thresholds:
key = list(threshold.keys())[0]
if key in threshold_entities:
_threshold = oneandone.client.Threshold(
entity=key,
warning_value=threshold[key]["warning"]["value"],
warning_alert=str(threshold[key]["warning"]["alert"]).lower(),
critical_value=threshold[key]["critical"]["value"],
critical_alert=str(threshold[key]["critical"]["alert"]).lower(),
)
_thresholds.append(_threshold)
_ports = []
for port in ports:
_port = oneandone.client.Port(
protocol=port["protocol"],
port=port["port"],
alert_if=port["alert_if"],
email_notification=str(port["email_notification"]).lower(),
)
_ports.append(_port)
_processes = []
for process in processes:
_process = oneandone.client.Process(
process=process["process"],
alert_if=process["alert_if"],
email_notification=str(process["email_notification"]).lower(),
)
_processes.append(_process)
_check_mode(module, True)
monitoring_policy = oneandone_conn.create_monitoring_policy(
monitoring_policy=_monitoring_policy, thresholds=_thresholds, ports=_ports, processes=_processes
)
if wait:
wait_for_resource_creation_completion(
oneandone_conn,
OneAndOneResources.monitoring_policy,
monitoring_policy["id"],
wait_timeout,
wait_interval,
)
changed = True if monitoring_policy else False
_check_mode(module, False)
return (changed, monitoring_policy)
except Exception as ex:
module.fail_json(msg=str(ex))
def remove_monitoring_policy(module, oneandone_conn):
"""
Removes a monitoring policy.
module : AnsibleModule object
oneandone_conn: authenticated oneandone object
"""
try:
mp_id = module.params.get("name")
monitoring_policy_id = get_monitoring_policy(oneandone_conn, mp_id)
if module.check_mode:
if monitoring_policy_id is None:
_check_mode(module, False)
_check_mode(module, True)
monitoring_policy = oneandone_conn.delete_monitoring_policy(monitoring_policy_id)
changed = True if monitoring_policy else False
return (changed, {"id": monitoring_policy["id"], "name": monitoring_policy["name"]})
except Exception as ex:
module.fail_json(msg=str(ex))
def main():
module = AnsibleModule(
argument_spec=dict(
auth_token=dict(type="str", no_log=True, default=os.environ.get("ONEANDONE_AUTH_TOKEN")),
api_url=dict(type="str", default=os.environ.get("ONEANDONE_API_URL")),
name=dict(type="str"),
monitoring_policy=dict(type="str"),
agent=dict(type="str"),
email=dict(type="str"),
description=dict(type="str"),
thresholds=dict(type="list", elements="dict", default=[]),
ports=dict(type="list", elements="dict", default=[]),
processes=dict(type="list", elements="dict", default=[]),
add_ports=dict(type="list", elements="dict", default=[]),
update_ports=dict(type="list", elements="dict", default=[]),
remove_ports=dict(type="list", elements="str", default=[]),
add_processes=dict(type="list", elements="dict", default=[]),
update_processes=dict(type="list", elements="dict", default=[]),
remove_processes=dict(type="list", elements="str", default=[]),
add_servers=dict(type="list", elements="str", default=[]),
remove_servers=dict(type="list", elements="str", default=[]),
wait=dict(type="bool", default=True),
wait_timeout=dict(type="int", default=600),
wait_interval=dict(type="int", default=5),
state=dict(type="str", default="present", choices=["present", "absent", "update"]),
),
supports_check_mode=True,
)
if not HAS_ONEANDONE_SDK:
module.fail_json(msg="1and1 required for this module")
if not module.params.get("auth_token"):
module.fail_json(msg="auth_token parameter is required.")
if not module.params.get("api_url"):
oneandone_conn = oneandone.client.OneAndOneService(api_token=module.params.get("auth_token"))
else:
oneandone_conn = oneandone.client.OneAndOneService(
api_token=module.params.get("auth_token"), api_url=module.params.get("api_url")
)
state = module.params.get("state")
if state == "absent":
if not module.params.get("name"):
module.fail_json(msg="'name' parameter is required to delete a monitoring policy.")
try:
(changed, monitoring_policy) = remove_monitoring_policy(module, oneandone_conn)
except Exception as ex:
module.fail_json(msg=str(ex))
elif state == "update":
if not module.params.get("monitoring_policy"):
module.fail_json(msg="'monitoring_policy' parameter is required to update a monitoring policy.")
try:
(changed, monitoring_policy) = update_monitoring_policy(module, oneandone_conn)
except Exception as ex:
module.fail_json(msg=str(ex))
elif state == "present":
for param in ("name", "agent", "email", "thresholds", "ports", "processes"):
if not module.params.get(param):
module.fail_json(msg=f"{param} parameter is required for a new monitoring policy.")
try:
(changed, monitoring_policy) = create_monitoring_policy(module, oneandone_conn)
except Exception as ex:
module.fail_json(msg=str(ex))
module.exit_json(changed=changed, monitoring_policy=monitoring_policy)
if __name__ == "__main__":
main()

View file

@ -1,418 +0,0 @@
#!/usr/bin/python
# Copyright (c) Ansible Project
# 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 annotations
DOCUMENTATION = r"""
module: oneandone_private_network
short_description: Configure 1&1 private networking
description:
- Create, remove, reconfigure, update a private network. This module has a dependency on 1and1 >= 1.0.
deprecated:
removed_in: 13.0.0
why: DNS fails to resolve the API endpoint used by the module.
alternative: There is none.
extends_documentation_fragment:
- community.general.attributes
attributes:
check_mode:
support: full
diff_mode:
support: none
options:
state:
description:
- Define a network's state to create, remove, or update.
type: str
default: 'present'
choices: ["present", "absent", "update"]
auth_token:
description:
- Authenticating API token provided by 1&1.
type: str
private_network:
description:
- The identifier (id or name) of the network used with update state.
type: str
api_url:
description:
- Custom API URL. Overrides the E(ONEANDONE_API_URL) environment variable.
type: str
name:
description:
- Private network name used with present state. Used as identifier (id or name) when used with absent state.
type: str
description:
description:
- Set a description for the network.
type: str
datacenter:
description:
- The identifier of the datacenter where the private network is created.
type: str
choices: [US, ES, DE, GB]
network_address:
description:
- Set a private network space, for example V(192.168.1.0).
type: str
subnet_mask:
description:
- Set the netmask for the private network, for example V(255.255.255.0).
type: str
add_members:
description:
- List of server identifiers (name or id) to be added to the private network.
type: list
elements: str
default: []
remove_members:
description:
- List of server identifiers (name or id) to be removed from the private network.
type: list
elements: str
default: []
wait:
description:
- Wait for the instance to be in state 'running' before returning.
default: true
type: bool
wait_timeout:
description:
- How long before wait gives up, in seconds.
type: int
default: 600
wait_interval:
description:
- Defines the number of seconds to wait when using the _wait_for methods.
type: int
default: 5
requirements:
- "1and1"
author:
- Amel Ajdinovic (@aajdinov)
- Ethan Devenport (@edevenport)
"""
EXAMPLES = r"""
- name: Create a private network
community.general.oneandone_private_network:
auth_token: oneandone_private_api_key
name: backup_network
description: Testing creation of a private network with ansible
network_address: 70.35.193.100
subnet_mask: 255.0.0.0
datacenter: US
- name: Destroy a private network
community.general.oneandone_private_network:
auth_token: oneandone_private_api_key
state: absent
name: backup_network
- name: Modify the private network
community.general.oneandone_private_network:
auth_token: oneandone_private_api_key
state: update
private_network: backup_network
network_address: 192.168.2.0
subnet_mask: 255.255.255.0
- name: Add members to the private network
community.general.oneandone_private_network:
auth_token: oneandone_private_api_key
state: update
private_network: backup_network
add_members:
- server identifier (id or name)
- name: Remove members from the private network
community.general.oneandone_private_network:
auth_token: oneandone_private_api_key
state: update
private_network: backup_network
remove_members:
- server identifier (id or name)
"""
RETURN = r"""
private_network:
description: Information about the private network.
type: dict
sample: {"name": "backup_network", "id": "55726DEDA20C99CF6F2AF8F18CAC9963"}
returned: always
"""
import os
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.oneandone import (
OneAndOneResources,
get_datacenter,
get_private_network,
get_server,
wait_for_resource_creation_completion,
wait_for_resource_deletion_completion,
)
HAS_ONEANDONE_SDK = True
try:
import oneandone.client
except ImportError:
HAS_ONEANDONE_SDK = False
DATACENTERS = ["US", "ES", "DE", "GB"]
def _check_mode(module, result):
if module.check_mode:
module.exit_json(changed=result)
def _add_servers(module, oneandone_conn, name, members):
try:
private_network_id = get_private_network(oneandone_conn, name)
if module.check_mode:
return bool(private_network_id and members)
network = oneandone_conn.attach_private_network_servers(
private_network_id=private_network_id, server_ids=members
)
return network
except Exception as e:
module.fail_json(msg=str(e))
def _remove_member(module, oneandone_conn, name, member_id):
try:
private_network_id = get_private_network(oneandone_conn, name)
if module.check_mode:
if private_network_id:
network_member = oneandone_conn.get_private_network_server(
private_network_id=private_network_id, server_id=member_id
)
if network_member:
return True
return False
network = oneandone_conn.remove_private_network_server(private_network_id=name, server_id=member_id)
return network
except Exception as ex:
module.fail_json(msg=str(ex))
def create_network(module, oneandone_conn):
"""
Create new private network
module : AnsibleModule object
oneandone_conn: authenticated oneandone object
Returns a dictionary containing a 'changed' attribute indicating whether
any network was added.
"""
name = module.params.get("name")
description = module.params.get("description")
network_address = module.params.get("network_address")
subnet_mask = module.params.get("subnet_mask")
datacenter = module.params.get("datacenter")
wait = module.params.get("wait")
wait_timeout = module.params.get("wait_timeout")
wait_interval = module.params.get("wait_interval")
if datacenter is not None:
datacenter_id = get_datacenter(oneandone_conn, datacenter)
if datacenter_id is None:
module.fail_json(msg=f"datacenter {datacenter} not found.")
try:
_check_mode(module, True)
network = oneandone_conn.create_private_network(
private_network=oneandone.client.PrivateNetwork(
name=name,
description=description,
network_address=network_address,
subnet_mask=subnet_mask,
datacenter_id=datacenter_id,
)
)
if wait:
wait_for_resource_creation_completion(
oneandone_conn, OneAndOneResources.private_network, network["id"], wait_timeout, wait_interval
)
network = get_private_network(oneandone_conn, network["id"], True)
changed = True if network else False
_check_mode(module, False)
return (changed, network)
except Exception as e:
module.fail_json(msg=str(e))
def update_network(module, oneandone_conn):
"""
Modifies a private network.
module : AnsibleModule object
oneandone_conn: authenticated oneandone object
"""
try:
_private_network_id = module.params.get("private_network")
_name = module.params.get("name")
_description = module.params.get("description")
_network_address = module.params.get("network_address")
_subnet_mask = module.params.get("subnet_mask")
_add_members = module.params.get("add_members")
_remove_members = module.params.get("remove_members")
changed = False
private_network = get_private_network(oneandone_conn, _private_network_id, True)
if private_network is None:
_check_mode(module, False)
if _name or _description or _network_address or _subnet_mask:
_check_mode(module, True)
private_network = oneandone_conn.modify_private_network(
private_network_id=private_network["id"],
name=_name,
description=_description,
network_address=_network_address,
subnet_mask=_subnet_mask,
)
changed = True
if _add_members:
instances = []
for member in _add_members:
instance_id = get_server(oneandone_conn, member)
instance_obj = oneandone.client.AttachServer(server_id=instance_id)
instances.extend([instance_obj])
private_network = _add_servers(module, oneandone_conn, private_network["id"], instances)
_check_mode(module, private_network)
changed = True
if _remove_members:
chk_changed = False
for member in _remove_members:
instance = get_server(oneandone_conn, member, True)
if module.check_mode:
chk_changed |= _remove_member(module, oneandone_conn, private_network["id"], instance["id"])
_check_mode(module, instance and chk_changed)
_remove_member(module, oneandone_conn, private_network["id"], instance["id"])
private_network = get_private_network(oneandone_conn, private_network["id"], True)
changed = True
return (changed, private_network)
except Exception as ex:
module.fail_json(msg=str(ex))
def remove_network(module, oneandone_conn):
"""
Removes a private network.
module : AnsibleModule object
oneandone_conn: authenticated oneandone object.
"""
try:
pn_id = module.params.get("name")
wait_timeout = module.params.get("wait_timeout")
wait_interval = module.params.get("wait_interval")
private_network_id = get_private_network(oneandone_conn, pn_id)
if module.check_mode:
if private_network_id is None:
_check_mode(module, False)
_check_mode(module, True)
private_network = oneandone_conn.delete_private_network(private_network_id)
wait_for_resource_deletion_completion(
oneandone_conn, OneAndOneResources.private_network, private_network["id"], wait_timeout, wait_interval
)
changed = True if private_network else False
return (changed, {"id": private_network["id"], "name": private_network["name"]})
except Exception as e:
module.fail_json(msg=str(e))
def main():
module = AnsibleModule(
argument_spec=dict(
auth_token=dict(type="str", no_log=True, default=os.environ.get("ONEANDONE_AUTH_TOKEN")),
api_url=dict(type="str", default=os.environ.get("ONEANDONE_API_URL")),
private_network=dict(type="str"),
name=dict(type="str"),
description=dict(type="str"),
network_address=dict(type="str"),
subnet_mask=dict(type="str"),
add_members=dict(type="list", elements="str", default=[]),
remove_members=dict(type="list", elements="str", default=[]),
datacenter=dict(choices=DATACENTERS),
wait=dict(type="bool", default=True),
wait_timeout=dict(type="int", default=600),
wait_interval=dict(type="int", default=5),
state=dict(type="str", default="present", choices=["present", "absent", "update"]),
),
supports_check_mode=True,
)
if not HAS_ONEANDONE_SDK:
module.fail_json(msg="1and1 required for this module")
if not module.params.get("auth_token"):
module.fail_json(msg="auth_token parameter is required.")
if not module.params.get("api_url"):
oneandone_conn = oneandone.client.OneAndOneService(api_token=module.params.get("auth_token"))
else:
oneandone_conn = oneandone.client.OneAndOneService(
api_token=module.params.get("auth_token"), api_url=module.params.get("api_url")
)
state = module.params.get("state")
if state == "absent":
if not module.params.get("name"):
module.fail_json(msg="'name' parameter is required for deleting a network.")
try:
(changed, private_network) = remove_network(module, oneandone_conn)
except Exception as e:
module.fail_json(msg=str(e))
elif state == "update":
if not module.params.get("private_network"):
module.fail_json(msg="'private_network' parameter is required for updating a network.")
try:
(changed, private_network) = update_network(module, oneandone_conn)
except Exception as e:
module.fail_json(msg=str(e))
elif state == "present":
if not module.params.get("name"):
module.fail_json(msg="'name' parameter is required for new networks.")
try:
(changed, private_network) = create_network(module, oneandone_conn)
except Exception as e:
module.fail_json(msg=str(e))
module.exit_json(changed=changed, private_network=private_network)
if __name__ == "__main__":
main()

View file

@ -1,306 +0,0 @@
#!/usr/bin/python
# Copyright (c) Ansible Project
# 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 annotations
DOCUMENTATION = r"""
module: oneandone_public_ip
short_description: Configure 1&1 public IPs
description:
- Create, update, and remove public IPs. This module has a dependency on 1and1 >= 1.0.
deprecated:
removed_in: 13.0.0
why: DNS fails to resolve the API endpoint used by the module.
alternative: There is none.
extends_documentation_fragment:
- community.general.attributes
attributes:
check_mode:
support: full
diff_mode:
support: none
options:
state:
description:
- Define a public IP state to create, remove, or update.
type: str
default: 'present'
choices: ["present", "absent", "update"]
auth_token:
description:
- Authenticating API token provided by 1&1.
type: str
api_url:
description:
- Custom API URL. Overrides the E(ONEANDONE_API_URL) environment variable.
type: str
reverse_dns:
description:
- Reverse DNS name. maxLength=256.
type: str
datacenter:
description:
- ID of the datacenter where the IP is created (only for unassigned IPs).
type: str
choices: [US, ES, DE, GB]
default: US
type:
description:
- Type of IP. Currently, only IPV4 is available.
type: str
choices: ["IPV4", "IPV6"]
default: 'IPV4'
public_ip_id:
description:
- The ID of the public IP used with update and delete states.
type: str
wait:
description:
- Wait for the instance to be in state 'running' before returning.
default: true
type: bool
wait_timeout:
description:
- How long before wait gives up, in seconds.
type: int
default: 600
wait_interval:
description:
- Defines the number of seconds to wait when using the _wait_for methods.
type: int
default: 5
requirements:
- "1and1"
author:
- Amel Ajdinovic (@aajdinov)
- Ethan Devenport (@edevenport)
"""
EXAMPLES = r"""
- name: Create a public IP
community.general.oneandone_public_ip:
auth_token: oneandone_private_api_key
reverse_dns: example.com
datacenter: US
type: IPV4
- name: Update a public IP
community.general.oneandone_public_ip:
auth_token: oneandone_private_api_key
public_ip_id: public ip id
reverse_dns: secondexample.com
state: update
- name: Delete a public IP
community.general.oneandone_public_ip:
auth_token: oneandone_private_api_key
public_ip_id: public ip id
state: absent
"""
RETURN = r"""
public_ip:
description: Information about the public IP that was processed.
type: dict
sample: {"id": "F77CC589EBC120905B4F4719217BFF6D", "ip": "10.5.132.106"}
returned: always
"""
import os
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.oneandone import (
OneAndOneResources,
get_datacenter,
get_public_ip,
wait_for_resource_creation_completion,
)
HAS_ONEANDONE_SDK = True
try:
import oneandone.client
except ImportError:
HAS_ONEANDONE_SDK = False
DATACENTERS = ["US", "ES", "DE", "GB"]
TYPES = ["IPV4", "IPV6"]
def _check_mode(module, result):
if module.check_mode:
module.exit_json(changed=result)
def create_public_ip(module, oneandone_conn):
"""
Create new public IP
module : AnsibleModule object
oneandone_conn: authenticated oneandone object
Returns a dictionary containing a 'changed' attribute indicating whether
any public IP was added.
"""
reverse_dns = module.params.get("reverse_dns")
datacenter = module.params.get("datacenter")
ip_type = module.params.get("type")
wait = module.params.get("wait")
wait_timeout = module.params.get("wait_timeout")
wait_interval = module.params.get("wait_interval")
if datacenter is not None:
datacenter_id = get_datacenter(oneandone_conn, datacenter)
if datacenter_id is None:
_check_mode(module, False)
module.fail_json(msg=f"datacenter {datacenter} not found.")
try:
_check_mode(module, True)
public_ip = oneandone_conn.create_public_ip(
reverse_dns=reverse_dns, ip_type=ip_type, datacenter_id=datacenter_id
)
if wait:
wait_for_resource_creation_completion(
oneandone_conn, OneAndOneResources.public_ip, public_ip["id"], wait_timeout, wait_interval
)
public_ip = oneandone_conn.get_public_ip(public_ip["id"])
changed = True if public_ip else False
return (changed, public_ip)
except Exception as e:
module.fail_json(msg=str(e))
def update_public_ip(module, oneandone_conn):
"""
Update a public IP
module : AnsibleModule object
oneandone_conn: authenticated oneandone object
Returns a dictionary containing a 'changed' attribute indicating whether
any public IP was changed.
"""
reverse_dns = module.params.get("reverse_dns")
public_ip_id = module.params.get("public_ip_id")
wait = module.params.get("wait")
wait_timeout = module.params.get("wait_timeout")
wait_interval = module.params.get("wait_interval")
public_ip = get_public_ip(oneandone_conn, public_ip_id, True)
if public_ip is None:
_check_mode(module, False)
module.fail_json(msg=f"public IP {public_ip_id} not found.")
try:
_check_mode(module, True)
public_ip = oneandone_conn.modify_public_ip(ip_id=public_ip["id"], reverse_dns=reverse_dns)
if wait:
wait_for_resource_creation_completion(
oneandone_conn, OneAndOneResources.public_ip, public_ip["id"], wait_timeout, wait_interval
)
public_ip = oneandone_conn.get_public_ip(public_ip["id"])
changed = True if public_ip else False
return (changed, public_ip)
except Exception as e:
module.fail_json(msg=str(e))
def delete_public_ip(module, oneandone_conn):
"""
Delete a public IP
module : AnsibleModule object
oneandone_conn: authenticated oneandone object
Returns a dictionary containing a 'changed' attribute indicating whether
any public IP was deleted.
"""
public_ip_id = module.params.get("public_ip_id")
public_ip = get_public_ip(oneandone_conn, public_ip_id, True)
if public_ip is None:
_check_mode(module, False)
module.fail_json(msg=f"public IP {public_ip_id} not found.")
try:
_check_mode(module, True)
deleted_public_ip = oneandone_conn.delete_public_ip(ip_id=public_ip["id"])
changed = True if deleted_public_ip else False
return (changed, {"id": public_ip["id"]})
except Exception as e:
module.fail_json(msg=str(e))
def main():
module = AnsibleModule(
argument_spec=dict(
auth_token=dict(type="str", no_log=True, default=os.environ.get("ONEANDONE_AUTH_TOKEN")),
api_url=dict(type="str", default=os.environ.get("ONEANDONE_API_URL")),
public_ip_id=dict(type="str"),
reverse_dns=dict(type="str"),
datacenter=dict(choices=DATACENTERS, default="US"),
type=dict(choices=TYPES, default="IPV4"),
wait=dict(type="bool", default=True),
wait_timeout=dict(type="int", default=600),
wait_interval=dict(type="int", default=5),
state=dict(type="str", default="present", choices=["present", "absent", "update"]),
),
supports_check_mode=True,
)
if not HAS_ONEANDONE_SDK:
module.fail_json(msg="1and1 required for this module")
if not module.params.get("auth_token"):
module.fail_json(msg="auth_token parameter is required.")
if not module.params.get("api_url"):
oneandone_conn = oneandone.client.OneAndOneService(api_token=module.params.get("auth_token"))
else:
oneandone_conn = oneandone.client.OneAndOneService(
api_token=module.params.get("auth_token"), api_url=module.params.get("api_url")
)
state = module.params.get("state")
if state == "absent":
if not module.params.get("public_ip_id"):
module.fail_json(msg="'public_ip_id' parameter is required to delete a public ip.")
try:
(changed, public_ip) = delete_public_ip(module, oneandone_conn)
except Exception as e:
module.fail_json(msg=str(e))
elif state == "update":
if not module.params.get("public_ip_id"):
module.fail_json(msg="'public_ip_id' parameter is required to update a public ip.")
try:
(changed, public_ip) = update_public_ip(module, oneandone_conn)
except Exception as e:
module.fail_json(msg=str(e))
elif state == "present":
try:
(changed, public_ip) = create_public_ip(module, oneandone_conn)
except Exception as e:
module.fail_json(msg=str(e))
module.exit_json(changed=changed, public_ip=public_ip)
if __name__ == "__main__":
main()

View file

@ -1,656 +0,0 @@
#!/usr/bin/python
# Copyright (c) Ansible Project
# 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 annotations
DOCUMENTATION = r"""
module: oneandone_server
short_description: Create, destroy, start, stop, and reboot a 1&1 Host server
description:
- Create, destroy, update, start, stop, and reboot a 1&1 Host server. When the server is created it can optionally wait
for it to be 'running' before returning.
deprecated:
removed_in: 13.0.0
why: DNS fails to resolve the API endpoint used by the module.
alternative: There is none.
extends_documentation_fragment:
- community.general.attributes
attributes:
check_mode:
support: full
diff_mode:
support: none
options:
state:
description:
- Define a server's state to create, remove, start or stop it.
type: str
default: present
choices: ["present", "absent", "running", "stopped"]
auth_token:
description:
- Authenticating API token provided by 1&1. Overrides the E(ONEANDONE_AUTH_TOKEN) environment variable.
type: str
api_url:
description:
- Custom API URL. Overrides the E(ONEANDONE_API_URL) environment variable.
type: str
datacenter:
description:
- The datacenter location.
type: str
default: US
choices: ["US", "ES", "DE", "GB"]
hostname:
description:
- The hostname or ID of the server. Only used when state is 'present'.
type: str
description:
description:
- The description of the server.
type: str
appliance:
description:
- The operating system name or ID for the server. It is required only for 'present' state.
type: str
fixed_instance_size:
description:
- The instance size name or ID of the server. It is required only for 'present' state, and it is mutually exclusive
with vcore, cores_per_processor, ram, and hdds parameters.
- 'The available choices are: V(S), V(M), V(L), V(XL), V(XXL), V(3XL), V(4XL), V(5XL).'
type: str
vcore:
description:
- The total number of processors. It must be provided with O(cores_per_processor), O(ram), and O(hdds) parameters.
type: int
cores_per_processor:
description:
- The number of cores per processor. It must be provided with O(vcore), O(ram), and O(hdds) parameters.
type: int
ram:
description:
- The amount of RAM memory. It must be provided with with O(vcore), O(cores_per_processor), and O(hdds) parameters.
type: float
hdds:
description:
- A list of hard disks with nested O(ignore:hdds[].size) and O(ignore:hdds[].is_main) properties. It must be provided
with O(vcore), O(cores_per_processor), and O(ram) parameters.
type: list
elements: dict
private_network:
description:
- The private network name or ID.
type: str
firewall_policy:
description:
- The firewall policy name or ID.
type: str
load_balancer:
description:
- The load balancer name or ID.
type: str
monitoring_policy:
description:
- The monitoring policy name or ID.
type: str
server:
description:
- Server identifier (ID or hostname). It is required for all states except 'running' and 'present'.
type: str
count:
description:
- The number of servers to create.
type: int
default: 1
ssh_key:
description:
- User's public SSH key (contents, not path).
type: raw
server_type:
description:
- The type of server to be built.
type: str
default: "cloud"
choices: ["cloud", "baremetal", "k8s_node"]
wait:
description:
- Wait for the server to be in state 'running' before returning. Also used for delete operation (set to V(false) if
you do not want to wait for each individual server to be deleted before moving on with other tasks).
type: bool
default: true
wait_timeout:
description:
- How long before wait gives up, in seconds.
type: int
default: 600
wait_interval:
description:
- Defines the number of seconds to wait when using the wait_for methods.
type: int
default: 5
auto_increment:
description:
- When creating multiple servers at once, whether to differentiate hostnames by appending a count after them or substituting
the count where there is a %02d or %03d in the hostname string.
type: bool
default: true
requirements:
- "1and1"
author:
- "Amel Ajdinovic (@aajdinov)"
- "Ethan Devenport (@edevenport)"
"""
EXAMPLES = r"""
- name: Create three servers and enumerate their names
community.general.oneandone_server:
auth_token: oneandone_private_api_key
hostname: node%02d
fixed_instance_size: XL
datacenter: US
appliance: C5A349786169F140BCBC335675014C08
auto_increment: true
count: 3
- name: Create three servers, passing in an ssh_key
community.general.oneandone_server:
auth_token: oneandone_private_api_key
hostname: node%02d
vcore: 2
cores_per_processor: 4
ram: 8.0
hdds:
- size: 50
is_main: false
datacenter: ES
appliance: C5A349786169F140BCBC335675014C08
count: 3
wait: true
wait_timeout: 600
wait_interval: 10
ssh_key: SSH_PUBLIC_KEY
- name: Removing server
community.general.oneandone_server:
auth_token: oneandone_private_api_key
state: absent
server: 'node01'
- name: Starting server
community.general.oneandone_server:
auth_token: oneandone_private_api_key
state: running
server: 'node01'
- name: Stopping server
community.general.oneandone_server:
auth_token: oneandone_private_api_key
state: stopped
server: 'node01'
"""
RETURN = r"""
servers:
description: Information about each server that was processed.
type: list
sample:
- {"hostname": "my-server", "id": "server-id"}
returned: always
"""
import os
import time
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.oneandone import (
OneAndOneResources,
get_appliance,
get_datacenter,
get_firewall_policy,
get_fixed_instance_size,
get_load_balancer,
get_monitoring_policy,
get_private_network,
get_server,
wait_for_resource_creation_completion,
wait_for_resource_deletion_completion,
)
HAS_ONEANDONE_SDK = True
try:
import oneandone.client
except ImportError:
HAS_ONEANDONE_SDK = False
DATACENTERS = ["US", "ES", "DE", "GB"]
ONEANDONE_SERVER_STATES = (
"DEPLOYING",
"POWERED_OFF",
"POWERED_ON",
"POWERING_ON",
"POWERING_OFF",
)
def _check_mode(module, result):
if module.check_mode:
module.exit_json(changed=result)
def _create_server(
module,
oneandone_conn,
hostname,
description,
fixed_instance_size_id,
vcore,
cores_per_processor,
ram,
hdds,
datacenter_id,
appliance_id,
ssh_key,
private_network_id,
firewall_policy_id,
load_balancer_id,
monitoring_policy_id,
server_type,
wait,
wait_timeout,
wait_interval,
):
try:
existing_server = get_server(oneandone_conn, hostname)
if existing_server:
if module.check_mode:
return False
return None
if module.check_mode:
return True
server = oneandone_conn.create_server(
oneandone.client.Server(
name=hostname,
description=description,
fixed_instance_size_id=fixed_instance_size_id,
vcore=vcore,
cores_per_processor=cores_per_processor,
ram=ram,
appliance_id=appliance_id,
datacenter_id=datacenter_id,
rsa_key=ssh_key,
private_network_id=private_network_id,
firewall_policy_id=firewall_policy_id,
load_balancer_id=load_balancer_id,
monitoring_policy_id=monitoring_policy_id,
server_type=server_type,
),
hdds,
)
if wait:
wait_for_resource_creation_completion(
oneandone_conn, OneAndOneResources.server, server["id"], wait_timeout, wait_interval
)
server = oneandone_conn.get_server(server["id"]) # refresh
return server
except Exception as ex:
module.fail_json(msg=str(ex))
def _insert_network_data(server):
for addr_data in server["ips"]:
if addr_data["type"] == "IPV6":
server["public_ipv6"] = addr_data["ip"]
elif addr_data["type"] == "IPV4":
server["public_ipv4"] = addr_data["ip"]
return server
def create_server(module, oneandone_conn):
"""
Create new server
module : AnsibleModule object
oneandone_conn: authenticated oneandone object
Returns a dictionary containing a 'changed' attribute indicating whether
any server was added, and a 'servers' attribute with the list of the
created servers' hostname, id and ip addresses.
"""
hostname = module.params.get("hostname")
description = module.params.get("description")
auto_increment = module.params.get("auto_increment")
count = module.params.get("count")
fixed_instance_size = module.params.get("fixed_instance_size")
vcore = module.params.get("vcore")
cores_per_processor = module.params.get("cores_per_processor")
ram = module.params.get("ram")
hdds = module.params.get("hdds")
datacenter = module.params.get("datacenter")
appliance = module.params.get("appliance")
ssh_key = module.params.get("ssh_key")
private_network = module.params.get("private_network")
monitoring_policy = module.params.get("monitoring_policy")
firewall_policy = module.params.get("firewall_policy")
load_balancer = module.params.get("load_balancer")
server_type = module.params.get("server_type")
wait = module.params.get("wait")
wait_timeout = module.params.get("wait_timeout")
wait_interval = module.params.get("wait_interval")
datacenter_id = get_datacenter(oneandone_conn, datacenter)
if datacenter_id is None:
_check_mode(module, False)
module.fail_json(msg=f"datacenter {datacenter} not found.")
fixed_instance_size_id = None
if fixed_instance_size:
fixed_instance_size_id = get_fixed_instance_size(oneandone_conn, fixed_instance_size)
if fixed_instance_size_id is None:
_check_mode(module, False)
module.fail_json(msg=f"fixed_instance_size {fixed_instance_size} not found.")
appliance_id = get_appliance(oneandone_conn, appliance)
if appliance_id is None:
_check_mode(module, False)
module.fail_json(msg=f"appliance {appliance} not found.")
private_network_id = None
if private_network:
private_network_id = get_private_network(oneandone_conn, private_network)
if private_network_id is None:
_check_mode(module, False)
module.fail_json(msg=f"private network {private_network} not found.")
monitoring_policy_id = None
if monitoring_policy:
monitoring_policy_id = get_monitoring_policy(oneandone_conn, monitoring_policy)
if monitoring_policy_id is None:
_check_mode(module, False)
module.fail_json(msg=f"monitoring policy {monitoring_policy} not found.")
firewall_policy_id = None
if firewall_policy:
firewall_policy_id = get_firewall_policy(oneandone_conn, firewall_policy)
if firewall_policy_id is None:
_check_mode(module, False)
module.fail_json(msg=f"firewall policy {firewall_policy} not found.")
load_balancer_id = None
if load_balancer:
load_balancer_id = get_load_balancer(oneandone_conn, load_balancer)
if load_balancer_id is None:
_check_mode(module, False)
module.fail_json(msg=f"load balancer {load_balancer} not found.")
if auto_increment:
hostnames = _auto_increment_hostname(count, hostname)
descriptions = _auto_increment_description(count, description)
else:
hostnames = [hostname] * count
descriptions = [description] * count
hdd_objs = []
if hdds:
for hdd in hdds:
hdd_objs.append(oneandone.client.Hdd(size=hdd["size"], is_main=hdd["is_main"]))
servers = []
for index, name in enumerate(hostnames):
server = _create_server(
module=module,
oneandone_conn=oneandone_conn,
hostname=name,
description=descriptions[index],
fixed_instance_size_id=fixed_instance_size_id,
vcore=vcore,
cores_per_processor=cores_per_processor,
ram=ram,
hdds=hdd_objs,
datacenter_id=datacenter_id,
appliance_id=appliance_id,
ssh_key=ssh_key,
private_network_id=private_network_id,
monitoring_policy_id=monitoring_policy_id,
firewall_policy_id=firewall_policy_id,
load_balancer_id=load_balancer_id,
server_type=server_type,
wait=wait,
wait_timeout=wait_timeout,
wait_interval=wait_interval,
)
if server:
servers.append(server)
changed = False
if servers:
for server in servers:
if server:
_check_mode(module, True)
_check_mode(module, False)
servers = [_insert_network_data(_server) for _server in servers]
changed = True
_check_mode(module, False)
return (changed, servers)
def remove_server(module, oneandone_conn):
"""
Removes a server.
module : AnsibleModule object
oneandone_conn: authenticated oneandone object.
Returns a dictionary containing a 'changed' attribute indicating whether
the server was removed, and a 'removed_server' attribute with
the removed server's hostname and id.
"""
server_id = module.params.get("server")
wait = module.params.get("wait")
wait_timeout = module.params.get("wait_timeout")
wait_interval = module.params.get("wait_interval")
changed = False
removed_server = None
server = get_server(oneandone_conn, server_id, True)
if server:
_check_mode(module, True)
try:
oneandone_conn.delete_server(server_id=server["id"])
if wait:
wait_for_resource_deletion_completion(
oneandone_conn, OneAndOneResources.server, server["id"], wait_timeout, wait_interval
)
changed = True
except Exception as ex:
module.fail_json(msg=f"failed to terminate the server: {ex}")
removed_server = {"id": server["id"], "hostname": server["name"]}
_check_mode(module, False)
return (changed, removed_server)
def startstop_server(module, oneandone_conn):
"""
Starts or Stops a server.
module : AnsibleModule object
oneandone_conn: authenticated oneandone object.
Returns a dictionary with a 'changed' attribute indicating whether
anything has changed for the server as a result of this function
being run, and a 'server' attribute with basic information for
the server.
"""
state = module.params.get("state")
server_id = module.params.get("server")
wait = module.params.get("wait")
wait_timeout = module.params.get("wait_timeout")
wait_interval = module.params.get("wait_interval")
changed = False
# Resolve server
server = get_server(oneandone_conn, server_id, True)
if server:
# Attempt to change the server state, only if it is not already there
# or on its way.
try:
if state == "stopped" and server["status"]["state"] == "POWERED_ON":
_check_mode(module, True)
oneandone_conn.modify_server_status(server_id=server["id"], action="POWER_OFF", method="SOFTWARE")
elif state == "running" and server["status"]["state"] == "POWERED_OFF":
_check_mode(module, True)
oneandone_conn.modify_server_status(server_id=server["id"], action="POWER_ON", method="SOFTWARE")
except Exception as ex:
module.fail_json(msg=f"failed to set server {server_id} to state {state}: {ex}")
_check_mode(module, False)
# Make sure the server has reached the desired state
if wait:
operation_completed = False
wait_timeout = time.time() + wait_timeout
while wait_timeout > time.time():
time.sleep(wait_interval)
server = oneandone_conn.get_server(server["id"]) # refresh
server_state = server["status"]["state"]
if state == "stopped" and server_state == "POWERED_OFF":
operation_completed = True
break
if state == "running" and server_state == "POWERED_ON":
operation_completed = True
break
if not operation_completed:
module.fail_json(msg=f"Timeout waiting for server {server_id} to get to state {state}")
changed = True
server = _insert_network_data(server)
_check_mode(module, False)
return (changed, server)
def _auto_increment_hostname(count, hostname):
"""
Allow a custom incremental count in the hostname when defined with the
string formatting (%) operator. Otherwise, increment using name-01,
name-02, name-03, and so forth.
"""
if "%" not in hostname:
hostname = f"{hostname}-%01d"
return [hostname % i for i in range(1, count + 1)]
def _auto_increment_description(count, description):
"""
Allow the incremental count in the description when defined with the
string formatting (%) operator. Otherwise, repeat the same description.
"""
if "%" in description:
return [description % i for i in range(1, count + 1)]
else:
return [description] * count
def main():
module = AnsibleModule(
argument_spec=dict(
auth_token=dict(type="str", default=os.environ.get("ONEANDONE_AUTH_TOKEN"), no_log=True),
api_url=dict(type="str", default=os.environ.get("ONEANDONE_API_URL")),
hostname=dict(type="str"),
description=dict(type="str"),
appliance=dict(type="str"),
fixed_instance_size=dict(type="str"),
vcore=dict(type="int"),
cores_per_processor=dict(type="int"),
ram=dict(type="float"),
hdds=dict(type="list", elements="dict"),
count=dict(type="int", default=1),
ssh_key=dict(type="raw", no_log=False),
auto_increment=dict(type="bool", default=True),
server=dict(type="str"),
datacenter=dict(choices=DATACENTERS, default="US"),
private_network=dict(type="str"),
firewall_policy=dict(type="str"),
load_balancer=dict(type="str"),
monitoring_policy=dict(type="str"),
server_type=dict(type="str", default="cloud", choices=["cloud", "baremetal", "k8s_node"]),
wait=dict(type="bool", default=True),
wait_timeout=dict(type="int", default=600),
wait_interval=dict(type="int", default=5),
state=dict(type="str", default="present", choices=["present", "absent", "running", "stopped"]),
),
supports_check_mode=True,
mutually_exclusive=(
["fixed_instance_size", "vcore"],
["fixed_instance_size", "cores_per_processor"],
["fixed_instance_size", "ram"],
["fixed_instance_size", "hdds"],
),
required_together=(["vcore", "cores_per_processor", "ram", "hdds"],),
)
if not HAS_ONEANDONE_SDK:
module.fail_json(msg="1and1 required for this module")
if not module.params.get("auth_token"):
module.fail_json(msg='The "auth_token" parameter or ONEANDONE_AUTH_TOKEN environment variable is required.')
if not module.params.get("api_url"):
oneandone_conn = oneandone.client.OneAndOneService(api_token=module.params.get("auth_token"))
else:
oneandone_conn = oneandone.client.OneAndOneService(
api_token=module.params.get("auth_token"), api_url=module.params.get("api_url")
)
state = module.params.get("state")
if state == "absent":
if not module.params.get("server"):
module.fail_json(msg="'server' parameter is required for deleting a server.")
try:
(changed, servers) = remove_server(module, oneandone_conn)
except Exception as ex:
module.fail_json(msg=str(ex))
elif state in ("running", "stopped"):
if not module.params.get("server"):
module.fail_json(msg="'server' parameter is required for starting/stopping a server.")
try:
(changed, servers) = startstop_server(module, oneandone_conn)
except Exception as ex:
module.fail_json(msg=str(ex))
elif state == "present":
for param in ("hostname", "appliance", "datacenter"):
if not module.params.get(param):
module.fail_json(msg=f"{param} parameter is required for new server.")
try:
(changed, servers) = create_server(module, oneandone_conn)
except Exception as ex:
module.fail_json(msg=str(ex))
module.exit_json(changed=changed, servers=servers)
if __name__ == "__main__":
main()

View file

@ -1,193 +0,0 @@
#!/usr/bin/python
#
# Copyright Ansible Project
# 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 annotations
DOCUMENTATION = r"""
author: "Willy Barro (@willybarro)"
requirements: [pushbullet.py]
module: pushbullet
short_description: Sends notifications to Pushbullet
description:
- This module sends push notifications through Pushbullet to channels or devices.
deprecated:
removed_in: 13.0.0
why: Module relies on Python package pushbullet.py which is not maintained and supports only up to Python 3.2.
alternative: There is none.
extends_documentation_fragment:
- community.general.attributes
attributes:
check_mode:
support: full
diff_mode:
support: none
options:
api_key:
type: str
description:
- Push bullet API token.
required: true
channel:
type: str
description:
- The channel TAG you wish to broadcast a push notification, as seen on the "My Channels" > "Edit your channel" at Pushbullet
page.
device:
type: str
description:
- The device NAME you wish to send a push notification, as seen on the Pushbullet main page.
push_type:
type: str
description:
- Thing you wish to push.
default: note
choices: ["note", "link"]
title:
type: str
description:
- Title of the notification.
required: true
body:
type: str
description:
- Body of the notification, for example details of the fault you are alerting.
url:
type: str
description:
- URL field, used when O(push_type=link).
notes:
- Requires C(pushbullet.py) Python package on the remote host. You can install it through C(pip) with C(pip install pushbullet.py).
- See U(https://github.com/randomchars/pushbullet.py).
"""
EXAMPLES = r"""
- name: Sends a push notification to a device
community.general.pushbullet:
api_key: "ABC123abc123ABC123abc123ABC123ab"
device: "Chrome"
title: "You may see this on Google Chrome"
- name: Sends a link to a device
community.general.pushbullet:
api_key: ABC123abc123ABC123abc123ABC123ab
device: Chrome
push_type: link
title: Ansible Documentation
body: https://docs.ansible.com/
- name: Sends a push notification to a channel
community.general.pushbullet:
api_key: ABC123abc123ABC123abc123ABC123ab
channel: my-awesome-channel
title: "Broadcasting a message to the #my-awesome-channel folks"
- name: Sends a push notification with title and body to a channel
community.general.pushbullet:
api_key: ABC123abc123ABC123abc123ABC123ab
channel: my-awesome-channel
title: ALERT! Signup service is down
body: Error rate on signup service is over 90% for more than 2 minutes
"""
import traceback
PUSHBULLET_IMP_ERR = None
try:
from pushbullet import PushBullet
from pushbullet.errors import InvalidKeyError, PushError
except ImportError:
PUSHBULLET_IMP_ERR = traceback.format_exc()
pushbullet_found = False
else:
pushbullet_found = True
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
# ===========================================
# Main
#
def main():
module = AnsibleModule(
argument_spec=dict(
api_key=dict(type="str", required=True, no_log=True),
channel=dict(type="str"),
device=dict(type="str"),
push_type=dict(type="str", default="note", choices=["note", "link"]),
title=dict(type="str", required=True),
body=dict(type="str"),
url=dict(type="str"),
),
mutually_exclusive=(["channel", "device"],),
supports_check_mode=True,
)
api_key = module.params["api_key"]
channel = module.params["channel"]
device = module.params["device"]
push_type = module.params["push_type"]
title = module.params["title"]
body = module.params["body"]
url = module.params["url"]
if not pushbullet_found:
module.fail_json(msg=missing_required_lib("pushbullet.py"), exception=PUSHBULLET_IMP_ERR)
# Init pushbullet
try:
pb = PushBullet(api_key)
target = None
except InvalidKeyError:
module.fail_json(msg="Invalid api_key")
# Checks for channel/device
if device is None and channel is None:
module.fail_json(msg="You need to provide a channel or a device.")
# Search for given device
if device is not None:
devices_by_nickname = {}
for d in pb.devices:
devices_by_nickname[d.nickname] = d
if device in devices_by_nickname:
target = devices_by_nickname[device]
else:
str_devices_by_nickname = "', '".join(devices_by_nickname)
module.fail_json(msg=f"Device '{device}' not found. Available devices: '{str_devices_by_nickname}'")
# Search for given channel
if channel is not None:
channels_by_tag = {}
for c in pb.channels:
channels_by_tag[c.channel_tag] = c
if channel in channels_by_tag:
target = channels_by_tag[channel]
else:
str_channels_by_tag = "', '".join(channels_by_tag)
module.fail_json(msg=f"Channel '{channel}' not found. Available channels: '{str_channels_by_tag}'")
# If in check mode, exit saying that we succeeded
if module.check_mode:
module.exit_json(changed=False, msg="OK")
# Send push notification
try:
if push_type == "link":
target.push_link(title, url, body)
else:
target.push_note(title, body)
module.exit_json(changed=False, msg="OK")
except PushError as e:
module.fail_json(msg=f"An error occurred, Pushbullet's response: {e}")
module.fail_json(msg="An unknown error has occurred")
if __name__ == "__main__":
main()

View file

@ -101,9 +101,10 @@ options:
description:
- If V(true), the payload matches Rocket.Chat prior to 7.4.0 format. This format has been used by the module since its
inception, but is no longer supported by Rocket.Chat 7.4.0.
- The default value of the option, V(true), is B(deprecated) since community.general 11.2.0 and will change to V(false) in community.general 13.0.0.
- The default value changed from V(true) to V(false) in community.general 13.0.0.
- This parameter is going to be removed in a future release when Rocket.Chat 7.4.0 becomes the minimum supported version.
type: bool
default: false
version_added: 10.5.0
"""
@ -229,7 +230,7 @@ def main():
validate_certs=dict(default=True, type="bool"),
color=dict(type="str", default="normal", choices=["normal", "good", "warning", "danger"]),
attachments=dict(type="list", elements="dict"),
is_pre740=dict(type="bool"),
is_pre740=dict(type="bool", default=False),
)
)
@ -246,15 +247,6 @@ def main():
attachments = module.params["attachments"]
is_pre740 = module.params["is_pre740"]
if is_pre740 is None:
module.deprecate(
"The default value 'true' for 'is_pre740' is deprecated and will change to 'false' in community.general 13.0.0."
" You can explicitly set 'is_pre740' in your task to avoid this deprecation warning",
version="13.0.0",
collection_name="community.general",
)
is_pre740 = True
payload = build_payload_for_rocketchat(
module, text, channel, username, icon_url, icon_emoji, link_names, color, attachments, is_pre740
)

View file

@ -1,379 +0,0 @@
#!/usr/bin/python
# Copyright (c) 2014, Anders Ingemann <aim@secoya.dk>
# 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 annotations
DOCUMENTATION = r"""
module: sensu_check
short_description: Manage Sensu checks
description:
- Manage the checks that should be run on a machine by I(Sensu).
- Most options do not have a default and are not added to the check definition unless specified.
- All defaults except O(path), O(state), O(backup) and O(metric) are not managed by this module, they are simply specified
for your convenience.
deprecated:
removed_in: 13.0.0
why: Sensu Core and Sensu Enterprise products have been End of Life since 2019/20.
alternative: Use Sensu Go and its accompanying collection C(sensu.sensu_go).
extends_documentation_fragment:
- community.general.attributes
attributes:
check_mode:
support: full
diff_mode:
support: none
options:
name:
type: str
description:
- The name of the check.
- This is the key that is used to determine whether a check exists.
required: true
state:
type: str
description:
- Whether the check should be present or not.
choices: ['present', 'absent']
default: present
path:
type: str
description:
- Path to the JSON file of the check to be added/removed.
- It is created if it does not exist (unless O(state=absent)).
- The parent folders need to exist when O(state=present), otherwise an error is thrown.
default: /etc/sensu/conf.d/checks.json
backup:
description:
- Create a backup file (if yes), including the timestamp information so you can get the original file back if you somehow
clobbered it incorrectly.
type: bool
default: false
command:
type: str
description:
- Path to the sensu check to run (not required when O(state=absent)).
handlers:
type: list
elements: str
description:
- List of handlers to notify when the check fails.
subscribers:
type: list
elements: str
description:
- List of subscribers/channels this check should run for.
- See sensu_subscribers to subscribe a machine to a channel.
interval:
type: int
description:
- Check interval in seconds.
timeout:
type: int
description:
- Timeout for the check.
- If not specified, it defaults to 10.
ttl:
type: int
description:
- Time to live in seconds until the check is considered stale.
handle:
description:
- Whether the check should be handled or not.
- Default is V(false).
type: bool
subdue_begin:
type: str
description:
- When to disable handling of check failures.
subdue_end:
type: str
description:
- When to enable handling of check failures.
dependencies:
type: list
elements: str
description:
- Other checks this one depends on.
- If dependencies fail handling of this check is disabled.
metric:
description:
- Whether the check is a metric.
type: bool
default: false
standalone:
description:
- Whether the check should be scheduled by the sensu client or server.
- This option obviates the need for specifying the O(subscribers) option.
- Default is V(false).
type: bool
publish:
description:
- Whether the check should be scheduled at all.
- You can still issue it using the sensu API.
- Default is V(false).
type: bool
occurrences:
type: int
description:
- Number of event occurrences before the handler should take action.
- If not specified, defaults to 1.
refresh:
type: int
description:
- Number of seconds handlers should wait before taking second action.
aggregate:
description:
- Classifies the check as an aggregate check, making it available using the aggregate API.
- Default is V(false).
type: bool
low_flap_threshold:
type: int
description:
- The low threshold for flap detection.
high_flap_threshold:
type: int
description:
- The high threshold for flap detection.
custom:
type: dict
description:
- A hash/dictionary of custom parameters for mixing to the configuration.
- You cannot rewrite other module parameters using this.
source:
type: str
description:
- The check source, used to create a JIT Sensu client for an external resource (for example a network switch).
author: "Anders Ingemann (@andsens)"
"""
EXAMPLES = r"""
# Fetch metrics about the CPU load every 60 seconds,
# the sensu server has a handler called 'relay' which forwards stats to graphite
- name: Get cpu metrics
community.general.sensu_check:
name: cpu_load
command: /etc/sensu/plugins/system/cpu-mpstat-metrics.rb
metric: true
handlers: relay
subscribers: common
interval: 60
# Check whether nginx is running
- name: Check nginx process
community.general.sensu_check:
name: nginx_running
command: /etc/sensu/plugins/processes/check-procs.rb -f /var/run/nginx.pid
handlers: default
subscribers: nginx
interval: 60
# Stop monitoring the disk capacity.
# Note that the check will still show up in the sensu dashboard,
# to remove it completely you need to issue a DELETE request to the sensu api.
- name: Check disk
community.general.sensu_check:
name: check_disk_capacity
state: absent
"""
import json
import traceback
from ansible.module_utils.basic import AnsibleModule
def sensu_check(module, path, name, state="present", backup=False):
changed = False
reasons = []
stream = None
try:
try:
stream = open(path)
config = json.load(stream)
except OSError as e:
if e.errno == 2: # File not found, non-fatal
if state == "absent":
reasons.append("file did not exist and state is `absent'")
return changed, reasons
config = {}
else:
module.fail_json(msg=f"{e}", exception=traceback.format_exc())
except ValueError:
msg = f"{path} contains invalid JSON"
module.fail_json(msg=msg)
finally:
if stream:
stream.close()
if "checks" not in config:
if state == "absent":
reasons.append("`checks' section did not exist and state is `absent'")
return changed, reasons
config["checks"] = {}
changed = True
reasons.append("`checks' section did not exist")
if state == "absent":
if name in config["checks"]:
del config["checks"][name]
changed = True
reasons.append("check was present and state is `absent'")
if state == "present":
if name not in config["checks"]:
check = {}
config["checks"][name] = check
changed = True
reasons.append("check was absent and state is `present'")
else:
check = config["checks"][name]
simple_opts = [
"command",
"handlers",
"subscribers",
"interval",
"timeout",
"ttl",
"handle",
"dependencies",
"standalone",
"publish",
"occurrences",
"refresh",
"aggregate",
"low_flap_threshold",
"high_flap_threshold",
"source",
]
for opt in simple_opts:
if module.params[opt] is not None:
if opt not in check or check[opt] != module.params[opt]:
check[opt] = module.params[opt]
changed = True
reasons.append(f"`{opt}' did not exist or was different")
else:
if opt in check:
del check[opt]
changed = True
reasons.append(f"`{opt}' was removed")
if module.params["custom"]:
# Convert to json
custom_params = module.params["custom"]
overwrited_fields = set(custom_params.keys()) & set(
simple_opts + ["type", "subdue", "subdue_begin", "subdue_end"]
)
if overwrited_fields:
msg = f'You can\'t overwriting standard module parameters via "custom". You are trying overwrite: {list(overwrited_fields)}'
module.fail_json(msg=msg)
for k, v in custom_params.items():
if k in config["checks"][name]:
if config["checks"][name][k] != v:
changed = True
reasons.append(f"`custom param {k}' was changed")
else:
changed = True
reasons.append(f"`custom param {k}' was added")
check[k] = v
simple_opts += custom_params.keys()
# Remove obsolete custom params
for opt in set(config["checks"][name].keys()) - set(
simple_opts + ["type", "subdue", "subdue_begin", "subdue_end"]
):
changed = True
reasons.append(f"`custom param {opt}' was deleted")
del check[opt]
if module.params["metric"]:
if "type" not in check or check["type"] != "metric":
check["type"] = "metric"
changed = True
reasons.append("`type' was not defined or not `metric'")
if not module.params["metric"] and "type" in check:
del check["type"]
changed = True
reasons.append("`type' was defined")
if module.params["subdue_begin"] is not None and module.params["subdue_end"] is not None:
subdue = {
"begin": module.params["subdue_begin"],
"end": module.params["subdue_end"],
}
if "subdue" not in check or check["subdue"] != subdue:
check["subdue"] = subdue
changed = True
reasons.append("`subdue' did not exist or was different")
else:
if "subdue" in check:
del check["subdue"]
changed = True
reasons.append("`subdue' was removed")
if changed and not module.check_mode:
if backup:
module.backup_local(path)
try:
try:
stream = open(path, "w")
stream.write(json.dumps(config, indent=2) + "\n")
except OSError as e:
module.fail_json(msg=f"{e}", exception=traceback.format_exc())
finally:
if stream:
stream.close()
return changed, reasons
def main():
arg_spec = {
"name": {"type": "str", "required": True},
"path": {"type": "str", "default": "/etc/sensu/conf.d/checks.json"},
"state": {"type": "str", "default": "present", "choices": ["present", "absent"]},
"backup": {"type": "bool", "default": False},
"command": {"type": "str"},
"handlers": {"type": "list", "elements": "str"},
"subscribers": {"type": "list", "elements": "str"},
"interval": {"type": "int"},
"timeout": {"type": "int"},
"ttl": {"type": "int"},
"handle": {"type": "bool"},
"subdue_begin": {"type": "str"},
"subdue_end": {"type": "str"},
"dependencies": {"type": "list", "elements": "str"},
"metric": {"type": "bool", "default": False},
"standalone": {"type": "bool"},
"publish": {"type": "bool"},
"occurrences": {"type": "int"},
"refresh": {"type": "int"},
"aggregate": {"type": "bool"},
"low_flap_threshold": {"type": "int"},
"high_flap_threshold": {"type": "int"},
"custom": {"type": "dict"},
"source": {"type": "str"},
}
required_together = [["subdue_begin", "subdue_end"]]
module = AnsibleModule(argument_spec=arg_spec, required_together=required_together, supports_check_mode=True)
if module.params["state"] != "absent" and module.params["command"] is None:
module.fail_json(msg="missing required arguments: command")
path = module.params["path"]
name = module.params["name"]
state = module.params["state"]
backup = module.params["backup"]
changed, reasons = sensu_check(module, path, name, state, backup)
module.exit_json(path=path, changed=changed, msg="OK", name=name, reasons=reasons)
if __name__ == "__main__":
main()

View file

@ -1,285 +0,0 @@
#!/usr/bin/python
# Copyright (c) 2017, Red Hat Inc.
# 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 annotations
DOCUMENTATION = r"""
module: sensu_client
author: "David Moreau Simard (@dmsimard)"
short_description: Manages Sensu client configuration
description:
- Manages Sensu client configuration.
- For more information, refer to the L(Sensu documentation, https://sensuapp.org/docs/latest/reference/clients.html).
deprecated:
removed_in: 13.0.0
why: Sensu Core and Sensu Enterprise products have been End of Life since 2019/20.
alternative: Use Sensu Go and its accompanying collection C(sensu.sensu_go).
extends_documentation_fragment:
- community.general.attributes
attributes:
check_mode:
support: full
diff_mode:
support: none
options:
state:
type: str
description:
- Whether the client should be present or not.
choices: ['present', 'absent']
default: present
name:
type: str
description:
- A unique name for the client. The name cannot contain special characters or spaces.
- If not specified, it defaults to the system hostname as determined by Ruby Socket.gethostname (provided by Sensu).
address:
type: str
description:
- An address to help identify and reach the client. This is only informational, usually an IP address or hostname.
- If not specified it defaults to non-loopback IPv4 address as determined by Ruby C(Socket.ip_address_list) (provided
by Sensu).
subscriptions:
type: list
elements: str
description:
- An array of client subscriptions, a list of roles and/or responsibilities assigned to the system (for example V(webserver)).
- These subscriptions determine which monitoring checks are executed by the client, as check requests are sent to subscriptions.
- The subscriptions array items must be strings.
safe_mode:
description:
- If safe mode is enabled for the client. Safe mode requires local check definitions in order to accept a check request
and execute the check.
type: bool
default: false
redact:
type: list
elements: str
description:
- Client definition attributes to redact (values) when logging and sending client keepalives.
socket:
type: dict
description:
- The socket definition scope, used to configure the Sensu client socket.
keepalives:
description:
- If Sensu should monitor keepalives for this client.
type: bool
default: true
keepalive:
type: dict
description:
- The keepalive definition scope, used to configure Sensu client keepalives behavior (for example keepalive thresholds
and so).
registration:
type: dict
description:
- The registration definition scope, used to configure Sensu registration event handlers.
deregister:
description:
- If a deregistration event should be created upon Sensu client process stop.
- Default is V(false).
type: bool
deregistration:
type: dict
description:
- The deregistration definition scope, used to configure automated Sensu client de-registration.
ec2:
type: dict
description:
- The ec2 definition scope, used to configure the Sensu Enterprise AWS EC2 integration (Sensu Enterprise users only).
chef:
type: dict
description:
- The chef definition scope, used to configure the Sensu Enterprise Chef integration (Sensu Enterprise users only).
puppet:
type: dict
description:
- The puppet definition scope, used to configure the Sensu Enterprise Puppet integration (Sensu Enterprise users only).
servicenow:
type: dict
description:
- The servicenow definition scope, used to configure the Sensu Enterprise ServiceNow integration (Sensu Enterprise users
only).
"""
EXAMPLES = r"""
# Minimum possible configuration
- name: Configure Sensu client
community.general.sensu_client:
subscriptions:
- default
# With customization
- name: Configure Sensu client
community.general.sensu_client:
name: "{{ ansible_fqdn }}"
address: "{{ ansible_default_ipv4['address'] }}"
subscriptions:
- default
- webserver
redact:
- password
socket:
bind: 127.0.0.1
port: 3030
keepalive:
thresholds:
warning: 180
critical: 300
handlers:
- email
custom:
- broadcast: irc
occurrences: 3
register: client
notify:
- Restart sensu-client
- name: Secure Sensu client configuration file
ansible.builtin.file:
path: "{{ client['file'] }}"
owner: "sensu"
group: "sensu"
mode: "0600"
- name: Delete the Sensu client configuration
community.general.sensu_client:
state: "absent"
"""
RETURN = r"""
config:
description: Effective client configuration, when state is present.
returned: success
type: dict
sample:
{
"name": "client",
"subscriptions": [
"default"
]
}
file:
description: Path to the client configuration file.
returned: success
type: str
sample: "/etc/sensu/conf.d/client.json"
"""
import json
import os
from ansible.module_utils.basic import AnsibleModule
def main():
module = AnsibleModule(
supports_check_mode=True,
argument_spec=dict(
state=dict(type="str", choices=["present", "absent"], default="present"),
name=dict(
type="str",
),
address=dict(
type="str",
),
subscriptions=dict(type="list", elements="str"),
safe_mode=dict(type="bool", default=False),
redact=dict(type="list", elements="str"),
socket=dict(type="dict"),
keepalives=dict(type="bool", default=True),
keepalive=dict(type="dict"),
registration=dict(type="dict"),
deregister=dict(type="bool"),
deregistration=dict(type="dict"),
ec2=dict(type="dict"),
chef=dict(type="dict"),
puppet=dict(type="dict"),
servicenow=dict(type="dict"),
),
required_if=[["state", "present", ["subscriptions"]]],
)
state = module.params["state"]
path = "/etc/sensu/conf.d/client.json"
if state == "absent":
if os.path.exists(path):
if module.check_mode:
msg = f"{path} would have been deleted"
module.exit_json(msg=msg, changed=True)
else:
try:
os.remove(path)
msg = f"{path} deleted successfully"
module.exit_json(msg=msg, changed=True)
except OSError as e:
msg = "Exception when trying to delete {path}: {exception}"
module.fail_json(msg=msg.format(path=path, exception=str(e)))
else:
# Idempotency: it is okay if the file doesn't exist
msg = f"{path} already does not exist"
module.exit_json(msg=msg)
# Build client configuration from module arguments
config = {"client": {}}
args = [
"name",
"address",
"subscriptions",
"safe_mode",
"redact",
"socket",
"keepalives",
"keepalive",
"registration",
"deregister",
"deregistration",
"ec2",
"chef",
"puppet",
"servicenow",
]
for arg in args:
if arg in module.params and module.params[arg] is not None:
config["client"][arg] = module.params[arg]
# Load the current config, if there is one, so we can compare
current_config = None
try:
current_config = json.load(open(path))
except (OSError, ValueError):
# File either doesn't exist or it is invalid JSON
pass
if current_config is not None and current_config == config:
# Config is the same, let's not change anything
module.exit_json(msg="Client configuration is already up to date", config=config["client"], file=path)
# Validate that directory exists before trying to write to it
if not module.check_mode and not os.path.exists(os.path.dirname(path)):
try:
os.makedirs(os.path.dirname(path))
except OSError as e:
module.fail_json(msg=f"Unable to create {os.path.dirname(path)}: {e}")
if module.check_mode:
module.exit_json(
msg="Client configuration would have been updated", changed=True, config=config["client"], file=path
)
try:
with open(path, "w") as client:
client.write(json.dumps(config, indent=4))
module.exit_json(msg="Client configuration updated", changed=True, config=config["client"], file=path)
except OSError as e:
module.fail_json(msg=f"Unable to write file {path}: {e}")
if __name__ == "__main__":
main()

View file

@ -1,293 +0,0 @@
#!/usr/bin/python
# Copyright (c) 2017, Red Hat Inc.
# 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 annotations
DOCUMENTATION = r"""
module: sensu_handler
author: "David Moreau Simard (@dmsimard)"
short_description: Manages Sensu handler configuration
description:
- Manages Sensu handler configuration.
- For more information, refer to the L(Sensu documentation, https://sensuapp.org/docs/latest/reference/handlers.html).
deprecated:
removed_in: 13.0.0
why: Sensu Core and Sensu Enterprise products have been End of Life since 2019/20.
alternative: Use Sensu Go and its accompanying collection C(sensu.sensu_go).
extends_documentation_fragment:
- community.general.attributes
attributes:
check_mode:
support: full
diff_mode:
support: none
options:
state:
type: str
description:
- Whether the handler should be present or not.
choices: ['present', 'absent']
default: present
name:
type: str
description:
- A unique name for the handler. The name cannot contain special characters or spaces.
required: true
type:
type: str
description:
- The handler type.
choices: ['pipe', 'tcp', 'udp', 'transport', 'set']
filter:
type: str
description:
- The Sensu event filter (name) to use when filtering events for the handler.
filters:
type: list
elements: str
description:
- An array of Sensu event filters (names) to use when filtering events for the handler.
- Each array item must be a string.
severities:
type: list
elements: str
description:
- An array of check result severities the handler handles.
- 'NOTE: event resolution bypasses this filtering.'
- "Example: [ 'warning', 'critical', 'unknown' ]."
mutator:
type: str
description:
- The Sensu event mutator (name) to use to mutate event data for the handler.
timeout:
type: int
description:
- The handler execution duration timeout in seconds (hard stop).
- Only used by pipe and tcp handler types.
default: 10
handle_silenced:
description:
- If events matching one or more silence entries should be handled.
type: bool
default: false
handle_flapping:
description:
- If events in the flapping state should be handled.
type: bool
default: false
command:
type: str
description:
- The handler command to be executed.
- The event data is passed to the process using STDIN.
- 'NOTE: the O(command) attribute is only required for Pipe handlers (that is, handlers configured with O(type=pipe)).'
socket:
type: dict
description:
- The socket definition scope, used to configure the TCP/UDP handler socket.
- 'NOTE: the O(socket) attribute is only required for TCP/UDP handlers (that is, handlers configured with O(type=tcp)
or O(type=udp)).'
pipe:
type: dict
description:
- The pipe definition scope, used to configure the Sensu transport pipe.
- 'NOTE: the O(pipe) attribute is only required for Transport handlers (that is, handlers configured with O(type=transport)).'
handlers:
type: list
elements: str
description:
- An array of Sensu event handlers (names) to use for events using the handler set.
- 'NOTE: the O(handlers) attribute is only required for handler sets (that is, handlers configured with O(type=set)).'
"""
EXAMPLES = r"""
# Configure a handler that sends event data as STDIN (pipe)
- name: Configure IRC Sensu handler
community.general.sensu_handler:
name: "irc_handler"
type: "pipe"
command: "/usr/local/bin/notify-irc.sh"
severities:
- "ok"
- "critical"
- "warning"
- "unknown"
timeout: 15
notify:
- Restart sensu-client
- Restart sensu-server
# Delete a handler
- name: Delete IRC Sensu handler
community.general.sensu_handler:
name: "irc_handler"
state: "absent"
# Example of a TCP handler
- name: Configure TCP Sensu handler
community.general.sensu_handler:
name: "tcp_handler"
type: "tcp"
timeout: 30
socket:
host: "10.0.1.99"
port: 4444
register: handler
notify:
- Restart sensu-client
- Restart sensu-server
- name: Secure Sensu handler configuration file
ansible.builtin.file:
path: "{{ handler['file'] }}"
owner: "sensu"
group: "sensu"
mode: "0600"
"""
RETURN = r"""
config:
description: Effective handler configuration, when state is present.
returned: success
type: dict
sample:
{
"name": "irc",
"type": "pipe",
"command": "/usr/local/bin/notify-irc.sh"
}
file:
description: Path to the handler configuration file.
returned: success
type: str
sample: "/etc/sensu/conf.d/handlers/irc.json"
name:
description: Name of the handler.
returned: success
type: str
sample: "irc"
"""
import json
import os
from ansible.module_utils.basic import AnsibleModule
def main():
module = AnsibleModule(
supports_check_mode=True,
argument_spec=dict(
state=dict(type="str", choices=["present", "absent"], default="present"),
name=dict(type="str", required=True),
type=dict(type="str", choices=["pipe", "tcp", "udp", "transport", "set"]),
filter=dict(type="str"),
filters=dict(type="list", elements="str"),
severities=dict(type="list", elements="str"),
mutator=dict(type="str"),
timeout=dict(type="int", default=10),
handle_silenced=dict(type="bool", default=False),
handle_flapping=dict(type="bool", default=False),
command=dict(type="str"),
socket=dict(type="dict"),
pipe=dict(type="dict"),
handlers=dict(type="list", elements="str"),
),
required_if=[
["state", "present", ["type"]],
["type", "pipe", ["command"]],
["type", "tcp", ["socket"]],
["type", "udp", ["socket"]],
["type", "transport", ["pipe"]],
["type", "set", ["handlers"]],
],
)
state = module.params["state"]
name = module.params["name"]
path = f"/etc/sensu/conf.d/handlers/{name}.json"
if state == "absent":
if os.path.exists(path):
if module.check_mode:
msg = f"{path} would have been deleted"
module.exit_json(msg=msg, changed=True)
else:
try:
os.remove(path)
msg = f"{path} deleted successfully"
module.exit_json(msg=msg, changed=True)
except OSError as e:
msg = "Exception when trying to delete {path}: {exception}"
module.fail_json(msg=msg.format(path=path, exception=str(e)))
else:
# Idempotency: it is okay if the file doesn't exist
msg = f"{path} already does not exist"
module.exit_json(msg=msg)
# Build handler configuration from module arguments
config = {"handlers": {name: {}}}
args = [
"type",
"filter",
"filters",
"severities",
"mutator",
"timeout",
"handle_silenced",
"handle_flapping",
"command",
"socket",
"pipe",
"handlers",
]
for arg in args:
if arg in module.params and module.params[arg] is not None:
config["handlers"][name][arg] = module.params[arg]
# Load the current config, if there is one, so we can compare
current_config = None
try:
current_config = json.load(open(path))
except (OSError, ValueError):
# File either doesn't exist or it is invalid JSON
pass
if current_config is not None and current_config == config:
# Config is the same, let's not change anything
module.exit_json(
msg="Handler configuration is already up to date", config=config["handlers"][name], file=path, name=name
)
# Validate that directory exists before trying to write to it
if not module.check_mode and not os.path.exists(os.path.dirname(path)):
try:
os.makedirs(os.path.dirname(path))
except OSError as e:
module.fail_json(msg=f"Unable to create {os.path.dirname(path)}: {e}")
if module.check_mode:
module.exit_json(
msg="Handler configuration would have been updated",
changed=True,
config=config["handlers"][name],
file=path,
name=name,
)
try:
with open(path, "w") as handler:
handler.write(json.dumps(config, indent=4))
module.exit_json(
msg="Handler configuration updated", changed=True, config=config["handlers"][name], file=path, name=name
)
except OSError as e:
module.fail_json(msg=f"Unable to write file {path}: {e}")
if __name__ == "__main__":
main()

View file

@ -1,270 +0,0 @@
#!/usr/bin/python
# Copyright (c) 2017, Steven Bambling <smbambling@gmail.com>
# 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 annotations
DOCUMENTATION = r"""
module: sensu_silence
author: Steven Bambling (@smbambling)
short_description: Manage Sensu silence entries
description:
- Create and clear (delete) a silence entries using the Sensu API for subscriptions and checks.
deprecated:
removed_in: 13.0.0
why: Sensu Core and Sensu Enterprise products have been End of Life since 2019/20.
alternative: Use Sensu Go and its accompanying collection C(sensu.sensu_go).
extends_documentation_fragment:
- community.general.attributes
attributes:
check_mode:
support: full
diff_mode:
support: none
options:
check:
type: str
description:
- Specifies the check which the silence entry applies to.
creator:
type: str
description:
- Specifies the entity responsible for this entry.
expire:
type: int
description:
- If specified, the silence entry is automatically cleared after this number of seconds.
expire_on_resolve:
description:
- If specified as true, the silence entry is automatically cleared once the condition it is silencing is resolved.
type: bool
reason:
type: str
description:
- If specified, this free-form string is used to provide context or rationale for the reason this silence entry was
created.
state:
type: str
description:
- Specifies to create or clear (delete) a silence entry using the Sensu API.
default: present
choices: ['present', 'absent']
subscription:
type: str
description:
- Specifies the subscription which the silence entry applies to.
- To create a silence entry for a client prepend C(client:) to client name. Example - C(client:server1.example.dev).
required: true
url:
type: str
description:
- Specifies the URL of the Sensu monitoring host server.
default: http://127.0.01:4567
"""
EXAMPLES = r"""
# Silence ALL checks for a given client
- name: Silence server1.example.dev
community.general.sensu_silence:
subscription: client:server1.example.dev
creator: "{{ ansible_user_id }}"
reason: Performing maintenance
# Silence specific check for a client
- name: Silence CPU_Usage check for server1.example.dev
community.general.sensu_silence:
subscription: client:server1.example.dev
check: CPU_Usage
creator: "{{ ansible_user_id }}"
reason: Investigation alert issue
# Silence multiple clients from a dict
silence:
server1.example.dev:
reason: 'Deployment in progress'
server2.example.dev:
reason: 'Deployment in progress'
- name: Silence several clients from a dict
community.general.sensu_silence:
subscription: "client:{{ item.key }}"
reason: "{{ item.value.reason }}"
creator: "{{ ansible_user_id }}"
with_dict: "{{ silence }}"
"""
RETURN = r"""
"""
import json
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.urls import fetch_url
def query(module, url, check, subscription):
headers = {
"Content-Type": "application/json",
}
url = url + "/silenced"
request_data = {
"check": check,
"subscription": subscription,
}
# Remove keys with None value
for k, v in dict(request_data).items():
if v is None:
del request_data[k]
response, info = fetch_url(module, url, method="GET", headers=headers, data=json.dumps(request_data))
if info["status"] == 500:
module.fail_json(msg=f"Failed to query silence {subscription}. Reason: {info}")
try:
json_out = json.loads(response.read())
except Exception:
json_out = ""
return False, json_out, False
def clear(module, url, check, subscription):
# Test if silence exists before clearing
(rc, out, changed) = query(module, url, check, subscription)
d = {i["subscription"]: i["check"] for i in out}
subscription_exists = subscription in d
if check and subscription_exists:
exists = check == d[subscription]
else:
exists = subscription_exists
# If check/subscription doesn't exist
# exit with changed state of False
if not exists:
return False, out, changed
# module.check_mode is inherited from the AnsibleMOdule class
if not module.check_mode:
headers = {
"Content-Type": "application/json",
}
url = url + "/silenced/clear"
request_data = {
"check": check,
"subscription": subscription,
}
# Remove keys with None value
for k, v in dict(request_data).items():
if v is None:
del request_data[k]
response, info = fetch_url(module, url, method="POST", headers=headers, data=json.dumps(request_data))
if info["status"] != 204:
module.fail_json(msg=f"Failed to silence {subscription}. Reason: {info}")
try:
json_out = json.loads(response.read())
except Exception:
json_out = ""
return False, json_out, True
return False, out, True
def create(module, url, check, creator, expire, expire_on_resolve, reason, subscription):
(rc, out, changed) = query(module, url, check, subscription)
for i in out:
if i["subscription"] == subscription:
if (
(check is None or check == i["check"])
and (creator == "" or creator == i["creator"])
and (reason == "" or reason == i["reason"])
and (expire is None or expire == i["expire"])
and (expire_on_resolve is None or expire_on_resolve == i["expire_on_resolve"])
):
return False, out, False
# module.check_mode is inherited from the AnsibleMOdule class
if not module.check_mode:
headers = {
"Content-Type": "application/json",
}
url = url + "/silenced"
request_data = {
"check": check,
"creator": creator,
"expire": expire,
"expire_on_resolve": expire_on_resolve,
"reason": reason,
"subscription": subscription,
}
# Remove keys with None value
for k, v in dict(request_data).items():
if v is None:
del request_data[k]
response, info = fetch_url(module, url, method="POST", headers=headers, data=json.dumps(request_data))
if info["status"] != 201:
module.fail_json(msg=f"Failed to silence {subscription}. Reason: {info['msg']}")
try:
json_out = json.loads(response.read())
except Exception:
json_out = ""
return False, json_out, True
return False, out, True
def main():
module = AnsibleModule(
argument_spec=dict(
check=dict(),
creator=dict(),
expire=dict(type="int"),
expire_on_resolve=dict(type="bool"),
reason=dict(),
state=dict(default="present", choices=["present", "absent"]),
subscription=dict(required=True),
url=dict(default="http://127.0.01:4567"),
),
supports_check_mode=True,
)
url = module.params["url"]
check = module.params["check"]
creator = module.params["creator"]
expire = module.params["expire"]
expire_on_resolve = module.params["expire_on_resolve"]
reason = module.params["reason"]
subscription = module.params["subscription"]
state = module.params["state"]
if state == "present":
(rc, out, changed) = create(module, url, check, creator, expire, expire_on_resolve, reason, subscription)
if state == "absent":
(rc, out, changed) = clear(module, url, check, subscription)
if rc != 0:
module.fail_json(msg="failed", result=out)
module.exit_json(msg="success", result=out, changed=changed)
if __name__ == "__main__":
main()

View file

@ -1,155 +0,0 @@
#!/usr/bin/python
# Copyright (c) 2014, Anders Ingemann <aim@secoya.dk>
# 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 annotations
DOCUMENTATION = r"""
module: sensu_subscription
short_description: Manage Sensu subscriptions
description:
- Manage which I(sensu channels) a machine should subscribe to.
deprecated:
removed_in: 13.0.0
why: Sensu Core and Sensu Enterprise products have been End of Life since 2019/20.
alternative: Use Sensu Go and its accompanying collection C(sensu.sensu_go).
extends_documentation_fragment:
- community.general.attributes
attributes:
check_mode:
support: full
diff_mode:
support: none
options:
name:
type: str
description:
- The name of the channel.
required: true
state:
type: str
description:
- Whether the machine should subscribe or unsubscribe from the channel.
choices: ['present', 'absent']
default: present
path:
type: str
description:
- Path to the subscriptions JSON file.
default: /etc/sensu/conf.d/subscriptions.json
backup:
description:
- Create a backup file (if yes), including the timestamp information so you can get the original file back if you somehow
clobbered it incorrectly.
type: bool
default: false
requirements: []
author: Anders Ingemann (@andsens)
"""
RETURN = r"""
reasons:
description: The reasons why the module changed or did not change something.
returned: success
type: list
sample: ["channel subscription was absent and state is 'present'"]
"""
EXAMPLES = r"""
# Subscribe to the nginx channel
- name: Subscribe to nginx checks
community.general.sensu_subscription: name=nginx
# Unsubscribe from the common checks channel
- name: Unsubscribe from common checks
community.general.sensu_subscription: name=common state=absent
"""
import json
import traceback
from ansible.module_utils.basic import AnsibleModule
def sensu_subscription(module, path, name, state="present", backup=False):
changed = False
reasons = []
try:
config = json.load(open(path))
except OSError as e:
if e.errno == 2: # File not found, non-fatal
if state == "absent":
reasons.append("file did not exist and state is 'absent'")
return changed, reasons
config = {}
else:
module.fail_json(msg=f"{e}", exception=traceback.format_exc())
except ValueError:
msg = f"{path} contains invalid JSON"
module.fail_json(msg=msg)
if "client" not in config:
if state == "absent":
reasons.append("'client' did not exist and state is 'absent'")
return changed, reasons
config["client"] = {}
changed = True
reasons.append("'client' did not exist")
if "subscriptions" not in config["client"]:
if state == "absent":
reasons.append("'client.subscriptions' did not exist and state is 'absent'")
return changed, reasons
config["client"]["subscriptions"] = []
changed = True
reasons.append("'client.subscriptions' did not exist")
if name not in config["client"]["subscriptions"]:
if state == "absent":
reasons.append("channel subscription was absent")
return changed, reasons
config["client"]["subscriptions"].append(name)
changed = True
reasons.append("channel subscription was absent and state is 'present'")
else:
if state == "absent":
config["client"]["subscriptions"].remove(name)
changed = True
reasons.append("channel subscription was present and state is 'absent'")
if changed and not module.check_mode:
if backup:
module.backup_local(path)
try:
open(path, "w").write(json.dumps(config, indent=2) + "\n")
except OSError as e:
module.fail_json(msg=f"Failed to write to file {path}: {e}", exception=traceback.format_exc())
return changed, reasons
def main():
arg_spec = {
"name": {"type": "str", "required": True},
"path": {"type": "str", "default": "/etc/sensu/conf.d/subscriptions.json"},
"state": {"type": "str", "default": "present", "choices": ["present", "absent"]},
"backup": {"type": "bool", "default": False},
}
module = AnsibleModule(argument_spec=arg_spec, supports_check_mode=True)
path = module.params["path"]
name = module.params["name"]
state = module.params["state"]
backup = module.params["backup"]
changed, reasons = sensu_subscription(module, path, name, state, backup)
module.exit_json(path=path, name=name, changed=changed, msg="OK", reasons=reasons)
if __name__ == "__main__":
main()

File diff suppressed because it is too large Load diff

View file

@ -1,138 +0,0 @@
#!/usr/bin/python
#
# Copyright Ansible Project
# 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 annotations
DOCUMENTATION = r"""
module: typetalk
short_description: Send a message to typetalk
description:
- Send a message to typetalk using typetalk API.
deprecated:
removed_in: 13.0.0
why: The typetalk service will be discontinued on Dec 2025. See U(https://nulab.com/blog/company-news/typetalk-sunsetting/).
alternative: There is none.
extends_documentation_fragment:
- community.general.attributes
attributes:
check_mode:
support: none
diff_mode:
support: none
options:
client_id:
type: str
description:
- OAuth2 client ID.
required: true
client_secret:
type: str
description:
- OAuth2 client secret.
required: true
topic:
type: int
description:
- Topic ID to post message.
required: true
msg:
type: str
description:
- Message body.
required: true
requirements: [json]
author: "Takashi Someda (@tksmd)"
"""
EXAMPLES = r"""
- name: Send a message to typetalk
community.general.typetalk:
client_id: 12345
client_secret: 12345
topic: 1
msg: install completed
"""
import json
from urllib.parse import urlencode
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.urls import ConnectionError, fetch_url
def do_request(module, url, params, headers=None):
data = urlencode(params)
if headers is None:
headers = dict()
headers = dict(
headers,
**{
"User-Agent": "Ansible/typetalk module",
},
)
r, info = fetch_url(module, url, data=data, headers=headers)
if info["status"] != 200:
exc = ConnectionError(info["msg"])
exc.code = info["status"]
raise exc
return r
def get_access_token(module, client_id, client_secret):
params = {
"client_id": client_id,
"client_secret": client_secret,
"grant_type": "client_credentials",
"scope": "topic.post",
}
res = do_request(module, "https://typetalk.com/oauth2/access_token", params)
return json.load(res)["access_token"]
def send_message(module, client_id, client_secret, topic, msg):
"""
send message to typetalk
"""
try:
access_token = get_access_token(module, client_id, client_secret)
url = f"https://typetalk.com/api/v1/topics/{topic}"
headers = {
"Authorization": f"Bearer {access_token}",
}
do_request(module, url, {"message": msg}, headers)
return True, {"access_token": access_token}
except ConnectionError as e:
return False, e
def main():
module = AnsibleModule(
argument_spec=dict(
client_id=dict(required=True),
client_secret=dict(required=True, no_log=True),
topic=dict(required=True, type="int"),
msg=dict(required=True),
),
supports_check_mode=False,
)
if not json:
module.fail_json(msg="json module is required")
client_id = module.params["client_id"]
client_secret = module.params["client_secret"]
topic = module.params["topic"]
msg = module.params["msg"]
res, error = send_message(module, client_id, client_secret, topic, msg)
if not res:
module.fail_json(msg=f"fail to send message with response code {error.code}")
module.exit_json(changed=True, topic=topic, msg=msg)
if __name__ == "__main__":
main()