def test_actions_url(self): """ Helper - get_element_notes_url """ host = Host({ '_id': '575a7dd74c988c170e857988', '_status': 'OK', 'name': 'test', '_created': 1470995433, '_updated': 1470995450, }) assert host html_actions = Helper.get_element_actions_url(host, default_title="Url", default_icon="globe") assert html_actions == [] host = Host({ '_id': '575a7dd74c988c170e857988', '_status': 'OK', 'name': 'test', '_created': 1470995433, '_updated': 1470995450, 'action_url': 'http://www.google.fr|url1::http://www.google.fr|My KB,,tag::http://www.my-KB.fr?host=$HOSTNAME$|Last URL,,tag::<strong>Lorem ipsum dolor sit amet</strong>, consectetur adipiscing elit. Proin et leo gravida, lobortis nunc nec, imperdiet odio. Vivamus quam velit, scelerisque nec egestas et, semper ut massa.,,http://www.my-KB.fr?host=$HOSTADDRESS$', }) assert host html_actions = Helper.get_element_actions_url(host, default_title="Url", default_icon="globe") # 3 declared actions, with different parameters # Expecting 3 links assert html_actions == [ '<a href="http://www.google.fr" target="_blank" role="button" data-toggle="popover urls" data-container="body" data-html="true" data-content="No description provided" data-trigger="hover focus" data-placement="bottom"><span class="fa fa-globe"></span> Url</a>', '<a href="http://www.google.fr" target="_blank" role="button" data-toggle="popover urls" data-container="body" data-html="true" data-content="No description provided" data-trigger="hover focus" data-placement="bottom"><span class="fa fa-globe"></span> url1</a>', '<a href="http://www.my-KB.fr?host=$HOSTNAME$" target="_blank" role="button" data-toggle="popover urls" data-container="body" data-html="true" data-content="No description provided" data-trigger="hover focus" data-placement="bottom"><span class="fa fa-tag"></span> My KB</a>', '<a href="http://www.my-KB.fr?host=$HOSTADDRESS$" target="_blank" role="button" data-toggle="popover urls" data-container="body" data-html="true" data-content="<strong>Lorem ipsum dolor sit amet</strong>, consectetur adipiscing elit. Proin et leo gravida, lobortis nunc nec, imperdiet odio. Vivamus quam velit, scelerisque nec egestas et, semper ut massa." data-trigger="hover focus" data-placement="bottom"><span class="fa fa-tag"></span> Last URL</a>' ]
def get_table(object_type, schema, embedded=False, identifier=None, credentials=None): """ Build the object_type table and get data to populate the table """ datamgr = request.environ['beaker.session']['datamanager'] # Pagination and search where = {'saved_filters': True} if request.query.get('search') is not None: where = Helper.decode_search(request.query.get('search', '')) # Get total elements count # total = datamgr.get_objects_count(object_type, search=where, refresh=True) # Build table structure dt = Datatable(object_type, datamgr, schema) # Build page title title = dt.title if '%d' in title: title = title % dt.records_total return { 'object_type': object_type, 'dt': dt, 'where': where, 'title': request.query.get('title', title), 'embedded': embedded, 'identifier': identifier, 'credentials': credentials }
def __init__(self, config=None): """ Application configuration :param: config :type: dict """ logger.info("Initializing...") # Store all the plugins self.plugins = [] # Store all the widgets self.widgets = {} # Store all the tables self.tables = {} # Store all the lists self.lists = {} # Helper class self.helper = Helper() # Application configuration self.app_config = config logger.info("Configuration: %s", self.app_config) # Load plugins in the plugins directory ... self.plugins_count = self.load_plugins( bottle.app(), os.path.join(os.path.abspath(os.path.dirname(__file__)), 'plugins'))
def get_date(self, _date, fmt=None, duration=False): """Format the provided `_date` as a string according to the specified format. If no date format is specified, it uses the one defined in the ElementState object that is the date format defined in the application configuration. If duration is True, the date is displayed as a pretty date: 1 day 12 minutes ago ... """ if _date == self.__class__._default_date: return _('Never dated!') if duration: return Helper.print_duration(_date, duration_only=False, x_elts=0) item_state = ElementState() if not fmt: if item_state.date_format: fmt = item_state.date_format # Make timestamp to datetime _date = datetime.utcfromtimestamp(_date) # Tell the datetime object that it's in UTC time zone since # datetime objects are 'naive' by default _date = _date.replace(tzinfo=item_state.tz_from) # Convert to required time zone _date = _date.astimezone(item_state.tz_to) if fmt: return _date.strftime(fmt) return _date.isoformat(' ')
def test_notes_url(self): """ Helper - get_element_notes_url """ host = Host({ '_id': '575a7dd74c988c170e857988', '_status': 'OK', 'name': 'test', '_created': 1470995433, '_updated': 1470995450, }) assert host html_notes = Helper.get_element_notes_url(host, default_title="Note", default_icon="tag") # Empty list when no notes exist assert html_notes == [] host = Host({ '_id': '575a7dd74c988c170e857988', '_status': 'OK', 'name': 'test', '_created': 1470995433, '_updated': 1470995450, 'notes': 'note simple|Libellé::note avec un libellé|KB1023,,tag::<strong>Lorem ipsum dolor sit amet</strong>, consectetur adipiscing elit. Proin et leo gravida, lobortis nunc nec, imperdiet odio. Vivamus quam velit, scelerisque nec egestas et, semper ut massa. Vestibulum id tincidunt lacus. Ut in arcu at ex egestas vestibulum eu non sapien. Nulla facilisi. Aliquam non blandit tellus, non luctus tortor. Mauris tortor libero, egestas quis rhoncus in, sollicitudin et tortor.|note simple|Tag::tagged note ...', 'notes_url': 'http://www.my-KB.fr?host=$HOSTADDRESS$|http://www.my-KB.fr?host=$HOSTNAME$', }) assert host html_notes = Helper.get_element_notes_url(host, default_title="Note", default_icon="tag") # 5 declared notes, but only 2 URLs # Expecting 5 links assert html_notes == [ '<a href="http://www.my-KB.fr?host=$HOSTADDRESS$" target="_blank" role="button" data-toggle="popover urls" data-container="body" data-html="true" data-content="note simple" data-trigger="hover focus" data-placement="bottom"><span class="fa fa-tag"></span> Note</a>', '<a href="http://www.my-KB.fr?host=$HOSTNAME$" target="_blank" role="button" data-toggle="popover urls" data-container="body" data-html="true" data-content="note avec un libellé" data-trigger="hover focus" data-placement="bottom"><span class="fa fa-tag"></span> Libellé</a>', '<a href="#" role="button" data-toggle="popover urls" data-container="body" data-html="true" data-content="<strong>Lorem ipsum dolor sit amet</strong>, consectetur adipiscing elit. Proin et leo gravida, lobortis nunc nec, imperdiet odio. Vivamus quam velit, scelerisque nec egestas et, semper ut massa. Vestibulum id tincidunt lacus. Ut in arcu at ex egestas vestibulum eu non sapien. Nulla facilisi. Aliquam non blandit tellus, non luctus tortor. Mauris tortor libero, egestas quis rhoncus in, sollicitudin et tortor." data-trigger="hover focus" data-placement="bottom"><span class="fa fa-tag"></span> KB1023</span></a>', '<a href="#" role="button" data-toggle="popover urls" data-container="body" data-html="true" data-content="note simple" data-trigger="hover focus" data-placement="bottom"><span class="fa fa-tag"></span> Note</span></a>', '<a href="#" role="button" data-toggle="popover urls" data-container="body" data-html="true" data-content="tagged note ..." data-trigger="hover focus" data-placement="bottom"><span class="fa fa-tag"></span> Tag</span></a>' ]
def bi_livestate(self): # pylint:disable=no-self-use """Returns the livestate for a specific business impact level Used by the view to update the live state page content """ user = request.environ['beaker.session']['current_user'] webui = request.app.config['webui'] datamgr = webui.datamgr panels = datamgr.get_user_preferences(user, 'livestate', {}) ls = Helper.get_html_livestate(datamgr, panels, int(request.query.get('bi', -1)), request.query.get('search', {}), actions=user.is_power()) response.status = 200 response.content_type = 'application/json' return json.dumps({'livestate': ls})
def setUp(self): print "" print "setting up ..." alignak_webui.app.config['HOST'] = '127.0.0.1' alignak_webui.app.config['PORT'] = 80 alignak_webui.app.config['DEBUG'] = False alignak_webui.app.config['TESTING'] = True cfg_file = "settings.cfg" print 'Required configuration file:', cfg_file sett = Settings(alignak_webui.app) found_cfg_files = sett.read(cfg_file, {}) if not found_cfg_files: print "Required configuration file not found." sys.exit(1) print 'Found configuration file:', cfg_file self.helper = Helper(alignak_webui.app)
def get_worldmap(): """ Get the hosts list to build a worldmap """ user = request.environ['beaker.session']['current_user'] datamgr = request.environ['beaker.session']['datamanager'] target_user = request.environ['beaker.session']['target_user'] username = user.get_username() if not target_user.is_anonymous(): username = target_user.get_username() # Fetch elements per page preference for user, default is 25 elts_per_page = datamgr.get_user_preferences(username, 'elts_per_page', 25) elts_per_page = elts_per_page['value'] # Pagination and search start = int(request.query.get('start', '0')) count = int(request.query.get('count', elts_per_page)) where = Helper.decode_search(request.query.get('search', '')) search = { 'page': start // (count + 1), 'max_results': count, 'sort': '-_id', 'where': where } # Get valid hosts valid_hosts = get_valid_elements(search) # Get last total elements count total = len(valid_hosts) count = min(len(valid_hosts), total) return { 'mapId': 'hostsMap', 'params': worldmap_parameters, 'hosts': valid_hosts, 'pagination': webui.helper.get_pagination_control('/worldmap', total, start, count), 'title': request.query.get('title', _('Hosts worldmap')) }
def __init__(self, app, name='alignak_webui', config=None): """ Application initialization :param: config :type: dict """ logger.info("Initializing application...") self.reduced_mode = os.environ.get('ALIGNAK_WEBUI_REDUCED', False) if self.reduced_mode: logger.warning("- reduced mode: %s!", self.reduced_mode) # Store all the plugins self.plugins = [] # Store all the widgets self.widgets = {} # Store all the tables self.tables = {} # Store all the lists self.lists = {} # Helper class self.helper = Helper() # Application configuration self.app = app self.name = name self.config = config # Application data manager and connection self.datamgr = None self.current_user = None # Load plugins in the plugins directory ... self.plugins_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'plugins') self.plugins_count = self.load_plugins(plugins_dir=self.plugins_dir) logger.info("loaded %d plugins from: %s", self.plugins_count, self.plugins_dir)
def get_servicegroup_members(servicegroup_id): """ Get the servicegroup services list """ datamgr = request.environ['beaker.session']['datamanager'] servicegroup = datamgr.get_servicegroup(servicegroup_id) if not servicegroup: # pragma: no cover, should not happen return webui.response_invalid_parameters( _('Services group element does not exist')) # Not JSON serializable! # items = servicegroup.members items = [] for service in servicegroup.members: lv_service = datamgr.get_livestate( {'where': { 'type': 'service', 'service': service.id }}) lv_service = lv_service[0] title = "%s - %s (%s)" % (lv_service.status, Helper.print_duration( lv_service.last_check, duration_only=True, x_elts=0), lv_service.output) items.append({ 'id': service.id, 'name': service.name, 'alias': service.alias, 'icon': lv_service.get_html_state(text=None, title=title), 'url': lv_service.get_html_link() }) response.status = 200 response.content_type = 'application/json' return json.dumps(items)
def show_worldmap(self, for_my_widget=False): """Get the hosts list to build a worldmap Get the list of the valid hosts t display onthe map If `for_my_widget` is True this function returns the list of the concerned hosts else it returns the worldmap view. :param for_my_widget: defaults to False :return: """ user = request.environ['beaker.session']['current_user'] datamgr = request.app.datamgr # Fetch elements per page preference for user, default is 25 elts_per_page = datamgr.get_user_preferences(user, 'elts_per_page', 25) # Pagination and search start = int(request.query.get('start', '0')) count = int(request.query.get('count', elts_per_page)) where = Helper.decode_search(request.query.get('search', ''), self.table) search = { 'page': (start // count) + 1, 'max_results': count, 'sort': '-_id', 'where': where } if self.plugin_parameters.get('hosts_business_impacts'): # Only get hosts which business impact is configured... logger.debug("worldmap, only hosts with BI in: %s", self.plugin_parameters['hosts_business_impacts']) search['where'].update( {'business_impact': {'$in': self.plugin_parameters['hosts_business_impacts']}}) if self.plugin_parameters.get('hosts_included'): # Include some hosts... logger.debug("worldmap, included hosts: %s", self.plugin_parameters['hosts_included']) search['where'].update({'name': { "$regex": self.plugin_parameters['hosts_included']}}) if self.plugin_parameters.get('hosts_excluded'): # Exclude some hosts... logger.debug("worldmap, excluded hosts: %s", self.plugin_parameters['hosts_excluded']) search['where'].update({'name': { "$regex": "^((?!%s).)*$" % self.plugin_parameters['hosts_excluded']}}) # Do not include the embedded fields to improve the loading time... hosts = datamgr.get_hosts(search, embedded=False) # Get positioned and not-positioned hosts positioned_hosts = self.get_map_elements(hosts) # Get last total elements count total = len(hosts) if hosts: total = hosts[0]['_total'] logger.info("worldmap, total %d hosts", total) if for_my_widget: return [h for h in positioned_hosts if h['positioned']] map_style = "width: %s; height: %s;" % (self.plugin_parameters.get('map_width', "100%"), self.plugin_parameters.get('map_height', "100%")) return { 'search_engine': self.search_engine, 'search_filters': self.search_filters, 'mapId': 'hostsMap', 'mapStyle': map_style, 'params': self.plugin_parameters, 'hosts': positioned_hosts, 'pagination': self.webui.helper.get_pagination_control( '/worldmap', total, start, count), 'title': request.query.get('title', _('Hosts worldmap')) }
class test_helper(unittest.TestCase): def setUp(self): print "" print "setting up ..." alignak_webui.app.config['HOST'] = '127.0.0.1' alignak_webui.app.config['PORT'] = 80 alignak_webui.app.config['DEBUG'] = False alignak_webui.app.config['TESTING'] = True cfg_file = "settings.cfg" print 'Required configuration file:', cfg_file sett = Settings(alignak_webui.app) found_cfg_files = sett.read(cfg_file, {}) if not found_cfg_files: print "Required configuration file not found." sys.exit(1) print 'Found configuration file:', cfg_file self.helper = Helper(alignak_webui.app) def tearDown(self): print "" print "tearing down ..." def test_01_print_date(self): print "---" now = time.time() # Timestamp errors s = self.helper.print_date(None) print "Result:", s self.assert_(s == 'n/a') s = self.helper.print_date(0) print "Result:", s self.assert_(s == 'n/a') # Now, default format s = self.helper.print_date(now) print "Result:", s # Now, specified format s = self.helper.print_date(now, fmt='%Y-%m-%d') print "Result:", s s = self.helper.print_date(now, fmt='%H:%M:%S') print "Result:", s def test_02_print_duration(self): print "---" now = time.time() # Timestamp errors s = self.helper.print_duration(None) print "Result:", s self.assert_(s == 'n/a') s = self.helper.print_duration(0) print "Result:", s self.assert_(s == 'n/a') # Now, default format s = self.helper.print_duration(now) print "Result:", s self.assert_(s == 'Now') # In the past ... # 2s ago s = self.helper.print_duration(now - 2) print "Result:", s self.assert_(s == '2s ago') # Only the duration string s = self.helper.print_duration(now - 2, duration_only=True) print "Result:", s self.assert_(s == '2s') # Got 2minutes s = self.helper.print_duration(now - 120) print "Result:", s self.assert_(s == '2m ago') # Go 2hours ago s = self.helper.print_duration(now - 3600*2) print "Result:", s self.assert_(s == '2h ago') # Go 2 days ago s = self.helper.print_duration(now - 3600*24*2) print "Result:", s self.assert_(s == '2d ago') # Go 2 weeks ago s = self.helper.print_duration(now - 86400*14) print "Result:", s self.assert_(s == '2w ago') # Go 2 months ago s = self.helper.print_duration(now - 86400*56) print "Result:", s self.assert_(s == '2M ago') # Go 1 year ago s = self.helper.print_duration(now - 86400*365*2) print "Result:", s self.assert_(s == '2y ago') # Now a mix of all of this :) s = self.helper.print_duration(now - 2 - 120 - 3600*2 - 3600*24*2 - 86400*14 - 86400*56) print "Result:", s self.assert_(s == '2M 2w 2d 2h 2m 2s ago') # Now with a limit, because here it's just a nightmare to read s = self.helper.print_duration(now - 2 - 120 - 3600*2 - 3600*24*2 - 86400*14 - 86400*56, x_elts=2) print "Result:", s self.assert_(s == '2M 2w ago') # Return to the future # Get the 2s ago s = self.helper.print_duration(now + 2) print "Result:", s self.assert_(s == 'in 2s') # Got 2minutes s = self.helper.print_duration(now + 120) print "Result:", s self.assert_(s == 'in 2m') # Go 2hours ago s = self.helper.print_duration(now + 3600*2) print "Result:", s self.assert_(s == 'in 2h') # Go 2 days ago s = self.helper.print_duration(now + 3600*24*2) print "Result:", s self.assert_(s == 'in 2d') # Go 2 weeks ago s = self.helper.print_duration(now + 86400*14) print "Result:", s self.assert_(s == 'in 2w') # Go 2 months ago s = self.helper.print_duration(now + 86400*56) print "Result:", s self.assert_(s == 'in 2M') # Go 1 year ago s = self.helper.print_duration(now + 86400*365*2) print "Result:", s self.assert_(s == 'in 2y') # Now a mix of all of this :) s = self.helper.print_duration(now + 2 + 120 + 3600*2 + 3600*24*2 + 86400*14 + 86400*56) print "Result:", s self.assert_(s == 'in 2M 2w 2d 2h 2m 2s') # Now with a limit, because here it's just a nightmare to read s = self.helper.print_duration(now + 2 - 120 + 3600*2 + 3600*24*2 + 86400*14 + 86400*56, x_elts=2) print "Result:", s self.assert_(s == 'in 2M 2w') def test_03_get_business_impact_text(self): print "---" # Parameters errors s = self.helper.get_html_business_impact(10) print "Result:", s self.assert_(s == 'n/a') s = self.helper.get_html_business_impact(6, text=False) print "Result:", s self.assert_(s == 'n/a') s = self.helper.get_html_business_impact(-1) print "Result:", s self.assert_(s == 'n/a') s = self.helper.get_html_business_impact(1, False, False) print "Result:", s self.assert_(s == 'n/a') s = self.helper.get_html_business_impact(1, text=False, icon=False) print "Result:", s self.assert_(s == 'n/a') # Default ... text and stars s = self.helper.get_html_business_impact(0) print "Result:", s self.assert_(s == 'None') s = self.helper.get_html_business_impact(1) print "Result:", s self.assert_(s == 'Low') s = self.helper.get_html_business_impact(2) print "Result:", s self.assert_(s == 'Normal') s = self.helper.get_html_business_impact(3) print "Result:", s self.assert_(s == 'Important <i class="fa fa-star text-primary"></i>') s = self.helper.get_html_business_impact(4) print "Result:", s self.assert_(s == 'Very important <i class="fa fa-star text-primary"></i><i class="fa fa-star text-primary"></i>') s = self.helper.get_html_business_impact(5) print "Result:", s self.assert_(s == 'Business critical <i class="fa fa-star text-primary"></i><i class="fa fa-star text-primary"></i><i class="fa fa-star text-primary"></i>') # Stars only ... s = self.helper.get_html_business_impact(0, text=False) print "Result:", s self.assert_(s == '') s = self.helper.get_html_business_impact(1, text=False) print "Result:", s self.assert_(s == '') s = self.helper.get_html_business_impact(2, text=False) print "Result:", s self.assert_(s == '') s = self.helper.get_html_business_impact(3, text=False) print "Result:", s self.assert_(s == '<i class="fa fa-star text-primary"></i>') s = self.helper.get_html_business_impact(4, text=False) print "Result:", s self.assert_(s == '<i class="fa fa-star text-primary"></i><i class="fa fa-star text-primary"></i>') s = self.helper.get_html_business_impact(5, text=False) print "Result:", s self.assert_(s == '<i class="fa fa-star text-primary"></i><i class="fa fa-star text-primary"></i><i class="fa fa-star text-primary"></i>') # Text only ... s = self.helper.get_html_business_impact(0, icon=False) print "Result:", s self.assert_(s == 'None') s = self.helper.get_html_business_impact(1, icon=False) print "Result:", s self.assert_(s == 'Low') s = self.helper.get_html_business_impact(2, icon=False) print "Result:", s self.assert_(s == 'Normal') s = self.helper.get_html_business_impact(3, icon=False) print "Result:", s self.assert_(s == 'Important') s = self.helper.get_html_business_impact(4, icon=False) print "Result:", s self.assert_(s == 'Very important') s = self.helper.get_html_business_impact(5, icon=False) def test_04_get_state_text(self): print "---" print "Parameters errors:" s = self.helper.get_html_state('', '') print "Result:", s self.assert_(s == 'n/a') s = self.helper.get_html_state('host', 'bad state') print "Result:", s self.assert_(s == 'n/a') s = self.helper.get_html_state('service', 'bad state') print "Result:", s self.assert_(s == 'n/a') s = self.helper.get_html_state('host', 'UP', extra='bad state') print "Result:", s self.assert_(s == 'n/a') s = self.helper.get_html_state('service', 'OK', extra='bad state') print "Result:", s self.assert_(s == 'n/a') s = self.helper.get_html_state('host', 'UP', icon=False, text=False) print "Result:", s self.assert_(s == 'n/a') s = self.helper.get_html_state('service', 'OK', icon=False, text=False) print "Result:", s self.assert_(s == 'n/a') def test_05_get_host_state_text(self): print "---" # Host, icon only (default) ... s = self.helper.get_html_state('host', 'UP') print "Result:", s self.assert_(s == '<center class="font-up"><span class="fa-stack" title="host state is UP"><i class="fa fa-circle fa-stack-2x font-up"></i><i class="fa fa-server fa-stack-1x fa-inverse"></i></span><div></div></center>') s = self.helper.get_html_state('host', 'DOWN') print "Result:", s self.assert_(s == '<center class="font-down"><span class="fa-stack" title="host state is DOWN"><i class="fa fa-circle fa-stack-2x font-down"></i><i class="fa fa-server fa-stack-1x fa-inverse"></i></span><div></div></center>') s = self.helper.get_html_state('host', 'UNREACHABLE') print "Result:", s self.assert_(s == '<center class="font-unreachable"><span class="fa-stack" title="host state is UNREACHABLE"><i class="fa fa-circle fa-stack-2x font-unreachable"></i><i class="fa fa-server fa-stack-1x fa-inverse"></i></span><div></div></center>') s = self.helper.get_html_state('host', 'UP', extra='FLAPPING') print "Result:", s self.assert_(s == '<center class="font-up"><span class="fa-stack" title="host state is UP and flapping"><i class="fa fa-circle fa-stack-2x font-up"></i><i class="fa fa-server fa-stack-1x font-up"></i></span><div></div></center>') s = self.helper.get_html_state('host', 'DOWN', extra='FLAPPING') print "Result:", s self.assert_(s == '<center class="font-down"><span class="fa-stack" title="host state is DOWN and flapping"><i class="fa fa-circle fa-stack-2x font-down"></i><i class="fa fa-server fa-stack-1x font-down"></i></span><div></div></center>') s = self.helper.get_html_state('host', 'DOWN', extra='ACKNOWLEDGED') print "Result:", s self.assert_(s == '<center class="font-down"><span class="fa-stack" style="opacity: 0.5" title="host state is DOWN and acknowledged"><i class="fa fa-circle fa-stack-2x font-down"></i><i class="fa fa-server fa-stack-1x fa-inverse"></i></span><div></div></center>') s = self.helper.get_html_state('host', 'DOWN', extra='IN_DOWNTIME') print "Result:", s self.assert_(s == '<center class="font-down"><span class="fa-stack" style="opacity: 0.5" title="host state is DOWN but downtime is scheduled"><i class="fa fa-circle fa-stack-2x font-down"></i><i class="fa fa-server fa-stack-1x fa-inverse"></i></span><div></div></center>') # Host, text only ... s = self.helper.get_html_state('host', 'UP', text=True, icon=False) print "Result:", s self.assert_(s == 'host state is UP') s = self.helper.get_html_state('host', 'DOWN', text=True, icon=False) print "Result:", s self.assert_(s == 'host state is DOWN') s = self.helper.get_html_state('host', 'UNREACHABLE', text=True, icon=False) print "Result:", s self.assert_(s == 'host state is UNREACHABLE') s = self.helper.get_html_state('host', 'UP', extra='FLAPPING', text=True, icon=False) print "Result:", s self.assert_(s == 'host state is UP and flapping') s = self.helper.get_html_state('host', 'DOWN', extra='FLAPPING', text=True, icon=False) print "Result:", s self.assert_(s == 'host state is DOWN and flapping') s = self.helper.get_html_state('host', 'DOWN', extra='ACKNOWLEDGED', text=True, icon=False) print "Result:", s self.assert_(s == 'host state is DOWN and acknowledged') s = self.helper.get_html_state('host', 'DOWN', extra='IN_DOWNTIME', text=True, icon=False) print "Result:", s self.assert_(s == 'host state is DOWN but downtime is scheduled') # Host, icon and text ... s = self.helper.get_html_state('host', 'UP', text=True, icon=True) print "Result:", s self.assert_(s == '<center class="font-up"><span class="fa-stack" title="host state is UP"><i class="fa fa-circle fa-stack-2x font-up"></i><i class="fa fa-server fa-stack-1x fa-inverse"></i></span><div>host state is UP</div></center>') s = self.helper.get_html_state('host', 'DOWN', text=True, icon=True) print "Result:", s self.assert_(s == '<center class="font-down"><span class="fa-stack" title="host state is DOWN"><i class="fa fa-circle fa-stack-2x font-down"></i><i class="fa fa-server fa-stack-1x fa-inverse"></i></span><div>host state is DOWN</div></center>') s = self.helper.get_html_state('host', 'UNREACHABLE', text=True, icon=True) print "Result:", s self.assert_(s == '<center class="font-unreachable"><span class="fa-stack" title="host state is UNREACHABLE"><i class="fa fa-circle fa-stack-2x font-unreachable"></i><i class="fa fa-server fa-stack-1x fa-inverse"></i></span><div>host state is UNREACHABLE</div></center>') s = self.helper.get_html_state('host', 'UP', extra='FLAPPING', text=True, icon=True) print "Result:", s self.assert_(s == '<center class="font-up"><span class="fa-stack" title="host state is UP and flapping"><i class="fa fa-circle fa-stack-2x font-up"></i><i class="fa fa-server fa-stack-1x font-up"></i></span><div>host state is UP and flapping</div></center>') s = self.helper.get_html_state('host', 'DOWN', extra='FLAPPING', text=True, icon=True) print "Result:", s self.assert_(s == '<center class="font-down"><span class="fa-stack" title="host state is DOWN and flapping"><i class="fa fa-circle fa-stack-2x font-down"></i><i class="fa fa-server fa-stack-1x font-down"></i></span><div>host state is DOWN and flapping</div></center>') s = self.helper.get_html_state('host', 'DOWN', extra='ACKNOWLEDGED', text=True, icon=True) print "Result:", s self.assert_(s == '<center class="font-down"><span class="fa-stack" style="opacity: 0.5" title="host state is DOWN and acknowledged"><i class="fa fa-circle fa-stack-2x font-down"></i><i class="fa fa-server fa-stack-1x fa-inverse"></i></span><div>host state is DOWN and acknowledged</div></center>') s = self.helper.get_html_state('host', 'DOWN', extra='IN_DOWNTIME', text=True, icon=True) print "Result:", s self.assert_(s == '<center class="font-down"><span class="fa-stack" style="opacity: 0.5" title="host state is DOWN but downtime is scheduled"><i class="fa fa-circle fa-stack-2x font-down"></i><i class="fa fa-server fa-stack-1x fa-inverse"></i></span><div>host state is DOWN but downtime is scheduled</div></center>') def test_06_get_service_state_text(self): print "---" # service, icon only (default) ... s = self.helper.get_html_state('service', 'OK') print "Result:", s self.assert_(s == '<center class="font-ok"><span class="fa-stack" title="service state is OK"><i class="fa fa-circle fa-stack-2x font-ok"></i><i class="fa fa-arrow-up fa-stack-1x fa-inverse"></i></span><div></div></center>') s = self.helper.get_html_state('service', 'CRITICAL') print "Result:", s self.assert_(s == '<center class="font-critical"><span class="fa-stack" title="service state is CRITICAL"><i class="fa fa-circle fa-stack-2x font-critical"></i><i class="fa fa-arrow-down fa-stack-1x fa-inverse"></i></span><div></div></center>') s = self.helper.get_html_state('service', 'WARNING') print "Result:", s self.assert_(s == '<center class="font-warning"><span class="fa-stack" title="service state is WARNING"><i class="fa fa-circle fa-stack-2x font-warning"></i><i class="fa fa-exclamation fa-stack-1x fa-inverse"></i></span><div></div></center>') s = self.helper.get_html_state('service', 'UNKNOWN') print "Result:", s self.assert_(s == '<center class="font-unknown"><span class="fa-stack" title="service state is UNKNOWN"><i class="fa fa-circle fa-stack-2x font-unknown"></i><i class="fa fa-question fa-stack-1x fa-inverse"></i></span><div></div></center>') s = self.helper.get_html_state('service', 'ok', extra='FLAPPING') print "Result:", s self.assert_(s == '<center class="font-ok"><span class="fa-stack" title="service state is OK and flapping"><i class="fa fa-circle fa-stack-2x font-ok"></i><i class="fa fa-arrow-up fa-stack-1x font-ok"></i></span><div></div></center>') s = self.helper.get_html_state('service', 'CRITICAL', extra='FLAPPING') print "Result:", s self.assert_(s == '<center class="font-critical"><span class="fa-stack" title="service state is CRITICAL and flapping"><i class="fa fa-circle fa-stack-2x font-critical"></i><i class="fa fa-arrow-down fa-stack-1x font-critical"></i></span><div></div></center>') s = self.helper.get_html_state('service', 'CRITICAL', extra='ACKNOWLEDGED') print "Result:", s self.assert_(s == '<center class="font-critical"><span class="fa-stack" style="opacity: 0.5" title="service state is CRITICAL and acknowledged"><i class="fa fa-circle fa-stack-2x font-critical"></i><i class="fa fa-arrow-down fa-stack-1x fa-inverse"></i></span><div></div></center>') s = self.helper.get_html_state('service', 'CRITICAL', extra='IN_DOWNTIME') print "Result:", s self.assert_(s == '<center class="font-critical"><span class="fa-stack" style="opacity: 0.5" title="service state is CRITICAL but downtime is scheduled"><i class="fa fa-circle fa-stack-2x font-critical"></i><i class="fa fa-arrow-down fa-stack-1x fa-inverse"></i></span><div></div></center>') # service, text only ... s = self.helper.get_html_state('service', 'OK', text=True, icon=False) print "Result:", s self.assert_(s == 'service state is OK') s = self.helper.get_html_state('service', 'CRITICAL', text=True, icon=False) print "Result:", s self.assert_(s == 'service state is CRITICAL') s = self.helper.get_html_state('service', 'WARNING', text=True, icon=False) print "Result:", s self.assert_(s == 'service state is WARNING') s = self.helper.get_html_state('service', 'UNKNOWN', text=True, icon=False) print "Result:", s self.assert_(s == 'service state is UNKNOWN') s = self.helper.get_html_state('service', 'OK', extra='FLAPPING', text=True, icon=False) print "Result:", s self.assert_(s == 'service state is OK and flapping') s = self.helper.get_html_state('service', 'CRITICAL', extra='FLAPPING', text=True, icon=False) print "Result:", s self.assert_(s == 'service state is CRITICAL and flapping') s = self.helper.get_html_state('service', 'CRITICAL', extra='ACKNOWLEDGED', text=True, icon=False) print "Result:", s self.assert_(s == 'service state is CRITICAL and acknowledged') s = self.helper.get_html_state('service', 'CRITICAL', extra='IN_DOWNTIME', text=True, icon=False) print "Result:", s self.assert_(s == 'service state is CRITICAL but downtime is scheduled') # service, icon and text ... s = self.helper.get_html_state('service', 'OK', text=True, icon=True) print "Result:", s self.assert_(s == '<center class="font-ok"><span class="fa-stack" title="service state is OK"><i class="fa fa-circle fa-stack-2x font-ok"></i><i class="fa fa-arrow-up fa-stack-1x fa-inverse"></i></span><div>service state is OK</div></center>') s = self.helper.get_html_state('service', 'CRITICAL', text=True, icon=True) print "Result:", s self.assert_(s == '<center class="font-critical"><span class="fa-stack" title="service state is CRITICAL"><i class="fa fa-circle fa-stack-2x font-critical"></i><i class="fa fa-arrow-down fa-stack-1x fa-inverse"></i></span><div>service state is CRITICAL</div></center>') s = self.helper.get_html_state('service', 'WARNING', text=True, icon=True) print "Result:", s self.assert_(s == '<center class="font-warning"><span class="fa-stack" title="service state is WARNING"><i class="fa fa-circle fa-stack-2x font-warning"></i><i class="fa fa-exclamation fa-stack-1x fa-inverse"></i></span><div>service state is WARNING</div></center>') s = self.helper.get_html_state('service', 'UNKNOWN', text=True, icon=True) print "Result:", s self.assert_(s == '<center class="font-unknown"><span class="fa-stack" title="service state is UNKNOWN"><i class="fa fa-circle fa-stack-2x font-unknown"></i><i class="fa fa-question fa-stack-1x fa-inverse"></i></span><div>service state is UNKNOWN</div></center>') s = self.helper.get_html_state('service', 'ok', extra='FLAPPING', text=True, icon=True) print "Result:", s self.assert_(s == '<center class="font-ok"><span class="fa-stack" title="service state is OK and flapping"><i class="fa fa-circle fa-stack-2x font-ok"></i><i class="fa fa-arrow-up fa-stack-1x font-ok"></i></span><div>service state is OK and flapping</div></center>') s = self.helper.get_html_state('service', 'CRITICAL', extra='FLAPPING', text=True, icon=True) print "Result:", s self.assert_(s == '<center class="font-critical"><span class="fa-stack" title="service state is CRITICAL and flapping"><i class="fa fa-circle fa-stack-2x font-critical"></i><i class="fa fa-arrow-down fa-stack-1x font-critical"></i></span><div>service state is CRITICAL and flapping</div></center>') s = self.helper.get_html_state('service', 'CRITICAL', extra='ACKNOWLEDGED', text=True, icon=True) print "Result:", s self.assert_(s == '<center class="font-critical"><span class="fa-stack" style="opacity: 0.5" title="service state is CRITICAL and acknowledged"><i class="fa fa-circle fa-stack-2x font-critical"></i><i class="fa fa-arrow-down fa-stack-1x fa-inverse"></i></span><div>service state is CRITICAL and acknowledged</div></center>') s = self.helper.get_html_state('service', 'CRITICAL', extra='IN_DOWNTIME', text=True, icon=True) print "Result:", s self.assert_(s == '<center class="font-critical"><span class="fa-stack" style="opacity: 0.5" title="service state is CRITICAL but downtime is scheduled"><i class="fa fa-circle fa-stack-2x font-critical"></i><i class="fa fa-arrow-down fa-stack-1x fa-inverse"></i></span><div>service state is CRITICAL but downtime is scheduled</div></center>') # Use label instead of built text ... s = self.helper.get_html_state('service', 'OK', text=True, icon=True, label='My own label') print "Result:", s self.assert_(s == '<center class="font-ok"><span class="fa-stack" title="service state is OK"><i class="fa fa-circle fa-stack-2x font-ok"></i><i class="fa fa-arrow-up fa-stack-1x fa-inverse"></i></span><div>My own label</div></center>') # Specify disabled state ... s = self.helper.get_html_state('service', 'OK', text=True, icon=True, label='My own label', disabled=True) print "Result:", s self.assert_(s == '<center class="font-ok"><span class="fa-stack" title="service state is OK"><i class="fa fa-circle fa-stack-2x font-greyed"></i><i class="fa fa-arrow-up fa-stack-1x fa-inverse"></i></span><div>My own label</div></center>') def test_07_get_url(self): print "---" print "Parameters errors:" s = self.helper.get_html_url(None, None) print "Result:", s self.assert_(s == 'n/a') s = self.helper.get_html_url('', '') print "Result:", s self.assert_(s == 'n/a') s = self.helper.get_html_url('host', None) print "Result:", s self.assert_(s == 'n/a') s = self.helper.get_html_url('bad_type', 'bad') print "Result:", s self.assert_(s == '<a href="/bad_type/bad" title="bad">bad</a>') s = self.helper.get_html_url('host', 'host1') print "Result:", s self.assert_(s == '<a href="/host/host1" title="host1">host1</a>') s = self.helper.get_html_url('service', 'service1') print "Result:", s self.assert_(s == '<a href="/service/service1" title="service1">service1</a>') s = self.helper.get_html_url('host', 'test', 'My label ...') print "Result:", s self.assert_(s == '<a href="/host/test" title="test">My label ...</a>') s = self.helper.get_html_url('contact', 'bob', 'My label ...') print "Result:", s self.assert_(s == '<a href="/contact/bob" title="bob">My label ...</a>') def test_08_get_id(self): print "---" print "Parameters errors:" s = self.helper.get_html_id(None, None) print "Result:", s self.assert_(s == 'n/a') s = self.helper.get_html_id('', '') print "Result:", s self.assert_(s == 'n/a') s = self.helper.get_html_id('host', None) print "Result:", s self.assert_(s == 'n/a') s = self.helper.get_html_id('bad_type', 'bad') print "Result:", s self.assert_(s == 'bad_type-bad') s = self.helper.get_html_id('host', 'host1') print "Result:", s self.assert_(s == 'host-host1') s = self.helper.get_html_id('service', 'service1') print "Result:", s self.assert_(s == 'service-service1') s = self.helper.get_html_id('host', 'test*/-_+)') print "Result:", s self.assert_(s == 'host-test-_') def test_09_search(self): print "---" # Initialize backend communication ... frontend.configure(alignak_webui.app.config.get('ui.backend', 'http://localhost:5000')) print "Frontend: %s", frontend.url_endpoint_root # Configure users' management backend User.set_backend(frontend) # Force authentication ... connection = frontend.login('admin', 'admin', force=True) assert_true(frontend.authenticated) assert_true(frontend.token) connection = frontend.connect(username='******') assert_true(frontend.authenticated) assert_true(frontend.connected) ls = self.helper.get_livestate() print "Search on element type ..." search = self.helper.search_livestate(ls, "type:all") print "Result, found %d elements" % len(search) search = self.helper.search_livestate(ls, "type:host") print "Result, found %d elements" % len(search) search = self.helper.search_livestate(ls, "type:service") print "Result, found %d elements" % len(search) search = self.helper.search_livestate(ls, "host") print "Result, found %d elements" % len(search) search = self.helper.search_livestate(ls, "service") print "Result, found %d elements" % len(search) search = self.helper.search_livestate(ls, "type:unknown") print "Result, found %d elements" % len(search) print "---" print "Search on element name and content ... " print "found in name ..." search = self.helper.search_livestate(ls, "charnay") print "Result, found %d elements" % len(search) # assert len(search) > 0 print "found in output ..." search = self.helper.search_livestate(ls, "time") print "Result, found %d elements" % len(search) # assert len(search) > 0 print "not found ..." search = self.helper.search_livestate(ls, "test") print "Result, found %d elements" % len(search) print "---" print "Search on element business impact ... " search = self.helper.search_livestate(ls, "bi:0") print "Result, found %d elements" % len(search) # assert len(search) > 0 search = self.helper.search_livestate(ls, "bi:=0") print "Result, found %d elements" % len(search) # assert len(search) > 0 search = self.helper.search_livestate(ls, "bi:>0") print "Result, found %d elements" % len(search) # assert len(search) > 0 search = self.helper.search_livestate(ls, "bi:>=0") print "Result, found %d elements" % len(search) # assert len(search) > 0 search = self.helper.search_livestate(ls, "bi:<5") print "Result, found %d elements" % len(search) # assert len(search) > 0 search = self.helper.search_livestate(ls, "bi:<=5") print "Result, found %d elements" % len(search) # assert len(search) > 0 search = self.helper.search_livestate(ls, "bi:>3") print "Result, found %d elements" % len(search) # assert len(search) > 0 print "---" print "Search on element state ... " search = self.helper.search_livestate(ls, "is:ack") print "Result, found %d elements" % len(search) search = self.helper.search_livestate(ls, "ack:true") print "Result, found %d elements" % len(search) search = self.helper.search_livestate(ls, "ack:yes") print "Result, found %d elements" % len(search) search = self.helper.search_livestate(ls, "ack:1") print "Result, found %d elements" % len(search) # assert len(search) > 0 search = self.helper.search_livestate(ls, "isnot:ack") print "Result, found %d elements" % len(search) search = self.helper.search_livestate(ls, "ack:false") print "Result, found %d elements" % len(search) search = self.helper.search_livestate(ls, "ack:no") print "Result, found %d elements" % len(search) search = self.helper.search_livestate(ls, "ack:0") print "Result, found %d elements" % len(search) # assert len(search) > 0 search = self.helper.search_livestate(ls, "is:downtime") print "Result, found %d elements" % len(search) search = self.helper.search_livestate(ls, "downtime:yes") print "Result, found %d elements" % len(search) search = self.helper.search_livestate(ls, "downtime:true") print "Result, found %d elements" % len(search) search = self.helper.search_livestate(ls, "downtime:1") print "Result, found %d elements" % len(search) search = self.helper.search_livestate(ls, "downtime:false") print "Result, found %d elements" % len(search) search = self.helper.search_livestate(ls, "downtime:no") print "Result, found %d elements" % len(search) search = self.helper.search_livestate(ls, "downtime:0") print "Result, found %d elements" % len(search) assert len(search) > 0 search = self.helper.search_livestate(ls, "isnot:downtime") print "Result, found %d elements" % len(search) assert len(search) > 0 search = self.helper.search_livestate(ls, "is:0") print "Result, found %d elements" % len(search) # assert len(search) > 0 search = self.helper.search_livestate(ls, "is:1") print "Result, found %d elements" % len(search) # assert len(search) > 0 search = self.helper.search_livestate(ls, "is:2") print "Result, found %d elements" % len(search) # assert len(search) > 0 search = self.helper.search_livestate(ls, "is:3") print "Result, found %d elements" % len(search) # assert len(search) > 0 search = self.helper.search_livestate(ls, "is:up") print "Result, found %d elements" % len(search) search = self.helper.search_livestate(ls, "up") print "Result, found %d elements" % len(search) # assert len(search) > 0 search = self.helper.search_livestate(ls, "is:down") print "Result, found %d elements" % len(search) search = self.helper.search_livestate(ls, "down") print "Result, found %d elements" % len(search) # assert len(search) > 0 search = self.helper.search_livestate(ls, "is:unreachable") print "Result, found %d elements" % len(search) search = self.helper.search_livestate(ls, "unreachable") print "Result, found %d elements" % len(search) # assert len(search) > 0 search = self.helper.search_livestate(ls, "is:ok") print "Result, found %d elements" % len(search) search = self.helper.search_livestate(ls, "OK") print "Result, found %d elements" % len(search) # assert len(search) > 0 search = self.helper.search_livestate(ls, "is:warning") print "Result, found %d elements" % len(search) search = self.helper.search_livestate(ls, "WARNING") print "Result, found %d elements" % len(search) # assert len(search) > 0 search = self.helper.search_livestate(ls, "is:critical") print "Result, found %d elements" % len(search) search = self.helper.search_livestate(ls, "CRITICAL") print "Result, found %d elements" % len(search) # assert len(search) > 0 search = self.helper.search_livestate(ls, "is:unknown") print "Result, found %d elements" % len(search) search = self.helper.search_livestate(ls, "UNKNOWN") print "Result, found %d elements" % len(search) # assert len(search) > 0 search = self.helper.search_livestate(ls, "is:pending") print "Result, found %d elements" % len(search) search = self.helper.search_livestate(ls, "PENDING") print "Result, found %d elements" % len(search) # assert len(search) > 0 search = self.helper.search_livestate(ls, "isnot:up") print "Result, found %d elements" % len(search) # assert len(search) > 0 search = self.helper.search_livestate(ls, "isnot:down") print "Result, found %d elements" % len(search) # assert len(search) > 0 search = self.helper.search_livestate(ls, "isnot:unreachable") print "Result, found %d elements" % len(search) # assert len(search) > 0 search = self.helper.search_livestate(ls, "isnot:ok") print "Result, found %d elements" % len(search) # assert len(search) > 0 search = self.helper.search_livestate(ls, "isnot:warning") print "Result, found %d elements" % len(search) # assert len(search) > 0 search = self.helper.search_livestate(ls, "isnot:critical") print "Result, found %d elements" % len(search) # assert len(search) > 0 search = self.helper.search_livestate(ls, "isnot:unknown") print "Result, found %d elements" % len(search) # assert len(search) > 0 search = self.helper.search_livestate(ls, "isnot:pending") print "Result, found %d elements" % len(search) # assert len(search) > 0 # Backend disconnection frontend.disconnect() def test_10_livesynthesis(self): print "---" # Initialize backend communication ... frontend.configure(alignak_webui.app.config.get('ui.backend', 'http://localhost:5000')) print "Frontend: %s", frontend.url_endpoint_root # Configure users' management backend User.set_backend(frontend) # Force authentication ... connection = frontend.login('admin', 'admin', force=True) assert_true(frontend.authenticated) assert_true(frontend.token) connection = frontend.connect(username='******') assert_true(frontend.authenticated) assert_true(frontend.connected) print "Get live synthesis ..." synthesis = self.helper.get_livesynthesis() print "Result:", synthesis assert_true('hosts_synthesis' in synthesis) assert_true('nb_elts' in synthesis['hosts_synthesis']) assert_true('services_synthesis' in synthesis) assert_true('nb_elts' in synthesis['services_synthesis']) print "Get HTML live synthesis ..." synthesis = self.helper.get_html_livesynthesis() print "Result:", synthesis assert 'hosts_states_popover' in synthesis assert 'host state is UP' in synthesis['hosts_states_popover'] assert 'host state is DOWN' in synthesis['hosts_states_popover'] assert 'host state is UNREACHABLE' in synthesis['hosts_states_popover'] assert 'hosts_state' in synthesis # assert False assert 'services_states_popover' in synthesis assert 'service state is OK' in synthesis['services_states_popover'] assert 'service state is WARNING' in synthesis['services_states_popover'] assert 'service state is CRITICAL' in synthesis['services_states_popover'] assert 'services_state' in synthesis # Backend disconnection frontend.disconnect() def test_11_livestate(self): print "---" # Initialize backend communication ... frontend.configure(alignak_webui.app.config.get('ui.backend', 'http://localhost:5000')) print "Frontend: %s", frontend.url_endpoint_root # Configure users' management backend User.set_backend(frontend) # Force authentication ... connection = frontend.login('admin', 'admin', force=True) assert_true(frontend.authenticated) assert_true(frontend.token) connection = frontend.connect(username='******') assert_true(frontend.authenticated) assert_true(frontend.connected) print "Get live state ..." print "Livestate_age: ", self.helper.livestate_age ls = self.helper.get_livestate() print "Livestate_age: ", self.helper.livestate_age print "Livestate: ", self.helper.livestate assert self.helper.livestate_age for item in ls: print "Item:", item assert_true('type' in item) assert_true('id' in item) assert_true('bi' in item) assert_true('name' in item) assert_true('friendly_name' in item) for item in self.helper.livestate: print "Item:", item assert_true('type' in item) assert_true('id' in item) assert_true('bi' in item) assert_true('name' in item) assert_true('friendly_name' in item) assert len(ls) == len(self.helper.livestate) print "Get HTML live state ..." print "Current user: "******"Items:", len(html['rows']) for bi in [0,1,2,3,4,5]: print "Get HTML live state (BI = %d) ..." % bi html = self.helper.get_html_livestate(bi=bi) assert 'bi' in html assert 'rows' in html assert 'panel_bi' in html print "Items:", len(html['rows']) / 2 # for row in html['rows']: # print "Item:", row print "Get HTML live state ... filter" html = self.helper.get_html_livestate(search_filter="type:host") assert 'bi' in html assert 'rows' in html assert 'panel_bi' in html print "Items:", len(html['rows']) for bi in [0,1,2,3,4,5]: print "Get HTML live state (BI = %d) ...and filter" % bi html = self.helper.get_html_livestate(bi=bi, search_filter="type:host") assert 'bi' in html assert 'rows' in html assert 'panel_bi' in html print "Items:", len(html['rows']) # Backend disconnection frontend.disconnect()
def get_service_timeline(self, element_id, widget_id='timeline', embedded=False, identifier=None, credentials=None): # pylint: disable=unused-argument, too-many-locals """Display a service timeline widget""" user = request.environ['beaker.session']['current_user'] datamgr = request.app.datamgr # Get service service = datamgr.get_service(element_id) if not service: return self.webui.response_invalid_parameters( _('Service does not exist')) # Search the required widget widget_place = request.params.get('widget_place', 'service') widget_template = request.params.get('widget_template', 'service_widget') # Search in the application widgets (all plugins widgets) for widget in self.webui.get_widgets_for(widget_place): if widget_id.startswith(widget['id']): widget_template = widget['template'] logger.info("Widget found, template: %s", widget_template) break else: logger.info( "Widget identifier not found: using default template and no options" ) logger.debug("get_service_timeline: found template: %s", widget_template) # Fetch elements per page preference for user, default is 25 elts_per_page = datamgr.get_user_preferences(user, 'elts_per_page', 25) # service history pagination and search parameters start = int(request.params.get('start', '0')) count = int(request.params.get('count', elts_per_page)) where = {'service': service.id} # Find known history types history_plugin = self.webui.find_plugin('Histories') if history_plugin: decoded_search = Helper.decode_search( request.params.get('search', ''), history_plugin.table) logger.info("Decoded search: %s", decoded_search) if decoded_search: where.update(decoded_search) search = { 'page': (start // count) + 1, 'max_results': count, 'where': where } # Get service history history = datamgr.get_history(search=search) if history is None: history = [] total = 0 if history: total = history[0]['_total'] # Search filters used for the timeline widget events_search_filters = { '01': (_('Web UI comments'), 'type:webui.comment'), '02': (_('Check results'), 'type:check.'), '03': (_('Alerts'), 'type:monitoring.alert'), '04': (_('Acknowledges'), 'type:monitoring.ack'), '05': (_('Downtimes'), 'type:monitoring.downtime'), '06': (_('Notifications'), 'type:monitoring.notification'), '07': ('', ''), } # Render the widget return template( '_widget', { 'widget_id': widget_id, 'widget_name': widget_template, 'widget_place': 'user', 'widget_template': widget_template, 'widget_uri': request.urlparts.path, 'options': {}, 'plugin_parameters': self.plugin_parameters, 'search_engine': True, 'search_filters': events_search_filters, 'service': service, 'history': history, 'pagination': self.webui.helper.get_pagination_control( '/service/%s#service_%s' % (service.id, widget_id), total, start, count), 'title': None, 'embedded': embedded, 'identifier': identifier, 'credentials': credentials })
def table_data(self, plugin_table): # Because there are many locals needed :) # pylint: disable=too-many-locals """Return elements data in json format as of Datatables SSP protocol More info: https://datatables.net/manual/server-side Example URL:: POST /? draw=1& columns[0][data]=alias& columns[0][name]=& columns[0][searchable]=true& columns[0][orderable]=true& columns[0][search][value]=& columns[0][search][regex]=false& ... order[0][column]=0& order[0][dir]=asc& start=0& length=10& search[value]=& search[regex]=false& Request parameters are Json formatted Request Parameters: - object_type: object type managed by the datatable - links: url prefix to be used by the links in the table - embedded: true / false whether the table is embedded by an external application **Note**: those three first parameters are not datatable specific parameters :) - draw, index parameter to be returned in the response Pagination: - start / length, for pagination Searching: - search (value or regexp) search[value]: Global search value. To be applied to all columns which are searchable search[regex]: true if search[value] is a regex Sorting: - order[i][column] / order[i][dir] index of the columns to order and sort direction (asc/desc) Columns: - columns[i][data]: Column's data source, as defined by columns.data. - columns[i][name]: Column's name, as defined by columns.name. - columns[i][searchable]: Flag to indicate if this column is searchable (true). - columns[i][orderable]: Flag to indicate if this column is orderable (true). - columns[i][search][value]: Search value to apply to this specific column. - columns[i][search][regex]: Flag to indicate if the search term for this column is a regex. Response data: - draw - recordsTotal: total records, before filtering (i.e. total number of records in the database) - recordsFiltered: Total records, after filtering (i.e. total number of records after filtering has been applied - not just the number of records being returned for this page of data). - data: The data to be displayed in the table. an array of data source objects, one for each row, which will be used by DataTables. - error (optional): Error message if an error occurs Not included if there is no error. """ # Manage request parameters ... logger.info("request data for table: %s, templates: %s", request.forms.get('object_type'), self.templates) # Because of specific datatables parameters name (eg. columns[0] ...) # ... some parameters have been json.stringify on client side ! params = {} for key in list(request.params.keys()): if key in ['columns', 'order', 'search']: params[key] = json.loads(request.params.get(key)) else: params[key] = request.params.get(key) # params now contains 'valid' query parameters as we should have found them ... logger.debug("table request parameters: %s", params) parameters = {} # Manage page number ... # start is the first requested row and we must transform to a page count ... first_row = int(params.get('start', '0')) # length is the number of requested rows rows_count = int(params.get('length', '25')) parameters['page'] = (first_row // rows_count) + 1 parameters['max_results'] = rows_count logger.debug("get %d rows from row #%d -> page: %d", rows_count, first_row, parameters['page']) # Columns ordering # order:[{"column":2,"dir":"desc"}] if 'order' in params and 'columns' in params and params['order']: sorted_columns = [] for order in params['order']: idx = int(order['column']) if params['columns'][idx] and params['columns'][idx]['data']: logger.debug( "sort by column %d (%s), order: %s ", idx, params['columns'][idx]['data'], order['dir'] ) if order['dir'] == 'desc': sorted_columns.append('-' + params['columns'][idx]['data']) else: sorted_columns.append(params['columns'][idx]['data']) if sorted_columns: parameters['sort'] = ','.join(sorted_columns) logger.info("backend order request parameters: %s", parameters) # Individual column search parameter s_columns = [] if 'columns' in params and params['columns']: for column in params['columns']: if 'searchable' not in column or 'search' not in column: # pragma: no cover continue if 'value' not in column['search'] or not column['search']['value']: continue logger.debug("search column '%s' for '%s'", column['data'], column['search']['value']) for field in self.table_columns: if field['data'] != column['data']: continue # Some specific types... if field['type'] == 'boolean': s_columns.append( {column['data']: column['search']['value'] == 'true'} ) elif field['type'] == 'integer': s_columns.append( {column['data']: int(column['search']['value'])} ) elif field['format'] == 'select': values = column['search']['value'].split(',') if len(values) > 1: s_columns.append( { column['data']: { "$in": values } } ) else: s_columns.append( {column['data']: values[0]} ) # ... the other fields :) else: # Do not care about 'smart' and 'caseInsensitive' boolean parameters ... if column['search']['regex']: s_columns.append( { column['data']: { "$regex": ".*" + column['search']['value'] + ".*" } } ) else: s_columns.append( {column['data']: column['search']['value']} ) break logger.info("backend search individual columns parameters: %s", s_columns) # Global search parameter # search:{"value":"test","regex":false} s_global = {} # pylint: disable=too-many-nested-blocks # Will be too complex else ... if 'search' in params and 'columns' in params and params['search']: # params['search'] contains something like: {u'regex': False, u'value': u'name:pi1'} # global regex is always ignored ... in favor of the column declared regex logger.info("global search requested: %s ", params['search']) if 'value' in params['search'] and params['search']['value']: # There is something to search for... logger.debug("search requested, value: %s ", params['search']['value']) # New strategy: decode search patterns... search = Helper.decode_search(params['search']['value'], plugin_table) logger.info("decoded search pattern: %s", search) # Old strategy: search for the value in all the searchable columns... # if not search: # logger.info("applying datatable search pattern...") # for column in params['columns']: # if not column['searchable']: # continue # logger.debug("search global '%s' for '%s'", # column['data'], params['search']['value']) # if 'regex' in params['search']: # if params['search']['regex']: # s_global.update( # {column['data']: { # "$regex": ".*" + params['search']['value'] + ".*"}}) # else: # s_global.update({column['data']: params['search']['value']}) s_global = search logger.info("backend search global parameters: %s", s_global) # Specific hack to filter the log check results that are not dated! if self.object_type == 'logcheckresult': s_columns.append({"last_check": {"$ne": 0}}) if s_columns and s_global: parameters['where'] = {"$and": [{"$and": s_columns}, s_global]} elif s_columns: parameters['where'] = {"$and": s_columns} elif s_global: parameters['where'] = s_global # Embed linked resources / manage templated resources parameters['embedded'] = {} for field in self.embedded: parameters['embedded'].update({field: 1}) logger.debug("backend embedded parameters: %s", parameters['embedded']) # Count total elements excluding templates if necessary if self.is_templated: if self.ui_visibility: self.records_total = self.datamgr.my_backend.count( self.object_type, params={'where': {'_is_template': self.templates, 'webui_visible': True}} ) else: self.records_total = self.datamgr.my_backend.count( self.object_type, params={'where': {'_is_template': self.templates}} ) if 'where' in parameters: parameters['where'].update({'_is_template': self.templates}) else: parameters['where'] = {'_is_template': self.templates} else: if self.ui_visibility: self.records_total = self.datamgr.my_backend.count( self.object_type, params={'where': {'webui_visible': True}} ) else: self.records_total = self.datamgr.my_backend.count(self.object_type) if self.ui_visibility: if 'where' in parameters: parameters['where'].update({'webui_visible': True}) else: parameters['where'] = {'webui_visible': True} # Request objects from the backend ... logger.info("table data get parameters: %s", parameters) items = self.datamgr.my_backend.get(self.object_type, params=parameters) logger.info("table data got %d items", len(items)) if not items: logger.info("No backend elements match search criteria: %s", parameters) # Empty response return json.dumps({ # draw is the request number ... "draw": int(params.get('draw', '0')), "recordsTotal": 0, "recordsFiltered": 0, "data": [] }) # Create an object ... object_class = [kc for kc in self.datamgr.known_classes if kc.get_type() == self.object_type] if not object_class: # pragma: no cover, should never happen! logger.warning("datatable, unknown object type: %s", self.object_type) # Empty response return json.dumps({ # draw is the request number ... "draw": int(params.get('draw', '0')), "recordsTotal": 0, "recordsFiltered": 0, "data": [] }) # Update table inner properties with the object class defined properties object_class = object_class[0] self.id_property = '_id' if hasattr(object_class, 'id_property'): self.id_property = object_class.id_property self.name_property = 'name' if hasattr(object_class, 'name_property'): self.name_property = object_class.name_property self.status_property = 'status' if hasattr(object_class, 'status_property'): self.status_property = object_class.status_property logger.debug("datatable, object type: '%s' and properties: %s / %s / %s", object_class, self.id_property, self.name_property, self.status_property) # Change item content... rows = [] # Total number of filtered records self.records_filtered = self.records_total for item in items: bo_object = object_class(item) if not bo_object.ui_visible: logger.debug("Not UI visible object: %s", bo_object) continue logger.info("table data object: %s", bo_object) logger.debug("table data object: %s", bo_object) # This is an awful hack that allows to update the objects filtered for a table. # Two main interests: # - update the backend because some massive modifications are necessary for testing # - prepare a massive update feature in the Web UI :) # ----- # if self.is_templated and self.templates and self.object_type == 'service': # logger.warning("service template: %s for host: %s", # bo_object.name, bo_object['host']) # if bo_object['host'].name.startswith('fdj'): # logger.info("To be updated...") # data = { # "check_freshness": True, # "freshness_threshold": 86400, # "passive_checks_enabled": True, # "active_checks_enabled": False # } # result = self.datamgr.update_object(element=bo_object, data=data) # if result is True: # logger.info("updated.") # else: # logger.error("update failed!") # ----- # Each item contains the total number of records matching the search filter self.records_filtered = item['_total'] row = {} row['DT_RowData'] = {} row['_id'] = bo_object.id for field in self.table_columns: logger.debug(" - field: %s", field) # Specific fields if field['data'] == self.name_property: # Create a link to navigate to the item page row[self.name_property] = bo_object.html_link # Store the item name in a specific field of the row. # The value will be retrieved by the table actions (ack, recheck, ...) row['DT_RowData'].update({"object_%s" % self.object_type: bo_object.name}) continue if field['data'] == self.status_property: # Replace the text status with the specific item HTML state row[self.status_property] = bo_object.get_html_state(text=None, title=bo_object.status) # Use the item status to specify the table row class # row['DT_RowClass'] = "table-row-%s" % (bo_object.status.lower()) continue if field['data'] in ['_overall_state_id', 'overall_state', 'overall_status']: # Get the item overall state from the data manager f_get_overall_state = getattr(self.datamgr, 'get_%s_overall_state' % self.object_type) if f_get_overall_state: (dummy, overall_status) = f_get_overall_state(bo_object) # Get element state configuration row[field['data']] = ElementState().get_html_state( self.object_type, bo_object, text=None, title=bo_object.overall_state_to_title[bo_object.overall_state], use_status=overall_status ) # Use the item overall state to specify the table row class row['DT_RowClass'] = "table-row-%s" % (overall_status) else: # pragma: no cover, should never happen! logger.warning("Missing get_overall_state method for: %s", self.object_type) row[field['data']] = 'XxX' continue if "business_impact" in field['data']: # Replace the BI count with the specific item HTML formatting row[field['data']] = Helper.get_html_business_impact(bo_object.business_impact) continue # Specific fields type if field['type'] == 'datetime' or field['format'] == 'datetime': # Replace the timestamp with the formatted date row[field['data']] = bo_object.get_date(bo_object[field['data']]) continue if field['type'] == 'boolean': # Replace the boolean vaue with the specific item HTML formatting row[field['data']] = Helper.get_on_off(bo_object[field['data']]) continue if field['type'] == 'list': # Replace the list with the specific list HTML formatting if hasattr(bo_object, field['data']): row[field['data']] = Helper.get_html_item_list( bo_object.id, field['data'], getattr(bo_object, field['data']), title=field['title'] ) else: row[field['data']] = 'Unknown' continue if field['type'] == 'dict': # Replace the dictionary with the specific dict HTML formatting row[field['data']] = Helper.get_html_item_list( bo_object.id, field['data'], getattr(bo_object, field['data']), title=field['title'] ) continue if field['type'] == 'objectid': if isinstance(bo_object[field['data']], BackendElement): row[field['data']] = bo_object[field['data']].get_html_link( prefix=request.params.get('links') ) # row['DT_RowData'].update({ # "object_%s" % field['data']: bo_object[field['data']].name # }) else: logger.warning("Table field is supposed to be an object: %s, %s = %s", bo_object.name, field['data'], getattr(bo_object, field['data'])) row[field['data']] = getattr(bo_object, field['data']) if row[field['data']] == field['resource']: row[field['data']] = '...' continue # For any non-specific fields, send the field value to the table row[field['data']] = getattr(bo_object, field['data'], 'unset') logger.debug(" -> field: %s", field) logger.debug("table data row: %s", row) # logger.debug("Table row: %s", row) rows.append(row) logger.debug("filtered records: %d out of total: %d", self.records_filtered, self.records_total) # Send response return json.dumps({ "draw": int(float(params.get('draw', '0'))), "recordsTotal": self.records_total, "recordsFiltered": self.records_filtered, "data": rows })
def show_worldmap(self, for_my_widget=False): """Get the hosts list to build a worldmap Get the list of the valid hosts t display onthe map If `for_my_widget` is True this function returns the list of the concerned hosts else it returns the worldmap view. :param for_my_widget: defaults to False :return: """ user = request.environ['beaker.session']['current_user'] datamgr = request.app.datamgr # Fetch elements per page preference for user, default is 25 elts_per_page = datamgr.get_user_preferences(user, 'elts_per_page', 25) # Pagination and search start = int(request.query.get('start', '0')) count = int(request.query.get('count', elts_per_page)) where = Helper.decode_search(request.query.get('search', ''), self.table) search = { 'page': (start // count) + 1, 'max_results': count, 'sort': '-_id', 'where': where } if self.plugin_parameters.get('hosts_business_impacts'): # Only get hosts which business impact is configured... logger.debug("worldmap, only hosts with BI in: %s", self.plugin_parameters['hosts_business_impacts']) search['where'].update({ 'business_impact': { '$in': self.plugin_parameters['hosts_business_impacts'] } }) if self.plugin_parameters.get('hosts_included'): # Include some hosts... logger.debug("worldmap, included hosts: %s", self.plugin_parameters['hosts_included']) search['where'].update( {'name': { "$regex": self.plugin_parameters['hosts_included'] }}) if self.plugin_parameters.get('hosts_excluded'): # Exclude some hosts... logger.debug("worldmap, excluded hosts: %s", self.plugin_parameters['hosts_excluded']) search['where'].update({ 'name': { "$regex": "^((?!%s).)*$" % self.plugin_parameters['hosts_excluded'] } }) # Do not include the embedded fields to improve the loading time... hosts = datamgr.get_hosts(search, embedded=False) # Get positioned and not-positioned hosts positioned_hosts = self.get_map_elements(hosts) # Get last total elements count total = len(hosts) if hosts: total = hosts[0]['_total'] logger.info("worldmap, total %d hosts", total) if for_my_widget: return [h for h in positioned_hosts if h['positioned']] map_style = "width: %s; height: %s;" % (self.plugin_parameters.get( 'map_width', "100%"), self.plugin_parameters.get('map_height', "100%")) return { 'search_engine': self.search_engine, 'search_filters': self.search_filters, 'mapId': 'hostsMap', 'mapStyle': map_style, 'params': self.plugin_parameters, 'hosts': positioned_hosts, 'pagination': self.webui.helper.get_pagination_control('/worldmap', total, start, count), 'title': request.query.get('title', _('Hosts worldmap')) }
def get_livestate_widget(self, embedded=False, identifier=None, credentials=None): # pylint: disable=too-many-locals """Get the livestate widget""" user = request.environ['beaker.session']['current_user'] webui = request.app.config['webui'] datamgr = webui.datamgr panels = datamgr.get_user_preferences(user, 'livestate', {}) ls = Helper.get_html_livestate(datamgr, panels, int(request.query.get('bi', -1)), request.query.get('search', {}), actions=user.is_power()) # Widget options widget_id = request.params.get('widget_id', '') if widget_id == '': return self.webui.response_invalid_parameters( _('Missing widget identifier')) widget_place = request.params.get('widget_place', 'dashboard') widget_template = request.params.get('widget_template', 'elements_table_widget') widget_icon = request.params.get('widget_icon', 'plug') logger.debug("Searching a widget %s for: %s (%s)", widget_id, widget_place, widget_template) # Search in the application widgets (all plugins widgets) options = {} for widget in self.webui.get_widgets_for(widget_place): logger.debug("found widget: %s (%s)", widget['name'], widget['id']) if widget_id.startswith(widget['id']): options = widget['options'] widget_template = widget['template'] widget_icon = widget['icon'] logger.debug("Widget %s found, template: %s, options: %s", widget_id, widget_template, options) break else: logger.warning("Widget identifier not found: %s", widget_id) return self.webui.response_invalid_parameters( _('Unknown widget identifier')) # Search in the saved dashboard widgets saved_widget = None saved_widgets = datamgr.get_user_preferences(user, 'dashboard_widgets', []) for widget in saved_widgets: if widget_id == widget['id']: saved_widget = widget logger.info("Saved widget found: %s", saved_widget) break # Widget freshly created tmp_options = [] if not saved_widget or 'options' not in saved_widget: for option in options: tmp_options.append("%s=%s" % (option, options[option]['value'])) saved_options = '|'.join(tmp_options) else: saved_options = saved_widget['options'] tmp_options = [] logger.info("Saved widget options: %s", saved_options) for option in saved_options.split('|'): option = option.split('=') logger.info("- saved option: %s", option) if len(option) > 1: if request.params.get(option[0], option[1]) != option[1]: tmp_options.append( "%s=%s" % (option[0], request.params.get(option[0]))) options[option[0]]['value'] = request.params.get(option[0]) else: tmp_options.append("%s=%s" % (option[0], option[1])) options[option[0]]['value'] = option[1] new_options = '|'.join(tmp_options) if saved_options != new_options: logger.info("Widget %s new options: %s", widget_id, new_options) # Search for the dashboard widgets saved_widgets = datamgr.get_user_preferences( user, 'dashboard_widgets', []) for widget in saved_widgets: if widget_id.startswith(widget['id']): widget['options'] = new_options datamgr.set_user_preferences(user, 'dashboard_widgets', saved_widgets) logger.info("Widget new options saved!") break saved_options = new_options title = request.params.get('title', _('Elements')) # Use required template to render the widget logger.info("Rendering widget %s", widget_id) return template( '_widget', { 'widget_id': widget_id, 'widget_name': widget_template, 'widget_place': widget_place, 'widget_template': widget_template, 'widget_icon': widget_icon, 'widget_uri': request.urlparts.path, 'plugin_parameters': self.plugin_parameters, 'livestate': ls, 'options': options, 'title': title, 'embedded': embedded, 'identifier': identifier, 'credentials': credentials })
def get_realm_members(self, element_id): """Get the realm hosts list""" datamgr = request.app.datamgr realm = datamgr.get_realm(element_id) if not realm: realm = datamgr.get_realm(search={'max_results': 1, 'where': {'name': element_id}}) if not realm: return self.webui.response_invalid_parameters(_('Element does not exist: %s') % element_id) # Get elements from the data manager search = { 'where': {'_realm': realm.id}, 'sort': '-business_impact, -ls_state_id, -ls_last_state_changed' } hosts = datamgr.get_hosts(search=search) logger.debug("get_realm_members, search: %s, found %d hosts", search, len(hosts)) # Get element state configuration items_states = ElementState() items = [] items.append({ 'id': -1, 'type': 'host', 'tr': """ <table class="table table-invisible table-condensed"> <thead><tr> <th></th> <th>%s</th> <th>%s</th> <th>%s</th> <th>%s</th> <th>%s</th> </tr></thead> <tbody> </tbody> </table> """ % ( _("BI"), _("Element"), _("Since"), _("Last check"), _("Output") ) }) for member in hosts: logger.debug("Realm member: %s", member) cfg_state = items_states.get_icon_state('host', member.status) tr = """<tr> <td>%s</td> <td>%s</td> <td>%s</td> <td class="hidden-xs">%s</td> <td class="hidden-xs">%s</td> <td class="hidden-sm hidden-xs">%s: %s</td> </tr>""" % ( member.get_html_state(text=None, title=member.alias), Helper.get_html_business_impact(member.business_impact, icon=True, text=False), member.get_html_link(), Helper.print_duration(member.last_state_changed, duration_only=True, x_elts=2), Helper.print_duration(member.last_check, duration_only=True, x_elts=2), Helper.print_date(member.last_check), member.output ) items.append({ 'id': member.id, 'type': 'host', 'name': member.name, 'alias': member.alias, 'status': member.status, 'icon': 'fa fa-%s item_%s' % (cfg_state['icon'], cfg_state['class']), 'state': member.get_html_state(text=None, title=member.alias), 'tr': tr }) response.status = 200 response.content_type = 'application/json' return json.dumps(items)
def get_realm_members(self, element_id): """Get the realm hosts list""" datamgr = request.app.datamgr realm = datamgr.get_realm(element_id) if not realm: realm = datamgr.get_realm(search={ 'max_results': 1, 'where': { 'name': element_id } }) if not realm: return self.webui.response_invalid_parameters( _('Element does not exist: %s') % element_id) # Get elements from the data manager search = { 'where': { '_realm': realm.id }, 'sort': '-business_impact, -ls_state_id, -ls_last_state_changed' } hosts = datamgr.get_hosts(search=search) logger.debug("get_realm_members, search: %s, found %d hosts", search, len(hosts)) # Get element state configuration items_states = ElementState() items = [] items.append({ 'id': -1, 'type': 'host', 'tr': """ <table class="table table-invisible table-condensed"> <thead><tr> <th></th> <th>%s</th> <th>%s</th> <th>%s</th> <th>%s</th> <th>%s</th> </tr></thead> <tbody> </tbody> </table> """ % (_("BI"), _("Element"), _("Since"), _("Last check"), _("Output")) }) for member in hosts: logger.debug("Realm member: %s", member) cfg_state = items_states.get_icon_state('host', member.status) tr = """<tr> <td>%s</td> <td>%s</td> <td>%s</td> <td class="hidden-xs">%s</td> <td class="hidden-xs">%s</td> <td class="hidden-sm hidden-xs">%s: %s</td> </tr>""" % (member.get_html_state(text=None, title=member.alias), Helper.get_html_business_impact( member.business_impact, icon=True, text=False), member.get_html_link(), Helper.print_duration(member.last_state_changed, duration_only=True, x_elts=2), Helper.print_duration( member.last_check, duration_only=True, x_elts=2), Helper.print_date(member.last_check), member.output) items.append({ 'id': member.id, 'type': 'host', 'name': member.name, 'alias': member.alias, 'status': member.status, 'icon': 'fa fa-%s item_%s' % (cfg_state['icon'], cfg_state['class']), 'state': member.get_html_state(text=None, title=member.alias), 'tr': tr }) response.status = 200 response.content_type = 'application/json' return json.dumps(items)
def table_data(self, plugin_table): # Because there are many locals needed :) # pylint: disable=too-many-locals """Return elements data in json format as of Datatables SSP protocol More info: https://datatables.net/manual/server-side Example URL:: POST /? draw=1& columns[0][data]=alias& columns[0][name]=& columns[0][searchable]=true& columns[0][orderable]=true& columns[0][search][value]=& columns[0][search][regex]=false& ... order[0][column]=0& order[0][dir]=asc& start=0& length=10& search[value]=& search[regex]=false& Request parameters are Json formatted Request Parameters: - object_type: object type managed by the datatable - links: url prefix to be used by the links in the table - embedded: true / false whether the table is embedded by an external application **Note**: those three first parameters are not datatable specific parameters :) - draw, index parameter to be returned in the response Pagination: - start / length, for pagination Searching: - search (value or regexp) search[value]: Global search value. To be applied to all columns which are searchable search[regex]: true if search[value] is a regex Sorting: - order[i][column] / order[i][dir] index of the columns to order and sort direction (asc/desc) Columns: - columns[i][data]: Column's data source, as defined by columns.data. - columns[i][name]: Column's name, as defined by columns.name. - columns[i][searchable]: Flag to indicate if this column is searchable (true). - columns[i][orderable]: Flag to indicate if this column is orderable (true). - columns[i][search][value]: Search value to apply to this specific column. - columns[i][search][regex]: Flag to indicate if the search term for this column is a regex. Response data: - draw - recordsTotal: total records, before filtering (i.e. total number of records in the database) - recordsFiltered: Total records, after filtering (i.e. total number of records after filtering has been applied - not just the number of records being returned for this page of data). - data: The data to be displayed in the table. an array of data source objects, one for each row, which will be used by DataTables. - error (optional): Error message if an error occurs Not included if there is no error. """ # Manage request parameters ... logger.info("request data for table: %s, templates: %s", request.forms.get('object_type'), self.templates) # Because of specific datatables parameters name (eg. columns[0] ...) # ... some parameters have been json.stringify on client side ! params = {} for key in list(request.params.keys()): if key in ['columns', 'order', 'search']: params[key] = json.loads(request.params.get(key)) else: params[key] = request.params.get(key) # params now contains 'valid' query parameters as we should have found them ... logger.debug("table request parameters: %s", params) parameters = {} # Manage page number ... # start is the first requested row and we must transform to a page count ... first_row = int(params.get('start', '0')) # length is the number of requested rows rows_count = int(params.get('length', '25')) parameters['page'] = (first_row // rows_count) + 1 parameters['max_results'] = rows_count logger.debug("get %d rows from row #%d -> page: %d", rows_count, first_row, parameters['page']) # Columns ordering # order:[{"column":2,"dir":"desc"}] if 'order' in params and 'columns' in params and params['order']: sorted_columns = [] for order in params['order']: idx = int(order['column']) if params['columns'][idx] and params['columns'][idx]['data']: logger.debug("sort by column %d (%s), order: %s ", idx, params['columns'][idx]['data'], order['dir']) if order['dir'] == 'desc': sorted_columns.append('-' + params['columns'][idx]['data']) else: sorted_columns.append(params['columns'][idx]['data']) if sorted_columns: parameters['sort'] = ','.join(sorted_columns) logger.info("backend order request parameters: %s", parameters) # Individual column search parameter s_columns = [] if 'columns' in params and params['columns']: for column in params['columns']: if 'searchable' not in column or 'search' not in column: # pragma: no cover continue if 'value' not in column[ 'search'] or not column['search']['value']: continue logger.debug("search column '%s' for '%s'", column['data'], column['search']['value']) for field in self.table_columns: if field['data'] != column['data']: continue # Some specific types... if field['type'] == 'boolean': s_columns.append({ column['data']: column['search']['value'] == 'true' }) elif field['type'] == 'integer': s_columns.append( {column['data']: int(column['search']['value'])}) elif field['format'] == 'select': values = column['search']['value'].split(',') if len(values) > 1: s_columns.append({column['data']: {"$in": values}}) else: s_columns.append({column['data']: values[0]}) # ... the other fields :) else: # Do not care about 'smart' and 'caseInsensitive' boolean parameters ... if column['search']['regex']: s_columns.append({ column['data']: { "$regex": ".*" + column['search']['value'] + ".*" } }) else: s_columns.append( {column['data']: column['search']['value']}) break logger.info("backend search individual columns parameters: %s", s_columns) # Global search parameter # search:{"value":"test","regex":false} s_global = {} # pylint: disable=too-many-nested-blocks # Will be too complex else ... if 'search' in params and 'columns' in params and params['search']: # params['search'] contains something like: {u'regex': False, u'value': u'name:pi1'} # global regex is always ignored ... in favor of the column declared regex logger.info("global search requested: %s ", params['search']) if 'value' in params['search'] and params['search']['value']: # There is something to search for... logger.debug("search requested, value: %s ", params['search']['value']) # New strategy: decode search patterns... search = Helper.decode_search(params['search']['value'], plugin_table) logger.info("decoded search pattern: %s", search) # Old strategy: search for the value in all the searchable columns... # if not search: # logger.info("applying datatable search pattern...") # for column in params['columns']: # if not column['searchable']: # continue # logger.debug("search global '%s' for '%s'", # column['data'], params['search']['value']) # if 'regex' in params['search']: # if params['search']['regex']: # s_global.update( # {column['data']: { # "$regex": ".*" + params['search']['value'] + ".*"}}) # else: # s_global.update({column['data']: params['search']['value']}) s_global = search logger.info("backend search global parameters: %s", s_global) # Specific hack to filter the log check results that are not dated! if self.object_type == 'logcheckresult': s_columns.append({"last_check": {"$ne": 0}}) if s_columns and s_global: parameters['where'] = {"$and": [{"$and": s_columns}, s_global]} elif s_columns: parameters['where'] = {"$and": s_columns} elif s_global: parameters['where'] = s_global # Embed linked resources / manage templated resources parameters['embedded'] = {} for field in self.embedded: parameters['embedded'].update({field: 1}) logger.debug("backend embedded parameters: %s", parameters['embedded']) # Count total elements excluding templates if necessary if self.is_templated: if self.ui_visibility: self.records_total = self.datamgr.my_backend.count( self.object_type, params={ 'where': { '_is_template': self.templates, 'webui_visible': True } }) else: self.records_total = self.datamgr.my_backend.count( self.object_type, params={'where': { '_is_template': self.templates }}) if 'where' in parameters: parameters['where'].update({'_is_template': self.templates}) else: parameters['where'] = {'_is_template': self.templates} else: if self.ui_visibility: self.records_total = self.datamgr.my_backend.count( self.object_type, params={'where': { 'webui_visible': True }}) else: self.records_total = self.datamgr.my_backend.count( self.object_type) if self.ui_visibility: if 'where' in parameters: parameters['where'].update({'webui_visible': True}) else: parameters['where'] = {'webui_visible': True} # Request objects from the backend ... logger.info("table data get parameters: %s", parameters) items = self.datamgr.my_backend.get(self.object_type, params=parameters) logger.info("table data got %d items", len(items)) if not items: logger.info("No backend elements match search criteria: %s", parameters) # Empty response return json.dumps({ # draw is the request number ... "draw": int(params.get('draw', '0')), "recordsTotal": 0, "recordsFiltered": 0, "data": [] }) # Create an object ... object_class = [ kc for kc in self.datamgr.known_classes if kc.get_type() == self.object_type ] if not object_class: # pragma: no cover, should never happen! logger.warning("datatable, unknown object type: %s", self.object_type) # Empty response return json.dumps({ # draw is the request number ... "draw": int(params.get('draw', '0')), "recordsTotal": 0, "recordsFiltered": 0, "data": [] }) # Update table inner properties with the object class defined properties object_class = object_class[0] self.id_property = '_id' if hasattr(object_class, 'id_property'): self.id_property = object_class.id_property self.name_property = 'name' if hasattr(object_class, 'name_property'): self.name_property = object_class.name_property self.status_property = 'status' if hasattr(object_class, 'status_property'): self.status_property = object_class.status_property logger.debug( "datatable, object type: '%s' and properties: %s / %s / %s", object_class, self.id_property, self.name_property, self.status_property) # Change item content... rows = [] # Total number of filtered records self.records_filtered = self.records_total for item in items: bo_object = object_class(item) if not bo_object.ui_visible: logger.debug("Not UI visible object: %s", bo_object) continue logger.info("table data object: %s", bo_object) logger.debug("table data object: %s", bo_object) # This is an awful hack that allows to update the objects filtered for a table. # Two main interests: # - update the backend because some massive modifications are necessary for testing # - prepare a massive update feature in the Web UI :) # ----- # if self.is_templated and self.templates and self.object_type == 'service': # logger.warning("service template: %s for host: %s", # bo_object.name, bo_object['host']) # if bo_object['host'].name.startswith('fdj'): # logger.info("To be updated...") # data = { # "check_freshness": True, # "freshness_threshold": 86400, # "passive_checks_enabled": True, # "active_checks_enabled": False # } # result = self.datamgr.update_object(element=bo_object, data=data) # if result is True: # logger.info("updated.") # else: # logger.error("update failed!") # ----- # Each item contains the total number of records matching the search filter self.records_filtered = item['_total'] row = {} row['DT_RowData'] = {} row['_id'] = bo_object.id for field in self.table_columns: logger.debug(" - field: %s", field) # Specific fields if field['data'] == self.name_property: # Create a link to navigate to the item page row[self.name_property] = bo_object.html_link # Store the item name in a specific field of the row. # The value will be retrieved by the table actions (ack, recheck, ...) row['DT_RowData'].update( {"object_%s" % self.object_type: bo_object.name}) continue if field['data'] == self.status_property: # Replace the text status with the specific item HTML state row[self.status_property] = bo_object.get_html_state( text=None, title=bo_object.status) # Use the item status to specify the table row class # row['DT_RowClass'] = "table-row-%s" % (bo_object.status.lower()) continue if field['data'] in [ '_overall_state_id', 'overall_state', 'overall_status' ]: # Get the item overall state from the data manager f_get_overall_state = getattr( self.datamgr, 'get_%s_overall_state' % self.object_type) if f_get_overall_state: (dummy, overall_status) = f_get_overall_state(bo_object) # Get element state configuration row[field['data']] = ElementState().get_html_state( self.object_type, bo_object, text=None, title=bo_object.overall_state_to_title[ bo_object.overall_state], use_status=overall_status) # Use the item overall state to specify the table row class row['DT_RowClass'] = "table-row-%s" % (overall_status) else: # pragma: no cover, should never happen! logger.warning( "Missing get_overall_state method for: %s", self.object_type) row[field['data']] = 'XxX' continue if "business_impact" in field['data']: # Replace the BI count with the specific item HTML formatting row[field['data']] = Helper.get_html_business_impact( bo_object.business_impact) continue # Specific fields type if field['type'] == 'datetime' or field['format'] == 'datetime': # Replace the timestamp with the formatted date row[field['data']] = bo_object.get_date( bo_object[field['data']]) continue if field['type'] == 'boolean': # Replace the boolean vaue with the specific item HTML formatting row[field['data']] = Helper.get_on_off( bo_object[field['data']]) continue if field['type'] == 'list': # Replace the list with the specific list HTML formatting if hasattr(bo_object, field['data']): row[field['data']] = Helper.get_html_item_list( bo_object.id, field['data'], getattr(bo_object, field['data']), title=field['title']) else: row[field['data']] = 'Unknown' continue if field['type'] == 'dict': # Replace the dictionary with the specific dict HTML formatting row[field['data']] = Helper.get_html_item_list( bo_object.id, field['data'], getattr(bo_object, field['data']), title=field['title']) continue if field['type'] == 'objectid': if isinstance(bo_object[field['data']], BackendElement): row[field['data']] = bo_object[ field['data']].get_html_link( prefix=request.params.get('links')) # row['DT_RowData'].update({ # "object_%s" % field['data']: bo_object[field['data']].name # }) else: logger.warning( "Table field is supposed to be an object: %s, %s = %s", bo_object.name, field['data'], getattr(bo_object, field['data'])) row[field['data']] = getattr(bo_object, field['data']) if row[field['data']] == field['resource']: row[field['data']] = '...' continue # For any non-specific fields, send the field value to the table row[field['data']] = getattr(bo_object, field['data'], 'unset') logger.debug(" -> field: %s", field) logger.debug("table data row: %s", row) # logger.debug("Table row: %s", row) rows.append(row) logger.debug("filtered records: %d out of total: %d", self.records_filtered, self.records_total) # Send response return json.dumps({ "draw": int(float(params.get('draw', '0'))), "recordsTotal": self.records_total, "recordsFiltered": self.records_filtered, "data": rows })
def get_livestate_widget(self, embedded=False, identifier=None, credentials=None): # pylint: disable=too-many-locals """Get the livestate widget""" user = request.environ['beaker.session']['current_user'] webui = request.app.config['webui'] datamgr = webui.datamgr panels = datamgr.get_user_preferences(user, 'livestate', {}) ls = Helper.get_html_livestate(datamgr, panels, int(request.query.get('bi', -1)), request.query.get('search', {}), actions=user.is_power()) # Widget options widget_id = request.params.get('widget_id', '') if widget_id == '': return self.webui.response_invalid_parameters(_('Missing widget identifier')) widget_place = request.params.get('widget_place', 'dashboard') widget_template = request.params.get('widget_template', 'elements_table_widget') widget_icon = request.params.get('widget_icon', 'plug') logger.debug("Searching a widget %s for: %s (%s)", widget_id, widget_place, widget_template) # Search in the application widgets (all plugins widgets) options = {} for widget in self.webui.get_widgets_for(widget_place): logger.debug("found widget: %s (%s)", widget['name'], widget['id']) if widget_id.startswith(widget['id']): options = widget['options'] widget_template = widget['template'] widget_icon = widget['icon'] logger.debug("Widget %s found, template: %s, options: %s", widget_id, widget_template, options) break else: logger.warning("Widget identifier not found: %s", widget_id) return self.webui.response_invalid_parameters(_('Unknown widget identifier')) # Search in the saved dashboard widgets saved_widget = None saved_widgets = datamgr.get_user_preferences(user, 'dashboard_widgets', []) for widget in saved_widgets: if widget_id == widget['id']: saved_widget = widget logger.info("Saved widget found: %s", saved_widget) break # Widget freshly created tmp_options = [] if not saved_widget or 'options' not in saved_widget: for option in options: tmp_options.append("%s=%s" % (option, options[option]['value'])) saved_options = '|'.join(tmp_options) else: saved_options = saved_widget['options'] tmp_options = [] logger.info("Saved widget options: %s", saved_options) for option in saved_options.split('|'): option = option.split('=') logger.info("- saved option: %s", option) if len(option) > 1: if request.params.get(option[0], option[1]) != option[1]: tmp_options.append("%s=%s" % (option[0], request.params.get(option[0]))) options[option[0]]['value'] = request.params.get(option[0]) else: tmp_options.append("%s=%s" % (option[0], option[1])) options[option[0]]['value'] = option[1] new_options = '|'.join(tmp_options) if saved_options != new_options: logger.info("Widget %s new options: %s", widget_id, new_options) # Search for the dashboard widgets saved_widgets = datamgr.get_user_preferences(user, 'dashboard_widgets', []) for widget in saved_widgets: if widget_id.startswith(widget['id']): widget['options'] = new_options datamgr.set_user_preferences(user, 'dashboard_widgets', saved_widgets) logger.info("Widget new options saved!") break saved_options = new_options title = request.params.get('title', _('Elements')) # Use required template to render the widget logger.info("Rendering widget %s", widget_id) return template('_widget', { 'widget_id': widget_id, 'widget_name': widget_template, 'widget_place': widget_place, 'widget_template': widget_template, 'widget_icon': widget_icon, 'widget_uri': request.urlparts.path, 'plugin_parameters': self.plugin_parameters, 'livestate': ls, 'options': options, 'title': title, 'embedded': embedded, 'identifier': identifier, 'credentials': credentials })
def get_currently(self): # pylint:disable=no-self-use, too-many-locals """Display currently page""" user = request.environ['beaker.session']['current_user'] webui = request.app.config['webui'] datamgr = webui.datamgr # Default states hosts_states = ['up', 'down', 'unreachable'] services_states = [ 'ok', 'warning', 'critical', 'unreachable', 'unknown' ] # Get the stored panels in user's preferences panels = datamgr.get_user_preferences(user, 'panels', None) if not panels: panels = { 'panel_hosts': { 'collapsed': False }, 'panel_services': { 'collapsed': False }, 'panel_ls_history_hosts': { 'collapsed': False }, 'panel_ls_history_services': { 'collapsed': False } } # Get the stored graphs graphs = datamgr.get_user_preferences(user, 'currently_graphs', None) if not graphs: graphs = { 'pie_graph_hosts': { 'legend': True, 'title': True, 'states': hosts_states }, 'pie_graph_services': { 'legend': True, 'title': True, 'states': services_states }, 'line_graph_hosts': { 'legend': True, 'title': True, 'states': hosts_states }, 'line_graph_services': { 'legend': True, 'title': True, 'states': services_states } } # Live state global and history (livesynthesis, ls_history) = datamgr.get_livesynthesis_history() hs = livesynthesis['hosts_synthesis'] ss = livesynthesis['services_synthesis'] ls_history = sorted(ls_history, key=lambda x: x['_timestamp'], reverse=False) collapsed = False if 'panel_hosts' in panels: collapsed = panels['panel_hosts']['collapsed'] p_h = Helper.get_html_hosts_count_panel( hs, self.webui.get_url('Hosts table'), collapsed=collapsed) collapsed = False if 'panel_services' in panels: collapsed = panels['panel_services']['collapsed'] p_s = Helper.get_html_services_count_panel( ss, self.webui.get_url('Services table'), collapsed=collapsed) collapsed = False if 'panel_ls_history_hosts' in panels: collapsed = panels['panel_ls_history_hosts']['collapsed'] lsh = Helper.get_html_hosts_ls_history(hs, ls_history, collapsed=collapsed) collapsed = False if 'panel_ls_history_services' in panels: collapsed = panels['panel_ls_history_services']['collapsed'] ssh = Helper.get_html_services_ls_history(ss, ls_history, collapsed=collapsed) return { 'panels': panels, 'panel_hosts': p_h, 'panel_ls_history_hosts': lsh, 'panel_services': p_s, 'panel_ls_history_services': ssh, 'title': request.query.get('title', _('Keep an eye')) }
def table_data(self): """ Return elements data in json format as of Datatables SSP protocol More info: https://datatables.net/manual/server-side Example URL:: POST /? draw=1& columns[0][data]=alias& columns[0][name]=& columns[0][searchable]=true& columns[0][orderable]=true& columns[0][search][value]=& columns[0][search][regex]=false& ... order[0][column]=0& order[0][dir]=asc& start=0& length=10& search[value]=& search[regex]=false& Request parameters are Json formatted Request Parameters: - draw, index parameter to be returned in the response Pagination: - start / length, for pagination Searching: - search (value or regexp) search[value]: Global search value. To be applied to all columns which are searchable search[regex]: true if searh[value] is a regex Sorting: - order[i][column] / order[i][dir] index of the columns to order and sort direction (asc/desc) Columns: - columns[i][data]: Column's data source, as defined by columns.data. - columns[i][name]: Column's name, as defined by columns.name. - columns[i][searchable]: Flag to indicate if this column is searchable (true). - columns[i][orderable]: Flag to indicate if this column is orderable (true). - columns[i][search][value]: Search value to apply to this specific column. - columns[i][search][regex]: Flag to indicate if the search term for this column is a regex. Response data: - draw - recordsTotal: total records, before filtering (i.e. total number of records in the database) - recordsFiltered: Total records, after filtering (i.e. total number of records after filtering has been applied - not just the number of records being returned for this page of data). - data: The data to be displayed in the table. an array of data source objects, one for each row, which will be used by DataTables. - error (optional): Error message if an error occurs Not included if there is no error. """ # Manage request parameters ... # Because of specific datatables parameters name (eg. columns[0] ...) # ... some parameters have been json.stringify on client side ! params = {} logger.debug("table request parameters: %s", request.forms) for key in request.forms.keys(): if key == 'columns' or key == 'order': params[key] = json.loads(request.forms.get(key)) elif key == 'search': params[key] = json.loads(request.forms.get(key)) else: params[key] = request.forms.get(key) # params now contains 'valid' query parameters as we should have found them ... logger.debug("table request parameters: %s", params) parameters = {} # Manage page number ... # start is the first requested row and we must transform to a page count ... first_row = int(params.get('start', '0')) # length is the number of requested rows rows_count = int(params.get('length', '25')) parameters['page'] = (first_row // rows_count) + 1 parameters['max_results'] = rows_count logger.info( "get %d rows from row #%d -> page: %d", rows_count, first_row, parameters['page'] ) # Columns ordering # order:[{"column":2,"dir":"desc"}] if 'order' in params and 'columns' in params and params['order']: sorted_columns = [] for order in params['order']: idx = int(order['column']) if params['columns'][idx] and params['columns'][idx]['data']: logger.debug( "sort by column %d (%s), order: %s ", idx, params['columns'][idx]['data'], order['dir'] ) if order['dir'] == 'desc': sorted_columns.append('-' + params['columns'][idx]['data']) else: sorted_columns.append(params['columns'][idx]['data']) if sorted_columns: parameters['sort'] = ','.join(sorted_columns) logger.info("backend order request parameters: %s", parameters) # Columns searching # Individual search parameter searched_columns = [] if 'columns' in params and params['columns']: for column in params['columns']: if 'searchable' not in column or 'search' not in column: # pragma: no cover continue if 'value' not in column['search'] or not column['search']['value']: continue logger.debug( "search column '%s' for '%s'", column['data'], column['search']['value'] ) column_type = 'string' for field in self.table_columns: if field['name'] == column['data']: column_type = field['type'] break if 'regex' in column['search']: if column['search']['regex']: if column_type == 'integer': searched_columns.append( '{ "%s": { "$regex": .*%s.* } }' % ( column['data'], column['search']['value'] ) ) else: searched_columns.append( '{ "%s": { "$regex": ".*%s.*" } }' % ( column['data'], column['search']['value'] ) ) else: if column_type == 'integer': searched_columns.append( '{ "%s": %s }' % ( column['data'], column['search']['value'] ) ) else: searched_columns.append( '{ "%s": "%s" }' % ( column['data'], column['search']['value'] ) ) logger.info("backend search columns parameters: %s", searched_columns) # Columns searching # Global search parameter # search:{"value":"test","regex":false} searched_global = [] if 'search' in params and 'columns' in params and params['search']: if 'value' in params['search'] and params['search']['value']: logger.debug( "search requested, value: %s ", params['search']['value'] ) for column in params['columns']: if 'searchable' in column and column['searchable']: logger.debug( "search global '%s' for '%s'", column['data'], params['search']['value'] ) if 'regex' in params['search']: if params['search']['regex']: searched_global.append( '{ "%s": { "$regex": ".*%s.*" } }' % ( column['data'], params['search']['value'] ) ) else: searched_global.append( '{ "%s": "%s" }' % ( column['data'], params['search']['value'] ) ) logger.info("backend search global parameters: %s", searched_global) if searched_columns and searched_global: parameters['where'] = '{"$and": [ %s, %s ] }' % ( '{"$and": [' + ','.join(searched_columns) + '] }', '{"$or": [' + ','.join(searched_global) + '] }' ) if searched_columns: parameters['where'] = '{"$and": [' + ','.join(searched_columns) + '] }' if searched_global: parameters['where'] = '{"$or": [' + ','.join(searched_global) + '] }' # Embed linked resources embedded = {} for field in self.table_columns: if field['type'] == 'objectid' and field['format'] != 'objectid': embedded.update({field['name']: 1}) if embedded: parameters['embedded'] = json.dumps(embedded) logger.info("backend embedded parameters: %s", parameters['embedded']) # Request ALL objects from the backend recordsTotal = self.get_total_records() # Request objects from the backend ... logger.info("backend get parameters: %s", parameters) resp = self.backend.get(self.object_type, params=parameters) logger.debug("response _meta: %s", resp['_meta']) logger.debug("response _links: %s", resp['_links']) # logger.debug("response _items: %s", resp['_items']) # Total number of filtered records recordsFiltered = len(resp['_items']) if '_meta' in resp and (searched_columns or searched_global): recordsFiltered = int(resp['_meta']['total']) recordsFiltered = int(resp['_meta']['total']) # Create an object ... if resp['_items']: bo_object = None for k in globals().keys(): if isinstance(globals()[k], type) and \ '_type' in globals()[k].__dict__ and \ globals()[k].getType() == self.object_type: bo_object = globals()[k]() logger.debug("created: %s", bo_object) break # Change item content ... for item in resp['_items']: logger.debug("Object: %s", bo_object) bo_object._update(item) for key in item.keys(): for field in self.table_columns: if field['name'] != key: continue # logger.debug("Setting field: %s", field) if field['name'] == 'name': item[key] = "%s %s" % (bo_object.get_html_state(), item[key]) break if field['name'] == 'status': item[key] = bo_object.get_html_state() break if field['type'] == 'datetime': item[key] = bo_object.get_date() break if field['type'] == 'boolean': item[key] = Helper.get_on_off(item[key]) break if field['type'] == 'objectid' and key in embedded: for k in globals().keys(): if isinstance(globals()[k], type) and \ '_type' in globals()[k].__dict__ and \ globals()[k]._type == field['format']: linked_object = globals()[k](item[key]) logger.debug("created: %s", linked_object) item[key] = linked_object.get_html_state( label=linked_object.get_name() ) break # Prepare response rsp = { # draw is the request number ... "draw": int(params.get('draw', '0')), "recordsTotal": recordsTotal, "recordsFiltered": recordsFiltered, "data": resp['_items'] } return json.dumps(rsp)
def show_minemap(self): # Yes, but that's how it is made, and it suits ;) # pylint: disable=too-many-locals """Get the hosts / services list to build a minemap""" user = request.environ['beaker.session']['current_user'] datamgr = request.app.datamgr # Fetch elements per page preference for user, default is 25 elts_per_page = datamgr.get_user_preferences(user, 'elts_per_page', 25) # Minemap search engine is based upon host table plugin = self.webui.find_plugin('host') # Pagination and search start = int(request.params.get('start', '0')) count = int(request.params.get('count', elts_per_page)) if count < 1: count = elts_per_page search = Helper.decode_search(request.params.get('search', ''), plugin.table) logger.info("Decoded search pattern: %s", search) for key, pattern in search.items(): logger.info("global search pattern '%s' / '%s'", key, pattern) search = { 'page': (start // count) + 1, 'max_results': count, 'where': search, 'sort': '-_overall_state_id' } # Get elements from the data manager # Do not include the embedded fields to improve the loading time... hosts = datamgr.get_hosts(search, embedded=False) minemap = [] columns = [] for host in hosts: # Each item contains the total number of records matching the search filter total = host['_total'] minemap_row = {'host_check': host} # Get all host services # Do not include the embedded fields to improve the loading time... services = datamgr.get_services(search={ 'where': { 'host': host.id }, 'sort': '-_overall_state_id' }, all_elements=True, embedded=False) for service in services: columns.append(service.name) minemap_row.update({service.name: service}) minemap.append(minemap_row) # Sort column names by most frequent ... count_columns = collections.Counter(columns) columns = [c for c, dummy in count_columns.most_common()] return { 'search_engine': self.search_engine, 'search_filters': self.search_filters, 'params': self.plugin_parameters, 'minemap': minemap, 'columns': columns, 'pagination': self.webui.helper.get_pagination_control('/minemap', total, start, count), 'title': request.query.get('title', _('Hosts minemap')) }
# You should have received a copy of the GNU Affero General Public License # along with (WebUI). If not, see <http://www.gnu.org/licenses/>. # import the unit testing module from __future__ import print_function import time import unittest2 from alignak_webui import set_app_config from alignak_webui.objects.item_timeperiod import TimePeriod from alignak_webui.utils.helper import Helper from alignak_webui.utils.settings import Settings helper = Helper() def setup_module(): print("") # Get configuration from only one file ... print("read configuration") cfg = Settings("settings.cfg") found_cfg_files = cfg.read('Alignak-WebUI') assert found_cfg_files set_app_config(cfg) class TestDate(unittest2.TestCase): def test_01_print_date(self):
def get_map_elements(self, hosts): # pylint:disable=no-self-use, too-many-locals """Get hosts valid for a map: :param hosts: list of hosts to search in :return: tuple with a list of positioned hosts and a list of not yet positioned hosts """ user = request.environ['beaker.session']['current_user'] datamgr = request.app.datamgr # Get elements from the data manager logger.info("worldmap, searching valid hosts...") default_lat = float(self.plugin_parameters['default_latitude']) default_lng = float(self.plugin_parameters['default_longitude']) positioned_hosts = [] for host in hosts: map_host = {} logger.debug("worldmap, found host '%s'", host.name) map_host['positioned'] = True if 'type' not in host.position or host.position['type'] != 'Point': # logger.warning("worldmap, host '%s', invalid position: %s", # host.name, host.position) # continue logger.warning("worldmap, host '%s' has an invalid position", host.name) continue else: logger.debug("worldmap, host '%s' located: %s", host.name, host.position) map_host['lat'] = host['position']['coordinates'][0] map_host['lng'] = host['position']['coordinates'][1] logger.debug("worldmap, host '%s' position: %s, %s", host.name, map_host['lat'], map_host['lng']) if map_host['lat'] == default_lat and map_host['lng'] == default_lng: logger.debug("worldmap, host '%s' is not yet positioned", host.name) map_host['positioned'] = False for attr in ['id', 'name', 'alias', 'business_impact', 'tags', 'position', 'tags', 'notes', 'notes_url', 'action_url', 'overall_state', 'overall_status', 'state_id', 'state_type', 'acknowledged', 'downtimed', 'last_check', 'output', 'long_output']: map_host[attr] = host[attr] host_iw = self.plugin_parameters['host_info_content'] host_iw = host_iw.replace("\n", '') host_iw = host_iw.replace("\r", '') host_iw = host_iw.replace("##id##", map_host['id']) host_iw = host_iw.replace("##name##", map_host['name']) host_iw = host_iw.replace("##state##", map_host['overall_status']) host_iw = host_iw.replace("##bi##", str(map_host['business_impact'])) host_iw = host_iw.replace("##url##", host.get_html_link()) host_iw = host_iw.replace("##html_bi##", Helper.get_html_business_impact(host.business_impact, icon=True, text=False)) host_iw = host_iw.replace("##html_state##", host.get_html_state(text=None, use_status=host.overall_status)) if user.is_power(): host_iw = host_iw.replace("##html_actions##", Helper.get_html_commands_buttons( host, _('<span class="fa fa-bolt"></span>') )) else: host_iw = host_iw.replace("##html_actions##", "") # Get host services # todo: using a projection with selected fields may help to improve more? search = { 'sort': '-_overall_state_id,name', 'where': {} } if self.plugin_parameters.get('services_excluded'): search['where'].update({'name': { "$regex": "^((?!%s).)*$" % self.plugin_parameters['services_excluded']}}) if self.plugin_parameters.get('services_included'): search['where'].update({'name': { "$regex": self.plugin_parameters['services_included']}}) if self.plugin_parameters.get('services_overall_state'): allowed_states = self.plugin_parameters.get('services_overall_state').split(',') allowed_states = [int(value) for value in allowed_states] # {'_overall_state_id': {'$in': [0, 3], '$nin': [4]}} search['where'].update({'_overall_state_id': {"$in": allowed_states}}) logger.info("worldmap, search services: %s", search) services = datamgr.get_host_services(host, search=search, embedded=False) services_iw = "" for service in services: svc_iw = self.plugin_parameters['service_info_content'] svc_iw = svc_iw.replace("\n", '') svc_iw = svc_iw.replace("\r", '') svc_iw = svc_iw.replace("##id##", service['id']) svc_iw = svc_iw.replace("##name##", service['name']) svc_iw = svc_iw.replace("##state##", service['overall_status']) svc_iw = svc_iw.replace("##bi##", str(service['business_impact'])) svc_iw = svc_iw.replace("##url##", service.get_html_link()) svc_iw = svc_iw.replace("##html_bi##", Helper.get_html_business_impact(service.business_impact, icon=True, text=False)) svc_iw = svc_iw.replace("##html_state##", service.get_html_state(text=None, use_status=service.overall_status)) if user.is_power(): svc_iw = svc_iw.replace("##html_actions##", Helper.get_html_commands_buttons( service, _('<span class="fa fa-bolt"></span>') )) else: svc_iw = svc_iw.replace("##html_actions##", "") services_iw += svc_iw logger.debug("worldmap, host '%s' services: %s", host.name, services) map_host.update({'services': services}) host_iw = host_iw.replace("##services##", services_iw) map_host.update({'content': host_iw}) positioned_hosts.append(map_host) return positioned_hosts
def get_map_elements(self, hosts): # pylint:disable=no-self-use, too-many-locals """Get hosts valid for a map: :param hosts: list of hosts to search in :return: tuple with a list of positioned hosts and a list of not yet positioned hosts """ user = request.environ['beaker.session']['current_user'] datamgr = request.app.datamgr # Get elements from the data manager logger.info("worldmap, searching valid hosts...") default_lat = float(self.plugin_parameters['default_latitude']) default_lng = float(self.plugin_parameters['default_longitude']) positioned_hosts = [] for host in hosts: map_host = {} logger.debug("worldmap, found host '%s'", host.name) map_host['positioned'] = True if 'type' not in host.position or host.position['type'] != 'Point': # logger.warning("worldmap, host '%s', invalid position: %s", # host.name, host.position) # continue logger.warning("worldmap, host '%s' has an invalid position", host.name) continue else: logger.debug("worldmap, host '%s' located: %s", host.name, host.position) map_host['lat'] = host['position']['coordinates'][0] map_host['lng'] = host['position']['coordinates'][1] logger.debug("worldmap, host '%s' position: %s, %s", host.name, map_host['lat'], map_host['lng']) if map_host['lat'] == default_lat and map_host[ 'lng'] == default_lng: logger.debug("worldmap, host '%s' is not yet positioned", host.name) map_host['positioned'] = False for attr in [ 'id', 'name', 'alias', 'business_impact', 'tags', 'position', 'tags', 'notes', 'notes_url', 'action_url', 'overall_state', 'overall_status', 'state_id', 'state_type', 'acknowledged', 'downtimed', 'last_check', 'output', 'long_output' ]: map_host[attr] = host[attr] host_iw = self.plugin_parameters['host_info_content'] host_iw = host_iw.replace("\n", '') host_iw = host_iw.replace("\r", '') host_iw = host_iw.replace("##id##", map_host['id']) host_iw = host_iw.replace("##name##", map_host['name']) host_iw = host_iw.replace("##state##", map_host['overall_status']) host_iw = host_iw.replace("##bi##", str(map_host['business_impact'])) host_iw = host_iw.replace("##url##", host.get_html_link()) host_iw = host_iw.replace( "##html_bi##", Helper.get_html_business_impact(host.business_impact, icon=True, text=False)) host_iw = host_iw.replace( "##html_state##", host.get_html_state(text=None, use_status=host.overall_status)) if user.is_power(): host_iw = host_iw.replace( "##html_actions##", Helper.get_html_commands_buttons( host, _('<span class="fa fa-bolt"></span>'))) else: host_iw = host_iw.replace("##html_actions##", "") # Get host services # todo: using a projection with selected fields may help to improve more? search = {'sort': '-_overall_state_id,name', 'where': {}} if self.plugin_parameters.get('services_excluded'): search['where'].update({ 'name': { "$regex": "^((?!%s).)*$" % self.plugin_parameters['services_excluded'] } }) if self.plugin_parameters.get('services_included'): search['where'].update({ 'name': { "$regex": self.plugin_parameters['services_included'] } }) if self.plugin_parameters.get('services_overall_state'): allowed_states = self.plugin_parameters.get( 'services_overall_state').split(',') allowed_states = [int(value) for value in allowed_states] # {'_overall_state_id': {'$in': [0, 3], '$nin': [4]}} search['where'].update( {'_overall_state_id': { "$in": allowed_states }}) logger.info("worldmap, search services: %s", search) services = datamgr.get_host_services(host, search=search, embedded=False) services_iw = "" for service in services: svc_iw = self.plugin_parameters['service_info_content'] svc_iw = svc_iw.replace("\n", '') svc_iw = svc_iw.replace("\r", '') svc_iw = svc_iw.replace("##id##", service['id']) svc_iw = svc_iw.replace("##name##", service['name']) svc_iw = svc_iw.replace("##state##", service['overall_status']) svc_iw = svc_iw.replace("##bi##", str(service['business_impact'])) svc_iw = svc_iw.replace("##url##", service.get_html_link()) svc_iw = svc_iw.replace( "##html_bi##", Helper.get_html_business_impact(service.business_impact, icon=True, text=False)) svc_iw = svc_iw.replace( "##html_state##", service.get_html_state(text=None, use_status=service.overall_status)) if user.is_power(): svc_iw = svc_iw.replace( "##html_actions##", Helper.get_html_commands_buttons( service, _('<span class="fa fa-bolt"></span>'))) else: svc_iw = svc_iw.replace("##html_actions##", "") services_iw += svc_iw logger.debug("worldmap, host '%s' services: %s", host.name, services) map_host.update({'services': services}) host_iw = host_iw.replace("##services##", services_iw) map_host.update({'content': host_iw}) positioned_hosts.append(map_host) return positioned_hosts
def get_host_timeline(self, element_id, widget_id='timeline', embedded=False, identifier=None, credentials=None): # pylint: disable=unused-argument, too-many-locals """Display an host timeline widget""" user = request.environ['beaker.session']['current_user'] datamgr = request.app.datamgr # Get the host host = datamgr.get_host(element_id) if not host: # Test if we got a name instead of an id host = datamgr.get_host(search={'max_results': 1, 'where': {'name': element_id}}) if not host: return self.webui.response_invalid_parameters(_('Host does not exist')) # Search the required widget widget_place = request.params.get('widget_place', 'host') widget_template = request.params.get('widget_template', 'host_widget') # Search in the application widgets (all plugins widgets) for widget in self.webui.get_widgets_for(widget_place): if widget_id.startswith(widget['id']): widget_template = widget['template'] logger.debug("Widget found, template: %s", widget_template) break else: logger.info("Widget identifier not found: using default template and no options") logger.debug("get_host_timeline: found template: %s", widget_template) # Fetch elements per page preference for user, default is 25 elts_per_page = datamgr.get_user_preferences(user, 'elts_per_page', 25) # Host history pagination and search parameters start = int(request.params.get('start', '0')) count = int(request.params.get('count', elts_per_page)) where = {'host': host.id} # Find known history types history_plugin = self.webui.find_plugin('Histories') if history_plugin: decoded_search = Helper.decode_search(request.params.get('search', ''), history_plugin.table) logger.info("Decoded search: %s", decoded_search) if decoded_search: where.update(decoded_search) search = { 'page': (start // count) + 1, 'max_results': count, 'where': where } # Get host history history = datamgr.get_history(search=search) if history is None: history = [] total = 0 if history: total = history[0]['_total'] # Search filters used for the timeline widget events_search_filters = { '01': (_('Web UI comments'), 'type:webui.comment'), '02': (_('Check results'), 'type:check.'), '03': (_('Alerts'), 'type:monitoring.alert'), '04': (_('Acknowledges'), 'type:monitoring.ack'), '05': (_('Downtimes'), 'type:monitoring.downtime'), '06': (_('Notifications'), 'type:monitoring.notification'), '07': ('', ''), } # Render the widget return template('_widget', { 'widget_id': widget_id, 'widget_name': widget_template, 'widget_place': 'user', 'widget_template': widget_template, 'widget_uri': request.urlparts.path, 'options': {}, 'plugin_parameters': self.plugin_parameters, 'search_engine': True, 'search_filters': events_search_filters, 'host': host, 'history': history, 'pagination': self.webui.helper.get_pagination_control( '/host/%s#host_%s' % (host.id, widget_id), total, start, count ), 'title': None, 'embedded': embedded, 'identifier': identifier, 'credentials': credentials })
def table_data(self): # Because there are many locals needed :) # pylint: disable=too-many-locals """ Return elements data in json format as of Datatables SSP protocol More info: https://datatables.net/manual/server-side Example URL:: POST /? draw=1& columns[0][data]=alias& columns[0][name]=& columns[0][searchable]=true& columns[0][orderable]=true& columns[0][search][value]=& columns[0][search][regex]=false& ... order[0][column]=0& order[0][dir]=asc& start=0& length=10& search[value]=& search[regex]=false& Request parameters are Json formatted Request Parameters: - object_type: object type managed by the datatable - links: urel prefix to be used by the links in the table - embedded: true / false whether the table is embedded by an external application **Note**: those three first parameters are not datatable specific parameters :) - draw, index parameter to be returned in the response Pagination: - start / length, for pagination Searching: - search (value or regexp) search[value]: Global search value. To be applied to all columns which are searchable search[regex]: true if search[value] is a regex Sorting: - order[i][column] / order[i][dir] index of the columns to order and sort direction (asc/desc) Columns: - columns[i][data]: Column's data source, as defined by columns.data. - columns[i][name]: Column's name, as defined by columns.name. - columns[i][searchable]: Flag to indicate if this column is searchable (true). - columns[i][orderable]: Flag to indicate if this column is orderable (true). - columns[i][search][value]: Search value to apply to this specific column. - columns[i][search][regex]: Flag to indicate if the search term for this column is a regex. Response data: - draw - recordsTotal: total records, before filtering (i.e. total number of records in the database) - recordsFiltered: Total records, after filtering (i.e. total number of records after filtering has been applied - not just the number of records being returned for this page of data). - data: The data to be displayed in the table. an array of data source objects, one for each row, which will be used by DataTables. - error (optional): Error message if an error occurs Not included if there is no error. """ # Manage request parameters ... logger.info("request data for table: %s", request.params.get('object_type')) # Because of specific datatables parameters name (eg. columns[0] ...) # ... some parameters have been json.stringify on client side ! params = {} for key in request.params.keys(): if key == 'columns' or key == 'order' or key == 'search': params[key] = json.loads(request.params.get(key)) else: params[key] = request.params.get(key) # params now contains 'valid' query parameters as we should have found them ... logger.debug("table request parameters: %s", params) parameters = {} # Manage page number ... # start is the first requested row and we must transform to a page count ... first_row = int(params.get('start', '0')) # length is the number of requested rows rows_count = int(params.get('length', '25')) parameters['page'] = (first_row // rows_count) + 1 parameters['max_results'] = rows_count logger.info("get %d rows from row #%d -> page: %d", rows_count, first_row, parameters['page']) # Columns ordering # order:[{"column":2,"dir":"desc"}] if 'order' in params and 'columns' in params and params['order']: sorted_columns = [] for order in params['order']: idx = int(order['column']) if params['columns'][idx] and params['columns'][idx]['data']: logger.debug("sort by column %d (%s), order: %s ", idx, params['columns'][idx]['data'], order['dir']) if order['dir'] == 'desc': sorted_columns.append('-' + params['columns'][idx]['data']) else: sorted_columns.append(params['columns'][idx]['data']) if sorted_columns: parameters['sort'] = ','.join(sorted_columns) logger.info("backend order request parameters: %s", parameters) # Individual column search parameter searched_columns = [] if 'columns' in params and params['columns']: for column in params['columns']: if 'searchable' not in column or 'search' not in column: # pragma: no cover continue if 'value' not in column[ 'search'] or not column['search']['value']: continue logger.debug("search column '%s' for '%s'", column['data'], column['search']['value']) for field in self.table_columns: if field['data'] != column['data']: continue # Some specific types... if field['type'] == 'boolean': searched_columns.append({ column['data']: column['search']['value'] == 'true' }) elif field['type'] == 'integer': searched_columns.append( {column['data']: int(column['search']['value'])}) elif field['format'] == 'select': values = column['search']['value'].split(',') if len(values) > 1: searched_columns.append( {column['data']: { "$in": values }}) else: searched_columns.append( {column['data']: values[0]}) # ... the other fields :) else: # Do not care about 'smart' and 'caseInsensitive' boolean parameters ... if column['search']['regex']: searched_columns.append({ column['data']: { "$regex": ".*" + column['search']['value'] + ".*" } }) else: searched_columns.append( {column['data']: column['search']['value']}) break logger.info("backend search columns parameters: %s", searched_columns) # Global search parameter # search:{"value":"test","regex":false} searched_global = [] # pylint: disable=too-many-nested-blocks # Will be too complex else ... if 'search' in params and 'columns' in params and params['search']: if 'value' in params['search'] and params['search']['value']: logger.debug("search requested, value: %s ", params['search']['value']) for column in params['columns']: if 'searchable' in column and column['searchable']: logger.debug("search global '%s' for '%s'", column['data'], params['search']['value']) if 'regex' in params['search']: if params['search']['regex']: searched_global.append({ column['data']: { "$regex": ".*" + params['search']['value'] + ".*" } }) else: searched_global.append({ column['data']: params['search']['value'] }) logger.info("backend search global parameters: %s", searched_global) if searched_columns and searched_global: parameters['where'] = { "$and": [{ "$and": searched_columns }, { "$or": searched_global }] } if searched_columns: parameters['where'] = {"$and": searched_columns} if searched_global: parameters['where'] = {"$or": searched_global} # Embed linked resources parameters['embedded'] = {} for field in self.table_columns: if field['type'] == 'objectid' and field['format'] != 'objectid': parameters['embedded'].update({field['data']: 1}) if parameters['embedded']: logger.info("backend embedded parameters: %s", parameters['embedded']) # Update global table records count, require total count from backend self.records_total = self.backend.count(self.object_type) # Request objects from the backend ... logger.debug("table data get parameters: %s", parameters) items = self.backend.get(self.object_type, params=parameters) if not items: # Empty response return json.dumps({ # draw is the request number ... "draw": int(params.get('draw', '0')), "recordsTotal": 0, "recordsFiltered": 0, "data": [] }) # Create an object ... object_class = [ kc for kc in self.datamgr.known_classes if kc.get_type() == self.object_type ][0] bo_object = object_class() # Update inner properties self.id_property = '_id' if hasattr(bo_object.__class__, 'id_property'): self.id_property = bo_object.__class__.id_property self.name_property = 'name' if hasattr(bo_object.__class__, 'name_property'): self.name_property = bo_object.__class__.name_property self.status_property = 'status' if hasattr(bo_object.__class__, 'status_property'): self.status_property = bo_object.__class__.status_property # Change item content ... for item in items: bo_object = object_class(item) logger.debug("livestate object item: %s", bo_object) for key in item.keys(): for field in self.table_columns: if field['data'] != key: continue if field['data'] == self.name_property: item[key] = bo_object.get_html_link( prefix=request.params.get('links')) if field['data'] == self.status_property: item[key] = bo_object.get_html_state() if field['data'] == "business_impact": item[key] = Helper.get_html_business_impact( bo_object.business_impact) # Specific fields type if field['type'] == 'datetime' or field['format'] == 'date': item[key] = bo_object.get_date(item[key]) if field['type'] == 'boolean': item[key] = Helper.get_on_off(item[key]) if field['type'] == 'list': item[key] = Helper.get_html_item_list( bo_object.id, key, getattr(bo_object, key), title=field['title']) if field['type'] == 'objectid' and \ key in parameters['embedded'] and item[key]: related_object_class = [ kc for kc in self.datamgr.known_classes if kc.get_type() == field['format'] ][0] linked_object = related_object_class(item[key]) item[key] = linked_object.get_html_link( prefix=request.params.get('links')) break # Very specific fields... if self.responsive: item['#'] = '' # Total number of filtered records self.records_filtered = self.records_total if 'where' in parameters and parameters['where'] != {}: logger.debug("update filtered records: %s", parameters['where']) self.records_filtered = len(items) logger.info("filtered records: %d out of total: %d", self.records_filtered, self.records_total) # Prepare response rsp = { # draw is the request number ... "draw": int(params.get('draw', '0')), "recordsTotal": self.records_total, "recordsFiltered": self.records_filtered, "data": items } return json.dumps(rsp)
def show_minemap(self): # Yes, but that's how it is made, and it suits ;) # pylint: disable=too-many-locals """Get the hosts / services list to build a minemap""" user = request.environ['beaker.session']['current_user'] datamgr = request.app.datamgr # Fetch elements per page preference for user, default is 25 elts_per_page = datamgr.get_user_preferences(user, 'elts_per_page', 25) # Minemap search engine is based upon host table plugin = self.webui.find_plugin('host') # Pagination and search start = int(request.params.get('start', '0')) count = int(request.params.get('count', elts_per_page)) if count < 1: count = elts_per_page search = Helper.decode_search(request.params.get('search', ''), plugin.table) logger.info("Decoded search pattern: %s", search) for key, pattern in search.items(): logger.info("global search pattern '%s' / '%s'", key, pattern) search = { 'page': (start // count) + 1, 'max_results': count, 'where': search, 'sort': '-_overall_state_id' } # Get elements from the data manager # Do not include the embedded fields to improve the loading time... hosts = datamgr.get_hosts(search, embedded=False) minemap = [] columns = [] for host in hosts: # Each item contains the total number of records matching the search filter total = host['_total'] minemap_row = {'host_check': host} # Get all host services # Do not include the embedded fields to improve the loading time... services = datamgr.get_services( search={ 'where': {'host': host.id}, 'sort': '-_overall_state_id' }, all_elements=True, embedded=False ) for service in services: columns.append(service.name) minemap_row.update({service.name: service}) minemap.append(minemap_row) # Sort column names by most frequent ... count_columns = collections.Counter(columns) columns = [c for c, dummy in count_columns.most_common()] return { 'search_engine': self.search_engine, 'search_filters': self.search_filters, 'params': self.plugin_parameters, 'minemap': minemap, 'columns': columns, 'pagination': self.webui.helper.get_pagination_control('/minemap', total, start, count), 'title': request.query.get('title', _('Hosts minemap')) }