mirror of
https://github.com/Icinga/icinga2.git
synced 2025-07-23 13:45:04 +02:00
Merge branch 'feature/unit-tests-5223' into next
This commit is contained in:
commit
eb4afb1eda
@ -16,3 +16,8 @@ file { '/etc/motd':
|
||||
owner => root,
|
||||
group => root
|
||||
}
|
||||
|
||||
user { 'vagrant':
|
||||
groups => 'icingacmd',
|
||||
require => Group['icingacmd']
|
||||
}
|
||||
|
@ -1 +1,43 @@
|
||||
These scripts are used by build.icinga.org to set up a test VM.
|
||||
Set of scripts to set up and test a virtual demo machine
|
||||
========================================================
|
||||
|
||||
This directory contains a few scripts primarily used by build.icinga.org.
|
||||
|
||||
* bootstrap-vm.sh
|
||||
Ensures that all required software is installed and its configuration
|
||||
is applied to the VM. (Usually not of interest for the typical user.)
|
||||
|
||||
* run_tests.sh
|
||||
This is a wrapper script intended to be ran manually by a user. (Note
|
||||
that you need to start this project's vagrant box for this to work!)
|
||||
|
||||
* run_tests.py
|
||||
The actual test-runner. Accepts two options (-C|--config, -O|--output) and
|
||||
expects one or more filenames or -patterns that should be run on the VM.
|
||||
|
||||
* run_tests.conf
|
||||
The default configuration file for the test-runner. (Used when running
|
||||
the wrapper script or when no custom configuration file is passed to the
|
||||
test-runner.)
|
||||
|
||||
Format:
|
||||
- commands: This section is mandatory and contains the commands to use.
|
||||
- settings: This section is mandatory and defines settings that are applied to
|
||||
all tests.
|
||||
- setups: This section is optional and contains setup routines that should
|
||||
be ran before (setup) and after (teardown) any matching test is
|
||||
executed. (Note that only one setup can be effective at a time.)
|
||||
|
||||
Example:
|
||||
"^v[1-9]\.test$": {
|
||||
"setup": {
|
||||
"copy": ["source >> target"], // Files that should be copied.
|
||||
// Note that these files remain
|
||||
// if not removed explicitly
|
||||
"clean": ["target"], // Files to delete from the system
|
||||
"exec": ["cmd1", "cmd2"] // Commands to execute on the system
|
||||
},
|
||||
"teardown": {
|
||||
// The same kind of instructions as above can be added here
|
||||
}
|
||||
}
|
||||
|
3
test/jenkins/apache_state.test
Executable file
3
test/jenkins/apache_state.test
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
sudo service httpd status
|
@ -1,14 +1,29 @@
|
||||
#!/bin/sh
|
||||
if [ "$1" != "run-by-jenkins" ]; then
|
||||
echo "This script should not be run manually."
|
||||
exit 1
|
||||
if [ "$1" != "--force" ]; then
|
||||
echo 'This script is NOT intended to be ran by an individual user.' \
|
||||
'If you are not human, pass "--force" as the first option to it!'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "10.10.27.1 packages.icinga.org" >> /etc/hosts
|
||||
if [ $# -lt 3 ]; then
|
||||
echo 'Too few arguments. You need to pass "--force <user> <host>"' \
|
||||
'to run this script.'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
groupadd vagrant
|
||||
|
||||
rmdir /vagrant && ln -s /root/icinga2 /vagrant
|
||||
puppet apply --modulepath=/vagrant/.vagrant-puppet/modules /vagrant/.vagrant-puppet/manifests/default.pp
|
||||
user=$2
|
||||
host=$3
|
||||
|
||||
SSH_OPTIONS="-o PasswordAuthentication=no"
|
||||
SSH="ssh $SSH_OPTIONS $user@$host"
|
||||
|
||||
$SSH "mkdir /vagrant"
|
||||
scp -qr ../../.vagrant-puppet $user@$host:/vagrant
|
||||
|
||||
$SSH "groupadd vagrant"
|
||||
$SSH "echo '10.10.27.1 packages.icinga.org' >> /etc/hosts"
|
||||
$SSH "puppet apply --modulepath=/vagrant/.vagrant-puppet/modules" \
|
||||
" /vagrant/.vagrant-puppet/manifests/default.pp"
|
||||
|
||||
exit 0
|
||||
|
87
test/jenkins/checkresult.test
Executable file
87
test/jenkins/checkresult.test
Executable file
@ -0,0 +1,87 @@
|
||||
#!/usr/bin/env python
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
|
||||
import utils
|
||||
|
||||
|
||||
STATE_OK = 0
|
||||
TYPE_PASSIVE_CHECK = 1
|
||||
|
||||
CHECK_INTERVAL = 300 # seconds
|
||||
CHECKRESULT_READ_INTERVAL = 5 # seconds
|
||||
CHECKRESULT_LOCATION = '/tmp/icinga2/checkresults'
|
||||
CHECKRESULT_TEMPLATE = """
|
||||
host_name=%(hostname)s
|
||||
service_description=%(servicename)s
|
||||
check_type=%(check_type)s
|
||||
check_options=0
|
||||
scheduled_check=0
|
||||
reschedule_check=0
|
||||
latency=0
|
||||
start_time=%(start_time)s
|
||||
finish_time=%(finish_time)s
|
||||
early_timeout=0
|
||||
exited_ok=%(excited_ok)s
|
||||
return_code=%(return_code)s
|
||||
output=%(output)s
|
||||
"""
|
||||
|
||||
|
||||
def main():
|
||||
run_query = lambda q: utils.run_mysql_query(q, b'/usr/bin/mysql')
|
||||
|
||||
# We need to wait a bit first as Icinga processes a
|
||||
# checkresult only if its newer than the last check
|
||||
query = 'select unix_timestamp(s.last_check) as last_check ' \
|
||||
'from icinga_servicestatus as s ' \
|
||||
'inner join icinga_services as c ' \
|
||||
'on s.service_object_id = c.service_object_id ' \
|
||||
"where c.display_name = 'PassiveService1'"
|
||||
state_time = float(next(iter(run_query(query)), {}).get('last_check', '0'))
|
||||
if state_time == 0:
|
||||
print '"PassiveService1" seems not to have been checked yet'
|
||||
return 1
|
||||
|
||||
if (state_time + CHECK_INTERVAL) - time.time() < 30:
|
||||
time.sleep(45)
|
||||
|
||||
# Now pass the checkresult in
|
||||
with open(os.path.join(CHECKRESULT_LOCATION, 'cfoobar'), 'w') as f:
|
||||
f.write(CHECKRESULT_TEMPLATE % {
|
||||
'hostname': 'nsca-ng',
|
||||
'servicename': 'PassiveService1',
|
||||
'check_type': TYPE_PASSIVE_CHECK,
|
||||
'start_time': time.time(),
|
||||
'finish_time': time.time(),
|
||||
'excited_ok': '1',
|
||||
'return_code': STATE_OK,
|
||||
'output': 'Passing in CheckResult header files works!'
|
||||
})
|
||||
|
||||
# And notfiy Icinga that the file has been completely written...
|
||||
with open(os.path.join(CHECKRESULT_LOCATION, 'cfoobar.ok'), 'w') as f:
|
||||
pass
|
||||
|
||||
# Lastly check whether the service changed its state
|
||||
time.sleep(CHECKRESULT_READ_INTERVAL * 2)
|
||||
|
||||
query = 'select s.output ' \
|
||||
'from icinga_servicestatus as s ' \
|
||||
'inner join icinga_services as c ' \
|
||||
'on s.service_object_id = c.service_object_id ' \
|
||||
"where c.display_name = 'PassiveService1'"
|
||||
output = next(iter(run_query(query)), {}).get('output', '')
|
||||
if output != 'Passing in CheckResult header files works!':
|
||||
print 'Checkresult header files seem not to be processed properly'
|
||||
return 1
|
||||
|
||||
print 'Checkresult header files are processed properly'
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
10
test/jenkins/external_commandpipe.test
Executable file
10
test/jenkins/external_commandpipe.test
Executable file
@ -0,0 +1,10 @@
|
||||
#!/bin/sh
|
||||
|
||||
if [ -e "/var/run/icinga2/cmd/icinga2.cmd" ];
|
||||
then
|
||||
echo "Icinga2 commandpipe found"
|
||||
exit 0
|
||||
else
|
||||
echo "Icinga2 commandpipe not found"
|
||||
exit 1
|
||||
fi
|
5
test/jenkins/files/configs/checkresult.conf
Normal file
5
test/jenkins/files/configs/checkresult.conf
Normal file
@ -0,0 +1,5 @@
|
||||
library "compat"
|
||||
|
||||
object CheckResultReader "reader" {
|
||||
spool_dir = "/tmp/icinga2/checkresults"
|
||||
}
|
190
test/jenkins/files/ido_tests.py
Normal file
190
test/jenkins/files/ido_tests.py
Normal file
@ -0,0 +1,190 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
CHECK_INTERVAL = 10 # minutes; The actual interval are 5 minutes but as other
|
||||
# tests might restart Icinga we need to take any
|
||||
# rescheduling into account
|
||||
|
||||
TABLE_PREFIX = 'icinga_'
|
||||
TABLES = [
|
||||
# Central tables
|
||||
'instances',
|
||||
'objects',
|
||||
# Debugging tables
|
||||
'conninfo',
|
||||
# Historical tables
|
||||
'acknowledgements',
|
||||
'commenthistory',
|
||||
'contactnotifications',
|
||||
'dbversion',
|
||||
'downtimehistory',
|
||||
'eventhandlers',
|
||||
'externalcommands',
|
||||
'flappinghistory',
|
||||
'hostchecks',
|
||||
'logentries',
|
||||
'notifications',
|
||||
'processevents',
|
||||
'servicechecks',
|
||||
'statehistory',
|
||||
'systemcommands',
|
||||
# Current status tables
|
||||
'comments',
|
||||
'customvariablestatus',
|
||||
'hoststatus',
|
||||
'programstatus',
|
||||
'runtimevariables',
|
||||
'scheduleddowntime',
|
||||
'servicestatus',
|
||||
'contactstatus',
|
||||
# Configuration tables
|
||||
'commands',
|
||||
'configfiles',
|
||||
'configfilevariables',
|
||||
'contact_addresses',
|
||||
'contact_notificationcommands',
|
||||
'contactgroup_members',
|
||||
'contactgroups',
|
||||
'contactnotificationmethods',
|
||||
'contacts',
|
||||
'customvariables',
|
||||
'host_contactgroups',
|
||||
'host_contacts',
|
||||
'host_parenthosts',
|
||||
'hostdependencies',
|
||||
'hostescalation_contactgroups',
|
||||
'hostescalation_contacts',
|
||||
'hostescalations',
|
||||
'hostgroup_members',
|
||||
'hostgroups',
|
||||
'hosts',
|
||||
'service_contactgroups',
|
||||
'service_contacts',
|
||||
'servicedependencies',
|
||||
'serviceescalation_contactgroups',
|
||||
'serviceescalation_contacts',
|
||||
'serviceescalations',
|
||||
'servicegroup_members',
|
||||
'servicegroups',
|
||||
'services',
|
||||
'timeperiod_timeranges',
|
||||
'timeperiods'
|
||||
]
|
||||
EXAMPLE_CONFIG = {
|
||||
'localhost': ['disk', 'http', 'icinga', 'load', 'ping4',
|
||||
'ping6', 'processes', 'ssh', 'users'],
|
||||
'nsca-ng': ['PassiveService1', 'PassiveService2']
|
||||
}
|
||||
|
||||
|
||||
def validate_tables(tables):
|
||||
"""
|
||||
Return whether all tables of the IDO database scheme exist in
|
||||
the given table listing
|
||||
|
||||
"""
|
||||
missing = [n for n in TABLES if TABLE_PREFIX + n not in tables]
|
||||
if missing:
|
||||
print 'Some tables are missing in the IDO'
|
||||
print 'Missing tables: ' + ', '.join(missing)
|
||||
return False
|
||||
|
||||
print 'All tables were found in the IDO'
|
||||
return True
|
||||
|
||||
|
||||
def verify_host_config(config_data):
|
||||
"""
|
||||
Return whether the example hosts exist in the given "hosts" table
|
||||
|
||||
"""
|
||||
if len([1 for e in config_data if e['alias'] in EXAMPLE_CONFIG]) == 2:
|
||||
print 'All example hosts are stored in the IDO'
|
||||
return True
|
||||
|
||||
print 'Some example hosts are missing in the IDO'
|
||||
return False
|
||||
|
||||
|
||||
def verify_service_config(config_data):
|
||||
"""
|
||||
Return whether the example services exist in the given "services" table
|
||||
|
||||
"""
|
||||
for hostname, servicename in ((h, s) for h, ss in EXAMPLE_CONFIG.iteritems()
|
||||
for s in ss):
|
||||
# Not very efficient, but suitable for just two hosts...
|
||||
if not any(1 for c in config_data
|
||||
if c['alias'] == hostname and
|
||||
c['display_name'] == servicename):
|
||||
print 'The config stored in the IDO is missing some services'
|
||||
return False
|
||||
|
||||
print 'The service config stored in the IDO is correct'
|
||||
return True
|
||||
|
||||
|
||||
def check_last_host_status_update(check_info):
|
||||
"""
|
||||
Return whether the example hosts are checked as scheduled
|
||||
|
||||
"""
|
||||
for info in check_info:
|
||||
if info['alias'] == 'localhost':
|
||||
last_check = datetime.fromtimestamp(float(info['last_check']))
|
||||
if datetime.now() - last_check > timedelta(minutes=CHECK_INTERVAL,
|
||||
seconds=10):
|
||||
print 'The last status update of host "localhost"' \
|
||||
' was more than {0} minutes ago'.format(CHECK_INTERVAL)
|
||||
return False
|
||||
elif info['alias'] == 'nsca-ng':
|
||||
if float(info['last_check']) > 0:
|
||||
print 'The host "nsca-ng" was checked even though' \
|
||||
' it should not be actively checked'
|
||||
return False
|
||||
|
||||
print 'The updates of both example hosts are processed as configured'
|
||||
return True
|
||||
|
||||
|
||||
def check_last_service_status_update(check_info):
|
||||
"""
|
||||
Return whether the example services are checked as scheduled
|
||||
|
||||
"""
|
||||
for info in check_info:
|
||||
if info['display_name'] in EXAMPLE_CONFIG.get(info['alias'], []):
|
||||
last_check = datetime.fromtimestamp(float(info['last_check']))
|
||||
if datetime.now() - last_check > timedelta(minutes=CHECK_INTERVAL,
|
||||
seconds=10):
|
||||
print 'The last status update of service "{0}" of' \
|
||||
' host "{1}" was more than {2} minutes ago' \
|
||||
''.format(info['display_name'], info['alias'],
|
||||
CHECK_INTERVAL)
|
||||
return False
|
||||
|
||||
print 'The updates of all example services are processed as configured'
|
||||
return True
|
||||
|
||||
|
||||
def check_logentries(logentry_info):
|
||||
"""
|
||||
Return whether the given logentry originates from host "localhost"
|
||||
and refers to its very last hard status change
|
||||
|
||||
"""
|
||||
if logentry_info and logentry_info[0]['alias'] == 'localhost':
|
||||
entry_time = datetime.fromtimestamp(float(logentry_info[0]['entry_time']))
|
||||
state_time = datetime.fromtimestamp(float(logentry_info[0]['state_time']))
|
||||
if entry_time - state_time > timedelta(seconds=10):
|
||||
print 'The last hard state of host "localhost"' \
|
||||
' seems not to have been logged'
|
||||
return False
|
||||
else:
|
||||
print 'No logs found in the IDO for host "localhost"'
|
||||
return False
|
||||
|
||||
print 'The last hard state of host "localhost" was properly logged'
|
||||
return True
|
||||
|
145
test/jenkins/files/utils.py
Normal file
145
test/jenkins/files/utils.py
Normal file
@ -0,0 +1,145 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import os
|
||||
import json
|
||||
import socket
|
||||
import subprocess
|
||||
|
||||
__all__ = ['parse_statusdata', 'run_mysql_query', 'run_pgsql_query',
|
||||
'LiveStatusSocket']
|
||||
|
||||
|
||||
MYSQL_PARAMS = b"-t -D icinga -u icinga --password=icinga -e".split()
|
||||
MYSQL_SEPARATOR = '|'
|
||||
|
||||
PGSQL_PARAMS = b"-nq -U icinga -d icinga -c".split()
|
||||
PGSQL_SEPARATOR = '|'
|
||||
PGSQL_ENVIRONMENT = {
|
||||
b'PGPASSWORD': b'icinga'
|
||||
}
|
||||
|
||||
|
||||
def parse_statusdata(data, intelligent_cast=True):
|
||||
parsed_data, data_type, type_data = {}, '', {}
|
||||
for line in (l for l in data.split(os.linesep)
|
||||
if l and not l.startswith('#')):
|
||||
if '{' in line:
|
||||
data_type = line.partition('{')[0].strip()
|
||||
elif '}' in line:
|
||||
parsed_data.setdefault(data_type, []).append(type_data)
|
||||
else:
|
||||
key, _, value = line.partition('=')
|
||||
|
||||
if intelligent_cast:
|
||||
value = _cast_status_value(value)
|
||||
|
||||
type_data[key.strip()] = value
|
||||
|
||||
return parsed_data
|
||||
|
||||
|
||||
def _cast_status_value(value):
|
||||
try:
|
||||
return int(value)
|
||||
except ValueError:
|
||||
try:
|
||||
return float(value)
|
||||
except ValueError:
|
||||
return value
|
||||
|
||||
|
||||
def run_mysql_query(query, path):
|
||||
p = subprocess.Popen([path] + MYSQL_PARAMS + [query.encode('utf-8')],
|
||||
stdout=subprocess.PIPE)
|
||||
return _parse_mysql_result([l.decode('utf-8') for l in p.stdout.readlines()])
|
||||
|
||||
|
||||
def _parse_mysql_result(resultset):
|
||||
result, header = [], None
|
||||
for line in (l for l in resultset if MYSQL_SEPARATOR in l):
|
||||
columns = [c.strip() for c in line[1:-3].split(MYSQL_SEPARATOR)]
|
||||
if header is None:
|
||||
header = columns
|
||||
else:
|
||||
result.append(dict((header[i], v) for i, v in enumerate(columns)))
|
||||
return result
|
||||
|
||||
|
||||
def run_pgsql_query(query, path):
|
||||
p = subprocess.Popen([path] + PGSQL_PARAMS + [query.encode('utf-8')],
|
||||
stdout=subprocess.PIPE, env=PGSQL_ENVIRONMENT)
|
||||
return _parse_pgsql_result([l.decode('utf-8') for l in p.stdout.readlines()])
|
||||
|
||||
|
||||
def _parse_pgsql_result(resultset):
|
||||
result, header = [], None
|
||||
for line in (l for l in resultset if PGSQL_SEPARATOR in l):
|
||||
columns = [c.strip() for c in line.split(PGSQL_SEPARATOR)]
|
||||
if header is None:
|
||||
header = columns
|
||||
else:
|
||||
result.append(dict((header[i], v) for i, v in enumerate(columns)))
|
||||
return result
|
||||
|
||||
|
||||
class LiveStatusError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class LiveStatusSocket(object):
|
||||
options = [
|
||||
'KeepAlive: on',
|
||||
'OutputFormat: json',
|
||||
'ResponseHeader: fixed16'
|
||||
]
|
||||
|
||||
def __init__(self, path):
|
||||
self.path = path
|
||||
|
||||
def __enter__(self):
|
||||
self.connect()
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_value, tb):
|
||||
self.close()
|
||||
|
||||
def connect(self):
|
||||
self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
self.sock.connect(self.path)
|
||||
|
||||
def close(self):
|
||||
self.sock.shutdown(socket.SHUT_RDWR)
|
||||
self.sock.close()
|
||||
|
||||
def query(self, command):
|
||||
self.send(command)
|
||||
statuscode, response = self.recv()
|
||||
|
||||
if statuscode != 200:
|
||||
raise LiveStatusError(statuscode, response)
|
||||
|
||||
return response
|
||||
|
||||
def send(self, query):
|
||||
full_query = '\n'.join([query] + self.options)
|
||||
self.sock.sendall((full_query + '\n\n').encode('utf-8'))
|
||||
|
||||
def recv(self):
|
||||
response = b''
|
||||
response_header = self.sock.recv(16)
|
||||
response_code = int(response_header[:3])
|
||||
response_length = int(response_header[3:].strip())
|
||||
|
||||
if response_length > 0:
|
||||
while len(response) < response_length:
|
||||
response += self.sock.recv(response_length - len(response))
|
||||
|
||||
response = response.decode('utf-8')
|
||||
|
||||
try:
|
||||
response = json.loads(response)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
return response_code, response
|
||||
|
38
test/jenkins/files/wait_for_ido.sh
Executable file
38
test/jenkins/files/wait_for_ido.sh
Executable file
@ -0,0 +1,38 @@
|
||||
#!/bin/sh
|
||||
|
||||
TIMEOUT=30
|
||||
|
||||
case $1 in
|
||||
mysql)
|
||||
TYPE='MySQL'
|
||||
CMD='/usr/bin/mysql -t -D icinga -u icinga --password=icinga -e'
|
||||
;;
|
||||
pgsql)
|
||||
TYPE='PostgreSQL'
|
||||
CMD='/usr/bin/psql -nq -U icinga -d icinga -c'
|
||||
export PGPASSWORD='icinga'
|
||||
;;
|
||||
*)
|
||||
echo "No IDO type specifier given!"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
tries=1
|
||||
while true
|
||||
do
|
||||
out="`$CMD 'select * from icinga_hosts'`"
|
||||
|
||||
if [ $tries -lt $TIMEOUT ] && [ "$out" == "" ];
|
||||
then
|
||||
sleep 1
|
||||
tries=$(($tries + 1))
|
||||
else
|
||||
if [ $tries -eq $TIMEOUT ];
|
||||
then
|
||||
echo "IDO ($TYPE) does not have any hosts or is not responding" >&2
|
||||
fi
|
||||
|
||||
break
|
||||
fi
|
||||
done
|
3
test/jenkins/icinga2_state.test
Executable file
3
test/jenkins/icinga2_state.test
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
sudo service icinga2 status
|
67
test/jenkins/ido_mysql.test
Executable file
67
test/jenkins/ido_mysql.test
Executable file
@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env python
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import sys
|
||||
|
||||
import utils
|
||||
import ido_tests
|
||||
|
||||
|
||||
def main():
|
||||
run_query = lambda q: utils.run_mysql_query(q, b'/usr/bin/mysql')
|
||||
|
||||
if not ido_tests.validate_tables([d['Tables_in_icinga']
|
||||
for d in run_query('show tables')]):
|
||||
return 1
|
||||
|
||||
host_info = run_query('select * from icinga_hosts')
|
||||
if not ido_tests.verify_host_config(host_info):
|
||||
return 1
|
||||
|
||||
service_info = run_query(
|
||||
'select c2.alias, c1.* from icinga_services as c1 '
|
||||
'inner join icinga_hosts as c2'
|
||||
' on c1.host_object_id = c2.host_object_id'
|
||||
)
|
||||
if not ido_tests.verify_service_config(service_info):
|
||||
return 1
|
||||
|
||||
hostchecks_data = run_query(
|
||||
'select c.alias, unix_timestamp(s.last_check) as last_check'
|
||||
' from icinga_hoststatus as s '
|
||||
'inner join icinga_hosts as c'
|
||||
' on s.host_object_id = c.host_object_id'
|
||||
)
|
||||
if not ido_tests.check_last_host_status_update(hostchecks_data):
|
||||
return 1
|
||||
|
||||
servicechecks_data = run_query(
|
||||
'select c2.alias, c1.display_name, unix_timestamp(s.last_check) as last_check'
|
||||
' from icinga_servicestatus as s '
|
||||
'inner join icinga_services as c1'
|
||||
' on s.service_object_id = c1.service_object_id '
|
||||
'inner join icinga_hosts as c2'
|
||||
' on c1.host_object_id = c2.host_object_id'
|
||||
)
|
||||
if not ido_tests.check_last_service_status_update(servicechecks_data):
|
||||
return 1
|
||||
|
||||
logentry_info = run_query(
|
||||
'select hosts.alias,'
|
||||
' max(unix_timestamp(logs.entry_time)) as entry_time,'
|
||||
' max(unix_timestamp(hist.state_time)) as state_time'
|
||||
' from icinga_logentries as logs '
|
||||
'inner join icinga_hosts as hosts'
|
||||
' on logs.object_id = hosts.host_object_id and hosts.alias = "localhost" '
|
||||
'inner join icinga_statehistory as hist'
|
||||
' on hist.object_id = hosts.host_object_id and hist.state_type = 1'
|
||||
)
|
||||
if not ido_tests.check_logentries(logentry_info):
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
|
69
test/jenkins/ido_pgsql.test
Executable file
69
test/jenkins/ido_pgsql.test
Executable file
@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env python
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import sys
|
||||
|
||||
import utils
|
||||
import ido_tests
|
||||
|
||||
|
||||
def main():
|
||||
run_query = lambda q: utils.run_pgsql_query(q, b'/usr/bin/psql')
|
||||
|
||||
if not ido_tests.validate_tables([d['Name'] for d in run_query('\\dt')
|
||||
if d['Type'] == 'table']):
|
||||
return 1
|
||||
|
||||
host_info = run_query('select * from icinga_hosts')
|
||||
if not ido_tests.verify_host_config(host_info):
|
||||
return 1
|
||||
|
||||
service_info = run_query(
|
||||
'select c2.alias, c1.* from icinga_services as c1 '
|
||||
'inner join icinga_hosts as c2'
|
||||
' on c1.host_object_id = c2.host_object_id'
|
||||
)
|
||||
if not ido_tests.verify_service_config(service_info):
|
||||
return 1
|
||||
|
||||
hostchecks_data = run_query(
|
||||
'select c.alias, unix_timestamp(s.last_check) as last_check'
|
||||
' from icinga_hoststatus as s '
|
||||
'inner join icinga_hosts as c'
|
||||
' on s.host_object_id = c.host_object_id'
|
||||
)
|
||||
if not ido_tests.check_last_host_status_update(hostchecks_data):
|
||||
return 1
|
||||
|
||||
servicechecks_data = run_query(
|
||||
'select c2.alias, c1.display_name, unix_timestamp(s.last_check) as last_check'
|
||||
' from icinga_servicestatus as s '
|
||||
'inner join icinga_services as c1'
|
||||
' on s.service_object_id = c1.service_object_id '
|
||||
'inner join icinga_hosts as c2'
|
||||
' on c1.host_object_id = c2.host_object_id'
|
||||
)
|
||||
if not ido_tests.check_last_service_status_update(servicechecks_data):
|
||||
return 1
|
||||
|
||||
logentry_info = run_query(
|
||||
'select hosts.alias,'
|
||||
' max(unix_timestamp(logs.entry_time)) as entry_time,'
|
||||
' max(unix_timestamp(hist.state_time)) as state_time'
|
||||
' from icinga_logentries as logs '
|
||||
'inner join icinga_hosts as hosts'
|
||||
' on logs.object_id = hosts.host_object_id '
|
||||
'inner join icinga_statehistory as hist'
|
||||
' on hist.object_id = hosts.host_object_id '
|
||||
"where hosts.alias = 'localhost' and hist.state_type = 1 "
|
||||
'group by hosts.alias'
|
||||
)
|
||||
if not ido_tests.check_logentries(logentry_info):
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
|
17
test/jenkins/livestatus_socket.test
Executable file
17
test/jenkins/livestatus_socket.test
Executable file
@ -0,0 +1,17 @@
|
||||
#!/bin/sh
|
||||
|
||||
if [ ! -e /var/run/icinga2/cmd/livestatus ];
|
||||
then
|
||||
sudo icinga2-enable-feature livestatus 1> /dev/null
|
||||
sudo service icinga2 restart 1> /dev/null
|
||||
sleep 1
|
||||
|
||||
if [ ! -e /var/run/icinga2/cmd/livestatus ];
|
||||
then
|
||||
echo "Icinga2 Livestatus socket not found"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Icinga2 Livestatus socket found"
|
||||
exit 0
|
10
test/jenkins/logfile.test
Executable file
10
test/jenkins/logfile.test
Executable file
@ -0,0 +1,10 @@
|
||||
#!/bin/sh
|
||||
|
||||
if sudo test -f /var/log/icinga2/icinga2.log;
|
||||
then
|
||||
echo "Icinga2 log file found"
|
||||
exit 0
|
||||
else
|
||||
echo "Icinga2 log file not found"
|
||||
exit 1
|
||||
fi
|
3
test/jenkins/mysql_state.test
Executable file
3
test/jenkins/mysql_state.test
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
sudo service mysqld status
|
3
test/jenkins/pgsql_state.test
Executable file
3
test/jenkins/pgsql_state.test
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
sudo service postgresql status
|
10
test/jenkins/pidfile.test
Executable file
10
test/jenkins/pidfile.test
Executable file
@ -0,0 +1,10 @@
|
||||
#!/bin/sh
|
||||
|
||||
if [ -f /var/run/icinga2/icinga2.pid ];
|
||||
then
|
||||
echo "Icinga2 pidfile found"
|
||||
exit 0
|
||||
else
|
||||
echo "Icinga2 pidfile not found"
|
||||
exit 1
|
||||
fi
|
@ -1,34 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
import sys
|
||||
from xml.dom.minidom import getDOMImplementation
|
||||
from subprocess import Popen, PIPE
|
||||
|
||||
impl = getDOMImplementation()
|
||||
result = impl.createDocument(None, "testsuite", None)
|
||||
testsuite = result.documentElement
|
||||
|
||||
for fn in sys.argv[1:]:
|
||||
process = Popen(["./" + fn], stdout=PIPE, stderr=PIPE)
|
||||
(stdoutdata, stderrdata) = process.communicate()
|
||||
|
||||
testcase = result.createElement("testcase")
|
||||
testcase.setAttribute("classname", "vm")
|
||||
testcase.setAttribute("name", fn)
|
||||
|
||||
systemout = result.createElement("system-out")
|
||||
systemout.appendChild(result.createTextNode(stdoutdata))
|
||||
testcase.appendChild(systemout)
|
||||
|
||||
systemerr = result.createElement("system-err")
|
||||
systemerr.appendChild(result.createTextNode(stderrdata))
|
||||
testcase.appendChild(systemerr)
|
||||
|
||||
if process.returncode != 0:
|
||||
failure = result.createElement("failure")
|
||||
failure.setAttribute("type", "returncode")
|
||||
failure.appendChild(result.createTextNode("code: " + str(process.returncode)))
|
||||
testcase.appendChild(failure)
|
||||
|
||||
testsuite.appendChild(testcase)
|
||||
|
||||
print result.toxml()
|
60
test/jenkins/run_tests.conf
Normal file
60
test/jenkins/run_tests.conf
Normal file
@ -0,0 +1,60 @@
|
||||
{
|
||||
"commands": {
|
||||
"copy": "scp -qF ssh_config {0} default:{1}",
|
||||
"exec": "ssh -F ssh_config default '{0}'",
|
||||
"clean": "ssh -F ssh_config default 'rm -f {0}'"
|
||||
},
|
||||
"settings": {
|
||||
"test_root": "/tmp"
|
||||
},
|
||||
"setups": {
|
||||
"^ido_[a-z]{2}sql.test$": {
|
||||
"setup": {
|
||||
"copy": [
|
||||
"files/ido_tests.py >> /tmp/ido_tests.py",
|
||||
"files/utils.py >> /tmp/utils.py"
|
||||
]
|
||||
},
|
||||
"teardown": {
|
||||
"clean": [
|
||||
"/tmp/ido_tests.py*",
|
||||
"/tmp/utils.py*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"checkresult.test": {
|
||||
"setup": {
|
||||
"copy": [
|
||||
"files/configs/checkresult.conf >> /tmp/checkresult.conf",
|
||||
"files/wait_for_ido.sh >> /tmp/wait_for_ido.sh",
|
||||
"files/utils.py >> /tmp/utils.py"
|
||||
],
|
||||
"exec": [
|
||||
"sudo mv /tmp/checkresult.conf /etc/icinga2/conf.d/",
|
||||
"mkdir -p -m 0777 /tmp/icinga2/checkresults",
|
||||
"sudo service icinga2 restart",
|
||||
"/tmp/wait_for_ido.sh mysql"
|
||||
]
|
||||
},
|
||||
"teardown": {
|
||||
"clean": ["/tmp/utils.py*"],
|
||||
"exec": [
|
||||
"sudo rm /etc/icinga2/conf.d/checkresult.conf",
|
||||
"sudo service icinga2 restart",
|
||||
"rmdir /tmp/icinga2/checkresults",
|
||||
"/tmp/wait_for_ido.sh mysql",
|
||||
"/tmp/wait_for_ido.sh pgsql && rm /tmp/wait_for_ido.sh"
|
||||
]
|
||||
}
|
||||
},
|
||||
"external_commands.test": {
|
||||
"setup": {
|
||||
"copy": ["files/utils.py >> /tmp/utils.py"]
|
||||
},
|
||||
"teardown": {
|
||||
"clean": ["/tmp/utils.py*"],
|
||||
"exec": ["sudo service icinga2 restart"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
147
test/jenkins/run_tests.py
Executable file
147
test/jenkins/run_tests.py
Executable file
@ -0,0 +1,147 @@
|
||||
#!/usr/bin/env python
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import json
|
||||
import glob
|
||||
import subprocess
|
||||
from optparse import OptionParser
|
||||
from xml.dom.minidom import getDOMImplementation
|
||||
|
||||
|
||||
try:
|
||||
from subprocess import DEVNULL
|
||||
except ImportError:
|
||||
DEVNULL = open(os.devnull, 'w')
|
||||
|
||||
|
||||
class TestSuite(object):
|
||||
def __init__(self, configpath):
|
||||
self._tests = []
|
||||
self._results = {}
|
||||
|
||||
self.load_config(configpath)
|
||||
|
||||
def add_test(self, filepath):
|
||||
self._tests.append(filepath)
|
||||
|
||||
def load_config(self, filepath):
|
||||
with open(filepath) as f:
|
||||
self._config = json.load(f)
|
||||
|
||||
def get_report(self):
|
||||
dom = getDOMImplementation()
|
||||
document = dom.createDocument(None, 'testsuite', None)
|
||||
xml_root = document.documentElement
|
||||
|
||||
for name, info in self._results.iteritems():
|
||||
testresult = document.createElement('testcase')
|
||||
testresult.setAttribute('classname', 'vm')
|
||||
testresult.setAttribute('name', name)
|
||||
|
||||
systemout = document.createElement('system-out')
|
||||
systemout.appendChild(document.createTextNode(info['stdout']))
|
||||
testresult.appendChild(systemout)
|
||||
|
||||
systemerr = document.createElement('system-err')
|
||||
systemerr.appendChild(document.createTextNode(info['stderr']))
|
||||
testresult.appendChild(systemerr)
|
||||
|
||||
if info['returncode'] != 0:
|
||||
failure = document.createElement('failure')
|
||||
failure.setAttribute('type', 'returncode')
|
||||
failure.appendChild(document.createTextNode(
|
||||
'code: {0}'.format(info['returncode'])))
|
||||
testresult.appendChild(failure)
|
||||
|
||||
xml_root.appendChild(testresult)
|
||||
|
||||
return document.toxml()
|
||||
|
||||
def run(self):
|
||||
for path in self._tests:
|
||||
test_name = os.path.basename(path)
|
||||
self._apply_setup_routines(test_name, 'setup')
|
||||
self._copy_test(path)
|
||||
self._results[test_name] = self._run_test(path)
|
||||
self._apply_setup_routines(test_name, 'teardown')
|
||||
|
||||
def _apply_setup_routines(self, test_name, context):
|
||||
instructions = next((t[1].get(context)
|
||||
for t in self._config.get('setups', {}).iteritems()
|
||||
if re.match(t[0], test_name)), None)
|
||||
if instructions is not None:
|
||||
for instruction in instructions.get('copy', []):
|
||||
source, _, destination = instruction.partition('>>')
|
||||
self._copy_file(source.strip(), destination.strip())
|
||||
for filepath in instructions.get('clean', []):
|
||||
self._remove_file(filepath)
|
||||
for command in instructions.get('exec', []):
|
||||
self._exec_command(command)
|
||||
|
||||
def _remove_file(self, path):
|
||||
command = self._config['commands']['clean'].format(path)
|
||||
subprocess.call(command, stdout=DEVNULL, shell=True)
|
||||
|
||||
def _exec_command(self, command):
|
||||
command = self._config['commands']['exec'].format(command)
|
||||
subprocess.call(command, stdout=DEVNULL, shell=True)
|
||||
|
||||
def _copy_file(self, source, destination):
|
||||
command = self._config['commands']['copy'].format(source, destination)
|
||||
subprocess.call(command, stdout=DEVNULL, shell=True)
|
||||
|
||||
def _copy_test(self, path):
|
||||
self._copy_file(path, os.path.join(self._config['settings']['test_root'],
|
||||
os.path.basename(path)))
|
||||
|
||||
def _run_test(self, path):
|
||||
command = self._config['commands']['exec']
|
||||
target = os.path.join(self._config['settings']['test_root'],
|
||||
os.path.basename(path))
|
||||
p = subprocess.Popen(command.format(target), stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE, shell=True)
|
||||
out, err = p.communicate()
|
||||
|
||||
return {
|
||||
'stdout': out.decode('utf-8'),
|
||||
'stderr': err.decode('utf-8'),
|
||||
'returncode': p.returncode
|
||||
}
|
||||
|
||||
|
||||
def parse_commandline():
|
||||
parser = OptionParser(version='0.1')
|
||||
parser.add_option('-C', '--config', default="run_tests.conf",
|
||||
help='The path to the config file to use [%default]')
|
||||
parser.add_option('-O', '--output',
|
||||
help='The file which to save the test results. '
|
||||
'(By default this goes to stdout)')
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main():
|
||||
options, arguments = parse_commandline()
|
||||
suite = TestSuite(options.config)
|
||||
|
||||
for path in (p for a in arguments for p in glob.glob(a)):
|
||||
suite.add_test(path)
|
||||
|
||||
suite.run()
|
||||
|
||||
report = suite.get_report()
|
||||
if options.output is None:
|
||||
print report.encode('utf-8')
|
||||
else:
|
||||
with open(options.output, 'w') as f:
|
||||
f.write(report.encode('utf-8'))
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
|
5
test/jenkins/run_tests.sh
Executable file
5
test/jenkins/run_tests.sh
Executable file
@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
vagrant ssh-config > ssh_config
|
||||
./run_tests.py *.test
|
||||
rm -f ssh_config
|
51
test/jenkins/statusdata.test
Executable file
51
test/jenkins/statusdata.test
Executable file
@ -0,0 +1,51 @@
|
||||
#!/bin/sh
|
||||
|
||||
if [ ! -f /var/cache/icinga2/status.dat ];
|
||||
then
|
||||
sudo icinga2-enable-feature statusdata 1> /dev/null
|
||||
sudo service icinga2 restart 1> /dev/null
|
||||
|
||||
n=0
|
||||
while [ $n -lt 3 ]
|
||||
do
|
||||
sleep 15
|
||||
|
||||
if [ -f /var/cache/icinga2/status.dat ];
|
||||
then
|
||||
break
|
||||
fi
|
||||
|
||||
n=$(( $n + 1))
|
||||
done
|
||||
|
||||
if [ $n -eq 3 ];
|
||||
then
|
||||
echo "Icinga2 status.dat not found"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Icinga2 status.dat found"
|
||||
|
||||
if [ -f /var/cache/icinga2/objects.cache ];
|
||||
then
|
||||
echo "Icinga2 objects.cache found"
|
||||
else
|
||||
echo "Icinga2 objects.cache not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
status_time=$(stat --format="%Y" /var/cache/icinga2/status.dat)
|
||||
|
||||
now=$(date +"%s")
|
||||
sleep $(((15 + 5) - ($now - $status_time)))
|
||||
|
||||
new_status_time=$(stat --format="%Y" /var/cache/icinga2/status.dat)
|
||||
|
||||
if [ $new_status_time -eq $status_time ];
|
||||
then
|
||||
echo "Icinga2 status.dat is not being updated"
|
||||
exit 1
|
||||
else
|
||||
echo "Icinga2 status.dat is being updated"
|
||||
fi
|
@ -1,3 +0,0 @@
|
||||
#!/bin/sh
|
||||
echo "Hello World!"
|
||||
exit 1
|
Loading…
x
Reference in New Issue
Block a user