(function (Icinga) { function colorMarker(worstState, icon) { var markerColor = 'awesome-marker'; // TODO: Different marker icon for not-OK states // if(worstState > 0) { // markerColor = markerColor + ' awesome-marker-square'; // } var marker = L.AwesomeMarkers.icon({ icon: icon, markerColor: state2color(worstState), className: markerColor }); return marker } function state2color(state) { switch (parseInt(state)) { case 0: return "green"; case 1: return "orange"; case 2: return "red"; case 3: return "purple"; default: return "blue"; } } function isFilterParameter(parameter) { return (parameter.charAt(0) === '(' || parameter.match('^[_]{0,1}(host|service)') || parameter.match('^(object|state)Type') || parameter.match('^problems')); } function getParameters(id) { var params = decodeURIComponent($('#map-' + id).closest('.module-map').data('icingaUrl')).split('&'); // remove module path from url parameters if (params.length > 0) { params[0] = params[0].replace(/^.*\?/, '') } return params } function unique(list) { var result = []; $.each(list, function (i, e) { if ($.inArray(e, result) == -1) result.push(e); }); return result; } function filterParams(id, extra) { var sURLVariables = getParameters(id); var params = [], i; if (extra !== undefined) { sURLVariables = $.merge(extra.split('&'), sURLVariables); sURLVariables = unique(sURLVariables); } for (i = 0; i < sURLVariables.length; i++) { // Protect Icinga filter syntax if (isFilterParameter(sURLVariables[i])) { params.push(sURLVariables[i]); continue; } } return params.join("&") } function showDefaultView() { if (map_default_lat !== null && map_default_long !== null) { if (map_default_zoom !== null) { cache[id].map.setView([map_default_lat, map_default_long], map_default_zoom); } else { cache[id].map.setView([map_default_lat, map_default_long]); } } else { cache[id].map.fitWorld() } } function toggleFullscreen() { icinga.ui.toggleFullscreen(); cache[id].map.invalidateSize(); cache[id].fullscreen = !cache[id].fullscreen; if (cache[id].fullscreen) { $('.controls').hide(); } else { $('.controls').show(); } } // TODO: Allow update of multiple parameters function updateUrl(pkey, pvalue) { // Don't update URL if in dashlet mode if (dashlet) { return; } var $target = $('.module-map'); var $currentUrl = $target.data('icingaUrl'); var basePath = $currentUrl.replace(/\?.*$/, ''); var searchPath = $currentUrl.replace(/^.*\?/, ''); var sURLVariables = (searchPath === basePath ? [] : searchPath.split('&')); var updated = false; for (var i = 0; i < sURLVariables.length; i++) { // Don't replace Icinga filters if (isFilterParameter(sURLVariables[i])) { continue; } var tmp = sURLVariables[i].split('='); if (tmp[0] == pkey) { sURLVariables[i] = tmp[0] + '=' + pvalue; updated = true; break; } } // Parameter is to be added if (!updated) { sURLVariables.push(pkey + "=" + pvalue); } $target.data('icingaUrl', basePath + '?' + sURLVariables.join('&')); icinga.history.pushCurrentState(); } function getWorstState(states) { var worstState = 0; var allPending = -1; var allUnknown = -1; var last = -1; if (states.length == 1) { return states[0]; } for (var i = 0, len = states.length; i < len; i++) { var state = states[i]; if (state < 3) { if (allPending == 1) { allPending = 0; } else if (allUnknown == 1) { allUnknown = 0; } } if (state > 2) { // PENDING if (state == 99) { if (allPending < 0 && last < 0) { allPending = 1; } // OK -> PENDING -> UNKNOWN -> WARNING -> CRITICAL state = 0.25; } // UNKNOWN if (state == 3) { if (allUnknown < 0 && last < 0) { allUnknown = 1; } // OK -> PENDING -> UNKNOWN -> WARNING -> CRITICAL state = 0.5; } } if (state > worstState) { worstState = state; } last = state; } if (allPending == 1) { worstState = 99; } if (allUnknown == 1) { worstState = 3; } // Restore PENDING and UNKNOWN if (worstState == 0.25) { worstState = 99; } else if (worstState == 0.5) { worstState = 3; } return worstState; } function mapCenter(hostname) { if (cache[id].hostMarkers[hostname]) { var el = cache[id].hostMarkers[hostname]; cache[id].map.panTo(cache[id].hostMarkers[hostname].getLatLng()) } } var cache = {}; var Map = function (module) { this.module = module; this.initialize(); this.timer; this.module.icinga.logger.debug('Map module loaded'); }; Map.prototype = { initialize: function () { this.timer = {}; this.module.on('rendered', this.onRenderedContainer); this.registerTimer() }, registerTimer: function (id) { this.timer = this.module.icinga.timer.register( this.updateAllMapData, this, 10000 ); return this; }, removeTimer: function (id) { this.module.icinga.timer.unregister(this.timer); return this }, onPopupOpen: function (evt) { $('.detail-link').on("click", function (ievt) { mapCenter(evt.popup._source.options.id); cache[id].map.invalidateSize(); }); }, updateAllMapData: function () { var _this = this; if (cache.length == 0) { this.removeTimer(id) return this } $.each(cache, function (id) { if (!$('#map-' + id).length) { delete cache[id] } else { _this.updateMapData({id: id}) } }); }, updateMapData: function (parameters) { var id = parameters.id; var show_host = parameters.show_host; var $that = this; function showHost(hostname) { if (cache[id].hostMarkers[hostname]) { var el = cache[id].hostMarkers[hostname]; cache[id].markers.zoomToShowLayer(el, function () { el.openPopup(); }) } } function removeOldMarkers(id, data) { // remove old markers $.each(cache[id].hostMarkers, function (identifier, d) { if ((data['hosts'] && !data['hosts'][identifier]) && (data['services'] && !data['services'][identifier])) { cache[id].markers.removeLayer(d); delete cache[id].hostMarkers[identifier]; } }); } function errorMessage(msg) { cache[id].map.spin(false); $map = cache[id].map; $map.openModal({ content: "

Could not fetch data from API:

" + msg + "
", onShow: function (evt) { $that.removeTimer(id) }, onHide: function (evt) { $that.registerTimer(id); } }); } function processData(json) { if (json['message']) { errorMessage(json['message']); return; } removeOldMarkers(id, json); $.each(json, function (type, element) { $.each(element, function (identifier, data) { if (data.length < 1 || data['coordinates'] == "") { console.log('found empty coordinates: ' + data) return true } var states = []; var icon; var services; var worstState; var display_name = (data['host_display_name'] ? data['host_display_name'] : hostname); if (type === 'hosts') { states.push((data['host_state'] == 1 ? 2 : data['host_state'])) } services = '
'; services += '

Services

'; services += '
'; services += ''; services += ''; $.each(data['services'], function (service_display_name, service) { states.push(service['service_state']); service_handled = ""; if ((data['host_state'] == 1 && data['host_acknowledged']) || (service['service_acknowledged'] == 1 || service['service_in_downtime'] == 1)) { service_handled = " handled"; } services += ''; services += ''; services += ''; if (type === 'services') { display_name = service_display_name + " (" + display_name + ")"; } }); services += ''; services += '
'; services += service_display_name; services += '' services += '' services += '
'; services += '
'; services += '
'; worstState = getWorstState(states); var marker_icon = (type === 'hosts' ? 'host' : 'service'); if (data['icon']) { marker_icon = data['icon']; } icon = colorMarker(worstState, marker_icon); var host_icon = ""; if (data['host_icon_image'] != "") { host_icon = '' + data['host_icon_image_alt'] + ''; } var host_status = type === 'hosts' && data['host_state'] == 1 ? "
" + translation['host-down'] + "
" : ""; var info = '
'; info += '

'; info += ''; info += ' '; info += ''; info += data['host_display_name'] + '

'; info += host_status; info += services; info += '
'; var marker; if (cache[id].hostMarkers[identifier]) { marker = cache[id].hostMarkers[identifier]; marker.options.state = worstState; marker.setIcon(icon); } else { marker = L.marker(data['coordinates'], { icon: icon, title: display_name, riseOnHover: true, id: identifier, state: worstState, }).addTo(cache[id].markers); cache[id].hostMarkers[identifier] = marker; cache[id].hostData[identifier] = data } marker.bindPopup(info); }) }); cache[id].markers.refreshClusters(); cache[id].map.spin(false); // TODO: Should be updated instant and not only on data refresh cache[id].map.invalidateSize(); if (show_host != "") { showHost(show_host); show_host = "" } } var url = icinga.config.baseUrl + '/map/data/points?' + filterParams(id, cache[id].parameters); $.getJSON(url, processData) .fail(function (jqxhr, textStatus, error) { errorMessage(error); }); }, onRenderedContainer: function (event) { // in module configuration we don't have a map, so return peacefully if (typeof id === 'undefined') { return; } cache[id] = {}; cache[id].map = L.map('map-' + id, { zoomControl: false, worldCopyJump: true } ); var osm = L.tileLayer(tile_url, { attribution: '© OpenStreetMap', subdomains: ['a', 'b', 'c'], maxNativeZoom: map_max_native_zoom, maxZoom: map_max_zoom, minZoom: map_min_zoom }); osm.addTo(cache[id].map); cache[id].markers = new L.MarkerClusterGroup({ iconCreateFunction: function (cluster) { var childCount = cluster_problem_count ? 0 : cluster.getChildCount(); var states = []; $.each(cluster.getAllChildMarkers(), function (id, el) { states.push(el.options.state); if (cluster_problem_count && el.options.state > 0) { childCount++; } }); var worstState = getWorstState(states); var c = ' marker-cluster-' + worstState; return new L.DivIcon({ html: '
' + childCount + '
', className: 'marker-cluster' + c, iconSize: new L.Point(40, 40) }); }, maxClusterRadius: function (zoom) { return (zoom <= disable_cluster_at_zoom) ? 80 : 1; // radius in pixels }, }); cache[id].hostMarkers = {}; cache[id].hostData = {}; cache[id].fullscreen = false; cache[id].parameters = url_parameters; // TODO: fixme // var basePath = $currentUrl.replace(/\?.*$/, ''); // var initialUrl = icinga. // $('#map-' + id).closest('.module-map').data('icingaUrl', url_parameters); showDefaultView(); cache[id].map.on('popupopen', this.onPopupOpen); L.control.zoom({ zoomInTitle: translation['btn-zoom-in'], zoomOutTitle: translation['btn-zoom-in'] } ).addTo(cache[id].map); if (!dashlet) { L.easyButton({ states: [{ icon: 'icon-dashboard', title: translation['btn-dashboard'], onClick: function (btn, map) { var dashletUri = "map" + window.location.search; var uri = icinga.config.baseUrl + "/" + "dashboard/new-dashlet?url=" + encodeURIComponent(dashletUri); window.open(uri, "_self") } }] }).addTo(cache[id].map); L.easyButton({ states: [{ icon: 'icon-resize-full-alt', title: translation['btn-fullscreen'], onClick: function (btn, map) { toggleFullscreen(); } }] }).addTo(cache[id].map); L.easyButton({ states: [{ icon: 'icon-globe', title: translation['btn-default'], onClick: function (btn, map) { showDefaultView(); } }] }).addTo(cache[id].map); L.control.locate({ icon: 'icon-pin', strings: {title: translation['btn-locate']} }).addTo(cache[id].map); cache[id].map.on('map-container-resize', function () { map.invalidateSize(); console.log("Resize") }); cache[id].map.on('moveend', function (e) { var center = cache[id].map.getCenter(); var lat = center.lat; var lng = center.lng; updateUrl('default_lat', lat); updateUrl('default_long', lng) }); cache[id].map.on('zoomend', function (e) { var zoomLevel = cache[id].map.getZoom(); updateUrl('default_zoom', zoomLevel) }); cache[id].map.on('click', function (e) { // only for debugging needed // var id = e.target._container.id.replace('map-', ''); if (e.originalEvent.ctrlKey) { var coord = 'vars.geolocation = "' + e.latlng.lat.toFixed(6) + ',' + e.latlng.lng.toFixed(6) + '"'; var popup = "

Location selected

" + "

To use this location with your host(s) or service(s), just add the following config to your object definition:

" + "
" + coord + "
"; var marker; marker = L.marker(e.latlng, {icon: colorMarker(99, 'globe')}); marker.bindPopup(popup); marker.addTo(cache[id].markers); marker.on('popupclose', function (evt) { cache[id].markers.removeLayer(marker); }); cache[id].markers.zoomToShowLayer(marker, function () { marker.openPopup(); }) } }); } cache[id].markers.addTo(cache[id].map); cache[id].map.spin(true); this.updateMapData({id: id, show_host: map_show_host}) } }; Icinga.availableModules.map = Map; }(Icinga));