Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add fact gathering module for amq #145

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ This collection has been tested against following Ansible versions: **>=2.15.0**

### Plugins:

* `activemq_facts`: return activemq configuration as ansible fact data
* `pbkdf2_hmac`: filter plugin used internally to generate unidirectional account password hashes
<!--end roles_paths -->

Expand Down
23 changes: 23 additions & 0 deletions molecule/default/verify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@
- name: Populate service facts
ansible.builtin.service_facts:

- name: Populate activemq facts
middleware_automation.amq.activemq_facts:
base_url: http://0.0.0.0:8161
auth_username: amq
auth_password: amqbrokerpass

- name: Print activemq gathered facts
ansible.builtin.debug:
var: ansible_facts.activemq

- name: Check if amq-broker service is started
ansible.builtin.assert:
that:
Expand Down Expand Up @@ -91,3 +101,16 @@
- diverts.count == 1
quiet: true
fail_msg: "Failed to retrieve generated divert"

- name: Check divert configuration via jolokia
ansible.builtin.assert:
that:
- ansible_facts.activemq.DivertNames | length > 0
- ansible_facts.activemq.DivertNames | first == "TESTDIVERT"

- name: Check queue configuration via jolokia
ansible.builtin.assert:
that:
- ansible_facts.activemq.AddressNames | length > 0
- "'client123.pubsub' in ansible_facts.activemq.AddressNames"
- "'importantTopic' in ansible_facts.activemq.AddressNames"
6 changes: 6 additions & 0 deletions molecule/mask_passwords/verify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@
- name: Populate service facts
ansible.builtin.service_facts:

- name: Populate activemq facts
middleware_automation.amq.activemq_facts:
auth_username: tesla
auth_password: password
validate_certs: false

- name: Check if amq-broker service is started
ansible.builtin.assert:
that:
Expand Down
4 changes: 4 additions & 0 deletions molecule/static_cluster/converge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
activemq_tls_keystore_password: securepass
activemq_tls_truststore_path: nfs/trust.ks
activemq_tls_truststore_password: securepass
activemq_users:
- user: amq
password: amqbrokerpass
roles: [ amq ]
activemq_acceptors:
- name: artemis
bind_address: 0.0.0.0
Expand Down
24 changes: 24 additions & 0 deletions molecule/static_cluster/verify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@
- name: Populate service facts
ansible.builtin.service_facts:

- name: Populate activemq facts
middleware_automation.amq.activemq_facts:
base_url: https://localhost:8161
auth_username: amq
auth_password: amqbrokerpass
validate_certs: false

- name: Check if amq-broker service is started
ansible.builtin.assert:
that:
Expand Down Expand Up @@ -43,3 +50,20 @@
- "'got backup lock' in slurped_log_instance2.content|b64decode"
- "'waiting live to fail before it gets active' in slurped_log_instance2.content|b64decode or 'waiting for primary to fail before activating' in slurped_log_instance2.content|b64decode"
quiet: true

- name: Check cluster status via jolokia facts (master)
ansible.builtin.assert:
that:
- ansible_facts.activemq.Active == true
- ansible_facts.activemq.Backup == false
- ansible_facts.activemq.SharedStore == true
- ansible_facts.activemq.HAPolicy == "Shared Store Primary"
when: inventory_hostname == 'instance1'

- name: Check cluster status via jolokia facts (backup)
ansible.builtin.assert:
that:
- ansible_facts.activemq.Active == false
- ansible_facts.activemq.Backup == true
- ansible_facts.activemq.SharedStore == true
when: inventory_hostname == 'instance2'
184 changes: 184 additions & 0 deletions plugins/modules/activemq_facts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (c) 2024, Red Hat Inc.
# Copyright (c) 2024, Guido Grazioli <ggraziol@redhat.com>
# Apache License, Version 2.0 (see LICENSE or https://www.apache.org/licenses/LICENSE-2.0)
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

from __future__ import absolute_import, division, print_function
__metaclass__ = type

DOCUMENTATION = r'''
---
module: activemq_facts
short_description: Return activemq configuration as fact data
description:
- Return artemis activemq configuration and runtime information from the jolokia endpoint as fact data.
version_added: "2.1.0"
requirements: ["A running instance of activemq with the jolokia port open"]
extends_documentation_fragment:
- action_common_attributes
- action_common_attributes.facts
attributes:
check_mode:
support: full
diff_mode:
support: none
facts:
support: full
platform:
platforms: all
options:
base_url:
description:
- URL to the activemq instance.
type: str
required: false
default: http://localhost:8161
aliases:
- url
broker_name:
description:
- Name of the broker instance
type: str
required: false
default: 'amq-broker'
aliases:
- broker
auth_username:
description:
- Username to authenticate for API access with.
type: str
required: true
aliases:
- username
auth_password:
description:
- Password to authenticate for API access with.
type: str
required: true
aliases:
- password
validate_certs:
description:
- Verify TLS certificates when using https.
type: bool
default: true
connection_timeout:
description:
- Controls the HTTP connections timeout period in seconds to jolokia API.
type: int
default: 10
author:
- Guido Grazioli (@guidograzioli)
'''

EXAMPLES = r'''
- name: Populate activemq facts
middleware_automation.amq.activemq_facts:

- name: Print activemq service facts
ansible.builtin.debug:
var: ansible_facts.activemq
'''

RETURN = r'''
ansible_facts:
description: Facts to add to ansible_facts about the activemq service
returned: always
type: complex
contains:
activemq:
description: The factual representation of an activemq instance configuration.
returned: always
type: dict
'''

import json
import traceback

from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.urls import open_url, basic_auth_header
from ansible.module_utils.six.moves.urllib.error import HTTPError
from ansible.module_utils.common.text.converters import to_native


URL_JOLOKIA_INFO = "{url}/console/jolokia/read/org.apache.activemq.artemis:broker=!%22{broker}!%22"


class JolokiaService(object):

def __init__(self, module):
self.module = module
self.baseurl = self.module.params.get('base_url')
self.broker = self.module.params.get('broker_name')
self.validate_certs = self.module.params.get('validate_certs')
self.connection_timeout = self.module.params.get('connection_timeout')
self.auth_username = self.module.params.get('auth_username')
self.auth_password = self.module.params.get('auth_password')

def gather_facts(self):
""" Obtain configuration from jolokia API

:return: dict of real, representation or None if none matching exist
"""

jolokia_url = URL_JOLOKIA_INFO.format(url=self.baseurl, broker=self.broker)

if not self.baseurl.lower().startswith(('http', 'https')):
raise ValueError("base url '%s' should either start with 'http' or 'https'." % self.baseurl)

restheaders = {}
restheaders["Authorization"] = basic_auth_header(self.auth_username, self.auth_password)
restheaders['Origin'] = self.baseurl if 'localhost' not in self.baseurl else 'https://0.0.0.0'

try:
return json.loads(to_native(open_url(jolokia_url, method='GET',
headers=restheaders,
timeout=self.connection_timeout,
validate_certs=self.validate_certs).read()))

except HTTPError as e:
if e.code == 404:
return None
else:
self.module.fail_json(msg='HTTP error calling jolokia api: %s' % (str(e)),
exception=traceback.format_exc())
except ValueError as e:
self.module.fail_json(msg='API returned incorrect JSON: %s' % (str(e)),
exception=traceback.format_exc())
except Exception as e:
self.module.fail_json(msg='General error calling jolokia api %s' % (str(e)),
exception=traceback.format_exc())


def amq_argument_spec():
"""
Returns argument_spec of options

:return: argument_spec dict
"""
return dict(
base_url=dict(type='str', aliases=['url'], required=False, default="http://localhost:8161", no_log=False),
broker_name=dict(type='str', aliases=['broker'], required=False, default="amq-broker"),
auth_username=dict(type='str', aliases=['username'], required=True),
auth_password=dict(type='str', aliases=['password'], required=True, no_log=True),
validate_certs=dict(type='bool', default=True),
connection_timeout=dict(type='int', default=10)
)


def main():
module = AnsibleModule(argument_spec=amq_argument_spec(), supports_check_mode=True,
required_together=([['auth_username', 'auth_password']]))
mod = JolokiaService(module)
svc = mod.gather_facts()
if not svc or "value" not in svc:
results = dict(skipped=True, msg="Failed to find info. This can be due to privileges or some other configuration issue.")
else:
results = dict(ansible_facts=dict(activemq=svc["value"]))
module.exit_json(**results)


if __name__ == '__main__':
main()