Skip to content

Commit

Permalink
Add Icinga Deploy handler and module (#205)
Browse files Browse the repository at this point in the history
* integrate handler

* adding testing

* changes based on review. Restructuring of objects to create independent deploy and deployment info module

* add changes of second review

* tmp

* rename vars and cleanup pep

* remove tests for icinga_deploy.py

* remove tests for icinga_deploy.py

* fix description of icinga deploy info module

* add tests for wrong host/pass for deployment

* fix typo

* fix error condition

* fix error condition

* fix error condition

* fix error condition

---------

Co-authored-by: fkhe <Falk.Haendler@t-systems.com>
Co-authored-by: Sebastian Gumprich <sebastian.gumprich@t-systems.com>
  • Loading branch information
3 people authored May 22, 2023
1 parent 2142d11 commit 62227a7
Show file tree
Hide file tree
Showing 35 changed files with 425 additions and 33 deletions.
6 changes: 6 additions & 0 deletions examples/icinga_deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
- name: Deploy the icinga config
t_systems_mms.icinga_director.icinga_deploy:
url: "{{ icinga_url }}"
url_username: "{{ icinga_user }}"
url_password: "{{ icinga_pass }}"
6 changes: 6 additions & 0 deletions examples/icinga_deploy_info.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
- name: Query the current deployment info in icinga
t_systems_mms.icinga_director.icinga_deploy_info:
url: "{{ icinga_url }}"
url_username: "{{ icinga_user }}"
url_password: "{{ icinga_pass }}"
4 changes: 4 additions & 0 deletions hacking/update_examples_and_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ for module in "$dir_path"/../plugins/modules/*.py; do
module_name="$(basename "${module}" .py)"
fqcn_name="t_systems_mms.icinga_director.$(basename "${module}" .py)"

if [[ $module_name == "icinga_deploy" ]]; then
continue
fi

# create examples
# https://stackoverflow.com/a/22221307
# this greps the examples from the plugins and puts them into a temp-file
Expand Down
41 changes: 38 additions & 3 deletions plugins/module_utils/icinga.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@
class Icinga2APIObject(object):
"""Interact with the icinga2 director API"""

module = None

def __init__(self, module, path, data):
self.module = module
self.params = module.params
Expand Down Expand Up @@ -126,10 +124,47 @@ def query(self, query="", resolved=False):
msg="bad return code while querying: %d. Error message: %s"
% (ret["code"], ret["error"])
)
return ret
except Exception as e:
self.module.fail_json(msg="exception when querying: " + str(e))

return ret
def query_deployment(self, configs=None, activities=None):
"""
Find the current deployment or the deployment specified with configs or activities
in the director and return the result of the api-call.
Parameters:
configs: type list, default empty, list of checksums for configs to search for.
If left empty, only the active_configuration will be returned.
activities: type list, default empty, list of checksums for activities to search for.
If left empty, only the active_configuration will be returned.
Returns:
the result of the api-call
"""

if configs is None:
configs = []

if activities is None:
activities = []

try:
ret = self.call_url(
path=self.path
+ "?configs="
+ ",".join(configs)
+ "&activities="
+ ",".join(activities)
)
if ret["code"] != 200:
self.module.fail_json(
msg="bad return code while querying: %d. Error message: %s"
% (ret["code"], ret["error"])
)
return ret
except Exception as e:
self.module.fail_json(msg="exception when querying: " + str(e))

def create(self):
"""
Expand Down
114 changes: 114 additions & 0 deletions plugins/modules/icinga_deploy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2023 T-Systems Multimedia Solutions GmbH
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#
# This module is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This software is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this software. If not, see <http://www.gnu.org/licenses/>.

from __future__ import absolute_import, division, print_function

__metaclass__ = type

DOCUMENTATION = """
---
module: icinga_deploy
short_description: Trigger deployment in Icinga2
description:
- Trigger a deployment to Icinga2 through the director API.
author: Falk Händler (@flkhndlr)
version_added: '1.33.0'
extends_documentation_fragment:
- ansible.builtin.url
- t_systems_mms.icinga_director.common_options
"""

EXAMPLES = """
- name: Deploy the icinga config
t_systems_mms.icinga_director.icinga_deploy:
url: "{{ icinga_url }}"
url_username: "{{ icinga_user }}"
url_password: "{{ icinga_pass }}"
"""

RETURN = r"""
checksum:
description:
- Checksum of the configuration that should be rolled out
returned: always
type: str
sample:
checksum: 294bdfb53c4da471e37317beed549a953c939424
"""


from time import sleep
from ansible.module_utils.urls import url_argument_spec
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.t_systems_mms.icinga_director.plugins.module_utils.icinga import (
Icinga2APIObject,
)
# ===========================================
# Module execution.
#


def main():
# use the predefined argument spec for url
argument_spec = url_argument_spec()

# add our own arguments
argument_spec.update(
url=dict(required=True),
)

# Define the main module
module = AnsibleModule(
argument_spec=argument_spec,
supports_check_mode=False,
)

# get the current deployment status
icinga_deploy_status = Icinga2APIObject(module=module, path="/config/deployment-status", data=[])
active_deployment = icinga_deploy_status.query_deployment()["data"]["active_configuration"]["config"]

# execute the deployment
icinga_deployment = Icinga2APIObject(module=module, path="/config/deploy", data=[])
result = icinga_deployment.create()
# the deployment is asynchronous and I don't know of a way to check if it is finished.
# so we need some sleep here. 2 seconds is a wild guess.
sleep(2)

# get the new deployment status
create_deployment = icinga_deploy_status.query_deployment()["data"]["active_configuration"]["config"]

# when the old checksum, the checksum to be created and the new checksum are the same, nothing changed
if result["data"]["checksum"] == active_deployment == create_deployment:
module.exit_json(
changed=False,
checksum=result["data"]["checksum"],
)
# when the current and new deployment are the same, but the checksum to be created is different, the deployment failed
elif create_deployment == active_deployment:
module.fail_json(msg="deployment failed")
# in other cases the deployment succeeded and changed something
else:
module.exit_json(
changed=True,
checksum=result["data"]["checksum"],
)


if __name__ == "__main__":
main()
140 changes: 140 additions & 0 deletions plugins/modules/icinga_deploy_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2023 T-Systems Multimedia Solutions GmbH
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#
# This module is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This software is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this software. If not, see <http://www.gnu.org/licenses/>.

from __future__ import absolute_import, division, print_function

__metaclass__ = type

DOCUMENTATION = """
---
module: icinga_deploy_info
short_description: Get deployment information through the director API
description:
- Get deployment information through the director API.
author: Falk Händler (@flkhndlr)
version_added: '1.33.0'
extends_documentation_fragment:
- ansible.builtin.url
- t_systems_mms.icinga_director.common_options
options:
configs:
description:
- A list of checksums of configs to query information for
type: list
elements: str
activities:
description:
- A list of checksums of activities to query information for
type: list
elements: str
"""

EXAMPLES = """
- name: Query the current deployment info in icinga
t_systems_mms.icinga_director.icinga_deploy_info:
url: "{{ icinga_url }}"
url_username: "{{ icinga_user }}"
url_password: "{{ icinga_pass }}"
"""

RETURN = r"""
active_configuration:
description:
- Checksums of the active configuration
- Contains current activity checksum, config checksum
- and a checksum for the stage_name
returned: if active configuration exists
type: dict
sample:
active_configuration:
activity: 3557598829f2a2fc4acc7b565fb54bae24754c67
config: 299d9d49e03435c6de562c4b22a26e63990d30a9
stage_name: 902cb282-e702-43ce-bb3c-962f850a1694
configs:
description:
- Checksum of the requested config and its state
returned: only if requested
type: list
sample:
configs:
b175ca0562434deeb4fb1fc03fd80cd7361b56df: deployed
b175ca0562434deeb4fb1fc03fd80cd7361b56de: active
activities:
description:
- checksum of the requested activities and its state
returned: only if requested
type: list
sample:
activities:
a4c955364bc7b77efd0323fc87d95307f827e30c: deployed
3557598829f2a2fc4acc7b565fb54bae24754c67: active
"""

from ansible.module_utils.urls import url_argument_spec
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.t_systems_mms.icinga_director.plugins.module_utils.icinga import (
Icinga2APIObject,
)


# ===========================================
# Module execution.
#
def main():
# use the predefined argument spec for url
argument_spec = url_argument_spec()

# add our own arguments
argument_spec.update(
url=dict(required=True),
configs=dict(type="list", required=False, default=None, elements="str"),
activities=dict(type="list", required=False, default=None, elements="str"),
)

# Define the main module
module = AnsibleModule(
argument_spec=argument_spec,
supports_check_mode=True,
)

icinga_object = Icinga2APIObject(module=module, path="/config/deployment-status", data=[])

object_list = icinga_object.query_deployment(
configs=module.params["configs"], activities=module.params["activities"]
)

config_list = {}
activity_list = {}

if "configs" in object_list["data"].keys():
config_list = dict(object_list["data"]["configs"].items())

if "activities" in object_list["data"].keys():
activity_list = dict(object_list["data"]["activities"].items())

module.exit_json(
objects=object_list,
active_configuration=object_list["data"]["active_configuration"],
configs=config_list,
activities=activity_list
)


if __name__ == "__main__":
main()
3 changes: 3 additions & 0 deletions roles/ansible_icinga/defaults/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,6 @@ icinga_servicegroups: []

# icinga_notification
icinga_notifications: []

# icinga deploy
icinga_deploy_config: false
8 changes: 8 additions & 0 deletions roles/ansible_icinga/handlers/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
- name: Deploy the icinga config
t_systems_mms.icinga_director.icinga_deploy:
url: "{{ icinga_url }}"
url_username: "{{ icinga_user }}"
url_password: "{{ icinga_pass }}"
when: icinga_deploy_config and icinga_deploy_config is defined
listen: config_deploy
1 change: 1 addition & 0 deletions roles/ansible_icinga/tasks/icinga_command.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@
loop_control:
loop_var: command
tags: command
notify: config_deploy
1 change: 1 addition & 0 deletions roles/ansible_icinga/tasks/icinga_command_template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,4 @@
loop_control:
loop_var: command_template
tags: command_template
notify: config_deploy
1 change: 1 addition & 0 deletions roles/ansible_icinga/tasks/icinga_endpoint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@
loop_control:
loop_var: endpoint
tags: endpoint
notify: config_deploy
1 change: 1 addition & 0 deletions roles/ansible_icinga/tasks/icinga_host.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,4 @@
loop_control:
loop_var: host
tags: host
notify: config_deploy
1 change: 1 addition & 0 deletions roles/ansible_icinga/tasks/icinga_host_template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,4 @@
loop_control:
loop_var: host_template
tags: host_template
notify: config_deploy
1 change: 1 addition & 0 deletions roles/ansible_icinga/tasks/icinga_hostgroup.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@
loop_control:
loop_var: hostgroup
tags: hostgroup
notify: config_deploy
1 change: 1 addition & 0 deletions roles/ansible_icinga/tasks/icinga_notification.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@
loop_control:
loop_var: notification
tags: notification
notify: config_deploy
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@
loop_control:
loop_var: notification_template
tags: notification_template
notify: config_deploy
Loading

0 comments on commit 62227a7

Please sign in to comment.