def replace_tile(): dashboard_id = request.get_json()['dashboard_id'] tile_id = request.get_json()['tile_id'] report_id = request.get_json()['report_id'] tile_config = request.get_json()['tile_config'] for_layout_id = request.get_json()['for_layout_id'] check_access(lambda: auth.access_dashboard(dashboard_id)) check_access(lambda: auth.access_report_instances(report_id)) tile = tiles.Tile.select(dashboard_id, tile_id) if not tile: return error(message='No tile found, please refresh the page') new_tile = tile.insert_similar(tile_config) repl_res = layouts.replace_tiles({tile: new_tile}, for_layout_id) if not repl_res: return error('A newer version of the dashboard is available, please refresh the page') log.info('Replaced tile %s with tile %s', tile_id, new_tile.tile_id) dataseries.update_default_options(new_tile) tpcreated_replacement = [[t1.tile_id, t2.tile_id] for (t1, t2) in repl_res.tile_replacement.items() if t2 != new_tile] return success(result=dict(new_tile=new_tile, new_layout_id=repl_res.new_layout.layout_id, tpcreated_replacement=tpcreated_replacement))
def delete(self): """Delete the report. The method detaches and deletes tiles that display the report. Report instances are NOT deleted by the method - the method :meth:`delete_multiple_instances` must be called before :meth:`delete` to achieve it. """ from mqe import dashboards from mqe import layouts owner_dashboards = dashboards.OwnerDashboards(self.owner_id) for dashboard in owner_dashboards.dashboards: layout = layouts.Layout.select(self.owner_id, dashboard.dashboard_id) if not layout: continue tiles_to_detach = [ tile for tile in layout.tile_dict if tile.report_id == self.report_id ] if tiles_to_detach: res = layouts.replace_tiles( {tile: None for tile in tiles_to_detach}, None) if not res: return False c.dao.ReportDAO.delete(self.owner_id, self.report_id) return True
def test_promote_other_master_tile(self): rd, master_tile = self.test_handle_tpcreator() tpcreated_ids = tpcreator.select_tpcreated_tile_ids(master_tile) tpcreated_tiles = Tile.select_multi(rd.dashboard_id, tpcreated_ids).values() #print 'tpcreated', [t.tags for t in tpcreated_tiles] new_master_repl = util.first(t for t in tpcreated_tiles if t.tile_options['tile_title'] == 'tpc0') new_master_tile = tpcreator.make_master_from_tpcreated( master_tile, new_master_repl) self.assertEqual('tpc0', new_master_tile.tile_options['tile_title']) self.assertTrue( layouts.replace_tiles({master_tile: new_master_tile}, None)) self.assertFalse(tpcreator.select_tpcreated_tile_ids(master_tile)) new_tpcreated_ids = tpcreator.select_tpcreated_tile_ids( new_master_tile) new_tpcreated_tiles = Tile.select_multi(rd.dashboard_id, new_tpcreated_ids).values() self.assertTrue(new_tpcreated_tiles) self.assertEqual([['p1:20'], ['p1:30']], sorted([t.tags for t in new_tpcreated_tiles])) d = [ OrderedDict([('user_name', 'robert10'), ('is_active', True), ('points', 128)]) ] rd.report.process_input(json.dumps(d), tags=['p1:15', 'p2:34']) self.assertEqual( len(new_tpcreated_ids) + 1, len(tpcreator.select_tpcreated_tile_ids(new_master_tile))) latest_tile = Tile.select(rd.dashboard_id, _select_tile_ids(rd.dashboard_id)[-1]) self.assertEqual(['p1:15'], latest_tile.tags) self.assertEqual(['monique', 'robert3', 'robert10'], [ ss.params['filtering_expr']['args'][0] for ss in latest_tile.series_specs() ])
def test_replace_wrong(self): tiles = call(TilePlacingDetachingTest.test_place_multiple) tile_other = tiles[0].insert_similar(tiles[0].get_tile_config()) tile_other2 = tiles[0].insert_similar(tiles[0].get_tile_config()) res = layouts.replace_tiles({tile_other: tile_other2}, None) self.assertIsNone(res)
def main(): vars = tutorial.main() owner_id = vars['owner_id'] owner_dashboards = vars['owner_dashboards'] dashboard = vars['dashboard'] SECTION('Using tags for identifying entities') from mqe.reports import Report cpu_report = Report.select_or_insert(owner_id, 'cpu_usage') metrics = [ ('user', 42.3), ('system', 13.4), ('io', 8.4), ] cpu_report.process_input(json.dumps(metrics), tags=['ip:192.168.1.18']) SECTION('Creating a master tile') from mqe.dataseries import SeriesSpec from mqe.tiles import Tile from mqe.layouts import place_tile, Layout, replace_tiles dashboard = owner_dashboards.insert_dashboard('CPU') master_tile_config = { 'tw_type': 'Range', 'tags': ['ip:192.168.1.18'], 'series_spec_list': [ SeriesSpec(1, 0, { 'op': 'eq', 'args': ['user'] }), SeriesSpec(1, 0, { 'op': 'eq', 'args': ['system'] }), ], 'tile_options': { 'tile_title': 'CPU usage', 'tpcreator_uispec': [{ 'tag': 'ip:192.168.1.18', 'prefix': 'ip:' }] } } master_tile = Tile.insert(owner_id, cpu_report.report_id, dashboard.dashboard_id, master_tile_config) print place_tile(master_tile) SECTION('Creating tiles from a master tile') metrics = json.dumps(metrics) cpu_report.process_input(metrics, tags=['ip:192.168.1.30']) cpu_report.process_input(metrics, tags=['ip:192.168.2.51']) cpu_report.process_input(metrics, tags=['ip:192.168.2.51']) layout = Layout.select(owner_id, dashboard.dashboard_id) for tile in layout.tile_dict: print tile.tags SECTION('Synchronizing options of tpcreated tiles') new_master_tile_config = { 'tw_type': 'Range', 'tags': ['ip:192.168.1.18'], 'series_spec_list': [ SeriesSpec(1, 0, { 'op': 'eq', 'args': ['user'] }), SeriesSpec(1, 0, { 'op': 'eq', 'args': ['system'] }), SeriesSpec(1, 0, { 'op': 'eq', 'args': ['io'] }), ], 'tile_options': { 'tile_title': 'CPU usage', 'tpcreator_uispec': [{ 'tag': 'ip:192.168.1.18', 'prefix': 'ip:' }] } } new_master_tile = Tile.insert(owner_id, cpu_report.report_id, dashboard.dashboard_id, new_master_tile_config) assert replace_tiles({master_tile: new_master_tile}, for_layout_id=None) layout = Layout.select(owner_id, dashboard.dashboard_id) for tile in layout.tile_dict: print len(tile.get_tile_data()['series_data']) SECTION('Expiring tiles and promoting new masters') from mqe.tpcreator import make_master_from_tpcreated old_master = [tile for tile in layout.tile_dict if tile.is_master_tile()][0] new_chosen_master = [ tile for tile in layout.tile_dict if tile.tags == ['ip:192.168.2.51'] ][0] assert not new_chosen_master.is_master_tile() new_master = make_master_from_tpcreated(old_master, new_chosen_master) res = replace_tiles({ old_master: new_master, new_chosen_master: None }, for_layout_id=None) print 'replaced %d tiles' % len(res.tile_replacement) layout = Layout.select(owner_id, dashboard.dashboard_id) tile = [ tile for tile in layout.tile_dict if tile.tags == ['ip:192.168.2.51'] ][0] print tile.is_master_tile()
def test_handle_tpcreator(self): rd = new_report_data('points') tile_config = { 'tw_type': 'Range', 'tags': ['p1:10'], 'series_spec_list': [ dataseries.SeriesSpec(2, 0, dict(op='eq', args=['monique'])), ], 'tile_options': { 'seconds_back': 600, 'tile_title': 'm0', 'sscs': dataseries.SeriesSpec(2, 0, dict(op='eq', args=['monique'])), } } tile_config['tile_options'][ 'tpcreator_uispec'] = tpcreator.suggested_tpcreator_uispec( tile_config['tags']) master_tile = Tile.insert(rd.owner_id, rd.report.report_id, rd.dashboard_id, tile_config) layouts.place_tile(master_tile) d = [ OrderedDict([('user_name', 'robert3'), ('is_active', True), ('points', 128)]) ] rd.report.process_input(json.dumps(d), tags=['p1:10']) master_tile = rd.only_tile_from_layout() self.assertEqual([], tpcreator.select_tpcreated_tile_ids(master_tile)) self.assertEqual([master_tile.tile_id], _select_tile_ids(rd.dashboard_id)) d = [ OrderedDict([('user_name', 'robert3'), ('is_active', True), ('points', 128)]) ] rd.report.process_input(json.dumps(d), tags=['p1:20']) self.assertEqual(2, len(_select_tile_ids(rd.dashboard_id))) tiles = Tile.select_multi(rd.dashboard_id, _select_tile_ids(rd.dashboard_id)).values() created_tile = util.first(tiles, key=lambda t: not t.is_master_tile()) self.assertEqual(['p1:20'], created_tile.tile_options['tags']) self.assertEqual(600, created_tile.tile_options['seconds_back']) self.assertEqual(tile_config['tile_options']['sscs'], created_tile.tile_options['sscs']) td = created_tile.get_tile_data() self.assertEqual('points (monique, robert3)', td['generated_tile_title']) self.assertEqual('[p1:20]', td['generated_tile_title_postfix']) d = [ OrderedDict([('user_name', 'robert3'), ('is_active', True), ('points', 128)]) ] rd.report.process_input(json.dumps(d), tags=['p1:30', 'p2:30']) self.assertEqual(3, len(_select_tile_ids(rd.dashboard_id))) del tile_config['tile_options']['tpcreator_uispec'] tile_config['tile_options']['tile_title'] = 'ot0' other_tile = Tile.insert(rd.owner_id, rd.report.report_id, rd.dashboard_id, tile_config) layouts.place_tile(other_tile) self.assertEqual(4, len(_select_tile_ids(rd.dashboard_id))) self.assertEqual(2, len(tpcreator.select_tpcreated_tile_ids(master_tile))) for i, tile_id in enumerate( tpcreator.select_tpcreated_tile_ids(master_tile)): tile = Tile.select(rd.dashboard_id, tile_id) tile_config = tile.get_tile_config() tile_config['tile_options']['tile_title'] = 'tpc%d' % i new_tile = tile.insert_similar(tile_config) layouts.replace_tiles({tile: new_tile}, None) return rd, Tile.select(master_tile.dashboard_id, master_tile.tile_id)
def main(): vars = tutorial.main() points_report = vars['points_report'] tile = vars['tile'] owner_id = vars['owner_id'] owner_dashboards = vars['owner_dashboards'] dashboard = vars['dashboard'] SECTION('Placing, detaching, replacing tiles') from mqe.layouts import Layout, place_tile layout = Layout.select(owner_id, dashboard.dashboard_id) new_tile = tile.copy(dashboard.dashboard_id) # we decided that new_tile should be put in the current layout res = place_tile(new_tile, for_layout_id=layout.layout_id) if not res: raise ValueError('Placing the tile unsuccessful') else: print 'New tile placed with visual_options', res.new_tiles[new_tile] from mqe.layouts import replace_tiles from mqe.tiles import Tile layout = Layout.select(owner_id, dashboard.dashboard_id) tile = Tile.select(dashboard.dashboard_id, layout.layout_dict.keys()[0]) tile_config = tile.get_tile_config() tile_config['tile_options']['tile_title'] = 'New Title' repl_tile = tile.insert_similar(tile_config) res = replace_tiles({tile: repl_tile}, for_layout_id=layout.layout_id) if not res: raise ValueError('Replacement of tiles unsuccessful') else: print 'Tiles replaced:', res.tile_replacement SECTION('Setting a custom layout') layout = Layout.select(owner_id, dashboard.dashboard_id) for visual_options in layout.layout_dict.values(): visual_options['height'] += 1 new_layout_id = layout.set() if not new_layout_id: raise ValueError('Updating the layout failed') SECTION('Layout mods') from mqe.layouts import replace_tiles_mod, place_tile_mod, apply_mods tile = repl_tile tile1 = tile.copy(dashboard.dashboard_id) tile2 = tile.copy(dashboard.dashboard_id) tile3 = tile.copy(dashboard.dashboard_id) layout = Layout.select(owner_id, dashboard.dashboard_id) mods = [ replace_tiles_mod({tile: tile1}), place_tile_mod(tile2), place_tile_mod(tile3), ] res = apply_mods(mods, owner_id, dashboard.dashboard_id, for_layout_id=layout.layout_id) if not res: raise ValueError('Operation failed') else: print res from mqe.layouts import LayoutModificationImpossible def detach_top_tiles_mod(): def do(layout_mod): tile_ids = [ tile_id for tile_id, visual_options in layout_mod.layout.layout_dict.items() if visual_options['y'] == 0 ] if not tile_ids: raise LayoutModificationImpossible() for tile_id in tile_ids: del layout_mod.layout.layout_dict[tile_id] layout_mod.detached_tiles.append( Tile.select(layout_mod.layout.dashboard_id, tile_id)) return do res = apply_mods([detach_top_tiles_mod()], owner_id, dashboard.dashboard_id, None) if not res: raise ValueError('Operation failed') else: print res def detach_top_tiles_using_replacement_mod(): def do(layout_mod): tiles = [ tile for tile, visual_options in layout_mod.layout.tile_dict.items() if visual_options['y'] == 0 ] if not tiles: raise LayoutModificationImpossible() replace_tiles_mod({tile: None for tile in tiles})(layout_mod) return do
def expire_tiles_without_data(tile_list, max_seconds_without_data, for_layout_id): """Delete and detach tiles from a dashboard which don't have data for at least the specified time period. :param tile_list: a list of :class:`Tile` objects to expire, belonging to the same dashboard :param int max_seconds_without_data: the maximal age (specified in seconds) of the tile's data to avoid the expiration :param ~uuid.UUID for_layout_id: the version of the layout to perform the expiration :return: ``layout_id`` of the new layout if the operation was successful, ``None`` otherwise """ from mqe import layouts from mqe import tpcreator regular_tiles = [t for t in tile_list if not t.is_master_tile() \ and _should_expire_tile(t, max_seconds_without_data)] master_tiles = [t for t in tile_list if t.is_master_tile() \ and _should_expire_tile(t, max_seconds_without_data)] log.info( 'Will try to expire %s regular and %s master tiles out of %s passed', len(regular_tiles), len(master_tiles), len(tile_list)) layout_id = for_layout_id if regular_tiles: repl_res = layouts.replace_tiles( {tile: None for tile in regular_tiles}, layout_id) if repl_res: log.info('Successfully expired regular tiles') layout_id = repl_res.new_layout.layout_id else: log.warn('Failed to expire regular tiles') master_repl = {} for master_tile in master_tiles: tpcreated_tile_ids = tpcreator.select_tpcreated_tile_ids(master_tile, layout_id, sort=True) if not tpcreated_tile_ids: continue new_master_base = Tile.select(master_tile.dashboard_id, tpcreated_tile_ids[0]) if not new_master_base: log.warn('Could not select master tile replacement') continue new_master = tpcreator.make_master_from_tpcreated( master_tile, new_master_base) master_repl[master_tile] = new_master master_repl[new_master_base] = None if master_repl: repl_res = layouts.replace_tiles(master_repl, layout_id) if repl_res: log.info('Successfully expired master tiles') layout_id = repl_res.new_layout.layout_id else: log.warn('Failed to expire master tiles') if layout_id == for_layout_id: return None return layout_id
def main(): vars = tutorial.main() points_report = vars['points_report'] owner_id = vars['owner_id'] owner_dashboards = vars['owner_dashboards'] dashboard = vars['dashboard'] SECTION('Tile_config and tile_options') from mqe.dataseries import SeriesSpec from mqe.tiles import Tile tile_config = { 'series_spec_list': [ SeriesSpec(2, 0, { 'op': 'eq', 'args': ['john'] }), SeriesSpec(2, 0, { 'op': 'eq', 'args': ['monique'] }), ], 'tile_options': { 'tile_title': 'Points by user', } } tile = Tile.insert(owner_id, points_report.report_id, dashboard.dashboard_id, tile_config) pprint(tile.tile_options) tile_options2 = tile.tile_options.copy() tile_options2['owner_id'] = uuid.uuid4() tile2 = Tile.insert_with_tile_options(dashboard.dashboard_id, tile_options2) SECTION('Updating tile\'s config') from mqe.layouts import Layout, replace_tiles layout = Layout.select(owner_id, dashboard.dashboard_id) tile = layout.tile_dict.keys()[0] tile_config = tile.get_tile_config() tile_config['tile_options']['seconds_back'] = 3600 repl_tile = tile.insert_similar(tile_config) replace_tiles({tile: repl_tile}, for_layout_id=layout.layout_id) SECTION('Formatting tile data - tilewidgets and drawers') tile_config = { 'tw_type': 'Range', 'series_spec_list': [ SeriesSpec(2, 0, { 'op': 'eq', 'args': ['john'] }), SeriesSpec(2, 0, { 'op': 'eq', 'args': ['monique'] }), ], 'tile_options': { 'tile_title': 'Points by user', 'drawer_type': 'ChartRangeDrawer', 'colors': ['red', 'blue'], 'seconds_back': 3600, } } SECTION('Updating tile data') tile_data = tile.get_tile_data() input = """\ user_name is_active points john true 144 monique true 241 """ res = points_report.process_input(input) last_report_instance_id = tile_data['series_data'][0]['data_points'][ -1].rid new_tile_data = tile.get_new_tile_data(last_report_instance_id) SECTION('Managing colors') from mqe.dataseries import update_default_options tile_config = { 'series_spec_list': [ SeriesSpec(2, 0, { 'op': 'eq', 'args': ['john'] }), SeriesSpec(2, 0, { 'op': 'eq', 'args': ['monique'] }), ], 'tile_options': { 'colors': ['blue', 'red'], } } tile = Tile.insert(owner_id, points_report.report_id, dashboard.dashboard_id, tile_config) print tile.get_tile_data()['combined_colors'] update_default_options(tile) tile_config_2 = { 'series_spec_list': [ SeriesSpec(2, 0, { 'op': 'eq', 'args': ['monique'] }), ], } tile_2 = Tile.insert(owner_id, points_report.report_id, dashboard.dashboard_id, tile_config_2) print tile_2.get_tile_data()['combined_colors'] SECTION('Data series names') series_spec = SeriesSpec(2, 0, {'op': 'eq', 'args': ['monique']}) series_spec.set_name("monique's points") tile_config = {'series_spec_list': [series_spec]} tile = Tile.insert(owner_id, points_report.report_id, dashboard.dashboard_id, tile_config) print tile.get_tile_data()['series_data'][0]['name'] SECTION('Creating custom tilewidgets and drawers') from mqe.tilewidgets import register_drawer_class, Drawer @register_drawer_class class MaxNumberDrawer(Drawer): drawer_type = 'MaxNumberDrawer' def process_tile_data(self, tile_data): max_number = 0 for series_data in tile_data['series_data']: for point in series_data['data_points']: if int(point.value) > max_number: max_number = int(point.value) tile_data['max_number'] = max_number tile_config = { 'series_spec_list': [ SeriesSpec(2, 0, { 'op': 'eq', 'args': ['john'] }), SeriesSpec(2, 0, { 'op': 'eq', 'args': ['monique'] }), ], 'tile_options': { 'drawer_type': 'MaxNumberDrawer', } } tile = Tile.insert(owner_id, points_report.report_id, dashboard.dashboard_id, tile_config) print tile.get_tile_data()['max_number']