Commit 9d5c50cd authored by Clara Dewarumez's avatar Clara Dewarumez

Merge branch 'develop' into 'master'

Merge branch develop into master (3.10.0)

Closes #1384 and #1388

See merge request !679
parents bb79510e bc62271b
Pipeline #6136 passed with stage
in 5 minutes and 16 seconds
......@@ -28,6 +28,23 @@ stage_test:
paths:
- tests_report
build_next:
stage: test
tags: ["canopsis"]
script:
- docker build -t canopsis/uiv3:develop -f docker/Dockerfile.canopsis-next-develop .
- docker-compose -f docker-compose_next.yml -p ${COMPOSE_PROJECT_NAME}-next up -d
- docker push canopsis/uiv3:develop
only:
changes:
- sources/webcore/src/canopsis-next/**/*
refs:
- develop
after_script:
- docker-compose -f docker-compose_next.yml -p ${COMPOSE_PROJECT_NAME}-next kill
- docker-compose -f docker-compose_next.yml -p ${COMPOSE_PROJECT_NAME}-next rm -sf
- docker-compose -f docker-compose_next.yml -p ${COMPOSE_PROJECT_NAME}-next down
after_script:
- docker-compose -f docker-compose.ci.yml -p ${COMPOSE_PROJECT_NAME} kill
- docker-compose -f docker-compose.ci.yml -p ${COMPOSE_PROJECT_NAME} rm -sf
......
......@@ -8,6 +8,65 @@ This document references all changes made to Canopsis since 2017/08/21. Ticket t
- [Alarms list]: Alarms on resources can be hidden when their parent component is down.
## Canopsis 3.10.0 - Due date : 2019-02-08
- [Documentation] Update Service Weather documentation
- [Documentation] Update available variables on customizable templates.
- [Documentation] The Centreon connector is now open-source
- [Documentation] Fix typos in pbehavior documentation
- [Go] Add webhooks plugin
- [Go] Fix issue with missing ack statistics
- [Go] Add conversion of patterns into MongoDB requests
- [Go] Add pattern list types
- [Go] Add entities in calls to AxePostProcessor.ProcessAlarms
- [Go] Add the possibility to declare a ticket without an event
- [Python] Add support for exclusion dates in pbehaviors
- [Python] Add webhook API
- [Tooling] Fix push_docker_images to push the go engines' images
- [Tooling] Remove python-ldap from docker images (WARNING: this will temporarily break support for ldap authentication, and should be fixed in the next release)
- [Tooling] Add engine-axe-cat docker image
- [Tooling] Fix SNMP MIBs import for CAT
- [UI] Fix default views
- [UI] Update to VueJS 2.5.21
- [UI] Fix - View deletion message
- [UI] Add optionnal "Comment" field on pbehavior creation
- [UI] Improve sidebar views links
- [UI] Fix - Variable type change, on filter editor's advanced mode
- [UI] Fix - View duplication
- [UI - UX] Fix - User profile menu's position
- [UI - UX] Improve user profile menu's styling
- [UI - Tabs] Add tab duplication functionnality
- [UI - VueX] Unused entities automatic clean-up
- [UI - Lodash] Add babel-plugin-lodash package
- [UI - Service Weather] Add automatic refresh when filter setting has changed
- [UI - Service Weather] Style improvments
- [UI - Service Weather] Fix - Blinking conditions and Displayed icons
- [UI - Service Weather] Add actions for actions done on entities
## Canopsis 3.9.0 - Due date : 2019-01-24
- [Documentation] Add documentation for action API
- [Tooling] Fix building scripts that used the wrong version of canopsis-next
- [Go] Add support for MongoDB replicaset
- [Go] Add a post-processing plugins system in the axe engine
- [Go] Define triggers in the axe engine
- [Python] Fix healthcheck API response when criticals is undefined
- [Python] Fix pbehavior handling when the rrule generates invalid dates
- [UI] Add "Help" buttons on ListAlarm/Context/Service weather widgets
- [UI] Add "Reorder tabs" functionnality
- [UI] Add "Refresh" button on all administration/exploitation views
- [UI] Improve left sidebar style
- [UI] Variables harmonization for all templates parameters (Info popup, More infos, Service weather)
- [UI / Calendar] Fix display bug when only 1 filter is set
- [UI / Calendar] Improve style (colors)
- [UI / ListAlarm] Add "Delete" alarms mass action
- [UI / ListAlarm] Refactor "Info popup" setting
- [UI / ListAlarm] Fix "Resolved" column problem
- [UI / ListAlarm] Improve "Info popup" style
- [UI / Context] Add rights
- [UI / Service weather] Fix bug on ListAlarm's modal's filter
- [UI / Service weather] Add entities names customization, in "More info" modal
## Canopsis 3.8.0 - Due date : 2019-01-10
- [Documentation] Clean up README.md
......
Canopsis version 3.8.0
Canopsis version 3.10.0
......@@ -28,15 +28,6 @@ function build_for_distribution() {
if [ "${CANOPSIS_BUILD_NEXT}" = "1" ]; then
echo "BUILDING CORE NEXT"
cd ${workdir}/sources/webcore/src/
rm canopsis-next/ -rf
next_tag=${tag}
if [ "${tag}" = "ci" ]; then
next_tag="develop"
fi
git clone git@git.canopsis.net:canopsis/canopsis-next.git -b ${next_tag}
rm canopsis-next/.git -rf
cd ${workdir}
docker build ${docker_args} -f docker/Dockerfile.canopsis-next -t canopsis/canopsis-next:${full_tag} .
......@@ -82,4 +73,3 @@ function build() {
build
cd ${workdir}
rm -rf "${workdir}/sources/webcore/src/canopsis-next"
---
version: '2'
services:
uiv3:
image: canopsis/uiv3:develop
ports:
- 9090:8080
restart: unless-stopped
environment:
- VUE_APP_API_HOST=http://localhost:8082
FROM node:8.12-alpine
WORKDIR /canopsis-next
ADD ./sources/webcore/src/canopsis-next/ /canopsis-next/
RUN cd /canopsis-next/ && \
yarn install
EXPOSE 8080
CMD ["yarn", "run", "serve"]
......@@ -51,6 +51,7 @@ apt-get -y --no-install-recommends install \
python2.7 \
rsync \
snmp \
snmp-mibs-downloader \
smitools \
sudo \
tmux \
......
......@@ -52,6 +52,7 @@ apt-get -y --no-install-recommends install \
python2.7 \
rsync \
snmp \
snmp-mibs-downloader \
smitools \
sudo \
tmux \
......
......@@ -19,7 +19,7 @@ for engine in {"core","cat","prov","cat-prov"}; do
done
# Go engines
if [ "${push_go}" = "Y" ]||[ "${push_go}" = "y" ]; then
for engine in {"init", "engine-axe","engine-che","engine-heartbeat","engine-stat","engine-watcher","engine-action"}; do
for engine in {"init","engine-axe","engine-che","engine-heartbeat","engine-stat","engine-watcher","engine-action"}; do
docker push canopsis/$engine:${CANOPSIS_TAG}
done
fi
......@@ -28,6 +28,6 @@ if [ "${push_pe}" = "Y" ]||[ "${push_pe}" = "y" ]; then
docker push canopsis/init-pe:${CANOPSIS_TAG}
docker push canopsis/canopsis-cat-pe:${CANOPSIS_TAG}
docker push canopsis/canopsis-cat-pe:longoutput-${CANOPSIS_TAG}
#docker push canopsis/canopsis-connector-email2canopsis-pe:${CANOPSIS_TAG}
docker push canopsis/canopsis-connector-email2canopsis-pe:${CANOPSIS_TAG}
#docker push canopsis/canopsis-connector-snmp2canopsis-pe:${CANOPSIS_TAG}
fi
......@@ -36,13 +36,15 @@ class PBehavior(object):
TYPE = 'type_'
REASON = 'reason'
SOURCE = 'source'
EXDATE = 'exdate'
TIMEZONE = 'timezone'
def __init__(self, _id, name, filter_, tstart, tstop, rrule, author,
connector=DEFAULT_CONNECTOR_VALUE,
connector_name=DEFAULT_CONNECTOR_NAME_VALUE,
comments=None, eids=None, type_=None, reason=None,
enabled=True, source=None,
*args, **kwargs):
enabled=True, source=None, exdate=None,
timezone="UTC", *args, **kwargs):
"""
:param str _id: pbehavior id
:param str name: pbehavior name
......@@ -59,6 +61,8 @@ class PBehavior(object):
:param str reason: explanation on pbehavior creation
:param bool enabled: allow this pbehavior to be used. This is NOT the same as the is_active property.
:param str source: if None, pbehavior was created from canopsis. if anything else, it was created from an external data like an event from Nagios or so.
:param list int exdate: a list of exclusion date as a timestamp
:param str timezone: a timezone name
"""
if filter_ is None:
filter_ = {}
......@@ -88,6 +92,15 @@ class PBehavior(object):
if source is not None and not isinstance(source, string_types):
raise TypeError('source must be None or a string, got {}'.format(type(source)))
if exdate is None:
exdate = []
elif not isinstance(exdate, list):
raise TypeError('exdate must be None or a list, got {}'.format(type(source)))
else:
for date in exdate:
if not isinstance(date, int):
raise TypeError('The date inside exdate must be an int, got {}'.format(type(source)))
self._id = _id
self.enabled = enabled
self.name = name
......@@ -103,6 +116,8 @@ class PBehavior(object):
self.type_ = type_
self.reason = reason
self.source = source
self.exdate = exdate
self.timezone = timezone
if args not in [(), None] or kwargs not in [{}, None]:
print('Ignored values on creation: {} // {}'.format(args, kwargs))
......@@ -171,7 +186,9 @@ class PBehavior(object):
self.ENABLED: self.enabled,
self.TYPE: self.type_,
self.REASON: self.reason,
self.SOURCE: self.source
self.SOURCE: self.source,
self.EXDATE: self.exdate,
self.TIMEZONE: self.timezone
}
return dictionnary
......@@ -3,6 +3,11 @@ Utils for pbehaviors.
"""
from dateutil.rrule import rrulestr
from dateutil import tz
from datetime import datetime
EXDATE_DATE_FORMAT = "%Y/%m/%d %H:%M:%S"
def check_valid_rrule(rrule):
......
......@@ -24,7 +24,7 @@ def exports(ws):
:returns: <Healthcheck>
"""
criticals = request.query.criticals.split(',') or None
if len(criticals) == 0:
if criticals == ['']:
criticals = None
health_obj = healthcheckManager.check(criticals=criticals)
if health_obj is None:
......
......@@ -40,7 +40,7 @@ from canopsis.webcore.utils import gen_json, gen_json_error, HTTP_ERROR
VALID_PBEHAVIOR_PARAMS = [
'name', 'filter_', 'author', 'tstart', 'tstop', 'rrule',
'enabled', 'comments', 'connector', 'connector_name', 'type_', 'reason',
'timezone'
'timezone', 'exdate'
]
......@@ -108,6 +108,13 @@ def check_values(data):
if 'rrule' in data:
check_valid_rrule(data['rrule'])
if PBehavior.EXDATE in data:
if isinstance(data[PBehavior.EXDATE], list):
for date in data[PBehavior.EXDATE]:
if not isinstance(date, int):
raise ValueError("The date inside exdate must be an int.")
else:
raise ValueError("Exdate must be a list of string.")
# useful when enabled doesn't exist in document
if ("enabled" not in data
or data["enabled"] is None
......@@ -135,7 +142,8 @@ class RouteHandlerPBehavior(object):
tstart, tstop, rrule=None,
enabled=True, comments=None,
connector='canopsis', connector_name='canopsis',
type_=PBehavior.DEFAULT_TYPE, reason='', timezone=None):
type_=PBehavior.DEFAULT_TYPE, reason='', timezone=None,
exdate=None):
"""
Create a pbehavior.
......@@ -152,6 +160,9 @@ class RouteHandlerPBehavior(object):
:param str type_: an associated type_
:param str reason: a reason to apply this behavior
"""
if exdate is None:
exdate = []
data = {
PBehavior.NAME: name,
PBehavior.FILTER: filter_,
......@@ -165,7 +176,8 @@ class RouteHandlerPBehavior(object):
PBehavior.CONNECTOR_NAME: connector_name,
PBehavior.TYPE: type_,
PBehavior.REASON: reason,
PBehavior.TIMEZONE: timezone
PBehavior.TIMEZONE: timezone,
PBehavior.EXDATE: exdate
}
check_values(data)
......@@ -183,7 +195,8 @@ class RouteHandlerPBehavior(object):
connector_name=connector_name,
type_=type_,
reason=reason,
timezone=timezone
timezone=timezone,
exdate=exdate
)
return result
......@@ -217,12 +230,15 @@ class RouteHandlerPBehavior(object):
def update(self, _id, name=None, filter_=None, tstart=None, tstop=None,
rrule=None, enabled=None, comments=None, connector=None,
connector_name=None, author=None, type_=None, reason=None,
timezone=None):
timezone=None, exdate=None):
"""
Update pbehavior fields. Fields to None will **not** be updated.
:param str _id: pbehavior id
"""
if exdate is None:
exdate = []
params = {
PBehavior.NAME: name,
PBehavior.FILTER: filter_,
......@@ -236,7 +252,8 @@ class RouteHandlerPBehavior(object):
PBehavior.CONNECTOR_NAME: connector_name,
PBehavior.TYPE: type_,
PBehavior.REASON: reason,
PBehavior.TIMEZONE: timezone
PBehavior.TIMEZONE: timezone,
PBehavior.EXDATE: exdate
}
check_values(params)
......@@ -309,7 +326,7 @@ def exports(ws):
'tstart', 'tstop', 'rrule',
'enabled', 'comments',
'connector', 'connector_name',
'type_', 'reason', 'timezone'
'type_', 'reason', 'timezone', 'exdate'
]
)
def create(
......@@ -317,7 +334,8 @@ def exports(ws):
tstart, tstop, rrule=None,
enabled=True, comments=None,
connector='canopsis', connector_name='canopsis',
type_=PBehavior.DEFAULT_TYPE, reason='', timezone=None
type_=PBehavior.DEFAULT_TYPE, reason='', timezone=None,
exdate=None
):
"""
Create a pbehavior.
......@@ -325,7 +343,7 @@ def exports(ws):
return rhpb.create(
name, filter, author, tstart, tstop, rrule,
enabled, comments, connector, connector_name, type_, reason,
timezone
timezone, exdate
)
@ws.application.post('/api/v2/pbehavior')
......@@ -399,7 +417,7 @@ def exports(ws):
'name', 'filter',
'tstart', 'tstop', 'rrule',
'enabled',
'timezone'
'timezone', 'exdate'
]
)
def update(
......@@ -408,7 +426,7 @@ def exports(ws):
tstart=None, tstop=None, rrule=None,
enabled=None, comments=None,
connector=None, connector_name=None,
author=None, type_=None, reason=None, timezone=None
author=None, type_=None, reason=None, timezone=None, exdate=None
):
"""
Update a pbehavior.
......@@ -427,7 +445,8 @@ def exports(ws):
author=author,
type_=type_,
reason=reason,
timezone=timezone
timezone=timezone,
exdate=exdate
)
@route(
......
# -*- coding: utf-8 -*-
# --------------------------------
# Copyright (c) 2019 "Capensis" [http://www.capensis.fr]
#
# This file is part of Canopsis.
#
# Canopsis is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Canopsis 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Canopsis. If not, see <http://www.gnu.org/licenses/>.
# ---------------------------------
import uuid
from bottle import request
from pymongo.errors import PyMongoError
from canopsis.common.collection import CollectionError
from canopsis.webhooks import WebhookManager
from canopsis.webcore.utils import (gen_json, gen_json_error,
HTTP_NOT_FOUND, HTTP_ERROR)
def exports(ws):
webhook_manager = WebhookManager(WebhookManager.default_collection())
@ws.application.get(
'/api/v2/webhook'
)
def get_webhook_list():
"""
Return the list of all webhooks.
:returns: <Webhook>
:rtype: list
"""
try:
document = webhook_manager.get_webhook_list()
except PyMongoError:
return gen_json_error(
{"description": "Can not retrieve the webhooks list from "
"database, contact your administrator."},
HTTP_ERROR)
return gen_json(document)
@ws.application.get(
'/api/v2/webhook/<webhook_id>'
)
def get_webhook_by_id(webhook_id):
"""
Return a webhook given the id.
:param webhook_id: ID of the webhook
:type webhook_id: str
:returns: <Webhook>
:rtype: dict
"""
try:
document = webhook_manager.get_webhook_by_id(webhook_id)
except PyMongoError:
return gen_json_error(
{"description": "Can not retrieve the webhook data from "
"database, contact your administrator."},
HTTP_ERROR)
if document is None:
return gen_json_error(
{"description": "No webhook found with ID " + webhook_id},
HTTP_ERROR)
return gen_json(document)
@ws.application.post(
'/api/v2/webhook'
)
def create_webhook():
"""
Create a new webhook.
:returns: ID of the webhook
:rtype: string
"""
try:
webhook = request.json
except ValueError:
return gen_json_error(
{'description': 'Invalid JSON'},
HTTP_ERROR
)
if webhook is None or not isinstance(webhook, dict):
return gen_json_error(
{'description': 'Nothing to create'}, HTTP_ERROR)
if '_id' not in webhook:
webhook['_id'] = str(uuid.uuid4())
try:
return webhook_manager.create_webhook(webhook)
except CollectionError as ce:
ws.logger.error('Webhook creation error : {}'.format(ce))
return gen_json_error(
{'description': 'Error while creating an webhook'},
HTTP_ERROR
)
@ws.application.put(
'/api/v2/webhook/<webhook_id>'
)
def update_webhook_by_id(webhook_id):
"""
Update an existing webhook.
:param webhook_id: ID of the webhook
:type webhook_id: str
:rtype: dict
"""
try:
webhook = request.json
except ValueError:
return gen_json_error(
{'description': 'Invalid JSON'},
HTTP_ERROR
)
if webhook is None or not isinstance(webhook, dict):
return gen_json_error(
{'description': 'Nothing to update'}, HTTP_ERROR)
try:
ok = webhook_manager.update_webhook_by_id(webhook, webhook_id)
except CollectionError as ce:
ws.logger.error('Webhook update error : {}'.format(ce))
return gen_json_error(
{'description': 'Error while updating an webhook'},
HTTP_ERROR
)
if not ok:
return gen_json_error(
{'description': 'Failed to update webhook'},
HTTP_ERROR
)
return gen_json({})
@ws.application.delete(
'/api/v2/webhook/<webhook_id>'
)
def delete_webhook_by_id(webhook_id):
"""
Delete an existing webhook, given its id.
:param webhook_id: ID of the webhook
:type webhook_id: str
:rtype: dict
"""
try:
ok = webhook_manager.delete_webhook_by_id(webhook_id)
except PyMongoError:
return gen_json_error(
{"description": "Can not retrieve the webhook data from "
"database, contact your administrator."},
HTTP_ERROR)
return gen_json({"status": ok})
# -*- coding: utf-8 -*-
from .manager import WebhookManager
<
# -*- coding: utf-8 -*-
# --------------------------------
# Copyright (c) 2019 "Capensis" [http://www.capensis.fr]
#
# This file is part of Canopsis.
#
# Canopsis is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Canopsis 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Canopsis. If not, see <http://www.gnu.org/licenses/>.
# ---------------------------------
from canopsis.common.mongo_store import MongoStore
from canopsis.common.collection import MongoCollection
class WebhookManager(object):
"""
Manager for the webhooks
"""
COLLECTION = "webhooks"
def __init__(self, mongo_collection):
"""
:param mongo_collection: `pymongo.collection.Collection` object.
"""
super(WebhookManager, self).__init__()
self.__collection = mongo_collection
@classmethod
def default_collection(cls):
"""
Returns the default collection for the manager.