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

#261 - compute overall state #262

Merged
merged 5 commits into from Feb 1, 2017
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
120 changes: 109 additions & 11 deletions alignak_backend/app.py
Expand Up @@ -1080,6 +1080,45 @@ def pre_host_patch(updates, original):
del updates['_updated']


def after_insert_host(items):
"""
Hook after host inserted.

:param items: host fields
:type items: dict
:return: None
"""
etags = {}
for dummy, item in enumerate(items):
overall_state = 0

acknowledged = item['ls_acknowledged']
downtimed = item['ls_downtimed']
state = item['ls_state']
state = state.upper()

if acknowledged:
overall_state = 1
elif downtimed:
overall_state = 2
else:
if state == 'UNREACHABLE':
overall_state = 3
elif state == 'DOWN':
overall_state = 4

# Do not care about services... when inserting an host,
# services are not yet existing for this host!

# Host overall was computed, update the host overall state
lookup = {"_id": item['_id']}
(_, _, etag, _) = patch_internal('host', {"_overall_state_id": overall_state}, False, False,
**lookup)
etags[item['_etag']] = etag
if len(etags) > 0:
g.replace_etags = etags


def pre_service_patch(updates, original):
"""
Hook before updating a service element.
Expand Down Expand Up @@ -1110,9 +1149,9 @@ def pre_service_patch(updates, original):
:type original: dict
:return: None
"""

if '_overall_state_id' in updates:
abort(make_response("Updating _overall_state_id for a service is forbidden", 412))
# Allow update because it must be done when inserting a service
# if '_overall_state_id' in updates:
# abort(make_response("Updating _overall_state_id for a service is forbidden", 412))

for key in updates:
if key not in ['_overall_state_id', '_updated', '_realm'] and not key.startswith('ls_'):
Expand All @@ -1121,11 +1160,7 @@ def pre_service_patch(updates, original):
# pylint: disable=too-many-boolean-expressions
if 'ls_state_type' in updates and updates['ls_state_type'] == 'HARD':
# We updated the service live state, compute the new overall state
if ('ls_state' in updates and updates['ls_state'] != original['ls_state']) or \
('ls_acknowledged' in updates and
updates['ls_acknowledged'] != original['ls_acknowledged']) or \
('ls_downtimed' in updates and
updates['ls_downtimed'] != original['ls_downtimed']):
if 'ls_state' in updates or 'ls_acknowledged' in updates or 'ls_downtimed' in updates:
overall_state = 0

acknowledged = original['ls_acknowledged']
Expand Down Expand Up @@ -1161,6 +1196,66 @@ def pre_service_patch(updates, original):
del updates['_updated']


def after_insert_service(items):
"""
Hook after service inserted.

:param items: host fields
:type items: dict
:return: None
"""
etags = {}
for dummy, item in enumerate(items):
overall_state = 0

acknowledged = item['ls_acknowledged']
downtimed = item['ls_downtimed']
state = item['ls_state']
state = state.upper()

if acknowledged:
overall_state = 1
elif downtimed:
overall_state = 2
else:
if state == 'WARNING':
overall_state = 3
elif state == 'CRITICAL':
overall_state = 4
elif state == 'UNKNOWN':
overall_state = 3
elif state == 'UNREACHABLE':
overall_state = 4

# Service overall was computed, update the service overall state
lookup = {"_id": item['_id']}
(_, _, etag, _) = patch_internal('service', {"_overall_state_id": overall_state}, False,
False, **lookup)
etags[item['_etag']] = etag
if len(etags) > 0:
g.replace_etags = etags


def update_etag(myrequest, payload):
"""In case POST item database hook use patch_internal, we update the new _etag in the
response

:param myrequest:
:param payload:
:return: None
"""
# pylint: disable=unused-argument
if not g.get('replace_etags', {}):
return
else:
for idx, resp in enumerate(payload.response):
resp = resp.decode('UTF-8')
for old_etag, new_etag in iteritems(g.replace_etags):
resp = resp.replace(old_etag, new_etag)
payload.response[idx] = resp
del g.replace_etags


def after_updated_service(updated, original):
"""
Hook called after a service got updated
Expand All @@ -1171,9 +1266,8 @@ def after_updated_service(updated, original):
:type original: dict
:return: None
"""
if '_overall_state_id' in updated and \
updated['_overall_state_id'] != original['_overall_state_id']:
# Service overall state changed, we should update its host overall state
if '_overall_state_id' in updated:
# Service overall was updated, we should update its host overall state
lookup = {"_id": original['host']}
patch_internal('host', {"_overall_state_id": -1}, False, False, **lookup)

Expand Down Expand Up @@ -1416,6 +1510,10 @@ def get_settings(prev_settings):
app.on_pre_GET += pre_get
app.on_insert_user += pre_user_post
app.on_update_user += pre_user_patch
app.on_inserted_host += after_insert_host
app.on_post_POST_host += update_etag
app.on_inserted_service += after_insert_service
app.on_post_POST_service += update_etag
app.on_update_host += pre_host_patch
app.on_update_service += pre_service_patch
app.on_updated_service += after_updated_service
Expand Down
13 changes: 8 additions & 5 deletions test/test_hook_template.py
Expand Up @@ -135,8 +135,8 @@ def test_host_templates(self):

schema = host_schema()
template_fields = {}
ignore_fields = ['name', 'realm', '_realm', '_template_fields',
'_templates', '_is_template',
ignore_fields = ['name', 'realm', '_realm', '_overall_state_id',
'_template_fields', '_templates', '_is_template',
'_templates_with_services']
for key in schema['schema']:
if key not in ignore_fields:
Expand Down Expand Up @@ -743,12 +743,15 @@ def test_host_services_template(self):
self.assertTrue(rs[12]['_is_template'])

# Now delete a template service
response = requests.get(self.endpoint + '/service/' + ret_new['_id'],
params=sort_id, auth=self.auth)
response = response.json()
headers_delete = {
'Content-Type': 'application/json',
'If-Match': ret_new['_etag']
'If-Match': response['_etag']
}
requests.delete(self.endpoint + '/service/' + ret_new['_id'], headers=headers_delete,
auth=self.auth)
requests.delete(self.endpoint + '/service/' + response['_id'],
headers=headers_delete, auth=self.auth)
response = requests.get(self.endpoint + '/service', params=sort_id, auth=self.auth)
resp = response.json()
service_name = []
Expand Down
48 changes: 40 additions & 8 deletions test/test_livesynthesis_history.py
Expand Up @@ -112,6 +112,7 @@ def setUpClass(cls):
datas['host'] = myhostsid[host['name']]
datas['check_command'] = rc[0]['_id']
datas['_realm'] = host['_realm']
datas['name'] = name
ret = requests.post(cls.endpoint + '/service', json=datas, headers=headers,
auth=cls.auth)
resp = ret.json()
Expand Down Expand Up @@ -156,26 +157,37 @@ def setUpClass(cls):
'ls_downtimed': False
}
for host in ['srv003', 'srv004', 'srv005']:
response = requests.get(cls.endpoint + '/host',
params={'where': json.dumps({'name': host})}, auth=cls.auth)
resp = response.json()
host_etag = resp['_items'][0]['_etag']
myetags[host] = host_etag
host_id = resp['_items'][0]['_id']
headers_patch = {
'Content-Type': 'application/json',
'If-Match': myetags[host]
'If-Match': host_etag
}
ret = requests.patch(cls.endpoint + '/host/' + myhostsid[host], json=data,
ret = requests.patch(cls.endpoint + '/host/' + host_id, json=data,
headers=headers_patch, auth=cls.auth)
resp = ret.json()
assert resp['_status'] == 'OK'
myetags[host] = resp['_etag']
# update services on this host to be unreachable
for service_name in ['ping', 'ssh']:
response = requests.get(cls.endpoint + '/service',
params={'where': json.dumps({'host': host_id,
'name': service_name})},
auth=cls.auth)
resp = response.json()
datas = {
'ls_state': 'UNREACHABLE',
'ls_state_type': 'HARD'
}
headers_patch = {
'Content-Type': 'application/json',
'If-Match': myetags[host + '.' + service_name]
'If-Match': resp['_items'][0]['_etag']
}
ret = requests.patch(cls.endpoint + '/service/' + myservicesid[host][service_name],
ret = requests.patch(cls.endpoint + '/service/' + resp['_items'][0]['_id'],
json=datas, headers=headers_patch, auth=cls.auth)
resp = ret.json()
assert resp['_status'] == 'OK'
Expand All @@ -187,19 +199,29 @@ def setUpClass(cls):
'ls_acknowledged': False,
'ls_downtimed': False
}
response = requests.get(cls.endpoint + '/service',
params={'where': json.dumps({'host': myhostsid['srv002'],
'name': 'ssh'})},
auth=cls.auth)
resp = response.json()
headers_patch = {
'Content-Type': 'application/json',
'If-Match': myetags['srv002.ssh']
'If-Match': resp['_items'][0]['_etag']
}
ret = requests.patch(cls.endpoint + '/service/' + myservicesid['srv002']['ssh'],
json=datas, headers=headers_patch, auth=cls.auth)
resp = ret.json()
assert resp['_status'] == 'OK'
myetags['srv002.ssh'] = resp['_etag']

response = requests.get(cls.endpoint + '/service',
params={'where': json.dumps({'host': myhostsid['srv006'],
'name': 'ping'})},
auth=cls.auth)
resp = response.json()
headers_patch = {
'Content-Type': 'application/json',
'If-Match': myetags['srv006.ping']
'If-Match': resp['_items'][0]['_etag']
}
ret = requests.patch(cls.endpoint + '/service/' + myservicesid['srv006']['ping'],
json=datas, headers=headers_patch, auth=cls.auth)
Expand All @@ -213,9 +235,14 @@ def setUpClass(cls):
'ls_acknowledged': True,
'ls_downtimed': False
}
response = requests.get(cls.endpoint + '/service',
params={'where': json.dumps({'host': myhostsid['srv002'],
'name': 'ping'})},
auth=cls.auth)
resp = response.json()
headers_patch = {
'Content-Type': 'application/json',
'If-Match': myetags['srv002.ping']
'If-Match': resp['_items'][0]['_etag']
}
ret = requests.patch(cls.endpoint + '/service/' + myservicesid['srv002']['ping'],
json=datas, headers=headers_patch, auth=cls.auth)
Expand Down Expand Up @@ -250,9 +277,14 @@ def setUpClass(cls):
'ls_acknowledged': False,
'ls_downtimed': False
}
response = requests.get(cls.endpoint + '/service',
params={'where': json.dumps({'host': myhostsid['srv001'],
'name': 'ssh'})},
auth=cls.auth)
resp = response.json()
headers_patch = {
'Content-Type': 'application/json',
'If-Match': myetags['srv001.ssh']
'If-Match': resp['_items'][0]['_etag']
}
ret = requests.patch(cls.endpoint + '/service/' + myservicesid['srv001']['ssh'],
json=datas, headers=headers_patch, auth=cls.auth)
Expand Down
36 changes: 29 additions & 7 deletions test/test_overall_state.py
Expand Up @@ -109,24 +109,46 @@ def test_update_host(self):
del data['realm']
data['_realm'] = self.realm_all
requests.post(self.endpoint + '/host', json=data, headers=headers, auth=self.auth)
response = requests.get(self.endpoint + '/host', params=sort_id, auth=self.auth)
params = {'sort': '_id', 'where': json.dumps({'_is_template': True})}
response = requests.get(self.endpoint + '/host', params=params, auth=self.auth)
resp = response.json()
self.assertEqual(len(resp['_items']), 2)
self.assertEqual(len(resp['_items']), 1)
rh = resp['_items']
# On host insertion, _overall_state_id field is 3, because host is UNREACHABLE
self.assertEqual(3, rh[0]['_overall_state_id'])

# Add service 1
data = json.loads(open('cfg/service_srv001_ping.json').read())
data['host'] = rh[1]['_id']
data['host'] = rh[0]['_id']
data['check_command'] = rc[2]['_id']
data['_realm'] = self.realm_all
requests.post(self.endpoint + '/service', json=data, headers=headers, auth=self.auth)
response = requests.post(self.endpoint + '/service', json=data,
headers=headers, auth=self.auth)
response = response.json()
response = requests.get(
self.endpoint + '/service/' + response['_id'], params=sort_id, auth=self.auth
)
ls_service = response.json()
# On service insertion, _overall_state_id field is 3, because service is UNKNOWN
self.assertEqual(3, ls_service['_overall_state_id'])

# Add service 2
data = json.loads(open('cfg/service_srv002_ping.json').read())
data['host'] = rh[1]['_id']
data['host'] = rh[0]['_id']
data['check_command'] = rc[2]['_id']
data['_realm'] = self.realm_all
requests.post(self.endpoint + '/service', json=data, headers=headers, auth=self.auth)
response = requests.post(self.endpoint + '/service', json=data,
headers=headers, auth=self.auth)
responsep = response.json()
response = requests.get(
self.endpoint + '/service/' + responsep['_id'], params=sort_id, auth=self.auth
)
ls_service = response.json()
# be sure the _etag return by post is same when get it
self.assertEqual(responsep['_etag'], ls_service['_etag'])

# On service insertion, _overall_state_id field is 3, because service is UNKNOWN
self.assertEqual(3, ls_service['_overall_state_id'])

# Get all services
response = requests.get(self.endpoint + '/service', params=sort_id, auth=self.auth)
Expand All @@ -152,7 +174,7 @@ def test_update_host(self):
self.endpoint + '/service/' + service['_id'], params=sort_id, auth=self.auth
)
ls_service = response.json()
# _overall_state_id field is 0
# On service insertion, _overall_state_id field is 3, because service is UNKNOWN
self.assertEqual(0, ls_service['_overall_state_id'])

# Get host
Expand Down