Merge pull request #37834 from ClaudiuPID/adds-icinga2-support

Icinga2 module and state draft proposal.
This commit is contained in:
Mike Place 2016-12-05 08:59:01 -07:00 committed by GitHub
commit fb984566d0
2 changed files with 398 additions and 0 deletions

122
salt/modules/icinga2.py Normal file
View File

@ -0,0 +1,122 @@
# -*- coding: utf-8 -*-
'''
Module to provide icinga2 compatibility to salt.
:depends: - icinga2 server
'''
# Import python libs
from __future__ import absolute_import
import logging
# Import Salt libs
import salt.utils
log = logging.getLogger(__name__)
def __virtual__():
'''
Only load this module if the mysql libraries exist
'''
# TODO: This could work on windows with some love
if salt.utils.is_windows():
return (False, 'The module cannot be loaded on windows.')
if salt.utils.which('icinga2'):
return True
return (False, 'Icinga2 not installed.')
def generate_ticket(domain):
'''
Generate and save an icinga2 ticket.
Returns::
icinga2 pki ticket --cn domain.tld
CLI Example:
.. code-block:: bash
salt '*' icinga2.generate_ticket domain.tld
'''
result = __salt__['cmd.run']("icinga2 pki ticket --cn {0}".format(domain))
return result
def generate_cert(domain):
'''
Generate an icinga2 client certificate and key.
Returns::
icinga2 pki new-cert --cn domain.tld --key /etc/icinga2/pki/domain.tld.key --cert /etc/icinga2/pki/domain.tld.crt
CLI Example:
.. code-block:: bash
salt '*' icinga2.generate_cert domain.tld
'''
result = __salt__['cmd.run']("icinga2 pki new-cert --cn {0} --key /etc/icinga2/pki/{0}.key --cert /etc/icinga2/pki/{0}.crt".format(domain))
return result
def save_cert(domain, master):
'''
Save the certificate for master icinga2 node.
Returns::
icinga2 pki save-cert --key /etc/icinga2/pki/domain.tld.key --cert /etc/icinga2/pki/domain.tld.crt --trustedcert /etc/icinga2/pki/trusted-master.crt --host master.domain.tld
CLI Example:
.. code-block:: bash
salt '*' icinga2.save_cert domain.tld master.domain.tld
'''
result = __salt__['cmd.run']("icinga2 pki save-cert --key /etc/icinga2/pki/{0}.key --cert /etc/icinga2/pki/{0}.cert --trustedcert /etc/icinga2/pki/trusted-master.crt \
--host {1}".format(domain, master))
return result
def request_cert(domain, master, ticket, port):
'''
Request CA cert from master icinga2 node.
Returns::
icinga2 pki request --host master.domain.tld --port 5665 --ticket TICKET_ID --key /etc/icinga2/pki/domain.tld.key --cert /etc/icinga2/pki/domain.tld.crt --trustedcert \
/etc/icinga2/pki/trusted-master.crt --ca /etc/icinga2/pki/ca.crt
CLI Example:
.. code-block:: bash
salt '*' icinga2.request_cert domain.tld master.domain.tld TICKET_ID
'''
result = __salt__['cmd.run']("icinga2 pki request --host {0} --port {1} --ticket {2} --key /etc/icinga2/pki/{3}.key --cert \
/etc/icinga2/pki/{3}.crt --trustedcert /etc/icinga2/pki/trusted-master.crt --ca /etc/icinga2/pki/ca.crt".format(master, port, ticket, domain))
return result
def node_setup(domain, master, ticket):
'''
Setup the icinga2 node.
Returns::
icinga2 node setup --ticket TICKET_ID --endpoint master.domain.tld --zone domain.tld --master_host master.domain.tld --trustedcert \
/etc/icinga2/pki/trusted-master.crt
CLI Example:
.. code-block:: bash
salt '*' icinga2.node_setup domain.tld master.domain.tld TICKET_ID
'''
result = __salt__['cmd.run']("icinga2 node setup --ticket {0} --endpoint {1} --zone {2} --master_host {1} --trustedcert /etc/icinga2/pki/trusted-master.crt".format(ticket, master, domain))
return result

276
salt/states/icinga2.py Normal file
View File

@ -0,0 +1,276 @@
# -*- coding: utf-8 -*-
'''
Icinga2 state
==========================
.. versionadded:: TODO
:depends: - Icinga2 Python module
:configuration: See :py:mod:`salt.modules.icinga2` for setup instructions.
The icinga2 module is used to execute commands.
Its output may be stored in a file or in a grain.
.. code-block:: yaml
command_id:
icinga2.generate_ticket
- name: domain.tld
- output: "/tmp/query_id.txt"
'''
# Import python libs
from __future__ import absolute_import
import os.path
# Import Salt libs
import salt.utils
def __virtual__():
'''
Only load if the icinga2 module is available in __salt__
'''
return 'icinga2.generate_ticket' in __salt__
def generate_ticket(name, output=None, grain=None, key=None, overwrite=True):
'''
Generate an icinga2 ticket on the master.
name
The domain name for which this ticket will be generated
output
grain: output in a grain
other: the file to store results
None: output to the result comment (default)
grain:
grain to store the output (need output=grain)
key:
the specified grain will be treated as a dictionary, the result
of this state will be stored under the specified key.
overwrite:
The file or grain will be overwritten if it already exists (default)
'''
ret = {'name': name,
'changes': {},
'result': True,
'comment': ''}
# Checking if execution is needed.
if output == 'grain':
if grain and not key:
if not overwrite and grain in __salt__['grains.ls']():
ret['comment'] = 'No execution needed. Grain {0} already set'.format(grain)
return ret
elif __opts__['test']:
ret['result'] = None
ret['comment'] = 'Ticket generation would be executed, storing result in grain: {0}'.format(grain)
return ret
elif grain:
if grain in __salt__['grains.ls']():
grain_value = __salt__['grains.get'](grain)
else:
grain_value = {}
if not overwrite and key in grain_value:
ret['comment'] = 'No execution needed. Grain {0}:{1} already set'.format(grain, key)
return ret
elif __opts__['test']:
ret['result'] = None
ret['comment'] = 'Ticket generation would be executed, storing result in grain: {0}:{1}'.format(grain, key)
return ret
else:
ret['result'] = False
ret['comment'] = "Error: output type 'grain' needs the grain parameter\n"
return ret
elif output:
if not overwrite and os.path.isfile(output):
ret['comment'] = 'No execution needed. File {0} already set'.format(output)
return ret
elif __opts__['test']:
ret['result'] = None
ret['comment'] = 'Ticket generation would be executed, storing result in file: {0}'.format(output)
return ret
elif __opts__['test']:
ret['result'] = None
ret['comment'] = 'Ticket generation would be executed, not storing result'
return ret
# Executing the command.
ticket = __salt__['icinga2.generate_ticket'](name).strip()
if ticket:
ret['comment'] = str(ticket)
if output == 'grain':
if grain and not key:
__salt__['grains.setval'](grain, ticket)
ret['changes']['ticket'] = "Executed. Output into grain: {0}".format(grain)
elif grain:
if grain in __salt__['grains.ls']():
grain_value = __salt__['grains.get'](grain)
else:
grain_value = {}
grain_value[key] = ticket
__salt__['grains.setval'](grain, grain_value)
ret['changes']['ticket'] = "Executed. Output into grain: {0}:{1}".format(grain, key)
elif output:
ret['changes']['ticket'] = "Executed. Output into {0}".format(output)
with salt.utils.fopen(output, 'w') as output_file:
output_file.write(str(ticket))
else:
ret['changes']['ticket'] = "Executed"
return ret
def generate_cert(name):
'''
Generate an icinga2 certificate and key on the client.
name
The domain name for which this certificate and key will be generated
'''
ret = {'name': name,
'changes': {},
'result': True,
'comment': ''}
cert = "/etc/icinga2/pki/{0}.crt".format(name)
key = "/etc/icinga2/pki/{0}.key".format(name)
# Checking if execution is needed.
if os.path.isfile(cert) and os.path.isfile(key):
ret['comment'] = 'No execution needed. Cert: {0} and key: {1} already generated.'.format(cert, key)
return ret
if __opts__['test']:
ret['result'] = None
ret['comment'] = 'Certificate and key generation would be executed'
return ret
# Executing the command.
cert_save = __salt__['icinga2.generate_cert'](name)
if not cert_save:
ret['comment'] = "Certificate and key generated"
ret['changes']['cert'] = "Executed. Certificate saved: {0}".format(cert)
ret['changes']['key'] = "Executed. Key saved: {0}".format(key)
return ret
def save_cert(name, master):
'''
Save the certificate on master icinga2 node.
name
The domain name for which this certificate will be saved
master
Icinga2 master node for which this certificate will be saved
'''
ret = {'name': name,
'changes': {},
'result': True,
'comment': ''}
cert = "/etc/icinga2/pki/trusted-master.crt"
# Checking if execution is needed.
if os.path.isfile(cert):
ret['comment'] = 'No execution needed. Cert: {0} already saved.'.format(cert)
return ret
if __opts__['test']:
ret['result'] = None
ret['comment'] = 'Certificate save for icinga2 master would be executed'
return ret
# Executing the command.
cert_save = __salt__['icinga2.save_cert'](name, master)
if not cert_save:
ret['comment'] = "Certificate for icinga2 master saved"
ret['changes']['cert'] = "Executed. Certificate saved: {0}".format(cert)
return ret
def request_cert(name, master, ticket, port="5665"):
'''
Request CA certificate from master icinga2 node.
name
The domain name for which this certificate will be saved
master
Icinga2 master node for which this certificate will be saved
ticket
Authentication ticket generated on icinga2 master
port
Icinga2 port, defaults to 5665
'''
ret = {'name': name,
'changes': {},
'result': True,
'comment': ''}
cert = "/etc/icinga2/pki/ca.crt"
# Checking if execution is needed.
if os.path.isfile(cert):
ret['comment'] = 'No execution needed. Cert: {0} already exists.'.format(cert)
return ret
if __opts__['test']:
ret['result'] = None
ret['comment'] = 'Certificate request from icinga2 master would be executed'
return ret
# Executing the command.
cert_request = __salt__['icinga2.request_cert'](name, master, ticket, port)
if not cert_request:
ret['comment'] = "Certificate request from icinga2 master executed"
ret['changes']['cert'] = "Executed. Certificate requested: {0}".format(cert)
return ret
ret['comment'] = "FAILED. Certificate requested failed with exit code: {0}".format(cert_request)
ret['result'] = False
return ret
def node_setup(name, master, ticket):
'''
Setup the icinga2 node.
name
The domain name for which this certificate will be saved
master
Icinga2 master node for which this certificate will be saved
ticket
Authentication ticket generated on icinga2 master
'''
ret = {'name': name,
'changes': {},
'result': True,
'comment': ''}
cert = "/etc/icinga2/pki/{0}.crt.orig".format(name)
key = "/etc/icinga2/pki/{0}.key.orig".format(name)
# Checking if execution is needed.
if os.path.isfile(cert) and os.path.isfile(cert):
ret['comment'] = 'No execution needed. Node already configured.'
return ret
if __opts__['test']:
ret['result'] = None
ret['comment'] = 'Node setup will be executed.'
return ret
# Executing the command.
node_setup = __salt__['icinga2.node_setup'](name, master, ticket)
if not node_setup:
ret['comment'] = "Node setup executed."
ret['changes']['cert'] = "Node setup finished successfully."
return ret
ret['comment'] = "FAILED. Node setup failed with exit code: {0}".format(node_setup)
ret['result'] = False
return ret